/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package geomExtension;

import java.awt.geom.*;
import java.text.*;
import java.util.*;
import DrawTop.*;
import util.*;


public class CubicCurve2DE extends Curve2D{

    int debug=0;

    public CubicCurve2DE(Segment2D[] segments){
        super(segments);
    }

    public CubicCurve2DE(Point2D[] Q){
        this.setData(Q);
    }

    public void setData(Point2D[] Q){
        //System.out.println("*** CubicCurve2DE.setData");
        int QLen=Q.length;
        if(Q==null||QLen<4||(QLen-QLen/3*3!=1)){
            System.err.println("*** Error in CubicCurve2DE.setData");
            return;
        }
        int nSeg=QLen/3;
        this.generalPath=new GeneralPath(Curve2D.Path2D_WIND);
        this.segments=new Segment2D[nSeg];
        this.closed=false;
        double dist=Vector2D.dist(Q[0], Q[QLen-1]);
        if(dist<Curve2D.closedTolerance) {
            this.closed=true;
            Q[QLen-1]=Q[0];
        }
        for(int i=0;i<nSeg;i++) {
            CubicCurve2D curve=new CubicCurve2D.Double(Q[3*i].getX(),Q[3*i].getY(), 
                    Q[3*i+1].getX(),Q[3*i+1].getY(), Q[3*i+2].getX(),Q[3*i+2].getY(),
                    Q[3*i+3].getX(),Q[3*i+3].getY());
            Segment2D segment=new Segment2D(Segment2D.CUBIC, curve);
            generalPath.append(curve,true);
            segments[i]=segment;
        }
    }
    
    public int getType2DE(){
        return Command.CUBIC_CURVE;
    }
//CubicCurve2DE proper
    public Point2D[] getPoints(){
        int nSeg=getNumOfSegments();
        Point2D[] P=new Point2D[nSeg+1];
        for(int i=0;i<nSeg;i++){
            CubicCurve2D curve=(CubicCurve2D)segments[i].getShape();
            if(i==0) P[0]=curve.getP1();
            P[i+1]=curve.getP2();
        }
        return P;
    }
    
//CubicCurve2DE proper
    public Point2D[] getCtrlPoints(){
        int nSeg=getNumOfSegments();
        Point2D[] ctrlP=new Point2D[3*nSeg+1];
        for(int i=0;i<nSeg;i++){
            CubicCurve2D curve=(CubicCurve2D)segments[i].getShape();
            if(i==0) ctrlP[3*i]=curve.getP1();
            ctrlP[3*i+1]=curve.getCtrlP1();
            ctrlP[3*i+2]=curve.getCtrlP2();
            ctrlP[3*i+3]=curve.getP2();
        }
        return ctrlP;
    }
    
//CubicCurve2DE proper
    public FergusonCurve2D getFergusonCurve2D(){
        int numSeg=this.getNumOfSegments();
        Point2D[] PT=new Point2D[numSeg+1];
        Vector2D[] Tin=new Vector2D[numSeg+1];
        Vector2D[] Tout=new Vector2D[numSeg+1];
        for(int i=0;i<=numSeg;i++){
            PT[i]=this.getP(i);
            Tin[i]=this.getTin(i);
            Tout[i]=this.getTout(i);
        }
        FergusonCurve2D fergusonCurve=new FergusonCurve2D(PT, Tin, Tout);
        return fergusonCurve;
    }

//CubicCurve2DE proper
    public Vector2D getTout(int index){
        if(index<0) {
            if(debug>0) System.err.println("*** Warning in CubicCurve2DE.getTout; it out of range;" +
                    " index="+index);
            return getTout(0);
        }
        if(index>=this.segments.length){
            if(debug>0) System.err.println("*** Warning in CubicCurve2DE.getTout; it out of range;" +
                    " index="+index);
            return getTin(this.segments.length);
        }
        CubicCurve2D curve=(CubicCurve2D)segments[index].getShape();
        Vector2D Q0=new Vector2D(curve.getP1());
        Vector2D Q1=new Vector2D(curve.getCtrlP1());
        Vector2D Tout=Vector2D.multiply(3.0, Vector2D.sub(Q1, Q0));
        return Tout;
    }

    public Vector2D getTin(int index){
        if(index<=0) {
            if(debug>0) System.err.println("*** Warning in CubicCurve2DE.getTin; it out of range;" +
                    " index="+index);
            return getTout(0);
        }
        if(index>this.segments.length){
            if(debug>0) System.err.println("*** Warning in CubicCurve2DE.getTin; it out of range;" +
                    " index="+index);
            return getTin(this.segments.length);
        }
        CubicCurve2D curve=(CubicCurve2D)segments[index-1].getShape();
        Vector2D Q2=new Vector2D(curve.getCtrlP2());
        Vector2D Q3=new Vector2D(curve.getP2());
        Vector2D Tout=Vector2D.multiply(3.0, Vector2D.sub(Q3, Q2));
        return Tout;
    }


    public CubicCurve2DE getTrimmedCurve2D(double t1, double t2){
        CubicCurve2DE trimmedCurve=null;
        Vector vector=new Vector();
        int it1=(int)t1;
        int it2=(int)t2;
        for(int i=it1;i<=it2;i++){
            double t01=0;
            double t02=1;
            if(i==it1) t01=t1-it1;
            if(i==it2) t02=t2-it2;
            Segment2D segment=this.getSegment2D(i);
            if(Math.abs(t02-t01)>Curve2D.eps) {
                Segment2D subSegment=segment.trimSegment(t01, t02);
                vector.add(subSegment);
            }
        }
        int size=vector.size();
        if(size==0) return null;
        Segment2D[] segments=new Segment2D[size];
        for(int i=0;i<size;i++) segments[i]=(Segment2D)vector.get(i);
        trimmedCurve=new CubicCurve2DE(segments);
        return trimmedCurve;
    }

//Curve2D
    public Object clone(){
        Point2D[] points=this.getCtrlPoints();
        return (new CubicCurve2DE(points));
    }
    
//Curve2D
    public String toString(){
        NumberFormat nf = NumberFormat.getNumberInstance();
        nf.setMaximumIntegerDigits(4);
        nf.setMinimumFractionDigits(1);
        String str="";
        str+="CubicCurve, segments="+segments.length+", close="+closed;
        for(int i=0;i<segments.length;i++){
            double t=i;
            Point2D p0=this.getP(t);
            Point2D p1=this.getP(t+1);
            Vector2D T0=this.getTangent(t+eps);
            Vector2D T1=this.getTangent(t+1-eps);
            str+="\n   segment["+i+"]" +
                    "  startP="+Util.Pt(p0)+
                    ", endP="+Util.Pt(p1)+
                    "  startTangent="+nf.format(T0.getX())+","+nf.format(T0.getY())+
                    ", endTangent="+nf.format(T1.getX())+","+nf.format(T1.getY());
        }
        return str;
    }

}
