package CookieCutter;

import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
import java.io.*;
import java.util.*;
import javax.swing.event.*;
import java.awt.geom.*;

public class PhysicsSimulation extends JFrame
{
    private Simulator simulator;
    private SimulatorPanel simulatorPanel;
    
    public PhysicsSimulation()
    {
	super("Physics Simulator");
	
	this.getContentPane().setLayout(new BorderLayout());
	this.getContentPane().add(getComponentsPane(), BorderLayout.CENTER);

	this.addWindowListener(new WindowAdapter()
	    {
		public void windowClosing(WindowEvent e)
		{
		    System.exit(1);
		}
	    });

	this.setSize(800, 600);
	this.show();
    }

    public Component getComponentsPane()
    {
	JPanel panel = new JPanel();
	panel.setLayout(new BorderLayout());

	panel.add(getOptionsPane(), BorderLayout.EAST);
	panel.add(getSimulatorPane(), BorderLayout.CENTER);

	return panel;
    }

    public Component getOptionsPane()
    {
	JPanel panel = new JPanel();
	panel.setLayout(new BorderLayout());

	JPanel panel2 = new JPanel();
	panel2.setLayout(new GridLayout(0, 1));

	//filename
	JLabel filenameLabel = new JLabel("Filename");
	final JTextField filenameTextField = new JTextField("../GameFiles/default.game");
	
	JPanel panel3 = new JPanel();
	panel3.setLayout(new GridLayout(1, 0));

	panel3.add(filenameLabel);
	panel3.add(filenameTextField);
	
	panel2.add(panel3);

	//cookie number
	JLabel cookieNumberLabel = new JLabel("Cookie #");
	final JTextField cookieNumberTextField = new JTextField("2");

	panel3 = new JPanel();
	panel3.setLayout(new GridLayout(1, 0));
	
	panel3.add(cookieNumberLabel);
	panel3.add(cookieNumberTextField);

	panel2.add(panel3);

	//copies
	JLabel copiesLabel = new JLabel("Copies");
	final JTextField copiesTextField = new JTextField("1");

	panel3 = new JPanel();
	panel3.setLayout(new GridLayout(1, 0));
	
	panel3.add(copiesLabel);
	panel3.add(copiesTextField);

	panel2.add(panel3);

	//reset button
	JButton reset = new JButton("Reset");
	reset.addActionListener(new ActionListener()
	    {
		public void actionPerformed(ActionEvent e)
		{
		    try
			{
			    File file = new File(filenameTextField.getText());
			    FileReader fr = new FileReader(file);
			    BufferedReader in = new BufferedReader(fr);

			    int copies = Integer.parseInt(copiesTextField.getText());
			    int cookieNumber = Integer.parseInt(cookieNumberTextField.getText());
			    
			    int count = 1;
			    while(count < cookieNumber)
				{
				    String line = in.readLine();
				    if(line == null)
					{
					    System.out.println("Invalid cookie number: " + cookieNumber);
					    System.out.println("File only contains: " + count);
					    break;
					}
				    else if(line.indexOf("!") >= 0)
					count++;
				}

			    if(count == cookieNumber)
				{
				    boolean done = false;
				    ArrayList list = new ArrayList();
				    while(!done)
					{
					    String line = in.readLine();
					    if(line == null)
						done = true;
					    else if(line.indexOf("#") >= 0)
						;
					    else if(line.indexOf("!") >= 0)
						done = true;
					    else
						{
						    StringTokenizer tokenizer = new StringTokenizer(line);
						    double x = Double.parseDouble(tokenizer.nextToken());
						    double y = Double.parseDouble(tokenizer.nextToken());
						    Vertex vertex = new Vertex(x, y);
						    list.add(vertex);
						}
					}
				    
				    Vertex [] vertices = new Vertex [list.size()];
				    for(int i=0;i<list.size();i++)
					vertices[i] = (Vertex) list.get(i);

				    Move moves = new Move(copies);
				    for(int i=0;i<copies;i++)
					moves.setCookiePosition(i, new Vertex(0.5, 0.5), 0);

				    simulator.reset(vertices, copies, moves);

				    simulatorPanel.repaint();
				}

			    in.close();
			}
		    catch(Exception ex)
			{
			    handleException(ex);
			}
		}
	    });

	//step button
	JLabel stepLabel = new JLabel("Step size:");
	final JTextField stepTextField = new JTextField("1");
	JButton step = new JButton("Step");
	step.addActionListener(new ActionListener()
	    {
		public void actionPerformed(ActionEvent e)
		{
		    try
			{
			    int n = Integer.parseInt(stepTextField.getText());
			    for(int i=0;i<n;i++)
				{
				    simulator.step();
				    simulatorPanel.repaint();
				}
			}
		    catch(Exception ex)
			{
			    System.out.println("Error: " + ex.getMessage());
			    ex.printStackTrace();
			}
		}
	    });

	panel3 = new JPanel();
	panel3.setLayout(new GridLayout(1, 0));
	
	panel3.add(stepLabel);
	panel3.add(stepTextField);

	panel2.add(reset);
	panel2.add(panel3);
	panel2.add(step);

	panel.add(new JLabel(), BorderLayout.CENTER);
	panel.add(panel2, BorderLayout.NORTH);

	return panel;
    }

