//<applet code="NumberLine" width=470 height=300></applet>
//width = 425 height = 250

import java.awt.*;
import java.applet.*;

/*IDEAS:
 *- To convert to an implementation where only arrows that have actually been selected are shown
 *- animateMove(Object moveObject, int startx, int starty, int endx, int endy) method/class to
 *		move an object from a beginning location to its destination
 * 
 * 
 */


public class NumberLine extends Applet
{
	NumberPanel np;
	Choice[] vectorChoice;			
	CheckBox[] vectorCheckBox;		//used to determine which vector is being modified
	CheckboxGroup vectorGroup;		//
	Button CalcButton = new Button("Calculate");
	Button ResetButton = new Button("Reset");
	int numVectors = 4;
	
	public void init()
	{
		setBackground(Color.white);

		if(numVectors < 1)	numVectors = 1;

		np = new NumberPanel(numVectors);

		vectorCheckBox = new CheckBox[numVectors];
		vectorChoice = new Choice[numVectors];
		vectorGroup = new CheckboxGroup();
		
		//First vector automatically represents a positive number.  If it were possible
		//	to make it negative, then there would be an implicit 0 from which it'd be
		//	subtracted.  It's easier to just make the first number positive by default,
		// but that could change in a future version of the program
		vectorCheckBox[0] = new CheckBox("Arrow 1",vectorGroup,true);
		vectorChoice[0] = new Choice();
		vectorChoice[0].add("Add");
		for(int i=1;i<numVectors;i++)
		{
			vectorCheckBox[i] = new CheckBox("Arrow "+ (i+1)+"",vectorGroup,false);
			vectorChoice[i] = new Choice();
			vectorChoice[i].add("Add");
			vectorChoice[i].add("Subtract");
		}
		
		//  ___
		// |   |
		// | o |
		// |   |
		// | o |
		// |   |
		// | o |
		// |   |
		// | o |
		// |___|
		//
		Panel p1 = new Panel();
		p1.setLayout(new GridLayout(numVectors,1));
		p1.setBackground(Color.white);
			for(int i=0;i<numVectors;i++)
			{
				p1.add(vectorCheckBox[i]);
			}

		//  ___________________
		// |            _____  |
		// | Vector 1: |_Add_| |
		// |            _____  |
		// | Vector 2: |_Add_| |
		// |            _____  |
		// | Vector 3: |_Add_| |
		// |            _____  |
		// | Vector 4: |_Add_| |
		// |___________________|
		//
		Panel p2 = new Panel();
		p2.setLayout(new GridLayout(numVectors,1));
		p2.setBackground(Color.white);
			for(int i=0;i<numVectors;i++)
			{
				p2.add(vectorChoice[i]);
			}
		
		//  _______________
		// |  ___________  |
		// | |           | |
		// | | Calculate | |
		// |  -----------  |
		// |  ___________  |
		// | |           | |
		// | |   Reset   | |
		// |  -----------  |
		// |_______________|
		Panel buttonPanel = new Panel();
		buttonPanel.setBackground(Color.white);
			buttonPanel.add(CalcButton);
			buttonPanel.add(ResetButton);
/*
		Panel opsPanel = new Panel();
		opsPanel.setLayout(new GridLayout(1,2,2,2));
		opsPanel.setBackground(Color.white);
			opsPanel.add(p1);
			opsPanel.add(p2);
*/
/*
		Panel mainPanel = new Panel();
		mainPanel.setLayout(new GridLayout(1,3,2,2));
		mainPanel.setBackground(Color.white);
			mainPanel.add(p1);
			mainPanel.add(p2);
			mainPanel.add(buttonPanel);
*/
		




		//  __________________________________________________
		// |  ______________________________________________  |
		// | |                                              | |
		// | |                                              | |
		// | |                                              | |
		// | |                  NumberPanel                 | |
		// | |                   450 x 200                  | |
		// | |                                              | |
		// | |                                              | |
		// | |                                              | |
		// | |______________________________________________| |
		// |  ___    ___________________     _______________  |
		// | |   |  |            _____  |   |  ___________  | |
		// | | o |  | Vector 1: |_Add_| |   | |           | | |
		// | |   |  |            _____  |   | | Calculate | | |
		// | | o |  | Vector 2: |_Add_| |   |  -----------  | |
		// | |   |  |            _____  |   |  ___________  | |
		// | | o |  | Vector 3: |_Add_| |   | |           | | |
		// | |   |  |            _____  |   | |   Reset   | | |
		// | | o |  | Vector 4: |_Add_| |   |  -----------  | |
		// | |___|  |___________________|   |_______________| |
		// |__________________________________________________|
		//
		add(np);
		add(p1);
		add(p2);
		add(buttonPanel);
	}

