/*
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOUSE. See the GNU
 * General Public License for more details.
 *
 * You should have recieved a copy of the GNU General Public License
 * along with this program; if not write to the Free Software
 * Foundation, inc., 59 Temple Place, Suite 330, Boston MA 02111-1307
 * USA
 */
/* 
 *
 * ControlNet.java
 *
 * author   Timothy F. Rohaly
 * version  1.0, 01/15/97
 */

package j3d.nurbs;
import j3d.*;


/**
 * Representation of a network of control points for a
 * NURBS curve or surface.
 * @author   Timothy F. Rohaly
 * @version  1.0, 1/15/97
 */
public class ControlNet implements Cloneable {

    protected int        numUControlPoints;
    protected int        numVControlPoints;
    protected Point4[][] controlPoints;


    /* 
     * Private constructor defined so that super.clone() will
     * be able to copy member variables.
     */
    private ControlNet() {
    }


    /**
     * Constructs a ControlNet from the specified Point4 array.
     * @param numUControlPoints the number of control points in the U
     *        direction
     * @param numVControlPoints the number of control points in the V
     *        direction
     * @param controlPoints the array of Point4 control points
     */
    public ControlNet(int numUControlPoints, int numVControlPoints,
                      Point4[][] controlPoints) {
        this.numUControlPoints = numUControlPoints;
        this.numVControlPoints = numVControlPoints;
        this.controlPoints = controlPoints;
    }


    /**
     * Constructs a ControlNet with the specified U, V dimensions.
     * @param numUControlPoints the number of control points in the U
     *        direction
     * @param numVControlPoints the number of control points in the V
     *        direction
     */
    public ControlNet(int numUControlPoints, int numVControlPoints) {
        this(numUControlPoints, numVControlPoints,
             new Point4[numUControlPoints][numVControlPoints]);
    }


    /**
     * Transpose the control points by swapping U and V.
     */
    public void transpose() {
        Point4[][] newPoints = new Point4[numVControlPoints][numUControlPoints];
        for (int i=0; i<numUControlPoints; i++) {
            for (int j=0; j<numVControlPoints; j++) {
                newPoints[j][i] = controlPoints[i][j];
            }
        }
        controlPoints = newPoints;
        int itemp = numUControlPoints;
        numUControlPoints = numVControlPoints;
        numVControlPoints = itemp;
    }


    /**
     * Scale the control points.
     * @param scale the scale
     */
    public void scale(float scale) {
        for (int i=0; i<numUControlPoints; i++) {
            for (int j=0; j<numVControlPoints; j++) {
                controlPoints[i][j].scale(scale);
            }
        }
    }


    /**
     * Scale the control points.
     * @param scale the scale
     */
    public void scale(float xscale, float yscale, float zscale) {
        for (int i=0; i<numUControlPoints; i++) {
            for (int j=0; j<numVControlPoints; j++) {
                controlPoints[i][j].scale(xscale, yscale, zscale);
            }
        }
    }


    /**
     * Translate the control points.
     * @param x the translation in x
     * @param y the translation in y
     * @param z the translation in z
     */
    public void translate(float x, float y, float z) {
        for (int i=0; i<numUControlPoints; i++) {
            for (int j=0; j<numVControlPoints; j++) {
                controlPoints[i][j].translate(x, y, z);
            }
        }
    }


    /**
     * Rotate the control points.
     * @param x the x component of the rotation axis
     * @param y the y component of the rotation axis
     * @param z the z component of the rotation axis
     * @param theta the rotation in radians
     */
    public void rotate(float x, float y, float z, float theta) {
        for (int i=0; i<numUControlPoints; i++) {
            for (int j=0; j<numVControlPoints; j++) {
                controlPoints[i][j].rotate(x, y, z, theta);
            }
        }
    }


    /**
     * Performs a deep copy of this ControlNet.
     */
    public Object clone() {
        try {
            ControlNet newnet = (ControlNet) super.clone();
            System.arraycopy(controlPoints, 0, newnet.controlPoints, 0,
                             numUControlPoints*numVControlPoints);
            return newnet;
        }
        catch (CloneNotSupportedException e) {
            String errorString = getClass().getName() + ":  " +
                                 "CloneNotSupportedException " +
                                 "for a Cloneable class";
            throw new InternalError(errorString);  // this should never happen
        }
    }