    public Component getSimulatorPane()
    {
	JPanel panel = new JPanel();
	panel.setLayout(new BorderLayout());
	panel.setBorder(BorderFactory.createTitledBorder("Simulator Panel"));
	
	simulator = new Simulator();
	simulatorPanel = new SimulatorPanel(simulator);
	JScrollPane scrollPane = new JScrollPane(simulatorPanel);
	
	panel.add(scrollPane, BorderLayout.CENTER);

	return panel;
    }

    public void handleException(Exception e)
    {
	System.out.println("Error: " + e.getMessage());
	e.printStackTrace();
    }

    public static void main(String [] args)
    {
	new PhysicsSimulation();
    }
}

class SimulatorPanel extends JPanel
{
    private Simulator simulator;

    public SimulatorPanel(Simulator simulator)
    {
	super();
	this.simulator = simulator;
	MouseInputAdapter mouseInputAdapter = new MouseInputAdapter()
	    {
		private int oldX, oldY;
		private int cookieIndex;

		public void mousePressed(MouseEvent e)
		{
		    oldX = e.getX();
		    oldY = e.getY();
		    cookieIndex = SimulatorPanel.this.simulator.getCookieIndex(oldX, oldY, getWidth(), getHeight());
		}

		public void mouseReleased(MouseEvent e)
		{
		}

		public void mouseDragged(MouseEvent e)
		{
		    if(e.getModifiers() == InputEvent.BUTTON1_MASK)
			{
			    SimulatorPanel.this.simulator.translateCookie(cookieIndex, e.getX() - oldX, e.getY() - oldY, getWidth(), getHeight());
			}
		    
		    if(e.getModifiers() == InputEvent.BUTTON3_MASK)
			{
			    SimulatorPanel.this.simulator.rotateCookie(cookieIndex, e.getX() - oldX, e.getY() - oldY, getWidth(), getHeight());
			}

		    oldX = e.getX();
		    oldY = e.getY();
		    SimulatorPanel.this.repaint();
		}
	    };

	this.addMouseListener(mouseInputAdapter);
	this.addMouseMotionListener(mouseInputAdapter);
    }

    public Dimension getPreferredSize()
    {
	return new Dimension(800, this.getHeight());
    }

    public void paint(Graphics g)
    {
	simulator.paint(g, this.getWidth(), this.getHeight());
    }
}
class Simulator
{
    private Vertex [] cookie;
    private int numCopies;
    private Move moves;
    private Move bestMove;
    private double margin = .0001;
    public Simulator()
    {
	super();
	cookie = null;
	numCopies = 0;
    }

    public Move getMove()
    {
	if(bestMove == null)
	    return moves;
	else
	    return bestMove;
    }

    public void reset(Vertex [] cookie, int numCopies, Move moves)
    {
	try
	    {
		this.cookie = cookie;
		this.numCopies = numCopies;
		this.moves = new Move(numCopies);
		for(int i=0;i<numCopies;i++)
		    {
			Vertex tempCenter = moves.GetCookieCenter(i);
			Vertex center = new Vertex(tempCenter.xpos(), tempCenter.ypos());
			Vertex random = new Vertex(Math.random() * 0.1 - 0.05, Math.random() * 0.1 - 0.05);
			center = add(center, random);
			double angle = moves.GetCookieAngle(i);
			this.moves.setCookiePosition(i, center, angle);
		    }

		//find right boundary
		double maximum = 0;
		for(int i=0;i<numCopies;i++)
		    {
			Vertex center = moves.GetCookieCenter(i);
			double angle = moves.GetCookieAngle(i);
			Vertex [] tempVertices = getVertices(cookie, center, angle);
			for(int j=0;j<tempVertices.length;j++)
			    if(tempVertices[j].xpos() > maximum)
				maximum = tempVertices[j].xpos();
		    }

		maximum += 0.0001;
		moves.setRightBoundary(maximum);
		bestMove = copyMove(moves);		
	    }
	catch(Exception e)
	    {
		handleException(e);
	    }
    }
    
public boolean correctMove(Move moves) throws Exception
{
	Vertex [][] vertices = new Vertex [numCopies][10];
	Vertex [] centers = new Vertex [numCopies];
	double [] angles = new double [numCopies];
	for(int i = 0; i < numCopies; i++)
	{
		centers[i] = moves.GetCookieCenter(i);
		angles[i] = moves.GetCookieAngle(i);
		vertices[i] = getVertices(cookie, centers[i], angles[i]);
		for(int j = 0; j < vertices[i].length; j++)
		{
			//check boundaries
				if(vertices[i][j].xpos() - margin < 0)
				    {
						//System.err.println("Past the left");
						return false;
				    }
				
				if(vertices[i][j].ypos() - margin < 0)
				    {
						//System.err.println("Past the top");
						return false;
				    }
				
				if(vertices[i][j].ypos() + margin > 1)
				    {
						//System.err.println("Past the bottom");
						return false;
				    }			
		}
	}
	return true;

}
    public void step()
    {
	try
	    {
		if(cookie != null)
		    {
			Vertex [][] vertices = new Vertex [numCopies][10];
			Vertex [] centers = new Vertex [numCopies];
			double [] angles = new double [numCopies];

			//move cookies due to gravity
			double gravity = 0.01;
		    	for(int i=0;i<numCopies;i++)
			    {
				centers[i] = moves.GetCookieCenter(i);
				angles[i] = moves.GetCookieAngle(i);
				Vertex force = new Vertex(-gravity, 0);
				centers[i] = new Vertex(centers[i].xpos() + force.xpos(), centers[i].ypos() + force.ypos());
				vertices[i] = getVertices(cookie, centers[i], angles[i]);
				moves.setCookiePosition(i, centers[i], angles[i]);
			    }

			rotateCollisions();

			translateCollisions();

			guaranteeCorrect();
			guaranteeCorrect();

			if(bestMove == null)
			    bestMove = copyMove(moves);
			else if(moves.GetRightBoundary() < bestMove.GetRightBoundary() && correctMove(moves))
			    bestMove = copyMove(moves);
			else
			{
				if(moves.GetRightBoundary() >= bestMove.GetRightBoundary())
				{
					
				}
			}
		    	
		    } // end of if statement
	    }
	catch(Exception e)
	    {
		System.out.println("Error: " + e.getMessage());
		e.printStackTrace();
	    }
    }

