package curve;

import curve.*;
import java.awt.*;

public class FreeBSplineCurve extends Curve {

  protected NumericTextField xText[],yText[];
  protected static final int numPoints=6;
  protected int markerSize[];

  // constructor without layout
  protected FreeBSplineCurve() {};

  // Creates new BSplineCurve
  public FreeBSplineCurve(DrawCurve aDrawCurve) {
    int ctr;
    String s;
    Dimension d;

    color=Color.black;
    drawCurve=aDrawCurve;
    if(drawCurve!=null) drawCurve.addCurve(this);
    marker=new Coordinate[numPoints];

    GridBagLayout layout=new GridBagLayout();
    setBackground(Color.lightGray);
    setLayout(layout);
    GridBagConstraints constraints=new GridBagConstraints();

    //Set some defaults
    constraints.weightx=constraints.weighty=1.0;
    constraints.insets=new Insets(1,1,1,1);
    constraints.fill=GridBagConstraints.NONE;

    //Title row
    titleLabel=new Label("Cubic B-Spline For 6 Points");
    titleLabel.setFont(new Font("Dialog",Font.BOLD,12));
    constraints.gridwidth=GridBagConstraints.REMAINDER;
    layout.setConstraints(titleLabel,constraints);
    add(titleLabel);

    //Add listeners
    MyActionListener actionListener=new MyActionListener();

    //Numbers to substitute into equation
    xText=new NumericTextField[numPoints];
    yText=new NumericTextField[numPoints];

    for(ctr=0;ctr<numPoints;ctr++) {
      s=new Integer(ctr).toString();
      xText[ctr]=addNewVariable(layout,"P"+s+"=",s,actionListener,false);
      yText[ctr]=addNewVariable(layout,null,s,actionListener,true);
    };

    for(ctr=0;ctr<numPoints;ctr++)
      marker[ctr]=new Coordinate(xText[ctr].asDouble(),yText[ctr].asDouble());

    markerSize=new int[numPoints];
    for(ctr=0;ctr<numPoints;ctr++) markerSize[ctr]=0;
  }

  public void setExercise() {
    int ctr;
    for(ctr=0;ctr<numPoints;ctr++) {
      marker[ctr].x=Math.random()*5;
      marker[ctr].y=Math.random()*5;
    };
  };

  //Get maximum and minimum values of x,y on curve
  public DoubleRect getMinMax() {
    int ctr;
    double yMin,yMax;
    DoubleRect result;
    Coordinate co;

    xMin=xMax=marker[0].x;
    yMin=yMax=marker[0].y;
    for(ctr=1;ctr<numPoints;ctr++) {
      if(marker[ctr].x<xMin) xMin=marker[ctr].x;
      if(marker[ctr].x>xMax) xMax=marker[ctr].x;
      if(marker[ctr].y<yMin) yMin=marker[ctr].y;
      if(marker[ctr].y>yMax) yMax=marker[ctr].y;
    };

    for(ctr=0;ctr<=1;ctr++) {
      co=getValue(ctr);
      if(co.x<xMin) xMin=co.x;
      if(co.x>xMax) xMax=co.x;
      if(co.y<yMin) yMin=co.y;
      if(co.y>yMax) yMax=co.y;
    };

    result=new DoubleRect(xMin,yMin,xMax-xMin,yMax-yMin);
    return result;
  };

  //fuction to set a marker, overrides Curve
  public void setMarker(int i,double x,double y) {
    int ctr;
    Coordinate co;

    if(marker!=null) //array initialised
    if(i>=0 && i<marker.length) {
      marker[i].x=x;
      marker[i].y=y;
      xText[i].setText(new PrintableDouble(x).asStringSF(3));
      yText[i].setText(new PrintableDouble(y).asStringSF(3));
    };
  };

  //Calculates Bernstein weight for a point
  protected double bernsteinWeight(int pointNumber,double t) {
    double tStretched,tau;
    double weight; //weight of this point

    tStretched=t*(numPoints-1); //location of t on 0..numPoints-1 scale
    tau=tStretched-pointNumber; //location of pointNumber on t scale
    tau=Math.abs(tau); //Gaussian is symmetric

    if(tau<=1)
      weight=(4-6*Math.pow(tau,2)+3*Math.pow(tau,3))/6;
    else if(tau<=2)
      weight=Math.pow((2-tau),3)/6;
    else
      weight=0;
    return weight;
  };

  //Calculates bernstein approximation for a point
  protected Coordinate bernsteinCubic(int pointNumber,double t) {
    double weight; //weight of this point
    Coordinate returnValue;

    weight=bernsteinWeight(pointNumber,t);
    returnValue=new Coordinate(marker[pointNumber]);
    returnValue.mul(weight);
    return returnValue;
  };

  //for param t 0 to 1, return x and y
  public Coordinate getValue(double t) {
    Coordinate result;
    int ctr;
    result=new Coordinate(0,0);
    for(ctr=0;ctr<numPoints;ctr++)
      result.add(bernsteinCubic(ctr,t));
    return result;
  };

  //fuction to draw markers and construction lines when called
  public void drawConstructs() {
    int ctr;
    if(drawCurve!=null) {
      for(ctr=0;ctr<numPoints;ctr++) {
        if(ctr>0) {
          drawCurve.drawMarker(this,marker[ctr]);
          drawCurve.drawLine(this,marker[ctr-1],marker[ctr]);
        } else
          drawCurve.drawMarkerP0(this,marker[ctr]);
      }
    };
  };

  //function dealing with changes in numbers
  protected void onActionPerformed(java.awt.event.ActionEvent event) {
    int ctr;
    for(ctr=0;ctr<numPoints;ctr++)
      marker[ctr]=new Coordinate(xText[ctr].asDouble(),yText[ctr].asDouble());
    if(drawCurve!=null) drawCurve.repaint();
  };

  //Function to draw construction lines for animation sequences
  public void drawAnimationConstructs(double t) {
    int ctr;
    double weight,heavyWeight; //heavyWeight will reach 1.0 on gaussian
    Color lineColor;
    if(drawCurve!=null) {
      for(ctr=0;ctr<numPoints;ctr++) {
        weight=bernsteinWeight(ctr,t);
        if((int)(weight*10)!=markerSize[ctr]) {
          drawCurve.drawLargerMarker(this,marker[ctr],markerSize[ctr]);
          markerSize[ctr]=(int)(weight*10);
          drawCurve.drawLargerMarker(this,marker[ctr],markerSize[ctr]);
        };
        if(weight>0) {
          heavyWeight=1.5*weight; //0 to 1
          lineColor=new Color(
            (int)(255-heavyWeight*(255-color.getRed())),
            (int)(255-heavyWeight*(255-color.getGreen())),
            (int)(255-heavyWeight*(255-color.getBlue())));
          drawCurve.drawLine(this,marker[ctr],getValue(t),lineColor);
        };
      };
    };
  };

  //Clean up required, as not every frame drawn
  public void cleanAnimationConstructs(double t) {
    int ctr;
    if(drawCurve!=null) {
      for(ctr=0;ctr<numPoints;ctr++) {
        if(markerSize[ctr]!=0) {
          drawCurve.drawLargerMarker(this,marker[ctr],markerSize[ctr]);
          markerSize[ctr]=0;
          drawCurve.drawMarker(this,marker[ctr]);
        };
      };
    };
  };
};
