/**
 *  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.graph;

import java.awt.Rectangle;

import java.io.Serializable;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import net.sf.jaxodraw.object.JaxoList;
import net.sf.jaxodraw.object.JaxoObject;
import net.sf.jaxodraw.object.JaxoObjectList;


/**
 * A JaxoSaveGraph is the skeleton of a JaxoGraph: it contains just a
 * list of JaxoObjects, a list of latex packages and a description of the graph.
 * The default list of latex packages currently contains
 * "axodraw4j", "pstricks" and "color".
 *
 * @since 2.0
 */
public class JaxoSaveGraph implements Serializable {
    /** The default initial size for array lists. */
    public static final int LIST_INIT_SIZE = 5;
    private static final long serialVersionUID = 2L;
    private final JaxoList<JaxoObject> objectList;
    private String description;
    private final List<String> packageList;
    private String jaxoDrawVersion;

    /** Creates a new JaxoSaveGraph with empty list of JaxoObjects,
     * empty description and default package list.
     */
    public JaxoSaveGraph() {
        this(new JaxoObjectList<JaxoObject>(LIST_INIT_SIZE), "",
                new ArrayList<String>(LIST_INIT_SIZE));
    }

    /** Creates a new JaxoSaveGraph with the given list of JaxoObjects
     * empty description and default package list.
     * @param list The new list of objects. Not null.
     */
    public JaxoSaveGraph(final JaxoList<JaxoObject> list) {
        this(list, "", new ArrayList<String>(LIST_INIT_SIZE));
    }

    /** Creates a new JaxoSaveGraph with the given list of objects,
     * description and default package list.
     * @param list List of JaxoObjects. Not null.
     * @param describe String which decsribes this JaxoSaveGraph. May be null.
     */
    public JaxoSaveGraph(final JaxoList<JaxoObject> list, final String describe) {
        this(list, describe, new ArrayList<String>(LIST_INIT_SIZE));
    }

    /** Creates a new JaxoSaveGraph with the given list of objects,
     * description and package list. If the default latex packages
     * are not contained in the package list, they are added.
     * @param list JaxoList that holds the JaxoObjects. Not null.
     * @param describe String which describes this JaxoSaveGraph. May be null.
     * @param packages List that holds the latex packages. Not null.
     */
    public JaxoSaveGraph(final JaxoList<JaxoObject> list, final String describe, final List<String> packages) {
        this.objectList = new JaxoObjectList<JaxoObject>(list);
        this.description = describe;
        this.packageList = new ArrayList<String>(packages);
        addDefaultPackages();
    }

    // methods to get/set the variables

    /** Returns the JaxoList holding the list of JaxoObjects.
     * @return The list of JaxoObjects. Not null.
     */
    public final JaxoList<JaxoObject> getObjectList() {
        return objectList;
    }

    /** Sets the list of JaxoObjects of this JaxoSaveGraph to list.
     * @param list The list holding the new list of JaxoObjects. Not null.
     */
    public final void setObjectList(final JaxoList<JaxoObject> list) {
        this.objectList.clear();
        this.objectList.addAll(list);
    }

    /** Returns the List of Latex packages.
    * @return The List of Latex packages. Not null.
    */
    public final List<String> getPackageList() {
        return packageList;
    }

    /** Sets the list of Latex packages of this JaxoSaveGraph.
     * If the default latex packages
     * are not contained in the given list, they are added.
     * @param list The list of Latex packages. Not null.
     */
    public final void setPackageList(final List<String> list) {
        this.packageList.clear();
        this.packageList.addAll(list);
        addDefaultPackages();
    }

    /** Sets the description text of this JaxoSaveGraph.
     * @param describe The description of this JaxoSaveGraph. May be null.
     */
    public final void setDescription(final String describe) {
        this.description = describe;
    }

    /** Returns the description text of this JaxoSaveGraph.
     * @return The decription of this JaxoSaveGraph. May be null.
     */
    public final String getDescription() {
        return description;
    }