    public Move copyMove(Move move)
    {
	try
	    {
		Move copy = new Move(numCopies);
		for(int i=0;i<numCopies;i++)
		    {
			Vertex center = move.GetCookieCenter(i).copy();
			double angle = move.GetCookieAngle(i);
			copy.setCookiePosition(i, center, angle);
		    }

		double boundary = move.GetRightBoundary();
		copy.setRightBoundary(boundary);
		return copy;
	    }
	catch(Exception e)
	    {
		handleException(e);
	    }

	return null;
    }

    public void rotateCollisions()
    {
	try
	    {
		Vertex [][] vertices = new Vertex [numCopies][10];
		double [] rotations = new double [numCopies];
		for(int i=0;i<numCopies;i++)
		    {
			Vertex center = moves.GetCookieCenter(i);
			double angle = moves.GetCookieAngle(i);
			vertices[i] = getVertices(cookie, center, angle);
			rotations[i] = 0;
		    }

		//check for collisions between boundaries
		for(int i=0;i<numCopies;i++)
		    {
			double scaleFactor = 0.4;
			Vertex [] vertices1 = vertices[i];
			for(int j=0;j<vertices1.length;j++)
			    {
				if(vertices1[j].xpos() - margin < 0)
				    {
					Vertex center1 = moves.GetCookieCenter(i);
					Vertex center2 = new Vertex(0, center1.ypos());
					Vertex vector1 = subtract(vertices1[j], center1);
					Vertex vector2 = subtract(vertices1[j], center2);
    					double direction = vector1.xpos() * vector2.ypos() - vector2.xpos() * vector1.ypos();
					rotations[i] += direction * scaleFactor;
				    }
				
				if(vertices1[j].ypos() - margin < 0)
				    {
					Vertex center1 = moves.GetCookieCenter(i);
					Vertex center2 = new Vertex(center1.xpos(), 0);
					Vertex vector1 = subtract(vertices1[j], center1);
					Vertex vector2 = subtract(vertices1[j], center2);
					double direction = vector1.xpos() * vector2.ypos() - vector2.xpos() * vector1.ypos();
					rotations[i] += direction * scaleFactor;
				    }
				
				if(vertices1[j].ypos() + margin > 1)
				    {
					Vertex center1 = moves.GetCookieCenter(i);
					Vertex center2 = new Vertex(center1.xpos(), 1);
					Vertex vector1 = subtract(vertices1[j], center1);
					Vertex vector2 = subtract(vertices1[j], center2);
					double direction = vector1.xpos() * vector2.ypos() - vector2.xpos() * vector1.ypos();
					rotations[i] += direction * scaleFactor;
				    }
			    }
		    }

		for(int i = 0; i < numCopies; i++)
		    {
			Vertex center = moves.GetCookieCenter(i);
			double angle = moves.GetCookieAngle(i);
			angle += rotations[i];
			vertices[i] = getVertices(cookie, center, angle);
			moves.setCookiePosition(i, center, angle);
		    }
			
		collideWithBoundary();

		for(int i=0;i<numCopies;i++)
		    {
			Vertex center = moves.GetCookieCenter(i);
			double angle = moves.GetCookieAngle(i);
			vertices[i] = getVertices(cookie, center, angle);
			rotations[i] = 0;
		    }

		boolean collisions = true;
		int iterations = 0;
		int maxIterations = 20;
		double scaleFactor = 0.01;
		while(collisions && (iterations++ < maxIterations))
		    {
			//check for collisions between cookies
			collisions = false;
			for(int i=0;i<numCopies;i++)
			    for(int j=0;j<numCopies;j++)
				{
				    if(i != j)
					{
					    Vertex [] vertices1 = vertices[i];
					    Vertex [] vertices2 = vertices[j];
					    //find all points from 1 in 2
					    for(int k=0;k<vertices1.length;k++)
						{
						    //System.out.println(k);
						    if(vertexInPolygon(vertices1[k], vertices2))
							{
							    //double direction = rotateDirection(vertices1, vertices2);
							    Vertex center1 = moves.GetCookieCenter(i);
							    Vertex center2 = moves.GetCookieCenter(j);
							    Vertex vector1 = subtract(vertices1[k], center1);
							    Vertex vector2 = subtract(vertices1[k], center2);
							    //double direction = vector1.xpos() * vector2.xpos() + vector1.ypos() * vector2.ypos();
							    double direction = vector1.xpos() * vector2.ypos() - vector2.xpos() * vector1.ypos();
							    //System.out.println(direction);
							    rotations[i] += direction * scaleFactor;
							    rotations[j] -= direction * scaleFactor;
							    collisions = true;
							}
						}
					}
				}

			//check for collisions between boundaries
			for(int i=0;i<numCopies;i++)
			    {
				Vertex [] vertices1 = vertices[i];
				for(int j=0;j<vertices1.length;j++)
				    {
					if(vertices1[j].xpos() - margin < 0)
					    {
						Vertex center1 = moves.GetCookieCenter(i);
						Vertex center2 = new Vertex(0, center1.ypos());
						Vertex vector1 = subtract(vertices1[j], center1);
						Vertex vector2 = subtract(vertices1[j], center2);
						//double direction = vector1.xpos() * vector2.xpos() + vector1.ypos() * vector2.ypos();
						double direction = vector1.xpos() * vector2.ypos() - vector2.xpos() * vector1.ypos();
						//System.out.println(direction);
						rotations[i] += direction * scaleFactor;
						collisions = true;
					    }

					if(vertices1[j].ypos() - margin < 0)
					    {
						Vertex center1 = moves.GetCookieCenter(i);
						Vertex center2 = new Vertex(center1.xpos(), 0);
						Vertex vector1 = subtract(vertices1[j], center1);
						Vertex vector2 = subtract(vertices1[j], center2);
						//double direction = vector1.xpos() * vector2.xpos() + vector1.ypos() * vector2.ypos();
						double direction = vector1.xpos() * vector2.ypos() - vector2.xpos() * vector1.ypos();
						//System.out.println(direction);
						rotations[i] += direction * scaleFactor;
						collisions = true;
					    }

					if(vertices1[j].ypos() + margin > 1)
					    {
						Vertex center1 = moves.GetCookieCenter(i);
						Vertex center2 = new Vertex(center1.xpos(), 1);
						Vertex vector1 = subtract(vertices1[j], center1);
						Vertex vector2 = subtract(vertices1[j], center2);
						//double direction = vector1.xpos() * vector2.xpos() + vector1.ypos() * vector2.ypos();
						double direction = vector1.xpos() * vector2.ypos() - vector2.xpos() * vector1.ypos();
						//System.out.println(direction);
						rotations[i] += direction * scaleFactor;
						collisions = true;
					    }
				    }
			    }
			
			for(int i = 0; i < numCopies; i++)
			    {
				Vertex center = moves.GetCookieCenter(i);
				double angle = moves.GetCookieAngle(i);
				angle += rotations[i];
				vertices[i] = getVertices(cookie, center, angle);
				moves.setCookiePosition(i, center, angle);
			    }
		    }
	    }
	
	catch(Exception e)
	    {
		handleException(e);
	    }
    }

