/*
 * @(#)PathListIterator.java    2.4.0   15 September 2005
 *
 * Copyright 2005
 * College of Computer and Information Science
 * Northeastern University
 * Boston, MA  02115
 *
 * The Java Power Tools software may be used for educational
 * purposes as long as this copyright notice is retained intact
 * at the top of all source files.
 *
 * To discuss possible commercial use of this software, 
 * contact Richard Rasala at Northeastern University, 
 * College of Computer and Information Science,
 * 617-373-2462 or rasala@ccs.neu.edu.
 *
 * The Java Power Tools software has been designed and built
 * in collaboration with Viera Proulx and Jeff Raab.
 *
 * Should this software be modified, the words "Modified from 
 * Original" must be included as a comment below this notice.
 *
 * All publication rights are retained.  This software or its 
 * documentation may not be published in any media either
 * in whole or in part without explicit permission.
 *
 * This software was created with support from Northeastern 
 * University and from NSF grant DUE-9950829.
 */

package edu.neu.ccs.gui;

import edu.neu.ccs.*;
import edu.neu.ccs.gui.*;

import java.awt.*;
import java.awt.geom.*;
import java.util.*;
import java.text.ParseException;

/**
 * <p>The <code>PathListIterator</code> class creates a Java
 * <code>PathIterator</code> based on an internal <code>PathList</code>.</p>
 *
 * <p>The caller may query or modify the internal <code>PathList</code>.</p>
 *
 * <p>If a path list is used in a constructor or in the method
 * <code>setPathList</code>, this iterator is linked to that path list.</p>
 *
 * <p>The caller may reset the iterator to its initial state to allow
 * multiple uses of the iterator.</p>
 *
 * <p>A factory method is provided to construct a new Java <code>Shape</code>
 * from the current iterator data.  This method uses the Java class
 * <code>GeneralPath</code>.</p>
 *
 * <p>In construction of a <code>Shape</code> via <code>GeneralPath</code>,
 * Java requires that the start node have type MOVE.  If this condition
 * does not hold, Java throws <code>IllegalPathStateException</code>.  In
 * this class, the requirement that the start node have type MOVE is tested
 * by the method <code>isValid()</code>.  If this method returns false and
 * the user calls <code>makeShape</code>, an empty <code>Shape</code> will
 * be returned by this call rather than throwing an exception.</p>
 */