	public void resetApplet()
	{
		vectorCheckBoxes[0].setState(true);
		for(int i=1;i<numVectors;i++)
		{
			vectorCheckBoxes[i].setState(false);
			vectorChoice[i].select(0);
		}
		
		np.resetPanel();
//		np = null;
//		np = new NumberPanel();
		repaint();
	}

	public void getControls()
	{
/*
		Panel p = new Panel();
		p.setLayout(new GridLayout());
		p.add(AddButton);
		p.add(SubtractButton);
		p.add(MultiplyButton);
		p.add(CalcButton);
		p.add(ResetButton);
		p.add(Num1Button);
		p.add(Num2Button);
		return p;
*/
	}

	public void paint(Graphics g)
	{

	}

	public boolean action(Event event, Object obj)
	{
		if(!np.animating && !np.animated)
		{
			for(int i=0;i<numVectors;i++)
			{
				if(event.target == vectorCheckBox[i])
				{
					np.selectedVector = i;
					break;
				}
				else if(event.target == vectorChoice[i])
				{
					setOperation(i,);
					np.opsArr[i] = (int)Integer.parseInt(vectorChoice[i].getSelectedIndex());
					break;
				}
			}
/*			
			for(int i=0;i<numVectors;i++)
			{
				np.opsArr[i] = (int)Integer.parseInt(vectorChoice[i].getSelectedIndex());
				//??
				String vectorString1 = String(vectorCheckBox[i].getSelectedCheckbox());
				//??
				String vectorString2 = vectorGroup.toString();
				np.graph2D = false;
	//			np.showCalc = true;
				np.repaint();
			}
*/
			
/*
			if(event.target == AddBox)
			{
				System.out.println("Add Button");
				np.showOperation = "add";
				np.graph2D = false;
	//			np.showCalc = true;
				np.repaint();
			}
			else if(event.target == SubtractBox)
			{
				System.out.println("Subtract Button");
				np.showOperation = "subtract";
				np.graph2D = false;
	//			np.showCalc = true;
				np.repaint();
			}
			else if(event.target == MultiplyBox)
			{
				System.out.println("Multiply Button");
				np.showOperation = "multiply";
				np.graph2D = true;
	//			np.showCalc = true;
				np.repaint();
			}
			else if(event.target == CalcButton)
			{
				System.out.println("Equals Button");
				np.calculate();
			}
			else if(event.target == Num1Box)
			{
				System.out.println("1st Number");
				np.selectedVector = "num1";
			}
			else if(event.target == Num2Box)
			{
				System.out.println("2nd Number");
				np.selectedVector = "num2";
			}
*/


		}
		else //(np.animating || np.animated)
		{

/*
			if(np.showOperation == "add")
				AddBox.setState(true);
			else if(np.showOperation == "subtract")
				SubtractBox.setState(true);
			else if(np.showOperation == "multiply")
				MultiplyBox.setState(true);

			if(np.selectedVector == "num1")
				Num1Box.setState(true);
			else if(np.selectedVector == "num2")
				Num2Box.setState(true);
*/


		}

		if(event.target == ResetButton)
		{
			System.out.println("Reset Button");
			resetApplet();
			np.repaint();
		}

		return true;
	}
} // END NumberLine class

/////////////////////////////////////////////////////
//                                                 //
//                   NumberPanel                   //
//                                                 //
/////////////////////////////////////////////////////

//Ideas: Make the numberline a background so all whole
// number points can be drawn quickly

//Need to do: change all references of num1, num2 (vectVal[]), v1, v2 (vect[])
//  replace showOperation with opsArr: +/- for each vect[]

class NumberPanel extends Panel implements Runnable
{
	Color myColor = new Color(255,255,200);
	Dimension panelSize = new Dimension(451,201);
	Dimension size = new Dimension(400,200);
	int centerX;
	int centerY;
	int curX;
	int curY;
	int border = 25;
//	public String showOperation;	//'add' || 'subtract' || 'multiply'
	private Char[]	opsArr;		//'+' = add, '-' = subtract
//	public String precision;		//'int' || 'double'
	public boolean graph2D;			//false = 1D (add/subtract); true = 2D (multiply)
//	public boolean showCalc;
	public boolean showResult;
	public boolean animateResult;
	
	public int selectedVector;
	public int numVectors;
	public VectorLine[] vect;
	int[]	vectVal;		//The value represented by each vector
	int	result;	//result of the operation(s)
	Color[] colorArr;		//Used to set the color of a vector.  Cycle around once the end of the array is reached