    public void translateCollisions()
    {
	try
	    {
		Vertex [][] vertices = new Vertex [numCopies][10];
		Vertex [] centers = new Vertex [numCopies];
		double [] angles = new double [numCopies];

		collideWithBoundary();
		
		for(int i=0;i<numCopies;i++)
		    {
			centers[i] = moves.GetCookieCenter(i);
			angles[i] = moves.GetCookieAngle(i);
			vertices[i] = getVertices(cookie, centers[i], angles[i]);
		    }	   
		
		//check for collisions and respond
		double delta = 0.001;
		Vertex [] forces = new Vertex [numCopies];
		boolean collisions = true;
		int iterations = 0;
		int maxIterations = 100;
		while(collisions && (iterations++ < maxIterations))
		    {
			collisions = false;
			for(int i=0;i<numCopies;i++)
			    forces[i] = new Vertex(0, 0);

			for(int i=0;i<numCopies;i++)
			    for(int j=0;j<numCopies;j++)
				{
				    if(i != j)
					{
					    Vertex [] vertices1 = vertices[i];
					    Vertex [] vertices2 = vertices[j];
					    Vertex center1 = centers[i];
					    Vertex center2 = centers[j];
					    Vertex force1 = null;
					    Vertex force2 = null;
					    if(cookiesIntersect(vertices1, vertices2))
						{
						    force1 = subtract(center1, center2);
						    force1 = normalize(force1);
						    force1 = scale(force1, delta);
						    force2 = scale(force1, -1);
						    forces[i] = add(forces[i], force1);
						    forces[j] = add(forces[j], force2);
						    collisions = true;
						}
					}
				}

			for(int i=0;i<numCopies;i++)
			    {
				centers[i] = add(centers[i], forces[i]);
				vertices[i] = getVertices(cookie, centers[i], angles[i]);
				moves.setCookiePosition(i, centers[i], angles[i]);
			    }

			collideWithBoundary();
			for(int i=0;i<numCopies;i++)
			    {
				centers[i] = moves.GetCookieCenter(i);
				angles[i] = moves.GetCookieAngle(i);
				vertices[i] = getVertices(cookie, centers[i], angles[i]);
			    }
		    }

		//System.out.println(iterations);
	    }
	catch(Exception e)
	    {
		handleException(e);
	    }
    }

