import java.awt.*;
import java.awt.geom.*;
class BSCu 
 {
  static M parent;
  Te te;
  static LC lc;
  static final double PI=Math.PI;
  static final double DR=PI/180;
  static final double RD=180/PI;
  int deg=2;
  int tpts,spts;
  int cp1=-1,cp2=-1;
  double inc=.05;
  double[] knots;
  int mi=-1;
  int bi=0;
  int ei=0;
  double x=0,y=0;
  int xi=0,yi=0;
  double scx=1,scy=1;
  double orgpw=3;
  Point p;
  Point2D p2d;
  dP dp;
  dP[] dpa=new dP[0];
  boolean[] boa=new boolean[0];
  Rec[] reca=new Rec[0];
  int omc=-1,msdp=-1;
  int pmode=1;
  dP odp;
  Graphics2D g;
  boolean closed=false;
  boolean handles=true;
  boolean cs=true;
  String name;
  double pw=3;
  K pk=new K(0.0,0.0,0.0,255);
  K shadk=new K(Color.black);
  double shadw=0;
  double snap=10;
  public BSCu() { }
  public BSCu(LC a) 
   {
    lc=a;
    g=lc.offsg;
   }
  public BSCu(String name,boolean closed,boolean handles,dP[] dpa)
   {
    tpts=dpa.length;
    spts=tpts;
    boa=new boolean[tpts];
    boa[0]=true;
    boa[tpts-1]=true;
    new BSCu(name,closed,handles,dpa,boa,pw,pk,shadw,shadk);
   }
  public BSCu(String name,boolean closed,boolean handles,dP[] dpa,boolean[] boa,double pw,K pk,double shadw,K shadk)
   {
    g=lc.offsg;
    this.name=name;
    this.closed=closed;
    this.handles=handles;
    this.dpa=dpa;
    this.boa=boa;
    this.pk=pk;
    this.pw=pw;
    this.shadk=shadk;
    this.shadw=shadw;
    tpts=dpa.length;
    if(boa.length != tpts)
     {
      boa=new boolean[tpts];
      boa[0]=true;
      boa[tpts-1]=true;
     }
    mkreca();
   }
  void setdeg(int a)
   {
    deg=a;
    rendera(true);
   }
  void setinc(double a)
   {
    inc=a;
    rendera(true);
   }
  void setclosed(boolean b)
   {
    closed=b;
    render();
   }
  void sethandles(boolean b)
   {
    handles=b;
    render();
   }
  void setdpa(dP[] a)
   {
    dpa=a;
    tpts=dpa.length;
    mkreca();
    render();
   }
  void setboa(boolean[] a)
   {
    if(tpts!=a.length) return;
    boa=a;
    render();
   }
  void setpk(K a)
   {
    pk=a;
    render();
   }
  void setshadk(K a)
   {
    shadk=a;
    render();
   }
  void setpw(double a)
   {
    pw=a;
    render();
   }
  void setshadw(double a)
   {
    shadw=a;
    render();
   }
  void scale(dP dp)
   {
    scx=dp.x;
    scy=dp.y;
    for(int i=0;i<tpts;i++) dpa[i].scale(scx,scy);
    mkreca();
   }
  void closeseg() 
   {
    closed=true; 
    render();
   }
  void afdP()
   {
    if(cp1 == -1 || cp2 == -1 || Math.abs(cp1-cp2) != 1 ) return;
    int pa=cp1;
    int pb=cp2;
    if(cp1>cp2)
     {
      pa=cp2;
      pb=cp1;
     }
    dP a=new dP(dP.ave(dpa[cp1],dpa[cp2]));
    //System.out.println("a.x:"+a.x);
    //System.out.println("a.y:"+a.y);
    dP[] dpb=new dP[tpts];
    boolean[] bob=new boolean[tpts];
    for(int i=0;i<tpts;i++)
     {
      dpb[i]=dpa[i];
      bob[i]=boa[i];
     }
    dpa=new dP[++tpts];
    boa=new boolean[tpts];
    int i=0;
    for(i=0;i<=pa ;i++) 
     {
      dpa[i]=dpb[i];
      boa[i]=bob[i];
     }
    dpa[pb]=a;
    boa[pb]=false;
    for(i=pb;i<tpts-1;i++)
     {
      dpa[i+1]=dpb[i];
      boa[i+1]=bob[i];
     }
    mkreca();
    render();
   }
  void rmdP()
   {
    if(cp1 == -1 || tpts ==0 ) return;
    tta("tpts:"+tpts);
    dP[] dpb=new dP[tpts - 1 ];
    boolean[] bob=new boolean[tpts - 1 ];
    for(int i=0;i<cp1;i++)
     {
      dpb[i]=dpa[i];
      bob[i]=boa[i];
     }
    for(int i=cp1+1;i<tpts;i++)
     {
      dpb[i-1]=dpa[i];
      bob[i-1]=boa[i];
     }
    dpa=new dP[--tpts];
    boa=new boolean[tpts];
    for(int i=0;i<tpts;i++) 
     {
      dpa[i]=dpb[i];
      boa[i]=bob[i];
     }
    mkreca();
    cp1=-1;
    render();
   }
  void snap(double d)
   {
    snap=d;
    if(snap==0) return;
    for(int i=0;i<dpa.length;i++) dpa[i]=dP.snap(dpa[i],snap);
    render();
   }
  void render()
   {
    rendera(false);
    renderb(true);
   }
  void rendera(boolean bo)
   {
    orgpw=lc.pw;
    if(shadw !=0) 
     {
      lc.spw(shadw);
      lc.offsgsetColor(shadk.c);
      renderaa(bo);
     }
    lc.spw(pw);
    lc.offsgsetColor(pk.c);
    //System.out.println("pw:"+pw+"  pk:"+pk.c);
    renderaa(bo);
    lc.spw(orgpw);
   }
  void renderaa(boolean bo)
   {
    int i=1;
    ei=0;
    bi=0;
    while(i<tpts)
     {
      if(boa[i])
       {
        ei=i;
        spts=ei-bi+1;
        if(spts ==2) deg=1;
        else deg=2;
        knotSequencer();
        double a=knots[deg-1];
        double b=knots[knots.length-deg];
        dP aa=bSpRecur(deg,0,a);
        dP bb;
        while (a< b-inc)
         {
          a+=inc;
          bb=bSpRecur(deg,0,a);
          lc.offsgdraw(new Line2D.Double(aa,bb)); 
          aa=bb;
         }
        bb=bSpRecur(deg,0,b-0.000001F);
        lc.offsgdraw(new Line2D.Double(aa,bb));
        bi=i;
       }
      i++;
     }
    if(closed)// assume that boa[0] is true
     {
      ei=0;
      spts=tpts-bi+1;
      if(spts ==2) deg=1;
      else deg=2;
      knotSequencer();
      double a=knots[deg-1];
      double b=knots[knots.length-deg];
      dP dpb=bSpRecur(deg,0,a);
      dP dpc;
      while (a< b-inc)
       {
        a+=inc;
        dpc=bSpRecur(deg,0,a);
        lc.offsgdraw(new Line2D.Double(dpb,dpc));
        dpb=dpc;
       }
      dpc=bSpRecur(deg,0,b-0.0000001F);
      lc.offsgdraw(new Line2D.Double(dpb,dpc));
     }
    if(bo) lc.repaint();
   }
  void renderb(boolean bo)
   {
    //System.out.println("handles:"+handles);
    if(! handles) return;
    lc.spw(1);
    //lc.offsgsetColor(Color.green.darker());
    for(int a=0;a<tpts-1;a++) lc.offsgdraw(new Line2D.Double(dpa[a],dpa[a+1]));
    if(closed) lc.offsgdraw(new Line2D.Double(dpa[tpts-1],dpa[0]));
    //lc.offsgsetColor(Color.blue);
    for(int a=0;a<tpts;a++) lc.offsgfill(reca[a].rect);
    lc.offsgsetColor(Color.black);
    if(bo) lc.repaint();
    lc.spw(orgpw);
   }
  boolean mmdp(dP dp)
   {
    for(int i=0;i<tpts;i++)
     {
      if(reca[i].contains(dp))
       {
        mi=i;
        //lc.offsgsetColor(Color.cyan);
        lc.offsgfill(reca[i].rect);
        if(cp1 !=i)
         {
          if(cp2 != -1)
           {
            lc.offsgsetColor(Color.black);
            lc.offsgfill(reca[cp2].rect);
           }
          cp2=cp1;
          cp1=i;
          if(cp2 != -1)
           {
            //lc.offsgsetColor(Color.green);
            lc.offsgfill(reca[cp2].rect);
           }
         }
        lc.offsgsetColor(Color.black);
        lc.repaint();
        return true;
       }
      if(mi==i)
       {
        mi=-1;
        //lc.offsgsetColor(Color.yellow);
        lc.offsgfill(reca[i].rect);
        lc.offsgsetColor(Color.black);
        lc.repaint();
       }
     }
    return false;
   }
  boolean mpdp(dP dp)
   {
    for(int i=0;i<tpts;i++) 
     {
      if(reca[i].contains(dp))
       {
        msdp=i;
        odp=new dP(dpa[msdp]);
        render();
        return true;
       }
     }
    return false;
   }
  void deldP(int i)
   {
    boolean[] boaa=new boolean[tpts-1];
    dP[] dpaa=new dP[tpts-1];
    for(int j=0;j<=i;j++)
     {
      boaa[j]=boa[j];
      dpaa[j]=dpa[j];
     }
    for(int j=1+i;j<tpts;j++)
     {
      boaa[j-1]=boa[j];
      dpaa[j-1]=dpa[j];
     }
    tpts--;
    boa=new boolean[tpts];
    dpa=new dP[tpts];
    for(int j=0;j<tpts;j++)
     {
      boa[j]=boaa[j];
      dpa[j]=dpaa[j];
     }
    mkreca();
   }
  void mkreca()
   {
    reca=new Rec[tpts];
    for(int j=0;j<tpts;j++) reca[j]=new Rec(dpa[j].sub(4,4),8,8);
   }
  boolean mcdp(dP dp)
   {
    for(int i=0;i<tpts;i++)
     {
      if(reca[i].contains(dp))
       {
         {
          if(pmode==4) deldP(i);
          else if(pmode==3) afdP();
          else if(pmode==2) boa[i]= !boa[i];
          else if(pmode==1);
         }
        return true;
       }
     }
    return false;
   }
  void mddp(dP dp)
   {
    dpa[msdp]=dp;
    int j=msdp%tpts;
    if(j==100) dpa[msdp+2]=dpa[msdp+1].sym(dpa[msdp]);
    else if(j==101)
     {
      dpa[msdp+1]=dpa[msdp].sub(odp.sub(dpa[msdp+1]));
      dpa[msdp-1]=dpa[msdp].sub(odp.sub(dpa[msdp-1]));
     }
    else if(j==102) dpa[msdp-2]=dpa[msdp-1].sym(dpa[msdp]);
    for(int i=msdp-j;i<msdp-j+tpts;i++) reca[i]=new Rec(dpa[i].sub(4,4),8,8);
    odp=dp;
    if(cs)  lc.clearscreen();
    render();
   }
  void mrdp(dP dp)
   {
    msdp=-1;
    render();
   }
  void ed()
   {
    tta1(name+".closed "+Te.Svo(closed));
    tta1(name+".handles "+Te.Svo(handles));
    tta1(name+".dpa "+Te.Svo(dpa));
    tta1(name+".boa "+Te.Svo(boa));
    tta1(name+".deg "+Te.Svo(deg));
    tta1(name+".inc "+Te.Svo(inc));
    tta1(name+".tpts "+Te.Svo(tpts));
    tta1(name+".pmode "+Te.Svo(pmode));
    tta1(name+".pw "+Te.Svo(pw));
    tta1(name+".pk "+Te.Svo(pk));
    tta1(name+".shadw "+Te.Svo(shadw));
    tta1(name+".shadk "+Te.Svo(shadk));
   }
  static void tta1(String s) { parent.tta1(s); }
  static void tta(String s) { parent.tta(s); }
  public dP bSpRecur(int l, int k, double p)
   {
    int i = 0;
    while (p>=knots[i]) i++;
    int a=i-deg;
    if (l==0) 
     {
      int t=bi+a+k;
      if(t>=tpts) return dpa[t-tpts];
      return dpa[t];
     }
    else
     {
      double alpha =(p -knots[k+l-1+a])/(knots[k+deg+a]-knots[k+l-1+a]);
      dP A=bSpRecur(l-1,k,p);
      dP B=bSpRecur(l-1,k+1,p);
      return new dP((1.0F-alpha)*A.x+alpha*B.x,(1.0F-alpha)*A.y+alpha*B.y);
     }
   }
  public void knotSequencer()
   {
    int knotTotal=deg+spts-1;
    knots=new double[knotTotal];
    int i=0;
    while (i<deg) knots[i++]=0.0;
    while (i<spts)
     {
      knots[i]=(i-deg+1)* 1.0;
      i++;
     }
    while (i<knotTotal) knots[i++]=(spts-deg)*1.0;
   }
 }
