//***********************************************************
//*
//* File:           Group4MK1.java
//* Authors:        Mark Berman, Jason Winokur, Lyubov Golub
//*
//*                 Modified from original DumbPlayer.java
//*                    by Abhinav Kamra
//***********************************************************

package CookieCutter.g4;

import java.io.Serializable;
import java.util.*;
import java.lang.Math.*;
import CookieCutter.*;
import CookieCutter.g4.Simulator;

public class Group4PlayerBoxen2 implements IFCPlayer {

    CookieCutter           engine;
    Vertex[][]		   cookies;
	Simulator simulator;
    int			   copies;
    static Random       random;
    static final String _CNAME = "Brett Favre";
    BoundingBox[] boxen;
    double betweenBoxes = .01;
    double margin = 0.01;
    int simulatorSteps;
    
    public void register(CookieCutter __cookiecutter) throws Exception {
        engine = __cookiecutter;
	cookies = engine.cookieshapes();
	copies = engine.cookieCopies();
	random = new Random();
	simulator = new Simulator();
	
	//the number of simulator steps depends on the total number of vertices
	int totalVertices = 0;
	for(int i = 0; i < cookies.length; i++)
	    {
		totalVertices += cookies[i].length;
	    }
	int product = totalVertices * copies;
	if(product < 30)
	    {
	    	simulatorSteps = 250;
	    }
	else if(product < 50)
	    {
			simulatorSteps = 200;
	    }
	else if(product < 70)
		{
			simulatorSteps = 150;
		}
	else if(product < 90)
		{
			simulatorSteps = 100;
		}
	else
		{
			simulatorSteps = 50;
		}
	//System.out.println("Product = " + product + " simulator steps " + simulatorSteps);	
    }

    public String name() throws Exception {
        return _CNAME;
    }

    public boolean interactive() throws Exception {
        return false;
    }

    public Move[] moves() throws Exception {
	
	//	rotateCookie(cookies[0], 3.14);
	
        Move[] RET = new Move[cookies.length];
        for (int i=0; i < cookies.length; i++) {
	    RET[i] = new Move(copies);
	    boxen = new BoundingBox[copies];
	    double angle = findBestAngle(cookies[i]);
	    double column = 0;
	    for(int j=0;j < copies;j++)
		{
		    boxen[j] = new BoundingBox(rotateCookie(cookies[i], angle), angle);
		    //set cookie position
		    
		    //see if there's room on the bottom		    
		    if(j>0)
			{
			    double height = boxen[j-1].ySize * 2 + boxen[j-1].position.ypos() + margin + betweenBoxes;		    	
			    double nextX = boxen[j-1].position.xpos();
			    double nextY = boxen[j-1].position.ypos() + boxen[j-1].ySize + margin;
			    if(height < 1)
		    		boxen[j].place(new Vertex(nextX, nextY));
			    else
				{
				    column++;
				    boxen[j].place(new Vertex((boxen[j].xSize + betweenBoxes) * column + betweenBoxes ,margin));
				}	
			}
		    else
			{
			    boxen[j].place(new Vertex((boxen[j].xSize + betweenBoxes) * column + betweenBoxes, margin));
			}
		    
		    RET[i].setCookiePosition(j,boxen[j].pos(), boxen[j].angle());
		}

	    simulator.reset(cookies[i], copies, RET[i]);
	    Move bestMove = null;
	    //System.out.println("About to step. " + simulatorSteps + " times.");
	    for(int k = 0; k <simulatorSteps; k++)
	    	{
		    simulator.step();
	    	}
	    RET[i] = simulator.getMove();
        }
        
        return RET;
    }
 
    public double findBestAngle(Vertex[] inCookie) throws Exception {
	double minAngle = 0;
	double minX;
	BoundingBox tempBB;
	tempBB = new BoundingBox(inCookie, 0);
	minX = tempBB.xSize;
	double currentAngle = 0.0;
	
	do {
	    tempBB = new BoundingBox(rotateCookie(inCookie, currentAngle), currentAngle);
	    if ((tempBB.xSize < minX) && (tempBB.ySize + 2 * margin <= 1.0)) {
		minX = tempBB.xSize;
		minAngle = currentAngle;
	    }
	    currentAngle += ( (2.0 * Math.PI) / 360.0); // by degrees
	    
	} while (currentAngle < (2 * Math.PI));

	//System.out.println("best angle is " + minAngle);
	return minAngle;
    }


