import java.applet.*;
import java.awt.*;
import java.util.*;
import java.lang.*;
import FunctionCurve;
import FloatPoint;

public class LiftScheme extends Applet {
  
  public void init() {
    setLayout( new BorderLayout(2, 2));
    setFont( new Font( "Helvetica", Font.BOLD, 14) );
    DrawPanel dp = new DrawPanel();
    CurveControls cc = new CurveControls(dp);
    add( "Center", dp );
    add( "South", cc);
    add( "North", new EditControls(dp, cc));
  }
}

class DrawPanel extends Panel { 

  FunctionCurve curve;
  FloatPoint selected = null;
  boolean able = true, ghosts = false;
  int func, drawmode;
  Vector oldcurves = new Vector(5);

  static final int POINTS = 33;  /*should be 2^n + 1*/
  static final int MIDPOINT = POINTS/2;
  static final int LINEAR = 3;
  static final int STEP = 1;
  static final int DELTA = 2;
  static final int FREE = 0;
  static final int BASECOLOR = 50;
  static final int WAVELET = 0;
  static final int SCALING = 1;

  public DrawPanel() {
    curve = new FunctionCurve();
    curve.setOrigin(250, 250);  //AHHHHHHHHH!;
    setBackground( Color.white );
  }

  public void clearPoints() {
    selected = null;
    curve.clear();
    drawFunc();
    if(ghosts)
      oldcurves.removeAllElements();
    repaint();
  }
  
  public boolean mouseDown(Event e, int x, int y) {
    selected = null;
    selected = curve.findPoint(x,y);
    if(selected != null)
      repaint();
    return true;
  }

  public boolean mouseUp(Event e, int x, int y) {
    if((selected == null) && able) {
      FloatPoint a = new FloatPoint(x,y);
      curve.addPoint(a);
      repaint();
    }	
    return true;
  }

  public boolean mouseDrag(Event e, int x, int y) {
    if(selected != null) {
      if(curve.depth != 1) {
	curve.movePoint(selected, (int)(selected.x+(curve.origin).x), y);
	if(ghosts)
	  updateGhosts();
      }
      else {
	if(curve.overlapping(selected, x))
	  return true;
	curve.movePoint(selected,x,y);
      }
      repaint();
    }
    return true;
  }
  
  public boolean keyUp(Event e, int k) {
    if(able&&(k==127)&&(selected!=null)) {
      curve.killPoint(selected);
      selected = null;
      repaint();
      return true;
    }
    return false;
  }
  
  public void enable() {able=true;}
  public void disable() {able=false;}

  public void setDrawMode(int mode) { 
    drawmode = mode;
    repaint();
  }

  public void setGhosts(boolean state) { 
    ghosts = state;
    if(ghosts)
      updateGhosts();
    repaint();
  }

  public void updateGhosts() {
    oldcurves.removeAllElements();
    FunctionCurve tempcurve = (FunctionCurve)curve.clone();
    for(int i = curve.depth/2; i>=1; i /= 2) {
      tempcurve.lift();
      oldcurves.addElement(tempcurve.clone());
    }
  }

  public boolean drop() {
    if( curve.canDrop() ) {
      if(ghosts)
	oldcurves.insertElementAt(curve.clone(), 0);
      curve.drop();
      disable();
      selected = null;
      repaint();
      return true;
    }
    else
      return false;
  }

  public void lift() {
    if(ghosts)
      oldcurves.removeElementAt(0);
    curve.lift();
    repaint();
  }

  public void setFunc(String s) {
    if(s.equals(new String("Free")) && (func != FREE))
      func = FREE;
    else
      if(s.equals(new String("Linear")) && (func != LINEAR))
	func = LINEAR;
      else
	if(s.equals(new String("Delta")) && (func != DELTA))
	  func = DELTA;
	else
	  if(s.equals(new String("Step")) && (func != STEP))
	    func = STEP;
	  else
	    return;
    //clearPoints();
  }

