import java.applet.Applet;
import java.awt.*;

public class MMorph extends Applet {
    
    public MMobject source, current, target; 
    private Scrollbar scrollbar; 
    private Button morph;
    private TextField increment, percentage, lambda;
    // increment and percentage values
    private float incr, perc, lamb;
    private boolean sync = true;
    private final float syncc = 100;
    private Checkbox synchronize;
    public static final Color lightBlue = new Color(150,150,250);
    
    // the morphable data
    private MMmdata mdata;
    
    // Graphics g;
//    boolean drawing;

    /**
     * Initializes the applet.  You never need to call this directly; it is
     * called automatically by the system once the applet is created.
     */
    public void init() {
    
	// initialize the objects
	source =  new MMobject ("Source");
        target =  new MMobject ("Target");
	current = new MMobject ("Current");
	
	// initialize some shared control stuff from the middle panel
	morph = new Button ("Morph");
	perc = (float) 50.00;
	incr = (float) 140;
	lamb = (float) 1;
	percentage = new TextField();
	increment = new TextField();
	lambda = new TextField();
	synchronize = new Checkbox("Synchronize");
	sync = true; 
	synchronize.setState(sync);
	set_string_perc();
	set_string_incr();
	set_string_lamb();
	scrollbar = new Scrollbar(Scrollbar.VERTICAL, (int) perc, 10, 0, 100);

	setLayout(new GridLayout(3,4, 12, 10));
	setBackground(Color.white); 
//	resize(780, 550);
	
	// set the buttons and scrollbars of the left side
	initControls();
	
	// some colors to make it
	initColors();
	
	// fix panels
	initBasicPanel(source); 
	initBasicPanel(current); 
	initBasicPanel(target); 

	initData();

	setMorph();
	doMorph();

    }

    /*
     * Called to start the applet.  You never need to call this directly; it
     * is called when the applet's document is visited.
     */
    public void start() {
//	resize(bounds().x, bounds().y);
	source.repaint();
	target.repaint();
	current.repaint();
    }

    /**
     * Called to stop the applet.  This is called when the applet's document is
     * no longer on the screen.  It is guaranteed to be called before destroy()
     * is called.  You never need to call this method directly
     */
    public void stop() {
	
    }

    /**
     * Cleans up whatever resources are being held.  If the applet is active
     * it is stopped.
     */
    public void destroy() {
    }

    // 
    // Set Default Values for the data
    //
    private void initData() {
	source.data = new MMdata(); 
	source.data.addPoint(new MMpoint(40, 30));
	source.data.addPoint(new MMpoint(30, 60));
	source.data.addPoint(new MMpoint(60, 90));
	source.data.addPoint(new MMpoint(100, 110));
	source.data.addPoint(new MMpoint(130, 50));
	source.data.addFinalNormal();
	source.poly.temp.readData(source.data);
	source.repaint();

	target.data = new MMdata(); 
	target.data.addPoint(new MMpoint(30, 30));
	target.data.addPoint(new MMpoint(20, 110));
	target.data.addPoint(new MMpoint(140, 110));
	target.data.addPoint(new MMpoint(130, 30));
	target.data.addPoint(new MMpoint(70, 20));
	target.data.addFinalNormal();
	target.poly.temp.readData(target.data);
	target.repaint();
}

    //
    // This creates the basic panels = 3 copies for source current target
    //
    private void initBasicPanel (MMobject m) {
	
	add(m.controls);
	add(m.poly); // linked in is the poly rep of the object

	// ECI panel holds the Extended Circular Image representation
	add(m.eci); // linked in is the eci rep of the object
		
	// Graph panel holds the Extended Circular Image representation
	add(m.graph); // linked in is the graph rep of the object

    }