public class PathListIterator
    implements PathIterator
{
    /** Shorthand constant for PathIterator.SEG_MOVETO. */
    public static final int MOVE  = PathIterator.SEG_MOVETO;
    
    /** Shorthand constant for PathIterator.SEG_LINETO. */
    public static final int LINE  = PathIterator.SEG_LINETO;
    
    /** Shorthand constant for PathIterator.SEG_QUADTO. */
    public static final int QUAD  = PathIterator.SEG_QUADTO;
    
    /** Shorthand constant for PathIterator.SEG_CUBICTO. */
    public static final int CUBIC = PathIterator.SEG_CUBICTO;
    
    /** Shorthand constant for PathIterator.SEG_CLOSE. */
    public static final int CLOSE = PathIterator.SEG_CLOSE;
    
    
    /** The path list used by this iterator. */
    protected PathList pathlist = null;
    
    /** The interator index. */
    private int pointer = 0;
    
    
    /**
     * <p>The default constructor that initializes the iterator
     * to a new <code>PathList</code> object with zero nodes and
     * winding rule <code>WindingRule.WIND_NON_ZERO</code>.</p>
     */
    public PathListIterator() {
        setPathList(null);
    }
    
    
    /**
     * <p>The constructor that initializes the iterator using
     * the given <code>PathList</code> object.</p>
     *
     * @param list the list to be cloned for the iterator
     */
    public PathListIterator(PathList list) {
        setPathList(list);
    }
    
    
    /**
     * <p>Sets the internal <code>PathList</code> that is the basis
     * for this iterator.</p>
     *
     * <p>Sets the internal <code>PathList</code> to a new path list
     * if the given path list is <code>null</code>.</p>
     *
     * @param pathlist the path list to set
     */
    public final void setPathList(PathList pathlist) {
        if (pathlist == null)
            this.pathlist = new PathList();
        else
            this.pathlist = pathlist;
    }
    
    
    /**
     * <p>Returns the internal <code>PathList</code> that is the basis
     * for this iterator.</p>
     *
     * <p>This method enables the user to query any properties of the
     * internal path list.</p>
     *
     * <p>In addition, the user can modify the internal path list.  We
     * believe that under certain circumstances this ability might be
     * quite valuable.  Nevertheless, we recommend caution in making
     * use of this ability.  In particular, call <code>reset()</code>
     * after making any changes so the interator will be reset to its
     * initial state.</p>
     *
     * <p>If the path list is empty or invalid then <code>isDone()</code>
     * will return true even when the iterator is in its initial state.</p>
     */
    public final PathList getPathList() {
        return pathlist;
    }
    
    
    /**
     * <p>Returns true if this <code>PathListIterator</code> is a valid path
     * iterator for the construction of a <code>Shape</code>.</p>
     *
     * <p>More precisely, returns true if the internal path list is valid,
     * that is, its size is zero or its size is positive and its start node
     * has type MOVE.</p>
     *
     * <p>If this method returns false then the <code>isDone()</code> method
     * will automatically return true so only an empty <code>Shape</code>
     * may be built using this iterator.</p>
     */
    public final boolean isValid() {
        return pathlist.isValid();
    }
    
    
    /** Returns the winding rule of the iterator as an int. */
    public final int getWindingRule() {
        return pathlist.getWindingRule().rule();
    }
    
    
    /**
     * <p>Returns true if the iteration pointer is beyond the last available
     * path segment in the internal path list or if this path list is not
     * a valid path list for the construction of a <code>Shape</code>.</p>
     *
     * <p>If an attempt is made to retrieve path segment data when
     * <code>isDone()</code> is true then an
     * <code>IndexOutOfBoundsException</code> will be thrown.</p>
     */
    public final boolean isDone() {
        return (! isValid()) || (pointer >= pathlist.size());
    }
    
    
    /**
     * Advances the iteration pointer one step or anchors it one step beyond
     * the end of the internal path list.
     */ 
    public final void next() {
        if (pointer < pathlist.size())
            pointer++;
    }
    
    
    /**
     * <p>Resets the iteration pointer to its initial state.</p>
     *
     * <p>If the path list is empty or invalid then <code>isDone()</code> will
     * return true even when the iterator is in its initial state.</p>
     */
    public final void reset() {
        pointer = 0;
    }
    
    
    /**
     * <p>Returns the type of the current segment as the return value and
     * stores the numeric data of the segment in the supplied array which
     * must be non-<code>null</code> and of size at least 6.</p>
     *
     * <p>Throws <code>IllegalArgumentException</code> if the supplied
     * array does not meet the preconditions.</p>
     *
     * <p>Overwrites the existing contents of the first 6 cells of the
     * supplied array.</p>
     *
     * <p>Throws <code>IndexOutOfBoundsException</code> if this method
     * is called when <code>isDone()</code> is true.</p>
     *
     * @param coords an array of size 6 to hold the segment data
     * @throws IllegalArgumentException
     * @throws IndexOutOfBoundsException
     */
    public final int currentSegment(float[] coords) {
        if (isDone())
            throw new IndexOutOfBoundsException
                ("Beyond final path node in PathListIterator");
        
        PathNode node = pathlist.get(pointer);
        
        return node.nodeInfo(coords);
    }
    
    
    /**
     * <p>Returns the type of the current segment as the return value and
     * stores the numeric data of the segment in the supplied array which
     * must be non-<code>null</code> and of size at least 6.</p>
     *
     * <p>Throws <code>IllegalArgumentException</code> if the supplied
     * array does not meet the preconditions.</p>
     *
     * <p>Overwrites the existing contents of the first 6 cells of the
     * supplied array.</p>
     *
     * <p>Throws <code>IndexOutOfBoundsException</code> if this method
     * is called when <code>isDone()</code> is true.</p>
     *
     * @param coords an array of size 6 to hold the segment data
     * @throws IllegalArgumentException
     * @throws IndexOutOfBoundsException
     */
    public final int currentSegment(double[] coords) {
        if (isDone())
            throw new IndexOutOfBoundsException
                ("Beyond final path node in PathListIterator");
        
        PathNode node = pathlist.get(pointer);
        
        return node.nodeInfo(coords);
    }
    
    
    /**
     * <p>Factory method that returns a new <code>Shape</code> based on the
     * current data in this iterator.<p>
     *
     * <p>The iterator is reset both before and after the shape is
     * constructed.</p>
     *
     * <p>This method currently uses <code>GeneralPath</code> in its
     * implementation.</p>
     *
     * <p>In construction of a <code>Shape</code> via <code>GeneralPath</code>,
     * Java requires that the start node have type MOVE.  If this condition
     * does not hold, Java throws <code>IllegalPathStateException</code>.  In
     * this class, the requirement that the start node have type MOVE is tested
     * by the method <code>isValid()</code>.  If this method returns false and
     * the user calls <code>makeShape</code>, an empty <code>Shape</code> will
     * be returned by this call rather than throwing an exception.</p>
     */
    public final Shape makeShape() {
        GeneralPath gp = new GeneralPath(getWindingRule());
        
        if (! pathlist.isValid())
            return gp;
        
        int N = pathlist.size();
        
        for (int i = 0; i < N; i++) {
            PathNode node = pathlist.get(i);
            
            int type = node.getNodeType();
            
            switch (type) {
                case MOVE:
                    gp.moveTo(node.getX1(), node.getY1());
                    break;
                
                case LINE:
                    gp.lineTo(node.getX1(), node.getY1());
                    break;
                
                case QUAD:
                    gp.quadTo
                        (node.getX1(), node.getY1(),
                         node.getX2(), node.getY2());
                    break;
                
                case CUBIC:
                    gp.curveTo
                        (node.getX1(), node.getY1(),
                         node.getX2(), node.getY2(),
                         node.getX3(), node.getY3());
                    break;
                
                case CLOSE:
                    gp.closePath();
                    break;
                
                default:
                    break;
            }
        }
        
        return gp;
    }
    
}