	int	graphMin,				//Min unit shown on graph (used as left-most point.  IMPORTANT for
										// calculating distances between points on the graph!!)
			graphMax,				//Max unit shown on graph
			graphUnitsPerMark,	//Number of units between the marks on the graph (initially 5)
										// (not necessarily the same as graphPixelsPerUnit, but can be)
			graphRange;				//Range of numbers the graph should cover.  This measures the spaces
										// between the marks, basically...  The real range will be one more
										// than the range says.  Ex: if graphRange==4 and graphMin==2, then
										// the graph will run from 2 -> 6...  that's 2, 3, 4, 5, and 6: 5
										// points on the graph.
	int graphPixelsPerUnit;	//Number of actual pixels between units on the graph (initially 5)
										// double because it may not be exact.  In fact, it probably won't be
										// an integer, since the factors of 350 are 2, 5, 5, and 7
										// Factors of 350: 2, 5, 7, (10), 14, (25), (35), (50), (70), 175
	private volatile Thread runner2;
	boolean animating, animated;

	private static final int MINMARKS = 2;		//Must have a mark at each end
	private static final int MAXMARKS = 15;	//Perfect for range of 70...

//For extra precision, later on
//	double double_num1;
//	double double_num2;

	// Export Panel Size
  	public Dimension   getMinimumSize() {return panelSize;}
	public Dimension getPreferredSize() {return panelSize;}

	public NumberPanel(int numberOfVectors)
	{
		setPanel(numberOfVectors);
	}

	public void resetPanel()
	{
		stop(); //stop thread of execution
		setPanel(); //sets Panel back to defaults
		repaint(); //repaint the panel
	}
	
	private void setPanel(int numberOfVectors)
	{
		centerX = size.width/2;
		centerY = size.height/2;
		curX = 0;
		curY = 0;
		setBackground(myColor);

		colorArr = Color[4];
		colorArr[0] = Color.blue;
		colorArr[1] = Color.red;
		colorArr[2] = Color.green;
		colorArr[3] = Color.black;
		numVectors = numberOfVectors;
		vect = new VectorLine[numberOfVectors];
		for(int i=0;i<numVectors;i++)
		{
			vect[i] = new VectorLine(centerX,centerY - 15*(1+i));	//15, 30, 45, 60..
			//4 is used here b/c that's the number of colors hardcoded in
			vect[i].setColor(colorArr[i%4]);
			vectVal[i] = 0;
		}
//		v1 = new VectorLine(centerX,centerY - 15);
//		v2 = new VectorLine(centerX,centerY - 30);
//		v1.setColor(Color.blue);
//		v2.setColor(Color.red);
		selectedVector = 0;
		showOperation = "add";
//		precision = "int";
		graph2D = false;
		showResult = false;
		animating = false;
		animated = false;
//		num1 = 0;
//		num2 = 0;
		graphMin = -35;
		graphMax = 35;
		graphPixelsPerUnit = 5;
		graphUnitsPerMark = 5;
	}

	public void paint(Graphics g)
	{
		if(graph2D == true) //used for multiplication
			drawGrid(g);
		else //used for addition/subtraction
			drawGrid(g);

		// Draw Vectors
		v1.show(g);
		v2.show(g);

		// Draw Frame
		g.setColor(Color.black);
		g.drawRect(0,0,size.width,size.height);

//		if(showCalc == true)
//		{
			String operator = " + ";
			if(showOperation == "add")
				operator = " + ";
			if(showOperation == "subtract")
				operator = " - ";
			if(showOperation == "multiply")
				operator = " x ";

			//Show calculation
			if (showResult == true)
			{
				// setFont(new Font());
				g.drawString(num1+operator+num2+" = "+result,centerX,size.height-20);
			}
			else
			{
				// setFont(new Font());
				g.drawString(num1+operator+num2+" = ",centerX,size.height-20);
			}
//		}
//		else //showCalc == false
//		{
//			g.drawString("Select an operation",centerX,size.height-20);
//		}
	}

	public void drawGrid(Graphics g)
	{
		g.setColor(Color.black);
		g.drawLine(border,centerY,size.width-border,centerY);
		g.drawLine(border,centerY+1,size.width-border,centerY+1);

		for (int i = graphMin;i<=graphMax;i+=graphUnitsPerMark)
		{
			int tempint = toCoord(i);

//			if (i%5 == 0)
//			{
				g.drawLine(tempint,centerY-5,tempint,centerY+5);
				if (i < 0)
				{
					if (i > -10)
						g.drawString(""+i,tempint-7,centerY+20);
					else //i < -10
						g.drawString(""+i,tempint-10,centerY+20);
				}
				else //i >= 0
				{
					if (i < 10)
						g.drawString(""+i,tempint-3,centerY+20);
					else //i > 10
						g.drawString(""+i,tempint-7,centerY+20);
				}
//			}
//			else // i is not a multiple of 5
//			{
//				g.drawLine(tempint,centerY-2,tempint,centerY+2);
//			}

		}
	}