    /**
     * Returns the JaxoDraw version that this JaxoSaveGraph was created with.
     *
     * @return the JaxoDraw version.
     *      May be null for graphs that were created with JaxoDraw versions earlier than 2.0.
     */
    public final String getJaxoDrawVersion() {
        return jaxoDrawVersion;
    }

    /**
     * Set the JaxoDraw version that this JaxoSaveGraph was created with.
     *
     * @param version the JaxoDraw version. Not null.
     */
    public final void setJaxoDrawVersion(final String version) {
        this.jaxoDrawVersion = version;
    }

    /** Returns the JaxoObject at position i of the object list.
     * @param i The index of the JaxoObject to be returned.
     * @return The JaxoObject at position i of the object list.
     *      May be null if the index is out of range.
     */
    public final JaxoObject listElementAt(final int i) {
        if ((i >= 0) && (i < listSize())) {
            return objectList.get(i);
        } else {
            return null;
        }
    }

    /** Returns the size of the current object list,
     * i.e., the number of objects it contains.
     * @return The number of objects currently in the object list.
     */
    public final int listSize() {
        return objectList.size();
    }

    // methods that modify the objectList

    /** Puts the specified JaxoObject into the foreground, i.e.,
     * to the last position in the object list.
     * @param object The JaxoObject to be put into the foreground.
     * @return True if the object was already in the foreground, i.e.,
     * the JaxoSaveGraph has not been modified.
     */
    public final boolean foreground(final JaxoObject object) {
        return objectList.toEnd(object);
    }

    /** Puts the specified JaxoObject into the background, i.e.,
     * to the first position in the object list.
     * @param object The JaxoObject to be put into the background.
     * @return True if the JaxoObject was already in the background, i.e.,
     * the JaxoSaveGraph has not been modified.
     */
    public final boolean background(final JaxoObject object) {
        return objectList.toFront(object);
    }

    /** Move the object at 'index' to 'newIndex', leaving all
     * other elements in order.
     * @param index The index of the object to move.
     * @param newIndex The new index of the object.
     */
    public final void move(final int index, final int newIndex) {
        objectList.move(index, newIndex);
    }

    /** Removes the specified JaxoObject from the list of objects.
     * @param object The JaxoObject to be removed from the list.
     * @return True if the SaveGraph contained the specified object,
     * ie, the SaveGraph was modified.
     */
    public final boolean delete(final JaxoObject object) {
        return objectList.remove(object);
    }

    /** Removes all marked JaxoObjects from the list of objects.
     * @return True if the SaveGraph was modified.
     */
    public final boolean deleteMarkedObjects() {
        return objectList.removeMarkedObjects();
    }

    /** Removes the specified objects from the list of objects.
     * @param objects Collection of JaxoObjects to be removed from the list.
     * @return True if the SaveGraph was modified.
     */
    public final boolean deleteAll(final Collection<JaxoObject> objects) {
        return objectList.removeAll(objects);
    }

    /** Adds the specified JaxoObject to the list of objects by appending it.
     * @param object The JaxoObject to be added to the list.
     *      May be null in which case no action is taken.
     * @return True if the SaveGraph was modified, ie object was not null.
     */
    public final boolean addObject(final JaxoObject object) {
        if (object != null) {
            return objectList.add(object);
        }
        return false;
    }

    /** Inserts a JaxoObject into the list of objects at a given position.
     * @param index The position where the object has to be inserted.
     * @param object The JaxoObject to be added to the list.
     *      May be null in which case no action is taken.
     * @return True if the SaveGraph was modified, ie object was not null.
     */
    public final boolean addObject(final int index, final JaxoObject object) {
        if (object != null) {
            objectList.add(index, object);
            return true;
        }
        return false;
    }

    /** Adds a latex package to the list of packages by appending it.
     * @param pack The name of the latex package to be added to the list. Not null.
     */
    public final void addPackage(final String pack) {
        packageList.add(pack);
    }

