/*
 * @(#)BaseVertexShape.java    2.4.0   8 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.util.*;

import java.awt.*;
import java.awt.geom.*;
import java.beans.*;


/**
 * <p>Class <code>BaseVertexShape</code> extends <code>BaseShape</code> to
 * provide the common functionality for those classes that either do not use
 * the tangent data of <code>BaseShape</code> or compute the tangent data
 * automatically from the vertex data and other internal settings.</p>
 *
 * <p>Class <code>BaseVertexShape</code> contains the common code for the
 * derived classes:</p>
 *
 * <ul>
 *   <li><code>PolygonShape</code></li>
 *   <li><code>PolygonDotsShape</code></li>
 *   <li><code>AutomaticShape</code></li>
 *   <li><code>AutomaticCurve</code></li>
 * </ul>
 *
 * <p>Class <code>BaseVertexShape</code> may not be instantiated directly
 * since it does not have a public constructor.</p>
 *
 * <p>If a derived class computes tangents automatically then this must be
 * done by overriding the method <code>makePath()</code> that is inherited
 * from <code>BaseShape</code>.  See the documentation in that class for
 * instructions on how to do the override.</p>
 *
 * @author  Richard Rasala
 * @version 2.4.0
 * @since   2.4.0
 */
public class BaseVertexShape
    extends BaseShape
{
    /** Bound property name to set one or more vertices. */
    public static final String SET_VERTEX    = "set.vertex";
    
    /** Bound property name to add one or more vertices. */
    public static final String ADD_VERTEX    = "add.vertex";
    
    /** Bound property name to remove one or more vertices. */
    public static final String REMOVE_VERTEX = "remove.vertex";
    

    /** The protected default constructor. */
    protected BaseVertexShape() { }
    
    
    /**
     * <p>Sets the vertex data of the shape using a 2-dimensional array
     * of single precision numbers and makes a new path.</p>
     *
     * <p>Preconditions:</p>
     * <ul>
     *   <li>The given vertex array is either <code>null</code> or
     *       <code>float[N][2]</code> for some N.</li>
     * </ul>
     *
     * <p>If the given vertex array is <code>null</code> then the
     * internal vertex array is set to <code>float[0][2]</code>.</p>
     * 
     * <p>Does nothing if the precondition fails.</p>
     *
     * <p>Fires property change: SET_VERTEX.</p>
     * 
     * @param vertex the vertex data to set
     */
    public final void setVertexData(float[][] vertex) {
        if (vertex == null) {
            this.vertex = new float[0][2];
        }
        else {
            if (!FloatArray.checkArray(vertex, 2))
                return;
            
            this.vertex = FloatArray.deepclone(vertex);
        }
        
        makePath();
        
        firePropertyChange(SET_VERTEX, null, null);
    }
    
    
    /**
     * <p>Sets the vertex at the given index to the given point
     * coordinates and makes a new path.</p>
     *
     * <p>Does nothing if the index is not in [0, length()).</p>
     *
     * <p>Fires property change: SET_VERTEX.
     *
     * @param index the vertex index
     * @param x the new x-coordinate for the vertex
     * @param y the new y-coordinate for the vertex
     */
    public final void setVertex(int index, float x, float y) {
        if ((index < 0) || (index >= vertex.length))
            return;
        
        vertex[index][0] = x;
        vertex[index][1] = y;
        
        makePath();
        
        firePropertyChange(SET_VERTEX, null, null);
    }
    
    
    /**
     * <p>Sets the vertex at the given index to a clone of the
     * given point and makes a new path.</p>
     *
     * <p>Does nothing if the index is not in [0, length()).</p>
     *
     * <p>Does nothing if point is not <code>float[2]</code>.</p>
     *
     * <p>Fires property change: SET_VERTEX.
     *
     * @param index the vertex index
     * @param point the new point for the vertex at the given index
     */
    public final void setVertex(int index, float[] point) {
        if ((point == null) || (point.length != 2))
            return;
        
        setVertex(index, point[0], point[1]);
    }
    
    
    /**
     * <p>Adds a new vertex at the given index with the given point
     * coordinates and makes a new path.</p>
     *
     * <p>If index is 0, the insertion is at the front.</p>
     *
     * <p>If index is length(), the insertion is at the end.</p>
     *
     * <p>Forces the given index into the range:</p>
     *
     * <pre>  0 &lt;= index &lt;= length()</pre>
     *
     * <p>Fires property change: ADD_VERTEX.</p>
     *
     * @param index the vertex index
     * @param x the x-coordinate for the new vertex to add
     * @param y the y-coordinate for the new vertex to add
     */
    public final void addVertex(int index, float x, float y) {
        int N = vertex.length;
        int M = N + 1;
        
        if (index < 0)
            index = 0;
        else
        if (index > N)
            index = N;
        
        float[][] oldvertex = vertex;
        
        vertex = new float[M][2];
        
        for (int i = 0; i < index; i++) {
            vertex[i][0] = oldvertex[i][0];
            vertex[i][1] = oldvertex[i][1];
        }
        
        vertex[index][0] = x;
        vertex[index][1] = y;
        
        for (int i = (index + 1); i < M; i++) {
            int j = i - 1;
            vertex[i][0] = oldvertex[j][0];
            vertex[i][1] = oldvertex[j][1];
        }
        
        makePath();
        
        firePropertyChange(ADD_VERTEX, null, null);
    }
    
    
    /**
     * <p>Adds a new vertex at the given index with a clone of the
     * given point and makes a new path.</p>
     *
     * <p>If index is 0, the insertion is at the front.</p>
     *
     * <p>If index is length(), the insertion is at the end.</p>
     *
     * <p>Forces the given index into the range:</p>
     *
     * <pre>    0 &lt;= index &lt;= length()</pre>
     *
     * <p>Fires property change: ADD_VERTEX.</p>
     *
     * @param index the vertex index
     * @param point the new vertex to add
     */
    public final void addVertex(int index, float[] point) {
        if ((point == null) || (point.length != 2))
            return;
        
        addVertex(index, point[0], point[1]);
    }
    
    
    /**
     * <p>Adds a new sequence of vertices at the given index
     * using clones of the given points and makes a new path.</p>
     *
     * <p>If index is 0, the insertion is at the front.</p>
     *
     * <p>If index is length(), the insertion is at the end.</p>
     *
     * <p>Forces the given index into the range:</p>
     *
     * <pre>  0 &lt;= index &lt;= length()</pre>
     *
     * <p>Does nothing if the given array is <code>null</code>
     * or does not have the form <code>float[K][2]</code>.</p>
     *
     * <p>Fires property change: ADD_VERTEX.</p>
     *
     * @param index  the start index
     * @param points the new vertices to add
     */
    public final void addVertices(int index, float[][] points) {
        if (!FloatArray.checkArray(points, 2))
            return;
        
        int K = points.length;
        
        if (K == 0)
            return;
        
        int N = vertex.length;
        int M = N + K;
        
        float[][] oldvertex = vertex;
        
        vertex = new float[M][2];
        
        if (index < 0)
            index = 0;
        else
        if (index > N)
            index = N;
        
        for (int i = 0; i < index; i++) {
            vertex[i][0] = oldvertex[i][0];
            vertex[i][1] = oldvertex[i][1];
        }
        
        for (int i = 0; i < K; i++) {
            int j = i + index;
            vertex[j][0] = points[i][0];
            vertex[j][1] = points[i][1];
        }
        
        for (int i = index; i < N; i++) {
            int j = i + K;
            vertex[j][0] = oldvertex[i][0];
            vertex[j][1] = oldvertex[i][1];
        }
        
        makePath();
        
        firePropertyChange(ADD_VERTEX, null, null);
    }
    
    
    /**
     * <p>Appends a new vertex with the given point
     * coordinates and makes a new path.</p>
     *
     * <p>Fires property change: ADD_VERTEX.</p>
     *
     * @param x the x-coordinate for the new vertex to append
     * @param y the y-coordinate for the new vertex to append
     */
    public final void appendVertex(float x, float y) {
        addVertex(vertex.length, x, y);
    }
    
    
    /**
     * <p>Appends a new vertex with a clone of the
     * given point and makes a new path.</p>
     *
     * <p>Fires property change: ADD_VERTEX.</p>
     *
     * @param point the new vertex to append
     */
    public final void appendVertex(float[] point) {
        addVertex(vertex.length, point);
    }
    
    
    /**
     * <p>Appends a new sequence of vertices
     * using clones of the given points and makes a new path.</p>
     *
     * <p>Does nothing if the given array is <code>null</code>
     * or does not have the form <code>float[K][2]</code>.</p>
     *
     * <p>Fires property change: ADD_VERTEX.</p>
     *
     * @param points the new vertices to append
     */
    public final void appendVertices(int index, float[][] points) {
        addVertices(vertex.length, points);
    }
    
    
    /**
     * <p>Removes the vertex at the given index, makes a new path,
     * and returns the vertex removed.</p>
     *
     * <p>If the index is not in [0, length()), no vertex is
     * removed and <code>null</code> is returned.</p>
     *
     * <p>Fires property change: REMOVE_VERTEX.</p>
     *
     * @param index the vertex index
     */
    public final float[] removeVertex(int index) {
        int N = vertex.length;
        int M = N - 1;
        
        if ((index < 0) || (index >= N))
            return null;
        
        float[][] oldvertex = vertex;
        
        vertex = new float[M][2];
        
        for (int i = 0; i < index; i++) {
            vertex[i][0] = oldvertex[i][0];
            vertex[i][1] = oldvertex[i][1];
        }
        
        for (int i = (index + 1); i < N; i++) {
            int j = i - 1;
            vertex[j][0] = oldvertex[i][0];
            vertex[j][1] = oldvertex[i][1];
        }
        
        makePath();
        
        firePropertyChange(REMOVE_VERTEX, null, null);
        
        return oldvertex[index];
    }
    
    
    /**
     * <p>Removes the vertices starting at the given index m inclusive
     * and ending at the given index n exclusive, makes a new path,
     * and returns an array with the vertices removed.</p>
     *
     * <p>Forces m and n into range if needed.</p>
     *
     * <p>Returns an array of length 0 if no nodes are removed.</p>
     *
     * <p>Fires property change: REMOVE_VERTEX.</p>
     *
     * @param m the starting index inclusive
     * @param n the ending index exclusive
     */
    public final float[][] removeVertices(int m, int n) {
        int N = vertex.length;
        
        if (m < 0)
            m = 0;
        
        if (n > N)
            n = N;
        
        int K = n - m;
        
        if (K <= 0)
            return new float[0][2];
        
        int M = N - K;
        
        float[][] oldvertex = vertex;
        
        vertex = new float[M][2];
        
        float[][] result = null;
        
        if (M == 0) {
            result = oldvertex;
        }
        else {
            result = new float[K][2];
            
            for (int i = 0; i < m; i++) {
                vertex[i][0] = oldvertex[i][0];
                vertex[i][1] = oldvertex[i][1];
            }
            
            for (int i = m; i < n; i++) {
                int j = i - m;
                result[j][0] = oldvertex[i][0];
                result[j][1] = oldvertex[i][1];
            }
            
            for (int i = n; i < N; i++) {
                int j = i - K;
                vertex[j][0] = oldvertex[i][0];
                vertex[j][1] = oldvertex[i][1];
            }
        }
        
        makePath();
        
        firePropertyChange(REMOVE_VERTEX, null, null);
        
        return result;
    }
    
    
    /**
     * <p>Removes all vertices, makes a new path,
     * and returns an array with the vertices removed.</p>
     *
     * <p>Fires property change: REMOVE_VERTEX.</p>
     */
    public final float[][] removeAllVertices() {
        return removeVertices(0, vertex.length);
    }
    
}