	public boolean mouseDown(Event evt, int x, int y)
	{
System.out.println("x:"+x+"\ty: "+y);
		if(!animating && !animated)
		{
			snapToGrid(x,y);
			System.out.println("X: "+curX+" Y: "+curY);
			if(selectedVector == "num1")
				v1.setEnd(curX,curY);
			else //if (selectedVector == "num2")
				v2.setEnd(curX,curY);
			repaint();
		}
		return true;
	}


	public boolean mouseDrag(Event evt, int x, int y)
	{
		if(!animating && !animated)
		{
			snapToGrid(x,y);
			System.out.println("X: "+curX+" Y: "+curY);
			if(selectedVector == "num1")
				v1.setEnd(curX,curY);
			else //if (selectedVector == "num1")
				v2.setEnd(curX,curY);
			repaint();
		}
		return true;
	}
	
	public void setOperation(int whichVector, char op)
	{
		if(op == '+' || op == '-')
			newArr[whichVector] = op;
	}

	private void snapToGrid(int x,int y)
	{
		curX = x;
		curY = y;
		if(precision == "int")
		{
			if(x < border)
				curX = border;
			else if (x > size.width-border)
				curX = size.width-border;
			else
			{
/*
				if((x%5 == 1) || (x%5 == 2))
					curX = (x/5) * 5;
				else //x%5 == 3 || x%5 ==4
					curX = ((x/5) + 1) * 5;
*/
				if(x % graphPixelsPerUnit < graphPixelsPerUnit/2)
					curX = (x/graphPixelsPerUnit) * graphPixelsPerUnit;
				else //(x % graphPixelsPerUnit >= graphPixelsPerUnit/2)
					curX = ((x/graphPixelsPerUnit) + 1) * graphPixelsPerUnit;
			}
		}

		if(selectedVector == "num1")
			num1 = (curX - centerX)/graphPixelsPerUnit;
		else //selectedVector == "num2"
			num2 = (curX - centerX)/graphPixelsPerUnit;
	}

	public int toCoord(int x)
	{
		//int range = 30;
		//int unitScale = (centerX*range);

		return graphPixelsPerUnit*x+centerX;
	}

	public void calculate()
	{
		showResult = true;
		animateResult = true;
//		showCalc = true;
		if(showOperation == "add")
		{
			result = num1 + num2;
		}
		else if(showOperation == "subtract")
		{
			result = num1 - num2;
		}
		else if(showOperation == "multiply")
		{
			result = num1 * num2;
		}
		repaint();
		start();
	}

	private void animateResult()
	{

	}

	//Threading stuff
	public void start()
	{
		runner2 = new Thread(this);
		runner2.start();
	}


/*public void run() {
        Thread thisThread = Thread.currentThread();
        while (blinker == thisThread) {
            try {
                thisThread.sleep(interval);

                synchronized(this) {
                    while (threadSuspended && blinker==thisThread)
                        wait();
                }
            } catch (InterruptedException e){
            }
            repaint();
        }
    }

    public synchronized void stop() {
        blinker = null;
        notify();
    }
*/

	public void stop()
	{
		if (runner2 != null)
		{
			runner2.stop();	//stop is unsafe!!!!!!!!!
			runner2 = null;
		}
	}

