package curve;

import curve.*;
import java.awt.*;

public class BSplineCurve extends FreeBSplineCurve {

  //empty constructor (no layout)
  protected BSplineCurve() {
    super();
  };
  
  // Creates new BSplineCurve
  public BSplineCurve(DrawCurve aDrawCurve) {
    super(aDrawCurve);
    int ctr;
    titleLabel.setText("Clamped B-Spline Curve");
    markerSize=new int[numPoints+2];
    for(ctr=-1;ctr<=numPoints;ctr++) markerSize[ctr+1]=0;
  }

  //Get maximum and minimum values of x,y on curve including phantom knots
  public DoubleRect getMinMax() {
    int ctr;
    double yMin,yMax;
    DoubleRect result;
    Coordinate currentMarker;
    
    xMin=xMax=getMarkerNumber(-1).x;
    yMin=yMax=getMarkerNumber(-1).y;
    for(ctr=0;ctr<=numPoints;ctr++) {
      currentMarker=getMarkerNumber(ctr);
      if(currentMarker.x<xMin) xMin=currentMarker.x;
      if(currentMarker.x>xMax) xMax=currentMarker.x;
      if(currentMarker.y<yMin) yMin=currentMarker.y;
      if(currentMarker.y>yMax) yMax=currentMarker.y;
    };
    
    result=new DoubleRect(xMin,yMin,xMax-xMin,yMax-yMin);
    return result;
  };

  //Retrieves marker from a number, includes phantom knots
  protected Coordinate getMarkerNumber(int markerNumber) {
    Coordinate returnValue;
    if(markerNumber==-1) {
      returnValue=new Coordinate(marker[0]);
      returnValue.mul(2);
      returnValue.sub(marker[1]);
    } else if(markerNumber==numPoints) {
      returnValue=new Coordinate(marker[numPoints-1]);
      returnValue.mul(2);
      returnValue.sub(marker[numPoints-2]);
    } else
      returnValue=new Coordinate(marker[markerNumber]);
    return returnValue;
  };
  
  //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=getMarkerNumber(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=-1;ctr<=numPoints;ctr++)
      result.add(bernsteinCubic(ctr,t));
    return result;
  };

  //fuction to draw markers and construction lines when called
  public void drawConstructs() {
    int ctr;
    Color lightColor; //Light Colour for phantoms
    
    if(drawCurve!=null) {
      lightColor=new Color(
        (int)(255-0.5*(255-color.getRed())),
        (int)(255-0.5*(255-color.getGreen())),
        (int)(255-0.5*(255-color.getBlue())));
      for(ctr=-1;ctr<=numPoints;ctr++) {
        if(ctr==-1) {
          drawCurve.drawMarker(this,getMarkerNumber(ctr),lightColor);
        } else if(ctr==0) {
          drawCurve.drawMarkerP0(this,getMarkerNumber(ctr));
          drawCurve.drawLine(this,getMarkerNumber(ctr-1),getMarkerNumber(ctr),
            lightColor);
        } else if(ctr==numPoints) {
          drawCurve.drawMarker(this,getMarkerNumber(ctr),lightColor);
          drawCurve.drawLine(this,getMarkerNumber(ctr-1),getMarkerNumber(ctr),
            lightColor);
        } else {
          drawCurve.drawMarker(this,getMarkerNumber(ctr));
          drawCurve.drawLine(this,getMarkerNumber(ctr-1),getMarkerNumber(ctr));
        };
      };
    };
  };

  //Function to draw construction lines for animation sequences
  //Marker numbers -1 to numpoints translated to 0 to numpoints+1
  public void drawAnimationConstructs(double t) {
    int ctr;
    double weight,heavyWeight; //heavyWeight will reach 1.0 on gaussian
    Color lineColor,lightColor;
    if(drawCurve!=null) {
      lightColor=new Color(
        (int)(255-0.5*(255-color.getRed())),
        (int)(255-0.5*(255-color.getGreen())),
        (int)(255-0.5*(255-color.getBlue())));
      for(ctr=-1;ctr<=numPoints;ctr++) {
        weight=bernsteinWeight(ctr,t);
        if((int)(weight*10)!=markerSize[ctr+1]) {
          if(ctr==-1 || ctr==numPoints) 
            drawCurve.drawLargerMarker(this,getMarkerNumber(ctr),
              markerSize[ctr+1],lightColor);
          else
            drawCurve.drawLargerMarker(this,getMarkerNumber(ctr),
              markerSize[ctr+1]);
          markerSize[ctr+1]=(int)(weight*10);
          if(ctr==-1 || ctr==numPoints) 
            drawCurve.drawLargerMarker(this,getMarkerNumber(ctr),
              markerSize[ctr+1],lightColor);
          else
            drawCurve.drawLargerMarker(this,getMarkerNumber(ctr),
              markerSize[ctr+1]);
        };
        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,getMarkerNumber(ctr),getValue(t),lineColor);
        };
      };
    };
  };

  //Clean up required
  public void cleanAnimationConstructs(double t) {
    int ctr;
    Color lightColor;
    if(drawCurve!=null) {
      lightColor=new Color(
        (int)(255-0.5*(255-color.getRed())),
        (int)(255-0.5*(255-color.getGreen())),
        (int)(255-0.5*(255-color.getBlue())));
      for(ctr=-1;ctr<=numPoints;ctr++) {
        if(markerSize[ctr+1]!=0) {
          if(ctr==-1 || ctr==numPoints) 
            drawCurve.drawLargerMarker(this,getMarkerNumber(ctr),
            markerSize[ctr+1],lightColor);
          else
            drawCurve.drawLargerMarker(this,getMarkerNumber(ctr),
              markerSize[ctr+1]);
          markerSize[ctr+1]=0;
          if(ctr==-1 || ctr==numPoints) 
            drawCurve.drawMarker(this,getMarkerNumber(ctr),lightColor);
          else
            drawCurve.drawMarker(this,getMarkerNumber(ctr));
        };
      };
    };
  }; 

};