    // Set the buttons on the left
    private void initControls () {

	// the buttons of the source GUI
	source.controls.setLayout(new GridLayout(5,1));
	source.controls.add(new Button("Clear Source"));
	source.controls.add(new Button("Set Source"));
	source.controls.add(new Button("Center Source"));
	source.controls.add(new Button("Scale to Target"));
	source.controls.add(new Button("Revert Source"));
	
	// the buttons of the target GUI
	target.controls.setLayout(new GridLayout(5,1)); 
	target.controls.add(new Button("Clear Target"));
	target.controls.add(new Button("Set Target"));
	target.controls.add(new Button("Center Target"));
	target.controls.add(new Button("Scale to Source"));
	target.controls.add(new Button("Revert Target"));
	
	// curbuttons = buttons of the current GUI
	Panel curb = new Panel();
	curb.setLayout(new GridLayout(8, 1)); 
	curb.add(new Button("Percentage"));
	Panel incb = new Panel();
	incb.setLayout(new BorderLayout());
	incb.add("East", new Button("+"));
	incb.add("West", new Button("-"));
	// use percentage created in the beginning
	percentage.setBackground(Color.white);
	incb.add("Center", percentage);
	curb.add(incb);
	
	curb.add(new Panel());
	curb.add(new Button("Total Cost"));
	Panel incb2 = new Panel();
	incb2.setLayout(new BorderLayout());
	incb2.add("East", new Button("*"));
	incb2.add("West", new Button("/"));
	// uses increment defined in the beginning
	increment.setBackground(Color.white);
//	increment.setText("10.0");
	incb2.add("Center", increment);
	curb.add(incb2);
		
	curb.add(synchronize);
	curb.add(new Button("Angle Cost"));
	Panel incb3 = new Panel();
	incb3.setLayout(new BorderLayout());
	incb3.add("East", new Button("*2"));
	incb3.add("West", new Button("/2"));
	// uses increment defined in the beginning
	lambda.setBackground(Color.white);
//	increment.setText("10.0");

	incb3.add("Center", lambda);
	curb.add(incb3);
	
	current.controls.add(curb); 
	
	// the buttons of the middle GUI
	current.controls.setLayout(new GridLayout(1,2)); 
	current.controls.setBackground(Color.white); 
	Panel doublep = new Panel();
	doublep.setLayout(new GridLayout(1, 2));
	// use scrollbar created at the top
/*	Panel scroll = new Panel();
	scroll.setBackground(Color.blue);
	scroll.setLayout(new GridLayout(1, 3));
	scroll.add(new Panel());
	doublep.add(scrollbar); 
	scroll.add(new Panel());
	doublep.add(scroll);
*/	doublep.add("West", scrollbar);	
	Panel leftbut = new Panel();
	leftbut.setLayout(new GridLayout(3,1));
	leftbut.add(new Button("Set"));
	// use morph button created in the beginning
	morph.setBackground(Color.red);
	leftbut.add(morph);
	leftbut.add(new Button("Reset"));
	doublep.add("Center", leftbut);
	current.controls.add(doublep);
	
    }
    
    private void initColors () {
    
	source.setBackground(new Color(150,250,150));
	current.setBackground(new Color(150,150,200));
	target.setBackground(new Color(250,150,150));

    }

    public boolean handleEvent(Event evt) {
	if (evt.id == Event.WINDOW_DESTROY) System.exit(0);
	else if (evt.id == Event.SCROLL_ABSOLUTE 
	    || evt.id == Event.SCROLL_LINE_DOWN
	    || evt.id == Event.SCROLL_LINE_UP
	    || evt.id == Event.SCROLL_PAGE_DOWN
	    || evt.id == Event.SCROLL_PAGE_UP) 
	{   float temp = (float) scrollbar.getValue(); 
	    if (temp != perc) {
		perc = temp;
		set_string_perc(); //percentage.setText(Integer.toString(scrollbar.getValue()));
		morph.setBackground(Color.red); 
		read_increment(); 
		read_percentage(); 
		scrollbar.setValue((int)perc); 

//		System.out.println("You told me to morph for percentage = "+Float.toString(perc));	    
		current.data.readData(mdata.morphAt(perc));
//		current.data.centerIn(current.poly.getGraphics().getClipRect());
		current.poly.temp.readData(current.data);
//		System.out.println("Done morphing about to current.repaint()");
		current.eci.repaint();
		current.graph.repaint();
		current.poly.repaint();
	    }
	    return true; 
	}
	return super.handleEvent(evt);
    }