  public void drawFunc() {
    
    int sep = (this.size()).width / POINTS;
    int midy = (this.size()).height / 2;
    
    switch(func) {
    case FREE: break;
    case LINEAR:
      for(int i=0; i<POINTS; i++)
	curve.addPoint(new FloatPoint(sep*i,midy));
      break;
    case DELTA:
      for(int i=0; i<POINTS; i++)
	if(i == MIDPOINT)
	  curve.addPoint(new FloatPoint(sep*i,midy-midy/2));
	else
	  curve.addPoint(new FloatPoint(sep*i,midy));
      break;
    case STEP:
      for(int i=0; i<POINTS; i++)
	if((i >= (MIDPOINT-1)) && (i <= (MIDPOINT+1)))
	  curve.addPoint(new FloatPoint(sep*i,midy-midy/2));
	else
	  curve.addPoint(new FloatPoint(sep*i,midy));
      break;
    }
  }

  public void paint( Graphics g ) {
    g.clearRect(0,0,(this.size()).width, (this.size()).height);
    g.setColor(Color.black);
    switch(drawmode) {
    case SCALING:	
      curve.smoothScalingCurve();
      break;
    case WAVELET:
      curve.smoothWaveletCurve();
      break;
    }
    curve.drawCurve(g);
    curve.drawPoints(g);
    if(ghosts && (oldcurves.size() != 0)) {
      int inc = (255-BASECOLOR)/oldcurves.size();
      int c = BASECOLOR;
      for(int i=0; i<oldcurves.size(); i++) {
	g.setColor(new Color(c,c,c));
	switch(drawmode) {
	case WAVELET:
	  ((FunctionCurve)oldcurves.elementAt(i)).smoothWaveletCurve();
	  break;
	case SCALING:
	  ((FunctionCurve)oldcurves.elementAt(i)).smoothScalingCurve();
	  break;
	}
	((FunctionCurve)oldcurves.elementAt(i)).drawCurve(g);
	c += inc;
      }
    }
    if(selected != null) {
      g.setColor(Color.red);
      curve.drawPoint(g, selected);
    }
    //System.out.println(curve.getIntegral());
  }
}

class EditControls extends Panel {

  DrawPanel tdp;
  CurveControls tcc;
  Button clear, drawmode;
  Checkbox ghosts;

  static final String scalingstring = new String("Scaling");
  static final String waveletstring = new String("Wavelet");

  public EditControls( DrawPanel dp, CurveControls cc ) {
    tdp = dp;
    tcc = cc;
    setLayout( new FlowLayout(FlowLayout.CENTER));
    setBackground( Color.lightGray );
    add( clear = new Button("Reset"));
    add( ghosts = new Checkbox("Ghosts"));
    add( drawmode = new Button(scalingstring));
    tdp.setDrawMode(tdp.SCALING);
    ghosts.setState(false);
  }
  
  public void paint( Graphics g ) {
    Rectangle r = bounds();
    g.setColor( Color.lightGray );
    g.draw3DRect( 0, 0, r.width, r.height, true );
  }

  public boolean action( Event e, Object arg ) {
    if(e.target == clear) {
      tdp.clearPoints();
      tdp.enable();
      tdp.selected = null;
      tdp.repaint();
      tcc.enable();
      tcc.reset();
      return true;
    }
    if(e.target == ghosts) {
      tdp.setGhosts(ghosts.getState());
      return true;
    }
    if(e.target == drawmode) {
      if(drawmode.getLabel() == waveletstring) {
	tdp.setDrawMode(tdp.SCALING);
	drawmode.setLabel(scalingstring);
      }
      else {
	tdp.setDrawMode(tdp.WAVELET);
	drawmode.setLabel(waveletstring);
      }
      return true;
    }
    return false;
  }
}

class CurveControls extends Panel {
  
  MainControls mc;
  ScrollsControls sc;

  public CurveControls (DrawPanel t) {
    setLayout(new GridLayout(2,1));
    setBackground( Color.lightGray );
    mc = new MainControls(t);
    add( mc );
    sc = new ScrollsControls(t, mc);
    add( sc );
  }

  public void paint(Graphics g) {
    Rectangle r = bounds();
    g.setColor( Color.lightGray );
    g.draw3DRect( 0, 0, r.width, r.height, true );
  }

  public void enable() {mc.enable();}

  public void reset() {
    sc.d = sc.S_MIN;
    (sc.scroll).setValue(sc.S_MIN);
    (sc.depth).setText("Depth: " + sc.d);
  }
}

class MainControls extends Panel {

  DrawPanel target;