    public void collideWithBoundary()
    {
	try
	    {
		Vertex [][] vertices = new Vertex [numCopies][10];
		Vertex [] centers = new Vertex [numCopies];
		double [] angles = new double [numCopies];
		
		for(int i=0;i<numCopies;i++)
		    {
			centers[i] = moves.GetCookieCenter(i);
			angles[i] = moves.GetCookieAngle(i);
			vertices[i] = getVertices(cookie, centers[i], angles[i]);
		    }	   
		
		//check collisions with boundarys
		for(int i=0;i<numCopies;i++)
		    {
			Vertex force = new Vertex(0, 0);
			for(int j=0;j<vertices[i].length;j++)
			    {
				if(vertices[i][j].xpos() - margin < 0)
				    force.setXpos(Math.max(force.xpos(), -vertices[i][j].xpos()));
				
				if(vertices[i][j].ypos() - margin < 0)
				    force.setYpos(Math.max(force.ypos(), -vertices[i][j].ypos()));
				
				if(vertices[i][j].ypos() + margin > 1)
				    force.setYpos(Math.min(force.ypos(), 1 - vertices[i][j].ypos()));
			    }
			
			centers[i] = new Vertex(centers[i].xpos() + force.xpos(), centers[i].ypos() + force.ypos());
			vertices[i] = getVertices(cookie, centers[i], angles[i]);
			moves.setCookiePosition(i, centers[i], angles[i]);
		    }
	    }
	catch(Exception e)
	    {
		handleException(e);
	    }
    }

    public void guaranteeCorrect()
    {
	try
	    {
		Vertex [][] vertices = new Vertex [numCopies][10];
		Vertex [] centers = new Vertex [numCopies];
		double [] angles = new double [numCopies];
		
		//move cookies due to gravity
		for(int i=0;i<numCopies;i++)
		    {
			centers[i] = moves.GetCookieCenter(i);
			angles[i] = moves.GetCookieAngle(i);
			vertices[i] = getVertices(cookie, centers[i], angles[i]);
		    }
		boolean collisions = true;
		int maxIterations = 20;
		int iterations = 0;
		//check collisions with boundarys
		while(collisions && (iterations++ < maxIterations))
		{
			collisions = false;
			for(int i=0;i<numCopies;i++)
		    {
				Vertex force = new Vertex(0, 0);
				for(int j=0;j<vertices[i].length;j++)
			    {
					if(vertices[i][j].xpos() - margin < 0)
					{
						force.setXpos(margin); //move right
				    	//force.setXpos(Math.max(force.xpos(), -vertices[i][j].xpos()));
				    	collisions = true;
				    }
				
					if(vertices[i][j].ypos() - margin < 0)
					{
						force.setYpos(margin); //move down
				    	//force.setYpos(Math.max(force.ypos(), -vertices[i][j].ypos()));
				    	collisions = true;
				    }
				
					if(vertices[i][j].ypos() + margin > 1)
					{
						force.setYpos(-1 * margin); //move up
				    	//force.setYpos(Math.min(force.ypos(), 1 - vertices[i][j].ypos()));
				    	collisions = true;
				    }
			    }
			
				centers[i] = new Vertex(centers[i].xpos() + force.xpos(), centers[i].ypos() + force.ypos());
				vertices[i] = getVertices(cookie, centers[i], angles[i]);
				moves.setCookiePosition(i, centers[i], angles[i]);			
		    }
		}

		//order copies by center left
		double delta = 0.001;
		Vertex translate = new Vertex(delta, 0);
		for(int i=0;i<numCopies;i++)
		    {
			int [] indices = getOrderByCenter();
			for(int j=i+1;j<numCopies;j++)
			    {
				int index1 = indices[i];
				int index2 = indices[j];
				while(cookiesIntersect(vertices[index1], vertices[index2]))
				    {
					Vertex center2 = moves.GetCookieCenter(index2);
					double angle2 = moves.GetCookieAngle(index2);
					center2 = add(center2, translate);
					vertices[index2] = getVertices(cookie, center2, angle2);
					moves.setCookiePosition(index2, center2, angle2);
				    }
			    }
		    }

		//find right boundary
		double maximum = 0;
		for(int i=0;i<numCopies;i++)
		    {
			Vertex center = moves.GetCookieCenter(i);
			double angle = moves.GetCookieAngle(i);
			Vertex [] tempVertices = getVertices(cookie, center, angle);
			for(int j=0;j<tempVertices.length;j++)
			    if(tempVertices[j].xpos() > maximum)
				maximum = tempVertices[j].xpos();
		    }

		maximum += 0.0001;
		moves.setRightBoundary(maximum);
	    }
	catch(Exception e)
	    {
		handleException(e);
	    }
    }
/*Lyuba's code 10/9/2003 */