    /** Removes all JaxoObjects from the list of objects.
     * @return True if the list was already empty before, i.e.,
     * the JaxoSaveGraph has not been modified.
     */
    public final boolean clear() {
        final boolean listIsEmpty = objectList.isEmpty();

        if (!listIsEmpty) {
            objectList.clear();
        }

        return listIsEmpty;
    }

    /** Removes all JaxoObjects from the list of objects and sets the
     * description to a string of size zero.
     * @return True if the list of objects and the string holding the
     * description were already empty, i.e.,
     * the JaxoSaveGraph has not been modified.
     */
    public final boolean clearAll() {
        boolean graphIsEmpty = true;

        if (!objectList.isEmpty() || (description != null && description.length() != 0)
                || (packageList.size() != 3)) {
            packageList.clear();
            addDefaultPackages();
            objectList.clear();
            setDescription("");
            graphIsEmpty = false;
        }

        return graphIsEmpty;
    }

    // other methods

    /** Returns the smallest bounding box that contains all the JaxoObjects
     * of this JaxoSaveGraph.
     * @return A Rectangle holding the bounds of the current JaxoSaveGraph,
     * or null if the graph is empty (ie contains no objects).
     */
    public final Rectangle getBounds() {
        return objectList.getBounds();
    }

    /** Returns the smallest bounding box that contains all the JaxoObjects
     * of this JaxoSaveGraph except the one given in 'except'.
     * @param except objects to exclude.
     * @return A Rectangle holding the bounds of the current JaxoSaveGraph,
     * or null if the graph is (effectively) empty (ie contains no objects).
     */
    public final Rectangle getBoundsExcept(final Collection<JaxoObject> except) {
        return objectList.getBoundsExcept(except);
    }

    /** Intersection of bounding box with given Rectangle.
     * This returns null if either any of the two rectangles are null,
     * or if they do not intersect.
     * @param inside The Rectangle to intersect with.
     * @return The intersection.
     * @see #getBounds()
     */
    public final Rectangle getBounds(final Rectangle inside) {
        return objectList.intersection(inside);
    }

    /**
     * Returns a copy of this JaxoSaveGraph.
     *
     * @return A copy of this JaxoSaveGraph.
     */
    public JaxoSaveGraph copyOf() {
        final JaxoList<JaxoObject> copyObjectList = objectList.copyOf();

        final int size = packageList.size();
        final List<String> copyPackageList = new ArrayList<String>(size);
        for (int i = 0; i < size; i++) {
            copyPackageList.add(packageList.get(i));
        }

        final JaxoSaveGraph copy =
                new JaxoSaveGraph(copyObjectList, description, copyPackageList);
        copy.setJaxoDrawVersion(jaxoDrawVersion);
        return copy;
    }

    /**
     * Determines if this JaxoSaveGraph is a copy of the specified one.
     *
     * @param graph the JaxoSaveGraph to compare with. Not null.
     * @return true if the two graphs are equal.
     */
    public boolean isCopy(final JaxoSaveGraph graph) {
        final boolean equalDesc = (description == null
                ? graph.getDescription() == null
                : description.equals(graph.getDescription()));
        final boolean equalVersion = (jaxoDrawVersion == null
                ? graph.getJaxoDrawVersion() == null
                : jaxoDrawVersion.equals(graph.getJaxoDrawVersion()));
        final boolean equalObjects = objectList.isCopy(graph.getObjectList());
        final boolean equalPackages = packageList.equals(graph.getPackageList());

        return (equalDesc && equalObjects && equalPackages && equalVersion);
    }

    private void addDefaultPackages() {
        if (packageList.contains("axodraw")) {
            packageList.remove("axodraw");
        }
        if (!packageList.contains("axodraw4j")) {
            packageList.add("axodraw4j");
        }
        if (!packageList.contains("pstricks")) {
            packageList.add("pstricks");
        }
        if (!packageList.contains("color")) {
            packageList.add("color");
        }
    }
}