  Choice
    predict = new Choice(),
    update = new Choice(), 
    reg = new Choice();
  String[]
    predicts = {"2","4","6"},
    updates = {"0","2","4"},
    regs = {"Regular","Irregular"};
  
  public MainControls(DrawPanel t) {
    target = t;	
    setLayout( new FlowLayout( FlowLayout.LEFT ) );
    setBackground( Color.lightGray );
    
    for(int i=0; i<predicts.length; i++)
      predict.addItem(predicts[i]);    
    for(int i=0; i<updates.length; i++)
      update.addItem(updates[i]);
    for(int i=0; i<regs.length; i++)
      reg.addItem(regs[i]);
    
    add(new Label("Predict:"));
    predict.select(predicts[0]);
    add(predict);
    (target.curve).p = Integer.parseInt(predict.getSelectedItem());
    
    add(new Label("  Update:"));
    update.select(updates[0]);
    add(update);
    (target.curve).u = Integer.parseInt(update.getSelectedItem());
    
    add(new Label("  Spacing:"));
    reg.select(regs[0]);
    add(reg);
    (target.curve).reg = true;  //set to regular;
  }
  
  public void paint( Graphics g ) {
    Rectangle r = bounds();
    g.setColor( Color.lightGray );
    g.draw3DRect( 0, 0, r.width, r.height, true );
  }
  
  public void disable() {
    predict.disable();
    update.disable();
    reg.disable();
  }

  public void enable() {
    predict.enable();
    update.enable();
    reg.enable();
  }

  public boolean action(Event e, Object arg) {
    if(e.target == predict) {
      (target.curve).p = Integer.parseInt(predict.getSelectedItem());
      if((target.curve).u>(target.curve).p) {
	 update.select(Integer.toString((target.curve).p));
	 (target.curve).u = (target.curve).p;
      }
      target.repaint();
      return true;
    }
    if(e.target == update) {
      (target.curve).u = Integer.parseInt(update.getSelectedItem());
      if((target.curve).u>(target.curve).p) {
	 update.select(Integer.toString((target.curve).p));
	 (target.curve).u = (target.curve).p;
      }
      return true;
    }
    if(e.target == reg) {
      String s = new String(reg.getSelectedItem());
      if(s.compareTo("Regular") == 0)
	(target.curve).reg = true;
      else (target.curve).reg = false;
      return true;
    }
    return false;
  }
}   

class ScrollsControls extends Panel {
  static final int S_MAX = 10;
  static final int S_MIN = 0;

  Scrollbar scroll = new Scrollbar(Scrollbar.HORIZONTAL, 0, 20, S_MIN, S_MAX);
  Label depth = new Label();
  Choice func = new Choice();
  String funcs[] = { "Free", "Linear", "Delta", "Step" };
  MainControls mct;
  DrawPanel dpt;
  int d = S_MIN;
  
  public ScrollsControls ( DrawPanel dp, MainControls mc) {
    dpt = dp;
    mct = mc;
    setLayout( new FlowLayout( FlowLayout.LEFT ) );
    setBackground( Color.lightGray );
    
    add( new Label("<-Inverse"));
    add( scroll );
    add( new Label("Forward-> "));
    add( depth );
    scroll.setValue(S_MIN);
    depth.setText("Depth: " + d);
    for(int i=0; i<funcs.length; i++)
      func.addItem(funcs[i]);
    func.select(funcs[0]);
    add( func );
  }

  public void paint( Graphics g ) {
    Rectangle r = bounds();
    g.setColor( Color.lightGray );
    g.draw3DRect( 0, 0, r.width, r.height, true );
  }

  public boolean handleEvent(Event e) {
    if(e.target == scroll) {
      if(scroll.getValue() > d)
	if( dpt.drop() ) {
	  d = scroll.getValue();
	  depth.setText("Depth: " + d);
	  mct.disable();
	  return true;
	}
	else
	  scroll.setValue(d);
      if(scroll.getValue() < d) {
	d = scroll.getValue();
	depth.setText("Depth: " + d);
	if(d == S_MIN) {
	  dpt.enable();
	  mct.enable();
	}
	dpt.lift();
	return true;
      }
    }
    if(e.target == func) {
      dpt.setFunc(new String(func.getSelectedItem()));
      return true;
    }
    return false;
  }
  
}








