/**
 *  Licensed under GPL. For more information, see
 *    http://jaxodraw.sourceforge.net/license.html
 *  or the LICENSE file in the jaxodraw distribution.
 */
package net.sf.jaxodraw.object;

import java.awt.Point;

import java.util.HashMap;
import java.util.Map;
import java.util.NoSuchElementException;

import net.sf.jaxodraw.util.JaxoConstants;


/**
 * Factory class to create instances of JaxoObjects.
 *
 * @since 2.0
 */
public final class JaxoObjectFactory {

    /** Maps object id's from JaxoConstants to class names. */
    private static final Map<Integer, String> OBJECT_KEYS;

    static {
        OBJECT_KEYS = new HashMap<Integer, String>(42); // # of elements * default load factor

        OBJECT_KEYS.put(JaxoConstants.FLINE, "net.sf.jaxodraw.object.line.JaxoFLine");
        OBJECT_KEYS.put(JaxoConstants.FLOOP, "net.sf.jaxodraw.object.loop.JaxoFLoop");
        OBJECT_KEYS.put(JaxoConstants.FARC, "net.sf.jaxodraw.object.arc.JaxoFArc");
        OBJECT_KEYS.put(JaxoConstants.SLINE, "net.sf.jaxodraw.object.line.JaxoSLine");
        OBJECT_KEYS.put(JaxoConstants.SLOOP, "net.sf.jaxodraw.object.loop.JaxoSLoop");
        OBJECT_KEYS.put(JaxoConstants.SARC, "net.sf.jaxodraw.object.arc.JaxoSArc");
        OBJECT_KEYS.put(JaxoConstants.GLINE, "net.sf.jaxodraw.object.line.JaxoGLine");
        OBJECT_KEYS.put(JaxoConstants.GLOOP, "net.sf.jaxodraw.object.loop.JaxoGLoop");
        OBJECT_KEYS.put(JaxoConstants.GARC, "net.sf.jaxodraw.object.arc.JaxoGArc");
        OBJECT_KEYS.put(JaxoConstants.PLINE, "net.sf.jaxodraw.object.line.JaxoPLine");
        OBJECT_KEYS.put(JaxoConstants.PLOOP, "net.sf.jaxodraw.object.loop.JaxoPLoop");
        OBJECT_KEYS.put(JaxoConstants.PARC, "net.sf.jaxodraw.object.arc.JaxoPArc");
        OBJECT_KEYS.put(JaxoConstants.GLLINE, "net.sf.jaxodraw.object.line.JaxoGlLine");
        OBJECT_KEYS.put(JaxoConstants.GLLOOP, "net.sf.jaxodraw.object.loop.JaxoGlLoop");
        OBJECT_KEYS.put(JaxoConstants.GLARC, "net.sf.jaxodraw.object.arc.JaxoGlArc");
        OBJECT_KEYS.put(JaxoConstants.FBEZ, "net.sf.jaxodraw.object.bezier.JaxoFBezier");
        OBJECT_KEYS.put(JaxoConstants.PBEZ, "net.sf.jaxodraw.object.bezier.JaxoPBezier");
        OBJECT_KEYS.put(JaxoConstants.GLBEZ, "net.sf.jaxodraw.object.bezier.JaxoGlBezier");
        OBJECT_KEYS.put(JaxoConstants.SBEZ, "net.sf.jaxodraw.object.bezier.JaxoSBezier");
        OBJECT_KEYS.put(JaxoConstants.GBEZ, "net.sf.jaxodraw.object.bezier.JaxoGBezier");
        OBJECT_KEYS.put(JaxoConstants.BOX, "net.sf.jaxodraw.object.fill.JaxoBox");
        OBJECT_KEYS.put(JaxoConstants.BLOB, "net.sf.jaxodraw.object.fill.JaxoBlob");
        OBJECT_KEYS.put(JaxoConstants.ZIGZAG, "net.sf.jaxodraw.object.line.JaxoZigZagLine");
        OBJECT_KEYS.put(JaxoConstants.TEXT, "net.sf.jaxodraw.object.text.JaxoPSText");
        OBJECT_KEYS.put(JaxoConstants.LATEX, "net.sf.jaxodraw.object.text.JaxoLatexText");
        OBJECT_KEYS.put(JaxoConstants.VERTEXT1, "net.sf.jaxodraw.object.vertex.JaxoVertexT1");
        OBJECT_KEYS.put(JaxoConstants.VERTEXT2, "net.sf.jaxodraw.object.vertex.JaxoVertexT2");
        OBJECT_KEYS.put(JaxoConstants.VERTEXT3, "net.sf.jaxodraw.object.vertex.JaxoVertexT3");
        OBJECT_KEYS.put(JaxoConstants.VERTEXT4, "net.sf.jaxodraw.object.vertex.JaxoVertexT4");
        OBJECT_KEYS.put(JaxoConstants.VERTEXT5, "net.sf.jaxodraw.object.vertex.JaxoVertexT5");
        OBJECT_KEYS.put(JaxoConstants.VERTEXT6, "net.sf.jaxodraw.object.vertex.JaxoVertexT6");
    }

