/*
   Copyright (c) 2012 Emitrom LLC. All rights reserved. 
   For licensing questions, please contact us at licensing@emitrom.com

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
 */

package com.emitrom.lienzo.client.core.shape.json;

import com.emitrom.lienzo.client.core.shape.Arc;
import com.emitrom.lienzo.client.core.shape.Arrow;
import com.emitrom.lienzo.client.core.shape.BezierCurve;
import com.emitrom.lienzo.client.core.shape.Bow;
import com.emitrom.lienzo.client.core.shape.Chord;
import com.emitrom.lienzo.client.core.shape.Circle;
import com.emitrom.lienzo.client.core.shape.Ellipse;
import com.emitrom.lienzo.client.core.shape.GridLayer;
import com.emitrom.lienzo.client.core.shape.Group;
import com.emitrom.lienzo.client.core.shape.IJSONSerializable;
import com.emitrom.lienzo.client.core.shape.IsoscelesTrapezoid;
import com.emitrom.lienzo.client.core.shape.Layer;
import com.emitrom.lienzo.client.core.shape.Line;
import com.emitrom.lienzo.client.core.shape.Movie;
import com.emitrom.lienzo.client.core.shape.Parallelogram;
import com.emitrom.lienzo.client.core.shape.Picture;
import com.emitrom.lienzo.client.core.shape.PolyLine;
import com.emitrom.lienzo.client.core.shape.Polygon;
import com.emitrom.lienzo.client.core.shape.QuadraticCurve;
import com.emitrom.lienzo.client.core.shape.Rectangle;
import com.emitrom.lienzo.client.core.shape.RegularPolygon;
import com.emitrom.lienzo.client.core.shape.Ring;
import com.emitrom.lienzo.client.core.shape.Scene;
import com.emitrom.lienzo.client.core.shape.Slice;
import com.emitrom.lienzo.client.core.shape.Spline;
import com.emitrom.lienzo.client.core.shape.Star;
import com.emitrom.lienzo.client.core.shape.Text;
import com.emitrom.lienzo.client.core.shape.Triangle;
import com.emitrom.lienzo.client.core.shape.Viewport;
import com.emitrom.lienzo.client.core.types.FastStringMap;
import com.emitrom.lienzo.client.core.util.Console;

/**
 * This class is a central repository for all {@link IJSONSerializable} factories.  
 * If you create a new class and you would like to be able to serialize / deserialize
 * it, you will need to register it here using {@link #registerFactory(String, IFactory)}.
 */
public final class FactoryRegistry
{
    private static final FactoryRegistry     s_instance   = createRegistry();

    private final FastStringMap<IFactory<?>> m_factoryMap = new FastStringMap<IFactory<?>>();

    private FactoryRegistry()
    {
        // There can be only one. Therefore it's a Singleton and can't be sub-classed, all methods and variables can be final
    }

    /**
     * Adds a {@link IFactory} to this registry.
     * <p>
     * Use this when you're creating your own class and you want to be able to deserialize
     * your node from a JSON string via {@link JSONDeserializer#fromString(String)}.
     * 
     * @param factory IFactory
     * @return this FactoryRegistry
     */
    public final FactoryRegistry registerFactory(IFactory<?> factory)
    {
        String type = factory.getTypeName();

        if (null == m_factoryMap.get(type))
        {
            m_factoryMap.put(type, factory);
        }
        else
        {
            Console.log("WARNING: IFactory for " + type + " was already registered. Try prefixing your type names e.g. with 'foo_' to avoid conflicts with the built-in Lienzo nodes.");
        }
        return this;
    }

    /**
     * Returns the {@link IFactory} for the specified type name.
     * 
     * @param typeName
     * @return IFactory
     */
    public final IFactory<?> getFactory(String typeName)
    {
        return m_factoryMap.get(typeName);
    }

    /**
     * Returns the singleton FactoryRegistry.
     * @return FactoryRegistry
     */
    public static final FactoryRegistry getInstance()
    {
        return s_instance;
    }

    private static final FactoryRegistry createRegistry()
    {
        // Make sure we register the built-in Lienzo types first,
        // so that toolkit users can't override them.

        FactoryRegistry registry = new FactoryRegistry();

        // Primitive types

        registry.registerFactory(new Arc.ArcFactory());

        registry.registerFactory(new Arrow.ArrowFactory());

        registry.registerFactory(new BezierCurve.BezierCurveFactory());

        registry.registerFactory(new Circle.CircleFactory());

        registry.registerFactory(new Ellipse.EllipseFactory());

        registry.registerFactory(new Line.LineFactory());

        registry.registerFactory(new Movie.MovieFactory());

        registry.registerFactory(new Parallelogram.ParallelogramFactory());

        registry.registerFactory(new Picture.PictureFactory());

        registry.registerFactory(new Polygon.PolygonFactory());

        registry.registerFactory(new PolyLine.PolyLineFactory());

        registry.registerFactory(new QuadraticCurve.QuadraticCurveFactory());

        registry.registerFactory(new Rectangle.RectangleFactory());

        registry.registerFactory(new RegularPolygon.RegularPolygonFactory());

        registry.registerFactory(new Slice.SliceFactory());

        registry.registerFactory(new Star.StarFactory());

        registry.registerFactory(new Text.TextFactory());

        registry.registerFactory(new Triangle.TriangleFactory());

        registry.registerFactory(new Spline.SplineFactory());

        registry.registerFactory(new Bow.BowFactory());

        registry.registerFactory(new Ring.RingFactory());

        registry.registerFactory(new Chord.ChordFactory());

        registry.registerFactory(new IsoscelesTrapezoid.IsoscelesTrapezoidFactory());

        // Container Types

        registry.registerFactory(new Group.GroupFactory());

        registry.registerFactory(new Layer.LayerFactory());

        registry.registerFactory(new GridLayer.GridLayerFactory());

        registry.registerFactory(new Scene.SceneFactory());

        registry.registerFactory(new Viewport.ViewportFactory());

        return registry;
    }
}