	public void run()
	{
		Thread thisThread = Thread.currentThread();
		while(animateResult)
		{
			animating = true;
			try
			{
				thisThread.sleep(400);
			}
			catch (InterruptedException e)
			{}


			if(result > graphMax)
			{
				//animate movement of numberline to the left if result > 35
				while(graphMax < result)
				{
					graphMax+=graphUnitsPerMark;
					graphMin+=graphUnitsPerMark;
					
					try {Thread.sleep(100); }
					catch (InterruptedException e){}
					repaint();
				}
			}
			else if (result < graphMin)
			{
				//animate movement of numberline to the right if result < 35
			}
			

			if(showOperation == "add")
			{
				int	v2tail = v2.tailx,
						v1end = v1.x,
						diff = v1end - v2tail;

				if(diff > 0)
				{
					for(;diff > 0;diff--)
					{
						v2.moveRight(1);
						try {Thread.sleep(50); }
						catch (InterruptedException e){}
						repaint();
					}
					animated = true;
				}
				else if(diff < 0)
				{
					for(;diff < 0;diff++)
					{
						v2.moveLeft(1);
						try {Thread.sleep(50); }
						catch (InterruptedException e){}
						repaint();
					}
					animated = true;
				}
			}
			else if(showOperation == "subtract")
			{
				int	v2tail = v2.tailx,
						v2end = v2.x,
						v1end = v1.x,
						diff = v1end - v2end;

				if(diff > 0)
				{
					for(;diff > 0;diff--)
					{
						v2.moveRight(1);
						try {Thread.sleep(50); }
						catch (InterruptedException e){}
						repaint();
					}
					animated = true;
				}
				else if(diff < 0)
				{
					for(;diff < 0;diff++)
					{
						v2.moveLeft(1);
						try {Thread.sleep(50); }
						catch (InterruptedException e){}
						repaint();
					}
					animated = true;
				}
			}
			else if(showOperation == "multiply")
			{

			}
			animating = false;
			animateResult = false;
			repaint();
		}
  } // End Run

} // END NumberPanel class


/////////////////////////////////////////////////////
//                                                 //
//                  VectorLine                     //
//                                                 //
/////////////////////////////////////////////////////


class VectorLine
{
	public int	x,
					y,
					tailx,
					taily;
/*
	int	border = 20,
			width  = 400,
			height = 200;
*/
	double	double_tailx,
				double_taily;

	boolean valid;
	String precision;
	Dimension size = new Dimension(401,201);
	int centerX;
	int centerY;
	Color vectorColor = Color.black;

	public VectorLine(int tailx,int taily)
	{
		valid = false;
		setTail(tailx,taily);
		precision= "int";
		centerX = size.width/2;
		centerY = size.height/2;
	}

	public VectorLine(int tailx,int taily,Dimension size)
	{
		valid = false;
		setTail(tailx,taily);
		this.size = size;
		precision= "int";
		centerX = size.width/2;
		centerY = size.height/2;
	}

	public VectorLine(double double_tailx,double double_taily)
	{
		valid = false;
		setTail(double_tailx,double_taily);
		precision = "double";
		centerX = size.width/2;
		centerY = size.height/2;
	}

	public VectorLine(double double_tailx,double double_taily,Dimension size)
	{
		valid = false;
		setTail(double_tailx,double_taily);
		this.size = size;
		precision = "double";
		centerX = size.width/2;
		centerY = size.height/2;
	}

	public boolean isValid()
	{
		//used when trying to perform an operation on the numberline... if all vectors aren't valid,
		// then reject the operation
		if (x == centerX)
			valid = false;
		else
			valid = true;
		return valid;
	}

	public void setColor(Color color)
	{
		vectorColor = color;
	}

	public void setEnd(int x, int y)
	{
		this.x = x;
		this.y = y;
		valid = true;
	}

	public void setTail(int tailx, int taily)
	{
		this.tailx = tailx;
		this.taily = taily;
		//valid = true;
	}

	public void setTail(double double_tailx, double double_taily)
	{
		this.double_tailx = double_tailx;
		this.double_taily = double_taily;
		valid = true;
	}

	public void set(double x, double y)
	{
		//this.x = Math.round();
	}

	public void show(Graphics g)
	{
		if (isValid())
		{
			g.setColor(vectorColor);
			drawArrow(tailx,taily,x,taily,g);
		}
	}

	public void moveRight(int increment)
	{
		x += increment;
		tailx += increment;
	}

	public void moveLeft(int increment)
	{
		x -= increment;
		tailx -= increment;
	}

	public void drawArrow(int x1, int y1, int x2, int y2, Graphics g)
	{
		double d = Math.atan((float)((double)(y1 - y2) / (double)(x1 - x2)));
		if(x1 - x2 < 0)
			d += 3.1415926535897931D;

		g.drawLine(x1, y1, x2, y2);

		g.drawLine(x2, y2, x2 + (int)(Math.cos(d + 0.29999999999999999D) * 10D),
			y2 + (int)(Math.sin(d + 0.29999999999999999D) * 10D));

		g.drawLine(x2, y2, x2 + (int)(Math.cos(d - 0.29999999999999999D) * 10D),
			y2 + (int)(Math.sin(d - 0.29999999999999999D) * 10D));
	}


	public void drawArc()
	{
		//Draw an arc from (tailx,taily) --> (x,taily)
		//Have: chord, altitude from chord to top of circle
		//Need to calculate: bounding rectangle, center of circle, arc to show
	}


} // END VectorLine class