    /**
     * Creates a String representation for the ControlNet
     * in the form of a VRML IndexedFaceSet node.
     * @return a string representation of the ControlNet
     */
    public String toVRMLString() {
        StringBuffer buffer = new StringBuffer(256);
        if (numVControlPoints >1)
            buffer.append("IndexedFaceSet {\n");
        else
            buffer.append("IndexedLineSet {\n");
        buffer.append("coord Coordinate {\n\tpoint[\n");
        //
        // Really, really need to sort through controlPoint
        // list here and eliminate duplicate points.  Otherwise
        // automatic normal generation gets screwed up.
        //
        for (int i=0; i<numUControlPoints; i++) {
            for (int j=0; j<numVControlPoints; j++) {
                buffer.append("\t\t" +
                               controlPoints[i][j].x/controlPoints[i][j].w +
                               " " +
                               controlPoints[i][j].y/controlPoints[i][j].w +
                               " " +
                               controlPoints[i][j].z/controlPoints[i][j].w +
                               ",\n");
            }
        }
        buffer.append("\t]\n}\n");
        buffer.append("\tcoordIndex [\n");
        for (int j=0; j<numUControlPoints-1; j++) {
            if (numVControlPoints >1) {
                for (int i=0; i<numVControlPoints-1; i++) {
                    int index = i + j*numVControlPoints;
                    buffer.append("" + index                       + ", "     +
                                       (index+1)                   + ", "     +
                                       (index+numVControlPoints)   + ", -1, " + 
                                       (index+numVControlPoints)   + ", "     + 
                                       (index+1)                   + ", "     +
                                       (index+1+numVControlPoints) + ", -1,\n" );
                }
            }
            else {
                    buffer.append("" + j + ", ");
            }
        }
        if (numVControlPoints >1) {
            buffer.append("\t]\n");
            buffer.append("    solid       FALSE\n");
            buffer.append("    ccw         TRUE\n");
            buffer.append("    convex      TRUE\n");
            buffer.append("    creaseAngle 0.5\n");
        }
        else {
            buffer.append("" + (numUControlPoints-1) + ", -1, ]\n");
        }
        buffer.append("}");

        return buffer.toString();
    }


    /**
     * Creates a String representation for the ControlNet
     * in the form of a VRML IndexedFaceSet node.
     * @return a string representation of the ControlNet
     */
    public int[] toVRMLCoordIndex() {
        int[] indices = null;
        if (numVControlPoints > 1) {
            indices  = new int[8*(numUControlPoints-1)*(numVControlPoints-1)];
        }
        else {
            indices  = new int[numUControlPoints*numVControlPoints+1];
        }
        int   position = 0;
        for (int j=0; j<numUControlPoints-1; j++) {
            if (numVControlPoints >1) {
                for (int i=0; i<numVControlPoints-1; i++) {
                    int index = i + j*numVControlPoints;
                    indices[position++] = index;
                    indices[position++] = index+1;
                    indices[position++] = index+numVControlPoints;
                    indices[position++] = -1;
                    indices[position++] = index+numVControlPoints;
                    indices[position++] = index+1;
                    indices[position++] = index+1+numVControlPoints;
                    indices[position++] = -1;
                }
            }
            else {
                indices[j] = j;
            }
        }
        if (numVControlPoints >1) {
            // Do nothing
        }
        else {
            indices[indices.length-2] = numUControlPoints-1;
            indices[indices.length-1] = -1;
        }
        return indices;
    }


    /**
     * Creates a representation for the ControlNet
     * in the form of an array of _Points.
     * @return an array of _Point representing of the ControlNet
     */
    public _Point[][] toCoordinateNode() {
    	_Point[][] mesh = new _Point[numUControlPoints][numVControlPoints];
    	
    	for (int i=0; i<numUControlPoints; i++) {
            for (int j=0; j<numVControlPoints; j++) {
                mesh[i][j] = new _Point(
                              controlPoints[i][j].x/controlPoints[i][j].w 
							  ,
                              controlPoints[i][j].y/controlPoints[i][j].w 
							  ,
                              controlPoints[i][j].z/controlPoints[i][j].w
                              );
            }
        }

    	return mesh;
    }


    /**
     * Creates a String representation for the ControlNet
     * @return a string representation of the ControlNet
     */
    public String toString() {
        StringBuffer buffer = new StringBuffer(1024);
        buffer.append(getClass().getName() + ": ");
        buffer.append("numUControlPoints=" + numUControlPoints + ", " +
                      "numVControlPoints=" + numVControlPoints);
        for (int j=0; j<numVControlPoints; j++) {
            for (int i=0; i<numUControlPoints; i++) {
                buffer.append("\n[" + i + "][" + j + "]=" +
                              controlPoints[i][j].toString());
            }
        }
        return buffer.toString();
    }
}
