/**
 *  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.Color;
import java.awt.Rectangle;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.Collection;

import net.sf.jaxodraw.util.graphics.JaxoGraphics2D;

/**
 * Implementation of JaxoList.
 * This should ensure that only JaxoObjects are added as elements.
 *
 * @param <E> the element type of this list.
 * @since 2.0
 */
public class JaxoObjectList<E extends JaxoObject> extends ArrayList<E>
        implements JaxoList<E> {
    private static final long serialVersionUID = 314159L;

      //
     // Constructors
    //

    /**
     * Just calls super().
     */
    public JaxoObjectList() {
        super();
    }

    /**
     * Add all elements of c to this List.
     * @param c the collection whose elements are to be placed into this list.
     */
    public JaxoObjectList(final Collection<? extends E> c) {
        super(c);
    }

    /**
     * Just calls super(initialCapacity).
     * @param initialCapacity the initial capacity of the list.
     */
    public JaxoObjectList(final int initialCapacity) {
        super(initialCapacity);
    }

      //
     // Painting methods
    //


    /** {@inheritDoc} */
    public void paint(final JaxoGraphics2D g) {
        for (final Iterator<E> i = iterator(); i.hasNext();) {
            i.next().paint(g);
        }
    }

    /** {@inheritDoc} */
    public void paintClipped(final JaxoGraphics2D g) {
        for (final Iterator<E> i = this.iterator(); i.hasNext();) {
            final E o = i.next();

            final Rectangle b = o.getBounds();

            if (((b.width == 0) && (b.height == 0)) // LEGACY
                    || g.hitClip(b.x, b.y, b.width, b.height)) {
                o.paint(g);
            }
        }
    }

    /** {@inheritDoc} */
    public void paintExcept(final Collection<? extends E> excludes, final JaxoGraphics2D g) {
        for (final Iterator<E> i = this.iterator(); i.hasNext();) {
            final E o = i.next();

            if (!excludes.contains(o)) {
                o.paint(g);
            }
        }
    }

    /** {@inheritDoc} */
    public void paintClippedExcept(final Collection<? extends E> excludes, final JaxoGraphics2D g) {
        for (final Iterator<E> i = this.iterator(); i.hasNext();) {
            final E o = i.next();

            if (!excludes.contains(o)) {
                final Rectangle b = o.getBounds();

                if (((b.width == 0) && (b.height == 0)) // LEGACY
                        || g.hitClip(b.x, b.y, b.width, b.height)) {
                    o.paint(g);
                }
            }
        }
    }

    /** {@inheritDoc} */
    @SuppressWarnings("unchecked")
    public JaxoList<E> copyOf() {
        final int size = size();
        final JaxoList<E> copyObjectList = new JaxoObjectList<E>(size);

        for (int i = 0; i < size; i++) {
            copyObjectList.add((E) get(i).copy());
        }

        return copyObjectList;
    }

    /** {@inheritDoc} */
    public boolean isCopy(final JaxoList<E> list) {
        if (list == null) {
            return false;
        }

        // Two lists are copies if they contain
        // copies of objects at equal positions
        boolean isCopy = false;

        final int length = size();
        if (length == list.size()) {
            boolean tmp = true;
            for (int i = 0; i < length; i++) {
                final E ob1 = get(i);
                final E ob2 = list.get(i);
                tmp = tmp && (ob1 == null ? ob2 == null : ob1.isCopy(ob2));
                if (!tmp) {
                    break;
                }
            }
            isCopy = tmp;
        }

        return isCopy;
    }

    /** {@inheritDoc} */
    public void moveAllObjects(final int deltaX, final int deltaY) {
        if ((deltaX == 0) && (deltaY == 0)) {
            return;
        }

        for (int i = 0; i < size(); i++) {
            get(i).moveBy(deltaX, deltaY);
        }
    }

    /** {@inheritDoc} */
    public void setColor(final Color c) {
        for (int i = 0; i < size(); i++) {
            get(i).setColor(c);
        }
    }

    /** {@inheritDoc} */
    public void move(final int index, final int newIndex) {
        if (index == newIndex) {
            return;
        }

        final E o = remove(index);
        add(newIndex, o);
    }

    /** {@inheritDoc} */
    public boolean toEnd(final E object) {
        boolean isLastElement = true;

        // Remove object from current list and append it
        // Start searching at the end - that way repeated
        // foreground() calls will be faster
        final int i = lastIndexOf(object);

        if (i != (size() - 1)) {
            remove(i);
            add(object);
            isLastElement = false;
        }

        return isLastElement;
    }

    /** {@inheritDoc} */
    public boolean toFront(final E object) {
        boolean isFirstElement = true;

        // Remove object from current list and prepend it
        final int i = indexOf(object);

        if (i != 0) {
            remove(i);
            add(0, object);
            isFirstElement = false;
        }

        return isFirstElement;
    }

    /** {@inheritDoc} */
    public boolean removeMarkedObjects() {
        boolean modified = false;
        for (int i = size() - 1; i > -1; i--) {
            final E ob = get(i);

            if (ob.isMarked()) {
                modified = remove(ob);
            }
        }
        return modified;
    }

    /** {@inheritDoc} */
    public Rectangle getBounds() {
        Rectangle bBox = null;
        final int length = this.size();

        for (int i = 0; i < length; i++) {
            final E ob = get(i);
            final Rectangle add = ob.getBounds();
            if (bBox == null) {
                bBox = new Rectangle(add);
            } else {
                bBox.add(add);
            }
        }

        return bBox;
    }

    /** {@inheritDoc} */
    public Rectangle getBoundsExcept(final Collection<? extends E> excludes) {
        Rectangle bBox = null;
        final int length = this.size();

        for (int i = 0; i < length; i++) {
            final E ob = get(i);

            if (excludes.contains(ob)) {
                continue;
            }

            final Rectangle add = ob.getBounds();
            if (bBox == null) {
                bBox = new Rectangle(add);
            } else {
                bBox.add(add);
            }
        }

        return bBox;
    }

    /** {@inheritDoc} */
    public Rectangle intersection(final Rectangle inside) {
        Rectangle intersect = null;
        final Rectangle bounds = getBounds();
        if ((inside != null) && (bounds != null)) {
            intersect = inside.intersection(bounds);
            if (intersect.isEmpty()) {
                intersect = null;
            }
        }
        return intersect;
    }

}