    //class for manipulating cookies in a bounding box. 
    //    boxes positions are defined b y their upper left corner
    //    offset keeps track of where the actual polygon centroid is 
    //    relative to the box

    
    class BoundingBox {
	public double xSize;
	public double ySize;
	public Vertex position;
	public Vertex offset;
	public Vertex[] cookie;
	public double angle;
	   
	public BoundingBox(Vertex[] inCookie, double inAngle) throws Exception {

	    double minX, maxX;
	    double minY, maxY;
	    cookie = inCookie;
	    
	    angle = inAngle;
	    minX = maxX = inCookie[0].xpos();
	    minY = maxY = inCookie[0].ypos();
	    
	    for(int i=0;i<inCookie.length;i++)
		{
		    if (inCookie[i].xpos() < minX)
			minX = inCookie[i].xpos();
		    if (inCookie[i].xpos() > maxX)
			maxX = inCookie[i].xpos();
		    if (inCookie[i].ypos() < minY)
			minY = inCookie[i].ypos();
		    if (inCookie[i].ypos() > maxY)
			maxY = inCookie[i].ypos();
		}
	    xSize = maxX - minX;
	    ySize = maxY - minY;
	    Vertex tempCenter = engine.centroid(inCookie);
	    offset = new Vertex(tempCenter.xpos() - minX, tempCenter.ypos() - minY);
	    
	}
	
	public double getEfficiency() throws Exception {
	    return (xSize * ySize - engine.area(cookie));
	}
	
	public void place(Vertex v) throws Exception {
	    position = v;
	    //recalculate vertices of cookie - makes it easier to check for valid moves
	    Vertex centroid = engine.centroid(cookie);
	    Vertex translate = new Vertex(position.xpos() - centroid.xpos(), position.ypos() - centroid.ypos());
	    for(int i = 0; i < cookie.length; i++)
	    {
	    	cookie[i].setXpos(cookie[i].xpos() + translate.xpos());
	    	cookie[i].setYpos(cookie[i].ypos() + translate.ypos());
	    }    
	}
	
	private Vertex[] getCorners() throws Exception {

	    Vertex[] v = new Vertex[4];
	    v[0] = position;
	    v[1] = new Vertex(position.xpos() + xSize, position.ypos());
	    v[2] = new Vertex(position.xpos() + xSize, position.ypos() + ySize);
	    v[3] = new Vertex(position.xpos(), position.ypos() + ySize);
	    return v;
	}

	public void drawBox() throws Exception {
	    Vertex[] corners = getCorners();
	    engine.drawLine(corners[0], corners[1]);
	    engine.drawLine(corners[1], corners[2]);
	    engine.drawLine(corners[2], corners[3]);
	    engine.drawLine(corners[3], corners[0]);
	}
	
	public Vertex pos()  throws Exception {
	    Vertex v = new Vertex(position.xpos() + offset.xpos(),
				  position.ypos() + offset.ypos());
	    return v;
	}

	public double angle() throws Exception {
	    return angle;
	}
	
	/* added 9/30/03 Lyuba */
	public Vertex[] getCookie()
	{
	    return cookie;
	}
    }

    private Vertex rotate(Vertex _origvert, Vertex origcenter, Vertex center, double angle) throws Exception
    {
	double dx = _origvert.xpos() - origcenter.xpos();
	double dy = _origvert.ypos() - origcenter.ypos();
	
	double newdx = dx*Math.cos(angle) - dy*Math.sin(angle);
	double newdy = dx*Math.sin(angle) + dy*Math.cos(angle);
	
	//System.out.println("origvert = " + convertToVirtual(_origvert) + "origcenter = " + convertToVirtual(origcenter));
	
	return new Vertex(center.xpos() + newdx, center.ypos() + newdy);
    }

    private Vertex[] rotateCookie(Vertex[] inCookie, double angle) throws Exception {
	
	Vertex[] RET = new Vertex[inCookie.length];
	Vertex center = engine.center(inCookie);
	int i;
	double xMin, yMin;

	xMin = 1000; //unrealistically high
	yMin = 1000; //likewise
	for(i=0;i<inCookie.length;i++) {
	    Vertex tempV;
	    tempV = rotate(inCookie[i], center, center, angle);
	    if (tempV.xpos() < xMin)
		xMin = tempV.xpos();
	    if (tempV.ypos() < yMin)
		yMin = tempV.ypos();
	}
	
	for(i=0;i<inCookie.length;i++) {
	    Vertex tempV;
	    tempV = rotate(inCookie[i], center, center, angle);
	    RET[i] = new Vertex(tempV.xpos() - xMin, tempV.ypos() - yMin);
	}

	return RET;
    } 
}