    public boolean action (Event evt, Object arg)
    {	if (evt.target.equals(synchronize)) {
	    sync = synchronize.getState();
	    if (sync) {
		read_lambda();
		incr = syncc / (lamb>1?lamb:1);
		set_string_incr();
	    }
	}
	else if (arg.equals("Clear Source")) { 
//	    source.poly.setBackground(Color.red);
	    source.poly.temp.readData(new MMdata()); 
	    source.poly.drawing = true; 
	    source.repaint();
	    current.repaint();
	} 
	else if (arg.equals("Set Source")) { 
//	    source.poly.setBackground(Color.blue); 
	    if (source.poly.temp.size()<2) {
		source.poly.temp.readData(new MMdata()); 
		source.poly.drawing = true; 
		source.repaint();
	    } else {
		source.poly.temp.addFinalNormal();
		source.data.readData(source.poly.temp);
		source.poly.drawing = false; 
//		source.graph.findMax();
		source.repaint(); 
		current.repaint();
		target.repaint();		
	    }
	} else if (arg.equals("Center Source")) { 
//	    source.poly.setBackground(new Color(150,250,150)); 
	    source.poly.centerData();
	    source.repaint();
	} else if (arg.equals("Scale to Target")) { 
//	    source.poly.setBackground(new Color(250,150,150)); 
	    if (target.poly.temp.getPerimeter()!=0) {
//		source.poly.temp.readData(source.data); 
		source.poly.temp.scaleToPerimeter(target.poly.temp.getPerimeter()); 
		source.repaint();
	    } else {
		source.poly.showWarning("Set Target First"); 
	    }
	} else if (arg.equals("Revert Source")) { 
//	    source.poly.setBackground(new Color(150,150,250)); 
	    source.poly.temp.readData(source.data); 
	    source.poly.drawing = false; 
	    source.repaint();
	    if (source.data.size()!=0) target.repaint();
    	}
	
	//
	//  handles buttons for the target
	//
	else if (arg.equals("Clear Target")) { 
//	    target.poly.setBackground(Color.red);
	    target.poly.temp.readData(new MMdata()); 
	    target.poly.drawing = true; 
	    target.repaint();
	    current.repaint();
	} 
	else if (arg.equals("Set Target")) { 
//	    target.poly.setBackground(Color.blue); 
	    if (target.poly.temp.size()<2) {
		target.poly.temp.readData(new MMdata()); 
		target.poly.drawing = true; 
		target.repaint();
	    } else {
		target.poly.temp.addFinalNormal();
		target.data.readData(target.poly.temp);
		target.poly.drawing = false; 
//		target.graph.findMax();
		source.repaint();
		current.repaint();
		target.repaint(); 
	    }
	} else if (arg.equals("Center Target")) { 
//	    target.poly.setBackground(new Color(150,250,150)); 
	    target.poly.centerData();
	    target.repaint();
	} else if (arg.equals("Scale to Source")) { 
//	    target.poly.setBackground(new Color(250,150,150)); 
	    if (source.poly.temp.getPerimeter()!=0) {
//		target.poly.temp.readData(source.data); 
		target.poly.temp.scaleToPerimeter(source.poly.temp.getPerimeter()); 
		target.repaint();
	    } else {
		target.poly.showWarning("Set Source First"); 
	    }
	} else if (arg.equals("Revert Target")) { 
//	    target.poly.setBackground(new Color(250,150,150)); 
	    target.poly.temp.readData(target.data); 
	    target.poly.drawing = false; 
	    if (target.data.size()!=0) source.repaint();
	    target.repaint();
	}
	
	else if (arg.equals("Percentage")) { read_percentage(); scrollbar.setValue((int)perc); doMorph();} 
	else if (arg.equals("Total Cost")) { 
	    sync = false; 
	    synchronize.setState(sync);
	    read_increment(); }
    	else if (arg.equals("+")) { 
	    read_percentage(); 
//	    read_increment(); 
//	    float temp = perc + incr; 
//	    if (temp >= 0 && temp <=100 && perc != temp) { 
//		perc = temp; scrollbar.setValue((int)perc); morph.setBackground(Color.red); }
	    // new stuff
	    if (perc<=90) perc += 10;
	    scrollbar.setValue((int)perc);
	    set_string_perc(); 
	    doMorph();
	}	    
	else if (arg.equals("-")) { read_percentage(); 
//	    read_increment(); 
//	    float temp = perc - incr; 
//	    if (temp >= 0 && temp <=100 && perc != temp) { 
//		perc = temp; scrollbar.setValue((int)perc); morph.setBackground(Color.red); }
	    if (perc >=10) perc -= 10; 
	    scrollbar.setValue((int)perc); 
	    set_string_perc(); 
	    doMorph();
	}
    	else if (arg.equals("*")) { read_increment(); 
//	    if (incr >= -1 && incr <= 99) incr++; 
	    incr *= 10;
	    sync = false; 
	    synchronize.setState(sync);
	    set_string_incr(); }
    	else if (arg.equals("/")) { read_increment(); 
//	    if (incr >= 1 && incr <= 101) incr--; 
	    incr /= 10;
	    sync = false; 
	    synchronize.setState(sync);
	    set_string_incr(); }
    	else if (arg.equals("*2")) { 
	    lamb *=2;
	    set_string_lamb(); }
    	else if (arg.equals("/2")) { 
	    lamb /=2;
	    set_string_lamb(); }
	else if (arg.equals("Angle Cost")) {
	    read_lambda();
	}
	else if (arg.equals("Set")) {
	    setMorph();
	    doMorph();
	}
	else if (arg.equals("Reset")) {
	    // percentage and scrollbar
	    perc = 50; set_string_perc();
	    scrollbar.setValue((int)perc); 

	    // angle cost synchronized
	    sync = true; synchronize.setState(sync);
	    lamb = (float) .125; set_string_lamb();
	    
	    // clear morph
	    current.data.readData(new MMdata());
	    current.poly.temp.readData(current.data); 
	    current.repaint();
	}
	else if (arg.equals("Morph")) { 
	    doMorph();
	}
	
	else return false; 
	return true; 
    }