    //checks if a point on cookie 1 is inside cookie 2
    public Vertex getPointInsidePolygon(Vertex[] vertices1, Vertex[] vertices2)
    {
	Vertex point = null;
	for(int i = 0; i < vertices1.length; i++)
	    {
		if(vertexInPolygon(vertices1[i], vertices2))
		    point = vertices1[i];
	    }
	return point;
		
    }

    public double dotProduct(Vertex v1, Vertex v2)
    {
	double product = 0;
	try
	    {
		product = v1.xpos() * v2.xpos() + v1.ypos() * v2.ypos();
	    }
	catch(Exception e)
	    {
		handleException(e);
	    }
	return product;
    }

    public double angleBetweenVectors(Vertex v1, Vertex v2)
    {
	double angle = 0.0;
	try
	    {
		//find the angle between vectors and x axis
		double angle1 = Math.atan2(v1.ypos(), v1.xpos());
		double angle2 = Math.atan2(v2.ypos(), v2.xpos());
		angle = angle1 - angle2;
		//System.out.println(angle1 + " " + angle2 + " " + angle);
	    }
	catch(Exception e)
	    {
		handleException(e);
	    }
	return angle;

    }
    
    public double rotateTorque(Vertex[] vertices1, Vertex[] vertices2)
    {
	double torque = 0;
	if(!cookiesIntersect(vertices1, vertices2))
	   return torque;
	try
	    {
		int whichCookie = 1; //which cookie has a point inside the other cookie
		Vertex point = getPointInsidePolygon(vertices1, vertices2);
		if(point==null)
		    {
			point = getPointInsidePolygon(vertices2, vertices1);
			if(point == null)   //too large of an overlap
			    return 0;
		whichCookie = 2;
		    }
		else 
		    whichCookie = 1;
		Vertex center1 = CookieCutter.centroid(vertices1);
		Vertex center2 = CookieCutter.centroid(vertices2);
		Vertex v1 = subtract(center1, point);
		Vertex v2 = subtract(point, center2);
		double angle = angleBetweenVectors(v1, v2);
		torque = magnitude(v1) * magnitude(v2) * Math.sin(angle);
	    }
	catch(Exception e)
	    {
		handleException(e);
	    }
	return torque;
    }
 
   //find the direction cookie 2 should rotate with respect to cookie 1
    public double rotateDirection(Vertex[] vertices1, Vertex[] vertices2)
    {
	try
	    {
		int whichCookie = 1; //which cookie has a point inside the other cookie
		Vertex point = getPointInsidePolygon(vertices1, vertices2);
		if(point==null)
		    {
			point = getPointInsidePolygon(vertices2, vertices1);
			if(point == null)   //too large of an overlap
			    return 0;
			whichCookie = 2;
		    }
		else 
		    whichCookie = 1;

		Vertex center1 = CookieCutter.centroid(vertices1);
		Vertex center2 = CookieCutter.centroid(vertices2);
		Vertex v1 = subtract(point, center1);
		Vertex v2 = subtract(point, center2);
		double angle = angleBetweenVectors(v1, v2);
		return Math.cos(angle) * magnitude(v1) * magnitude(v2);
		/*if(Math.cos(angle) < 0)
		    {
			System.out.println("Counter clockwise");
			if(whichCookie == 2)
			    return 1;
			else return -1;
		    }
		else
		    {
			System.out.println("Clockwise");
			if(whichCookie == 2)
			    return -1;
			else return 1;
		    }
		*/
	    }
	catch(Exception e)
	    {
		handleException(e);
	    }
	return 0;
    }
    public Vertex[] getAbsoluteVertices(int cookieIndex)
    {
	Vertex center = moves.GetCookieCenter(cookieIndex);
	double angle = moves.GetCookieAngle(cookieIndex);
	Vertex[] polygon = getVertices(this.cookie, center, angle);
	return polygon;
    }