    // Don't instantiate
    private JaxoObjectFactory() {
        // empty on purpose
    }

    /**
     * Returns a new instance of a JaxoObject.
     * The object is only instantiated, not initialized.
     * A NoSuchElementException is thrown if type is not a known object.
     *
     * @param type The type of object to be created,
     *      as defined in {@link JaxoConstants JaxoConstants}).
     * @return A new instance of a JaxoObject. Always non-null.
     */
    public static JaxoObject newObject(final int type) {
        final String objectId = OBJECT_KEYS.get(type);

        if (objectId == null) {
            throw new NoSuchElementException("No object for this type!");
        }

        return getObject(objectId);
    }

    /**
     * Returns a new instance of a JaxoObject.
     * The object is only instantiated, not initialized.
     * A NoSuchElementException is thrown if objectId is not a known object.
     *
     * @param objectId The class name of the object to be instantiated.
     * @return A new instance of a JaxoObject. Always non-null.
     */
    public static JaxoObject newObject(final String objectId) {

        if (!OBJECT_KEYS.containsValue(objectId)) {
            throw new NoSuchElementException("No object for this id!");
        }

        return getObject(objectId);
    }

    /**
     * Creates a new JaxoObject, initialized with values taken from the
     * {@link net.sf.jaxodraw.util.JaxoPrefs Preferences}.
     * A NoSuchElementException is thrown if type is not a known object.
     *
     * @param type The type of object to be created
     *      (defined in {@link JaxoConstants JaxoConstants}).
     * @param location the points of the object.
     *      The size of the array has to be equal to the point count of the object.
     * @return A new JaxoObject. Always non-null.
     */
    public static JaxoObject newObject(final int type, final Point[] location) {

        final JaxoObject newob = newObject(type);

        newob.setPreferences();
        newob.setPoints(location);

        return newob;
    }

    /**
     * Adds a new type of JaxoObject to the list of know objects.
     * No test is done here if the object can be instantiated.
     *
     * @param type The type of object to be created, has to be different from any
     *      of the pre-defined types in {@link JaxoConstants JaxoConstants}).
     * @param  objectId The class name of the object of the given type.
     *      This is used to instantiate the object.
     */
    public static void addObjectType(final int type, final String objectId) {
        if (objectId == null) {
            throw new IllegalArgumentException("objectId can't be null!");
        }

        if (OBJECT_KEYS.containsValue(objectId)) {
            throw new IllegalArgumentException("duplicate objectId!");
        }

        if (OBJECT_KEYS.containsKey(type)) {
            throw new IllegalArgumentException("type already in use!");
        }

        OBJECT_KEYS.put(type, objectId);
    }

      //
     // private methods
    //

    private static JaxoObject getObject(final String objectId) {
        try {
            return ((JaxoObject) Class.forName(objectId).newInstance());
        } catch (ClassNotFoundException e) {
            throw new IllegalArgumentException("No such object: " + objectId, e);
        } catch (IllegalAccessException e) {
            throw new IllegalArgumentException("Cannot access: " + objectId, e);
        } catch (InstantiationException e) {
            throw new IllegalArgumentException("Cannot instantiate: " + objectId, e);
        }
    }
}