    private void setMorph() {
	
	    if (source.poly.temp.size()<2 && target.poly.temp.size()<2)
		current.poly.showWarning("Set Source and Target");
	    else if (source.poly.temp.size()<2)
		current.poly.showWarning("Set Source First");
	    else if (target.poly.temp.size()<2)
		current.poly.showWarning("Set Target First");
	    else {
		// first read in the two possibly unfinished polygons 
		if (source.poly.drawing) {
		    source.poly.temp.addFinalNormal();
		    source.data.readData(source.poly.temp);
		    source.poly.drawing = false; 
		} 
		if (target.poly.drawing) {
		    target.poly.temp.addFinalNormal();
		    target.data.readData(target.poly.temp);
		    target.poly.drawing = false; 
		}
		// or show the existing ones	
		source.repaint();
		target.repaint(); 
		
		// then create the morph data
		mdata = new MMmdata(source.data, target.data, incr, lamb);
	    }
   }
    	

    private void doMorph () {
	    read_percentage(); 
	    scrollbar.setValue((int)perc); 

//	    System.out.println("You told me to morph for percentage = "+Float.toString(perc));	    
	    current.data.readData(mdata.morphAt(perc));
//	    current.data.centerIn(current.poly.getGraphics().getClipRect());
	    current.poly.temp.readData(current.data);
//	    System.out.println("Done morphing about to current.repaint()");
	    current.eci.repaint();
	    current.graph.repaint();
	    current.poly.repaint();
    }    
	
    private void read_percentage() {
        try {
	    float temp = Float.valueOf(percentage.getText()).floatValue();
	    if ((temp <= 100) && (temp >=0) && perc != temp) {
		perc = temp;
		morph.setBackground(Color.red);
	    }
	} catch (NumberFormatException e) {
	} 
	    set_string_perc();
    }

    private void read_increment() {
	try {
	    float temp = Float.valueOf(increment.getText()).floatValue();
	    if (//(temp <= 100) && 
		(temp >0)) {
		incr = temp;
	    }
	    set_string_incr();
	} catch (NumberFormatException e) {
	    set_string_incr();
	} 
    }

    private void read_lambda() {
	try {
	    float temp = Float.valueOf(lambda.getText()).floatValue();
	    lamb = temp;
	    set_string_lamb();
	} catch (NumberFormatException e) {
	    set_string_lamb();
	} 
    }
    
    private void set_string_perc () {

//	String percstr = String.valueOf(perc);
//	System.out.println("Setting Percentage string from "+percstr+" of length "+String.valueOf(percstr.length()));
//	percentage.setText(percstr.length()>4?percstr.substring(0,3):percstr);
    	percentage.setText(String.valueOf(perc));
	
    }
    
    private void set_string_incr () {

//	String incrstr = String.valueOf(incr);
//	System.out.println("Setting Increment string from "+incrstr+" of length "+String.valueOf(incrstr.length()));
//	increment.setText(String.valueOf(incrstr.length()>8?incrstr.substring(0,7):incrstr));
	increment.setText(String.valueOf(incr));
	
    }
    
     private void set_string_lamb () {

//	String lambstr = String.valueOf(lamb);
//	System.out.println("Setting Increment string from "+incrstr+" of length "+String.valueOf(incrstr.length()));
//	lambda.setText(String.valueOf(lambstr.length()>6?lambstr.substring(0,5):lambstr));
	if (sync) {
	    incr = syncc / (lamb>1?lamb:1);
	    set_string_incr();
	}
	lambda.setText(String.valueOf(lamb));
	
    }
}