    public boolean vertexInPolygon(Vertex point, int cookieIndex)
    {

	try
	    {
		Vertex[] polygon = getAbsoluteVertices(cookieIndex);
		int numIntersections = 0;
		//System.out.println("Point coords: " + point.xpos() + point.ypos());
		Line2D longLine = new Line2D.Double(point.xpos(), point.ypos(), point.xpos(), 0);

		for(int i = 0; i < polygon.length; i++)
		    {
			Line2D line = getLine(polygon[i], polygon[(i+1)%polygon.length]);
			if(line.intersectsLine(longLine))
			    {
				numIntersections++;
			    }
			
		    }
		if(numIntersections%2==1)
		    {
			//System.out.println("Point in cookie");
			return true;
		    }
		else 
		    {
			//System.out.println("Point outside of cookie");
			return false;
		    }
	    }
	catch(Exception e)
	    {
		handleException(e);
		return false;
	    }

    }


    public boolean vertexInPolygon(Vertex point, Vertex[] polygon)
    {

	try
	    {
		int numIntersections = 0;
		//System.out.println("Point coords: " + point.xpos() + point.ypos());
		Line2D longLine = new Line2D.Double(point.xpos(), point.ypos(), point.xpos(), 0);

		for(int i = 0; i < polygon.length; i++)
		    {
			Line2D line = getLine(polygon[i], polygon[(i+1)%polygon.length]);
			if(line.intersectsLine(longLine))
			    {
				numIntersections++;
			    }
			
		    }
		if(numIntersections%2==1)
		    {
			//System.out.println("Point in cookie");
			return true;
		    }
		else 
		    {
			//System.out.println("Point outside of cookie");
			return false;
		    }
	    }
	catch(Exception e)
	    {
		handleException(e);
		return false;
	    }

    }
/*End Lyuba's code */


    public int [] getOrderByCenter()
    {
	int [] indices = new int [numCopies];

	try
	    {
		for(int i=0;i<numCopies;i++)
		    indices[i] = i;

		for(int i=0;i<numCopies;i++)
		    for(int j=i+1;j<numCopies;j++)
			{
			    int index1 = indices[i];
			    int index2 = indices[j];
			    Vertex center1 = moves.GetCookieCenter(index1);
			    Vertex center2 = moves.GetCookieCenter(index2);
			    if(center2.xpos() < center1.xpos())
				{
				    int temp = index1;
				    index1 = index2;
				    index2 = temp;
				}
			    indices[i] = index1;
			    indices[j] = index2;
			}
	    }
	catch(Exception e)
	    {
		handleException(e);
	    }

	return indices;
    }

    public Vertex add(Vertex v1, Vertex v2)
    {
	try
	    {
		Vertex result = new Vertex(v1.xpos() + v2.xpos(), v1.ypos() + v2.ypos());
		return result;
	    }
	catch(Exception e)
	    {
		handleException(e);
	    }

	return null;
    }

    public Vertex subtract(Vertex v1, Vertex v2)
    {
	try
	    {
		Vertex result = new Vertex(v1.xpos() - v2.xpos(), v1.ypos() - v2.ypos());
		return result;
	    }
	catch(Exception e)
	    {
		handleException(e);
	    }
	
	return null;
    }

    public Vertex scale(Vertex v1, double factor)
    {
	try
	    {
		Vertex result = new Vertex(v1.xpos() * factor, v1.ypos() * factor);
		return result;
	    }
	catch(Exception e)
	    {
		handleException(e);
	    }

	return null;
    }

    public double magnitude(Vertex v)
    {
	try
	    {
		return Math.sqrt(v.xpos() * v.xpos() + v.ypos() * v.ypos());
	    }
	catch(Exception e)
	    {
		handleException(e);
	    }

	return 0;
    }

    public Vertex normalize(Vertex v)
    {
	try
	    {
		double magnitude = magnitude(v);
		if(magnitude != 0)
		    return scale(v, 1 / magnitude);
		else
		    return v.copy();
	    }
	catch(Exception e)
	    {
		handleException(e);
	    }
	
	return v;
    }

    public int getCookieIndex(int x, int y, int width, int height)
    {
	for(int i=0;i<numCopies;i++)
	    {
		Vertex [] vertices = getVertices(cookie, moves.GetCookieCenter(i), moves.GetCookieAngle(i));
		Polygon polygon = getPolygon(vertices, width, height);
		if(polygon.contains(x, y))
		    return i;
	    }
	
	return -1;
    }

    public void translateCookie(int cookieIndex, int delx, int dely, int width, int height)
    {
	try
	    {
		if(cookieIndex >= 0)
		    {
			Vertex center = moves.GetCookieCenter(cookieIndex);
			double scale = 1.0 / height;
			double dx = scale * delx;
			double dy = scale * dely;
			center.setXpos(center.xpos() + dx);
			center.setYpos(center.ypos() + dy);
		    }
	    }
	catch(Exception e)
	    {
		handleException(e);
	    }
    }

    public void rotateCookie(int cookieIndex, int delx, int dely, int width, int height)
    {
	try
	    {
		if(cookieIndex >= 0)
		    {
			double angle = moves.GetCookieAngle(cookieIndex);
			double scale = 1.0 / height;
			double dy = scale * dely * 3;
			double dx = scale * delx * 3;
			Vertex center = moves.GetCookieCenter(cookieIndex);
			moves.setCookiePosition(cookieIndex, center, angle + dy - dx);
		    }
	    }
	catch(Exception e)
	    {
		handleException(e);
	    }
    }
    
    public Vertex [] getVertices(Vertex [] cookie, Vertex center, double angle)
    {
	try
	    {
		Vertex centroid = CookieCutter.centroid(cookie);
		Vertex [] vertices = new Vertex [cookie.length];
		for(int i=0;i<cookie.length;i++)
		    {
			double dx = cookie[i].xpos() - centroid.xpos();
			double dy = cookie[i].ypos() - centroid.ypos();
			double newdx = dx * Math.cos(angle) - dy * Math.sin(angle);
			double newdy = dx * Math.sin(angle) + dy * Math.cos(angle);
			vertices[i] = new Vertex(center.xpos() + newdx, center.ypos() + newdy);
		    }
		return vertices;
	    }
	catch(Exception e)
	    {
		handleException(e);
	    }

	return null;
    }

    public Polygon getPolygon(Vertex [] vertices, int width, int height)
    {
	try
	    {
		Polygon polygon = new Polygon();
		for(int i=0;i<vertices.length;i++)
		    {
			polygon.addPoint((int) Math.round(vertices[i].xpos() * height), (int) Math.round(vertices[i].ypos() * height));
		    }
		return polygon;
	    }
	catch(Exception e)
	    {
		handleException(e);
	    }
	
	return null;
    }

    public Line2D getLine(Vertex vertex1, Vertex vertex2)
    {
	try
	    {
		Line2D line = new Line2D.Double(vertex1.xpos(), vertex1.ypos(), vertex2.xpos(), vertex2.ypos());
		return line;
	    }
	catch(Exception e)
	    {
		handleException(e);
	    }

	return null;
    }

    public boolean cookiesIntersect(Vertex [] cookie1, Vertex [] cookie2)
    {
	try
	    {
		for(int i=0;i<cookie1.length;i++)
		    {
			Line2D line1 = getLine(cookie1[i], cookie1[(i+1) % cookie1.length]);
			for(int j=0;j<cookie2.length;j++)
			    {
				Line2D line2 = getLine(cookie2[j], cookie2[(j+1) % cookie2.length]);
				if(line1.intersectsLine(line2))
				    return true;
			    }
		    }
		
		return false;
	    }
	catch(Exception e)
	    {
		handleException(e);
	    }

	return false;
    }

    public boolean cookieIntersectsBoundary(Vertex [] cookie)
    {
	try
	    {
		for(int i=0;i<cookie.length;i++)
		    {
			if(cookie[i].xpos() - margin < 0)
			    return true;

			if(cookie[i].ypos() + margin > 1)
			    return true;

			if(cookie[i].ypos() - margin < 0)
			    return true;
		    }

		return false;			   
	    }
	catch(Exception e)
	    {
		handleException(e);
	    }

	return false;
    }

    public void paint(Graphics g, int width, int height)
    {
	try
	    {
		Color tempColor = g.getColor();
		
		g.setColor(Color.white);
		g.fillRect(0, 0, width, height);
		
		Vertex [][] vertices = new Vertex [numCopies][10];
		Polygon [] polygons = new Polygon [numCopies];
		for(int i=0;i<numCopies;i++)
		    {
			vertices[i] = getVertices(cookie, moves.GetCookieCenter(i), moves.GetCookieAngle(i));
			polygons[i] = getPolygon(vertices[i], width, height);
		    }

		boolean [] intersects = new boolean [numCopies];
		for(int i=0;i<numCopies;i++)
		    for(int j=i+1;j<numCopies;j++)
			{
			    if(cookiesIntersect(vertices[i], vertices[j]))
				{
				    intersects[i] = true;
				    intersects[j] = true;
				}
			}

		for(int i=0;i<numCopies;i++)
		    if(cookieIntersectsBoundary(vertices[i]))
			intersects[i] = true;

		if(moves != null)
		    {
			g.setColor(new Color(255, 255, 0));
			double boundary = moves.GetRightBoundary();
			double scale = height;
			g.drawLine((int) (boundary * scale), 0, (int) (boundary * scale), height);
		    }

		if(bestMove != null)
		    {
			g.setColor(new Color(0, 255, 255));
			double boundary = bestMove.GetRightBoundary();
			double scale = height;
			g.drawLine((int) (boundary * scale), 0, (int) (boundary * scale), height);
		    }

		for(int i=0;i<numCopies;i++)
		    {
			if(intersects[i])
			    g.setColor(new Color(255, 0, 0, 128));
			else
			    g.setColor(new Color(0, 255, 0, 255));

			g.fillPolygon(polygons[i]);
		    }
		
		g.setColor(tempColor);
	    }
	catch(Exception e)
	    {
		handleException(e);
	    }
    }

    public void handleException(Exception e)
    {
	System.out.println("Error: " + e.getMessage());
	e.printStackTrace();
    }
}
