//***********************************************************
//*
//* File:           CookieCutter.java
//* Author:         Abhinav Kamra
//* Contact:        kamra-at-cs.columbia.edu
//* Update:         11.9.2002
//*
//* Description:    Game model for Project 2, CS4444
//*                 Fall 2003
//*
//*
//***********************************************************


package CookieCutter;

import ui.*;
import java.util.*;
import java.io.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import java.awt.geom.*;
import java.awt.image.*;
import javax.swing.table.TableModel;
import java.text.NumberFormat;
import javax.imageio.*;

public final class CookieCutter implements IFCConstants, IFCModel {

    Class[]                 _classlist;
    Class[]                 _playerlist;
    String[]                _gamefilelist;
    PlayerWrapper[]         _players;
    String                  _gamefile;
    int                     _maxrounds;
    int                     _rounds;
    int                     _numplayers;
    int                     _state;
    int                     _pindex;
    Vertex[][]              _cookieshapes;
    int			    _cookiecopies;	// Number of Copies of Each Cookie to be placed
    GeneralPath[][]           _paths;
    GeneralPath[][]           _bestpaths;
    Move[]                  _moves;
    Vertex[][]              _polygons;
    ArrayList               _trace;
    transient IFCUI         _ui;
    IFCConfiguration        _config;
    static Random           _random;
    JTextField              _input;
    ControlPanel            _control;
    HumanPanel              _humanpanel;
    ViewPanel               _view;
    ArrayList               _lines;
    boolean                 _tracing;
    boolean                 _registered;
    double                  _delay;
    double                  _currentScore;
    double                  _bestScore;
    static final int        _CPLACING               = 1;
    static final int        _CHUMANPLACING          = 2;
    static final int        _CWAITING               = 3;
    static final int        _CFINISHED              = 4;
    static final int[]      _CSTATES                = { _CPLACING, _CWAITING, _CFINISHED };
    static final String     _CNAME                  = "CookieCutter !! Yum Yum";
    static final String     _CPROPERTIES_FILE       = "gamemodel.properties";
    static final String     _CGAME_FILE_DIRECTORY   = "GameFiles/";
    static final String     _CSPLIT_CHARS           = ",\t\n ";
    static final int        _CMIN_ROUNDS            = 1;
    static final int        _CMAX_ROUNDS            = 1;
    static final int        _CMIN_COPIES            = 1;
    static final int        _CMAX_COPIES            = 10;
    static final int        _CMIN_PLAYERS           = 1;
    static final int        _CMAX_PLAYERS           = 20;
    static final long       _CDELAY                 = 750;
    static final double     _CXFINAL_MIN            = 600;
    static final double     _CXFINAL_MAX            = 800;
    static final double     _CYFINAL_MIN            = 0;
    static final double     _CYFINAL_MAX            = 200;
    static final double     _CXCONVERTED_MAX        = 4;
    static final double     _CYCONVERTED_MAX        = 1;
    static final double     _CTOLERANCE             = .0001;
    static final double     _PI			    = 3.1416;
    static final double     _FAILSCORE		    = 99.9;

    // For Human Player
    int			_cookieindex;		
    int			_copyindex;		
    boolean		_humanplaying;
    boolean		_playmode;


    //********************************************
    //*
    //* Constructors
    //*
    //********************************************
    public CookieCutter() throws Exception {
        create(createDefaultConfiguration());
    }

    public CookieCutter(IFCConfiguration __config) throws Exception {
        create(__config);
    }

    public CookieCutter(IFCTournament __tournament) throws Exception {
        run(__tournament);
    }

    //********************************************
    //*
    //* Constructor Delegates
    //*
    //********************************************
public void run(IFCTournament __tournament) throws Exception
{
	IFCGameRecord[] games;
	int _MAX;

	games = __tournament.games();
        if (games == null) {
            throw new Exception("Error:  Null game record list");
        }
        _MAX = games.length;
        _random = new Random();

        for (int i=0; i < _MAX; i++)
	{
		System.err.println("[Game]: "+i+" Starting at " + System.currentTimeMillis()/60000.0 + " Minutes");
		_playerlist = games[i].players();
		if (_playerlist == null)
		{
			throw new Exception("Error:  Null player list in game record: "+i);
		}

        	_numplayers = _playerlist.length;
        	_cookiecopies = games[i].cookieCopies();
        	_maxrounds = 1;
        	_gamefile = games[i].gameFile();
        	_rounds = _maxrounds;
        	_state = _CPLACING;
        	parseGameFile(_gamefile);

        	_players = new PlayerWrapper[_numplayers];
        	for (int j=0; j < _numplayers; j++) {
                	if (_playerlist[j] == null) {
                	    throw new Exception("Error:  Null player, game record: "+i+", player: "+j);
                	}
        	    _players[j] = new PlayerWrapper(_playerlist[j]);
        	    _players[j].register(this);
                	if (_players[j].interactive()) {
                	    throw new Exception("Error:  Interactive Players Are Not Permitted In Tournaments");
                	}
        	}

		// Configuration Done !! Lets run the tournament
		_pindex = 0;
		_bestpaths = null;
		_bestScore = Double.MAX_VALUE;
		while(step()) { }

		double[] scores = new double[_numplayers];
		double[] times = new double[_numplayers];
        	for (int j=0; j < _numplayers; j++)
		{
			scores[j] = _players[j].score();
			times[j] = _players[j].totaltime();
		}
		games[i].setScores(scores);
		games[i].setTimes(times);

		if(_bestpaths != null)
		{
		try {
				_paths = _bestpaths;
				_view = new ViewPanel();
				//BufferedImage image = new BufferedImage(_view.getPreferredWidth(), _view.getPreferredHeight(), BufferedImage.TYPE_INT_RGB);
				BufferedImage image = new BufferedImage((int) Math.ceil(200 * _bestScore), 200, BufferedImage.TYPE_INT_RGB);
				Graphics2D g2 = image.createGraphics();
				_view.paintComponent(g2);
				g2.dispose();
				ImageIO.write(image, "jpeg", new File("Results/Best." + _gamefile + "." + _cookiecopies + ".jpg"));
            	} catch (Exception EXC) {
            	    System.out.println(EXC.getMessage());
            	    EXC.printStackTrace();
            	}
		}
	}
}
       
    void create(IFCConfiguration __config) throws Exception {
        _config = __config;
        _classlist = _config.classList();
        _playerlist = _config.playerList();
        _numplayers = _config.numPlayers();
        _cookiecopies = _config.cookieCopies();
        _maxrounds = _config.numRounds();
        _gamefilelist = _config.gameFileList();
        _gamefile = _config.gameFile();
        _rounds = _maxrounds;
        _random = new Random();
        _state = _CPLACING;
        _trace = new ArrayList();
        _lines = new ArrayList();
        _delay = _CDELAY;
        parseGameFile(_gamefile);

        _players = new PlayerWrapper[_numplayers];
        for (int i=0; i < _numplayers; i++) {
            _players[i] = new PlayerWrapper(_playerlist[i]);
            _players[i].register(this);
        }
        _control = new ControlPanel();
        _humanpanel = new HumanPanel();
        _view = new ViewPanel();
    }


    //********************************************
    //*
    //* Initial Configuration
    //*
    //********************************************
    public static IFCConfiguration createDefaultConfiguration() throws Exception {
        IFCConfiguration RET = new Configuration();
        String[] toks;
        Class[] classes;
        Class[] players;
        String[] gamefilelist;
        int _MAX;
        Properties properties;
        Random random = new Random();
        ParseValue pv;

        RET.setNumRoundsBounds(_CMIN_ROUNDS, _CMAX_ROUNDS);
        RET.setNumPlayersBounds(_CMIN_PLAYERS, _CMAX_PLAYERS);

        properties = Util.gatherProperties(_CPROPERTIES_FILE);
        RET.setLogFile(properties.getProperty("LOG_FILE").trim());
        RET.setGameFile(properties.getProperty("GAME_FILE").trim());

        pv = ParseValue.parseIntegerValue(properties.getProperty("NUM_ROUNDS").trim(), _CMIN_ROUNDS, _CMAX_ROUNDS);
        if (!pv.isValid()) {
            throw new Exception("Properties parameter out of range, Number of Rounds");
        }
        RET.setNumRounds(((Integer) pv.value()).intValue());

        toks = Util.split(_CSPLIT_CHARS, properties.getProperty("CLASS_LIST").trim());
        _MAX = toks.length;
        classes = new Class[_MAX];
        for (int i=0; i < _MAX; i++) {
            classes[i] = Class.forName(toks[i]);
        }
        RET.setClassList(classes);

        toks = Util.split(_CSPLIT_CHARS, properties.getProperty("PLAYER_LIST").trim());
        _MAX = toks.length;
        if (_MAX < _CMIN_PLAYERS || _MAX > _CMAX_PLAYERS) {
            throw new Exception("Properties parameter out of range, Number of Players (Player List)");
        }
        players = new Class[_MAX];
        for (int i=0; i < _MAX; i++) {
            players[i] = Class.forName(toks[i]);
        }
        RET.setPlayerList(players);

        toks = Util.split(_CSPLIT_CHARS, properties.getProperty("GAME_FILE_LIST").trim());
        _MAX = toks.length;
        gamefilelist = new String[_MAX];
        System.arraycopy(toks, 0, gamefilelist, 0, _MAX);
        RET.setGameFileList(gamefilelist);

        return RET;
    }


    //********************************************
    //*
    //* Exposed Methods
    //*
    //********************************************
    public int remainingRounds() throws Exception {
        return _rounds;
    }

    public int maxRounds() throws Exception {
        return _maxrounds;
    }

    public int numPlayers() throws Exception {
        return _numplayers;
    }

    public int cookieCopies() throws Exception {
        return _cookiecopies;
    }

    public void print(String __str) throws Exception {
        if (_registered) {
            _ui.print(__str);
        }
    }

    public void println(String __str) throws Exception {
        if (_registered) {
            _ui.println(__str);
        }
    }

    public void println() throws Exception {
        if (_registered) {
            _ui.println();
        }
    }

    public double findMinRightBoundary(Vertex[][] polygons) throws Exception
    {
	    double x;
	    double currmin = 0.0;
	    for(int i=0;i < polygons.length;i++)
	    {
	    	for(int j=0;j < polygons[i].length;j++)
		{
			x = polygons[i][j].xpos();
			if(x > currmin)
				currmin = x;
		}
	    }
	    return currmin;
    }
    
    public void drawLine(Vertex __beg, Vertex __end) throws Exception {
        Vertex[] line = new Vertex[2];
        line[0] = convertToScreen(__beg);
        line[1] = convertToScreen(__end);

        _lines.add(line);
    }

    public static boolean intersects(Vertex __v1, Vertex __v2, Vertex __v3, Vertex __v4) throws Exception {
        return Line2D.linesIntersect(__v1.xpos(), __v1.ypos(), __v2.xpos(), __v2.ypos(), __v3.xpos(), __v3.ypos(), __v4.xpos(), __v4.ypos());
    }
    
    public void toggleTrace(boolean __tracing) throws Exception {
        _tracing = __tracing;
    }
    
    public void setDelay(long __delay) throws Exception {
        if (__delay >= 0) {
            _delay = __delay;
        }
    }

    public static double distance(double __x,double __y, double __x1, double __y1,double __x2,double __y2) throws Exception {
        double dx;
        double dy;
        double t;

        dx = __x2 - __x1;
        dy = __y2 - __y1;

        if (dx < _CTOLERANCE && dx > -_CTOLERANCE && dy < _CTOLERANCE && dy > -_CTOLERANCE) {
            dx = __x1 - __x;
            dy = __y1 - __y;
            return dx*dx + dy*dy;
        }
        t = (dx * (__x - __x1) + dy * (__y - __y1)) / (dx * dx + dy * dy);

        if (t < 0) {
            t = 0;
        }
        else
        if (t > 1) {
            t = 1;
        }
        dx = dx * t + __x1 - __x;
        dy = dy * t + __y1 - __y;

        return dx*dx + dy*dy;
    }

    public static double[] distance_intersect(double __x,double __y, double __x1, double __y1, double __x2, double __y2) throws Exception {
        double dx;
        double dy;
        double x;
        double y;
        double t;
        double[] RET = new double[3];

        x = __x;
        y = __y;
        dx = __x2 - __x1;
        dy = __y2 - __y1;

        if (dx < _CTOLERANCE && dx > -_CTOLERANCE && dy < _CTOLERANCE && dy > -_CTOLERANCE) {
            dx = __x1 - x;
            dy = __y1 - y;
        } else {
            t = (dx * (x - __x1) + dy * (y - __y1)) / (dx * dx + dy * dy);

            if (t < 0.0) {
                t = 0.0;
            }
            else
            if (t > 1.0) {
                t = 1.0;
            }
            dx = dx * t + __x1 - x;
            dy = dy * t + __y1 - y;
        }

        RET[0] = x + dx;
        RET[1] = y + dy;
        RET[2] = dx*dx + dy*dy;
        return RET;
    }

    public static double[] intersects(Vertex __P, Vertex __D, Vertex __C, double __radius) throws Exception {
    
        double[] diff = new double[2];
        double a0;
        double a1;
        double a2;
        double discr;
        double inva2;
        double[] RET;

        diff[0] = __P.xpos() - __C.xpos();
        diff[1] = __P.ypos() - __C.ypos();
        
        a0 = (diff[0] * diff[0] + diff[1] * diff[1]) - __radius * __radius;
        a1 = __D.xpos() * diff[0] + __D.ypos() * diff[1];
        a2 = __D.xpos() * __D.xpos() + __D.ypos() * __D.ypos();
        
        discr = a1 * a1 - a0 * a2;
        if (discr > 0) {
            RET = new double[2];
            inva2 = (double) 1 / a2;
            discr = Math.sqrt(discr);
            RET[0] = (-a1 - discr) * inva2;
            RET[1] = (-a1 + discr) * inva2;
        }
        else
        if (discr < 0) {
            RET = new double[0];
        }
        else {
            RET = new double[1];
            RET[0] = -a1 / a2;
        }

        return RET;
    }
    
    public static boolean intersects_line_arc(Vertex __beg, Vertex __end, Vertex __abeg, Vertex __aend, Vertex __center, double __radius, Vertex[] __ipoints) throws Exception {

        int _MAX;
        double[] roots;
        Vertex d = new Vertex(__end.xpos() - __beg.xpos(), __end.ypos() - __beg.ypos());
        int RET = 0;

        roots = intersects(__beg, d, __center, __radius);
        _MAX = roots.length;

        if (_MAX == 0) {
            return false;
        }

        if (_MAX == 1) {
            if (roots[0] < 0.0 || roots[0] > 1.0) {
                _MAX = 1;
            }
        } else {
            if (roots[1] < 0 || roots[0] > 1.0) {
                _MAX = 0;
            }
            else {
                if (roots[1] <= 1.0) {
                    if (roots[0]  < 0.0) {
                        _MAX = 1;
                        roots[0] = roots[1];
                    }
                } else {
                    _MAX = (roots[0] >= 0.0 ? 1 : 0);
                }
            }
        }
        
        for (int i=0; i < _MAX; i++) {
            __ipoints[RET] = new Vertex(__beg.xpos() + roots[i] * d.xpos(), __beg.ypos() + roots[i] * d.ypos());
            if (contains(__abeg, __aend, __center, __radius, __ipoints[RET])) {
                RET++;
            }
        }

        return (RET != 0);
    }

    public static boolean contains(Vertex __abeg, Vertex __aend, Vertex __center, double __radius, Vertex __point) throws Exception {

        double lhs = (__point.xpos() - __abeg.xpos())*(__aend.ypos() - __abeg.ypos());
        double rhs = (__point.ypos() - __abeg.ypos())*(__aend.xpos() - __abeg.xpos());
        double dist = Math.sqrt((__point.xpos() - __center.xpos()) * (__point.xpos() - __center.xpos()) + 
                                (__point.ypos() - __center.ypos()) * (__point.ypos() - __center.ypos()));

        return (lhs <= rhs && Math.abs(dist - __radius) < _CTOLERANCE);
    }

    public double area(Vertex[] __polygon) throws Exception {
        int _MAX = __polygon.length;
        double RET = 0;
        int j;

        for (int i=0; i< _MAX; i++) {
            j = (i + 1) % _MAX;
            RET += __polygon[i].xpos() * __polygon[j].ypos();
            RET -= __polygon[i].ypos() * __polygon[j].xpos();
        }
        RET = (RET < 0) ? RET / (-2.0) : RET / 2.0;
        return RET;
    }

	public static Vertex centroid(Vertex[] __polygon) throws Exception
	{

        	int _MAX = __polygon.length;
        	double x=0.0;
        	double y=0.0;

        	for (int i=0; i < _MAX; i++)
		{
			x += __polygon[i].xpos();
			y += __polygon[i].ypos();
		}
		x /= _MAX;
		y /= _MAX;

		return new Vertex(x, y);
	}
    
    public Vertex center(Vertex[] __polygon) throws Exception {
        int _MAX = __polygon.length;
        int j;
        double x=0;
        double y=0;
        double value;

        for (int i=0; i < _MAX; i++) {
            j = (i + 1) % _MAX;
            value = (__polygon[i].xpos() * __polygon[j].ypos()) -
                    (__polygon[j].xpos() * __polygon[i].ypos());
            x += (__polygon[i].xpos() + __polygon[j].xpos()) * value;
            y += (__polygon[i].ypos() + __polygon[j].ypos()) * value;
        }
        value = (double) 1 / (area(__polygon) * 6.0);
        x *= value;
        y *= value;

        return new Vertex(x, y);
    }

    public Vertex[][] cookieshapes() throws Exception {
        int _MAX = _cookieshapes.length;
        int _MAXJ;
        Vertex[][] RET = new Vertex[_MAX][];

        for (int i=0; i < _MAX; i++) {
            _MAXJ = _cookieshapes[i].length;
            RET[i] = new Vertex[_MAXJ];
            for (int j=0; j < _MAXJ; j++) {
                RET[i][j] = convertToVirtual(_cookieshapes[i][j]);
            }
        }
        return RET;
    }
    
    public boolean validTranslation(Vertex[] __polygon, Vertex[] __next, double __p1, double __p2) throws Exception {
        return validTranslationAux(__polygon, __next, __p1, __p2, true);
    }

    public boolean validRotation(Vertex[] __polygon, Vertex[] __next, double __theta, double __xpos, double __ypos) throws Exception {
        return validRotationAux(__polygon, __next, __theta, __xpos, __ypos, true);
    }

    //********************************************
    //*
    //* IFCModel
    //*
    //********************************************
    public void register(IFCUI __ui) throws Exception {
        _ui = __ui;
        _ui.register(this);
        _registered = true;

        println("[Player Configuration]: ");
        for (int i=0; i < _numplayers; i++) {
            println("\t[Player" + i + "]: " + _players[i].name());
        }
        refresh();
    }

    public String name() throws Exception {
        return _CNAME;
    }

    public JPanel exportHumanPanel() throws Exception {
        return _humanpanel;
    }

    public JPanel exportControlPanel() throws Exception {
        return _control;
    }

    public JPanel exportViewPanel() throws Exception {
        return _view;
    }

    public JButton[] exportTools() throws Exception {
        return _control.exportTools();
    }
    
    public JMenu exportMenu() throws Exception {
        return null;
    }

    public IFCConfiguration exportConfiguration() throws Exception {
        return _config;
    }

    //********************************************
    //*
    //* Private Methods
    //*
    //********************************************
    private void refresh() throws Exception {
        if (_registered) {
            _ui.refresh();
        }
    }

    private void reset() throws Exception {
        if (_registered) {
            _ui.reset();
        }
    }

    private void parseGameFile(String __gamefile) throws Exception {
        BufferedReader br;
        File file;
        StringBuffer sb = new StringBuffer();
        String[] toks;
        String str;
        ArrayList vlist = new ArrayList();
        ArrayList olist = new ArrayList();
        int _MAX;
        int _MAXJ;
        int count=0;

        file = new File(_CGAME_FILE_DIRECTORY + __gamefile);
        if (file.exists()) {
            br = new BufferedReader(new FileReader(file));
        }
        else {
            file = new File(__gamefile);
            if (file .exists()) {
                br = new BufferedReader(new FileReader(file));
            }
            else {
                throw new Exception("Error:  Game File Not Found: "+__gamefile);
            }
        }
        while ((str = br.readLine()) != null) {
            if (str.length() < 1) {
                continue;
            }
            if (str.charAt(0) == '#') {
                continue;
            }
            if (str.charAt(0) == '!') {
                if (sb.length() > 0) {
                    toks = Util.split(",\t\n ", new String(sb));
                    _MAX = toks.length;
                    for (int i=0; i < _MAX; i+=2) {
                        vlist.add(convertToScreen(new Vertex(Double.parseDouble(toks[i]), Double.parseDouble(toks[i+1]))));
                    }
                    olist.add((Vertex[]) vlist.toArray(new Vertex[0]));
                    sb.delete(0, sb.length() - 1);
                    vlist.clear();
                }
                continue;
            }
            sb.append(str);
            sb.append("\n");
        }
        if (sb.length() > 0) {
            toks = Util.split(",\t\n ", new String(sb));
            _MAX = toks.length;
            for (int i=0; i < _MAX; i+=2) {
                vlist.add(convertToScreen(new Vertex(Double.parseDouble(toks[i]), Double.parseDouble(toks[i+1]))));
            }
            olist.add((Vertex[]) vlist.toArray(new Vertex[0]));
        }
        _cookieshapes = (Vertex[][]) olist.toArray(new Vertex[0][]);

// Kamra - Cookie Change
/*
        _MAX = _cookieshapes.length;
        _paths = new GeneralPath[_MAX];
        for (int i=0; i < _MAX; i++) {
            _paths[i] = new GeneralPath(GeneralPath.WIND_EVEN_ODD, _MAX);
            _paths[i].moveTo((float) _cookieshapes[i][0].xpos(), (float) _cookieshapes[i][0].ypos());
            _MAXJ = _cookieshapes[i].length;
            for (int j=1; j < _MAXJ; j++) {
                _paths[i].lineTo((float) _cookieshapes[i][j].xpos(), (float) _cookieshapes[i][j].ypos());
            }
            _paths[i].closePath();
        }
*/
    }

// Kamra - Cookie Change
/*
    private boolean processPolygon(Vertex[] __polygon) throws Exception {
        int _MAX = __polygon.length;

        _polygon = new Vertex[_MAX];
        for (int i=0; i < _MAX; i++) {
            _polygon[i] = convertToScreen(__polygon[i]);
        }

        _trace.clear();
        _trace.add(_polygon);
        return true;
    }
*/

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 boolean CheckValidity(double Xbound) throws Exception
{
	double Ybound = convertYTranslation(1.0);

	int _MAXI = _cookiecopies;

	for(int i=0;i < _MAXI;i++)
	{
		for(int p1=0;p1 < _polygons[i].length;p1++)
		{
				Vertex a = _polygons[i][p1];
//System.out.println("Checking"+a+" with bounds "+Xbound+", "+Ybound);
				if((a.xpos() < 0.0)||(a.xpos() > Xbound)||(a.ypos() < 0.0)||(a.ypos() > Ybound))
					return false;
		}
		for(int j=0;j < i;j++)
		{
			for(int p1=0;p1 < _polygons[i].length;p1++)
			{
				int p2 = (p1+1)%_polygons[i].length;
				for(int q1=0;q1 < _polygons[j].length;q1++)
				{
					int q2 = (q1+1)%_polygons[j].length;
					if(intersects(_polygons[i][p1], _polygons[i][p2], _polygons[j][q1], _polygons[j][q2]))
					{
						/*
	System.out.println("Cookie Intersection "+i+"."+p1+", "+j+"."+p2);
	System.out.println(_polygons[i][p1]);
	System.out.println(_polygons[i][p2]);
	System.out.println(_polygons[j][q1]);
	System.out.println(_polygons[j][q2]);
	*/
						return false;
					}
				}
			}
		}
	}
	return true;
}
    
private GeneralPath MakePath(Vertex[] _orig, Vertex center, double angle, double startboundary, double endboundary, GeneralPath[] paths, int MAX) throws Exception
{
	Vertex prev;
	GeneralPath path;
	int _MAXI = _orig.length;
	Vertex origcenter = centroid(_orig);
	double Xtrans = center.xpos() - origcenter.xpos();
	double Ytrans = center.ypos() - origcenter.ypos();

	
	path = new GeneralPath(GeneralPath.WIND_EVEN_ODD, _MAXI);

	Vertex firstvertex = Rotate(_orig[0], origcenter, center, angle);
	_polygons[MAX][0] = firstvertex;
	prev = firstvertex;
	path.moveTo((float)(startboundary + firstvertex.xpos()), (float)firstvertex.ypos());
	for(int i=1;i < _MAXI;i++)
	{
		Vertex tmpvertex = Rotate(_orig[i], origcenter, center, angle);
		_polygons[MAX][i] = tmpvertex;
		path.lineTo((float)(startboundary + tmpvertex.xpos()), (float)tmpvertex.ypos());
		prev = tmpvertex;
	}
	path.closePath();
	return path;
}

private GeneralPath MakePathFromMoveElement(Vertex[] _orig, Move _move, int copyindex, double _humanboundary) throws Exception
{
	GeneralPath path;
	double maxX = 0.0;
	
	int _MAXI = _orig.length;
	Vertex origcenter = centroid(_orig);
	Vertex center = convertToScreen(_move.GetCookieCenter(copyindex));
	double angle = _move.GetCookieAngle(copyindex);

	double Xtrans = center.xpos() - origcenter.xpos();
	double Ytrans = center.ypos() - origcenter.ypos();

	double startboundary = convertXTranslation(_humanboundary);
	
	path = new GeneralPath(GeneralPath.WIND_EVEN_ODD, _MAXI);

	Vertex firstvertex = Rotate(_orig[0], origcenter, center, angle);
	if(firstvertex.xpos() > maxX)
		maxX = firstvertex.xpos();
	path.moveTo((float)(startboundary + firstvertex.xpos()), (float)firstvertex.ypos());
	for(int i=1;i < _MAXI;i++)
	{
		Vertex tmpvertex = Rotate(_orig[i], origcenter, center, angle);
		if(tmpvertex.xpos() > maxX)
			maxX = tmpvertex.xpos();
		path.lineTo((float)(startboundary + tmpvertex.xpos()), (float)tmpvertex.ypos());
	}
	path.closePath();
	_move.AdjustRightBoundary(convertXVirtual(maxX));
	return path;
}

private GeneralPath[] GetPathsFromMove(Vertex[] _orig, Move _move, double startboundary, double endboundary) throws Exception
{
	GeneralPath[] paths;
	paths = new GeneralPath[_cookiecopies];
	_polygons = new Vertex[_cookiecopies][];
	for(int i=0;i < _cookiecopies;i++)
	{
		_polygons[i] = new Vertex[_orig.length];
		Vertex center = convertToScreen(_move.GetCookieCenter(i));
		double angle = _move.GetCookieAngle(i);
		paths[i] = MakePath(_orig, center, angle, startboundary, endboundary, paths, i);
	}
	return paths;
}

// Kamra - Cookie Change
    private boolean processMoves(Move[] _moves1) throws Exception
	{
		double startboundary = 0.0;
		double endboundary = 0.0;
		double boundary = 0.0;
		Vertex[][] _cookies;
		boolean valid = true;
		//_cookies = cookieshapes();
		_cookies = _cookieshapes;

		int _MAXI = _cookies.length;
		if(_moves1.length != _MAXI)
		{
			println("Solution not provided for all "+_MAXI+" Cookie shapes");
			return false;
		}
		_paths = new GeneralPath[_MAXI][];
		_currentScore = 0.0;
		for(int i=0;i < _MAXI;i++)
		{
			if(_moves1[i] != null)
			{
				boundary = _moves1[i].GetRightBoundary();
				_currentScore += boundary;
				endboundary += convertXTranslation(boundary);
				_paths[i] = GetPathsFromMove(_cookies[i], _moves1[i], startboundary, endboundary);
				if(!CheckValidity(endboundary - startboundary))
					valid = false;
				startboundary = endboundary;
			}
			else
				return false;
		}
		// All done...
		return valid;
	}

// Kamra - Cookie Change
/*
        Vertex[] next = new Vertex[_polygon.length];
        Vertex v;
        double p1 = __move.param1();
        double p2 = __move.param2();
        double p3 = __move.param3();
        boolean RET;

        switch (__move.type()) {
            case _CROTATION:    {
                                    v = center(_polygon);
                                    RET = validRotationAux(_polygon, next, p1, v.xpos(), v.ypos(), false);
                                    break;
            }
            case _CROTATION_ARBITRARY:  {
                                    RET = validRotationAux(_polygon, next, p1, p2, p3, false);
                                    break;
            }
            case _CTRANSLATION: {
                                    RET = validTranslationAux(_polygon, next, p1, p2, false);
                                    _trace.add(next);
                                    break;
            }
            default:            {
                                    throw new Exception("Error:  Unknown Transformation Type: "+__move.type());
            }
        }
        _polygon = next;
        return RET;
*/

    private void delay() throws Exception {
        long stime = System.currentTimeMillis();

        while (System.currentTimeMillis() - stime < _delay) { };
    }

    private boolean enclosed(Vertex[] __polygon) throws Exception {
        int _MAX = __polygon.length;
        boolean RET = true;
        double xpos;
        double ypos;

        for (int i=0; i < _MAX; i++) {
	    // Kamra - Cookie Change
            //xpos = _polygon[i].xpos();
            xpos = __polygon[i].xpos();
            //ypos = _polygon[i].ypos();
            ypos = __polygon[i].ypos();
            if (xpos > _CXFINAL_MAX ||  xpos < _CXFINAL_MIN || ypos > _CYFINAL_MAX || ypos < _CYFINAL_MIN) {
                RET = false;
                break;
            }
        }
        return RET;
    }

   private Vertex convertToVirtual(Vertex __vertex) throws Exception {
        double xpos;
        double ypos;

        xpos = __vertex.xpos() / ((_CXFINAL_MAX / _CXCONVERTED_MAX));
        //ypos = (_CYFINAL_MAX - __vertex.ypos())  / ((_CYFINAL_MAX / _CYCONVERTED_MAX));
        ypos = __vertex.ypos()  / ((_CYFINAL_MAX / _CYCONVERTED_MAX));

        return new Vertex(xpos, ypos);
    }
    
    private Vertex convertToScreen(Vertex __vertex) throws Exception {
        double xpos;
        double ypos;

        xpos = __vertex.xpos() * ((_CXFINAL_MAX / _CXCONVERTED_MAX));
        //ypos = (_CYCONVERTED_MAX - __vertex.ypos()) * ((_CYFINAL_MAX * _CYCONVERTED_MAX));
        ypos = __vertex.ypos() * ((_CYFINAL_MAX / _CYCONVERTED_MAX));
       
        return new Vertex(xpos, ypos);
    }

    private double convertXVirtual(double __xpos) throws Exception {
        return __xpos / ((_CXFINAL_MAX / _CXCONVERTED_MAX));
    }

    private double convertXTranslation(double __xpos) throws Exception {
        return __xpos * ((_CXFINAL_MAX / _CXCONVERTED_MAX));
    }

    private double convertYTranslation(double __ypos) throws Exception {
        //return -__ypos * ((_CYFINAL_MAX / _CYCONVERTED_MAX));
        return __ypos * ((_CYFINAL_MAX / _CYCONVERTED_MAX));
    }
    

    private boolean validTranslationAux(Vertex[] __polygon, Vertex[] __next, double __p1, double __p2, boolean __virtual) throws Exception {
        int _MAXP = __polygon.length;
        int _MAXI = _cookieshapes.length;
        int _MAXJ;
        boolean RET = true;
        double p1 = convertXTranslation(__p1);
        double p2 = convertYTranslation(__p2);
        Vertex v;
        int pos;

        System.out.println("p1: " + p1);
        System.out.println("p2: " + p2);
        if (__next == null || __next.length != _MAXP) {
            __next = new Vertex[_MAXP];
        }
        for (int p=0; p < _MAXP; p++) {
            if (__virtual) {
                v = convertToScreen(__polygon[p]);
                __next[p] = new Vertex( v.xpos() + p1, v.ypos() + p2);
            } else {
                __next[p] = new Vertex( __polygon[p].xpos() + p1, __polygon[p].ypos() + p2);
            }
            System.out.println("next["+p+"]: " + __next[p]);
        }
        for (int i=0; i < _MAXI; i++) {
            _MAXJ = _cookieshapes[i].length;
            for (int j=0; j < _MAXJ; j++) {
                pos = (j + 1) % _MAXJ;
                for (int p=0; p < _MAXP; p++) {
                    if (intersects(_cookieshapes[i][j], _cookieshapes[i][pos], __polygon[p], __next[p])) {
                        RET = false;
                        break;
                    }
                }
                if (!RET) {
                    break;
                }
            }
            if (!RET) {
                break;
            }
        }

        if (RET) {
            for (int p=0; p < _MAXP; p++) {
                pos = (p + 1) % _MAXP;
                for (int i=0; i < _MAXI; i++) {
                    _MAXJ = _cookieshapes[i].length;
                    for (int j=0; j < _MAXJ; j++) {
                       if (__virtual) {
                            v = new Vertex(_cookieshapes[i][j].xpos() - p1, _cookieshapes[i][j].ypos() - p2);
                            v = convertToScreen(v);
                       } else {
                            v = new Vertex(_cookieshapes[i][j].xpos() - p1, _cookieshapes[i][j].ypos() - p2);
                       }
                       if (intersects(_cookieshapes[i][j], v, __polygon[p], __polygon[pos])) {
                            RET = false;
                            break;
                        }
                    }
                    if (!RET) {
                        break;
                    }
                }
                if (!RET) {
                    break;
                }
            }
        }

        if (__virtual) {
            for (int p=0; p < _MAXP; p++) {
                __next[p] = convertToVirtual(__next[p]);
            }
        }
        
        System.out.println("Outputting: ");
        for (int p=0; p < _MAXP; p++) {
            System.out.println("next["+p+"]: "+__next[p]);
        }
        return RET;
    }

    private boolean validRotationAux(Vertex[] __polygon, Vertex[] __next, double __theta, double __xpos, double __ypos, boolean __virtual) throws Exception {
        int _MAXP = __polygon.length;
        int _MAXI = _cookieshapes.length;
        int _MAXJ;
        boolean RET = true;
        double xpos;
        double ypos;
        int pos;
        Vertex center = convertToScreen(new Vertex(__xpos, __ypos));
        Vertex[] points = new Vertex[2];
        double dist;
        Vertex v;

        if (__next == null || __next.length != _MAXP) {
            __next = new Vertex[_MAXP];
        }
        for (int p=0; p < _MAXP; p++) {
            if (__virtual) {   
                v = convertToScreen(__polygon[p]);
                __next[p] = new Vertex(v.xpos() - center.xpos(), v.ypos() - center.ypos());
            } else {
                __next[p] = new Vertex(__polygon[p].xpos() - __xpos, __polygon[p].ypos() - __ypos);
            }
        }
        for (int p=0; p < _MAXP; p++) {
            xpos = __next[p].xpos();
            ypos =__next[p].ypos();
            __next[p].setXpos(xpos * Math.cos(__theta) - ypos * Math.sin(__theta));
            __next[p].setYpos(xpos * Math.sin(__theta) + ypos * Math.cos(__theta));
        }
        for (int p=0; p < _MAXP; p++) {
            __next[p].setXpos(__next[p].xpos() + __xpos);
            __next[p].setYpos(__next[p].ypos() + __ypos);
        }
        for (int i=0; i < _MAXI; i++) {
            _MAXJ = _cookieshapes[i].length;
            for (int j=0; j < _MAXJ; j++) {
                pos = (j + 1) % _MAXJ;
                for (int p=0; p < _MAXP; p++) {
                    dist = Math.sqrt((__polygon[p].xpos() - __xpos) * (__polygon[p].xpos() - __xpos) +
                                     (__polygon[p].ypos() - __ypos) * (__polygon[p].ypos() - __ypos));
                    if (intersects_line_arc(_cookieshapes[i][j], _cookieshapes[i][pos], __polygon[p], __next[p], center, dist, points)) {
                        RET = false;
                        break;
                    }
                }
                if (!RET) {
                    break;
                }
            }
            if (!RET) {
                break;
            }
        }
        if (__virtual) {
            for (int p=0; p < _MAXP; p++) {
                __next[p] = convertToVirtual(__next[p]);
            }
        }
        return RET;
    }

    //********************************************
    //*
    //* State Transition
    //*
    //********************************************

void play() throws Exception
{
	while (step()) { }
	this.refresh();
	_control.refresh();
	CookieCutter.this.refresh();
}

    boolean step() throws Exception {
        boolean valid = true;

        switch (_state) {
            case _CWAITING: {
                println("Please Make A Move, "+_players[_pindex].name());
		return false;
		}

            case _CPLACING:  {
                if (!_players[_pindex].interactive()) {
                    println("\t[Player "+_pindex+"]: ");
                    _lines.clear();
                    _trace.clear();
                    _tracing = false;
                    _delay = _CDELAY;

// Kamra - Cookie Change
/*
                    original = _players[_pindex].polygon();
                    valid = processPolygon(original);
                    refresh();
                    delay();
*/
			long stime = System.currentTimeMillis();
                        _moves = _players[_pindex].moves();
			long etime = System.currentTimeMillis();
                        if (_moves != null && _moves.length != 0) {
                            print("\t\t[Move Sequence]:");
                            for (int i=0; i < _moves.length; i++)
                                println(""+_moves[i]);
                                valid = processMoves(_moves);
                                refresh();
/*
                                delay();
                                if (!valid) {
                                    println("\t\tRefer To Your Geometry Textbooks (Invalid Move), Player["+_pindex+"]");
                                }
*/
                        } else {
                            println("\t[No Moves Specified]");
                        }
                    if (valid) {
			double myscore = _currentScore/_cookieshapes.length;
                        _players[_pindex].setScore(myscore);
                        _players[_pindex].setTotalTime((etime - stime)/1000.0);
			// START -- For Tournament
				if(myscore < _bestScore)
				{
					_bestScore = myscore;
					_bestpaths = new GeneralPath[_cookieshapes.length][];
					for(int p=0;p < _cookieshapes.length;p++)
					{
						_bestpaths[p] = new GeneralPath[_cookiecopies];
						for(int q=0;q < _cookiecopies;q++)
						{
							_bestpaths[p][q] = new GeneralPath(_paths[p][q]);
						}
					}
				}
			// END -- For Tournament
                        println("\t\tCongratulations, Player["+_pindex+"]!");
                        println("\t\tYour Score Is: "+_players[_pindex].score());
                    } else {
                        println("\t\tCarefully Note The Boundaries Of The Cookies, Player["+_pindex+"]");
                        println("\t\tYou Have Failed To Achieve Your Objective, Player["+_pindex+"]");
                        _players[_pindex].setScore(_FAILSCORE);
                        _players[_pindex].setTotalTime((etime - stime)/1000.0);
                    }
                } else {
			// Kamra -- Cookie Change
			_cookieindex = 0;
			_copyindex = 0;
			_humanplaying = true;
			_moves = new Move[_cookieshapes.length];
			for(int i=0;i < _cookieshapes.length;i++)
				_moves[i] = new Move(_cookiecopies);
                    _state = _CWAITING;
		    break;
                }
                if (++_pindex == _players.length) {
                    _state = _CFINISHED;
                }
                break;
            }
	    case _CHUMANPLACING:
	     {
                    println("\t[Player "+_pindex+"]: ");
                    _lines.clear();
                    _trace.clear();
                    _tracing = false;
                    _delay = _CDELAY;
                        if (_moves != null && _moves.length != 0) {
                            print("\t\t[Move Sequence]:");
                            for (int i=0; i < _moves.length; i++)
                                println(""+_moves[i]);
                                valid = processMoves(_moves);
                                refresh();
                        } else {
                            println("\t[No Moves Specified]");
                        }
                    if (valid) {
                        _players[_pindex].setScore(_currentScore/_cookieshapes.length);
                        println("\t\tCongratulations, Player["+_pindex+"]!");
                        println("\t\tYour Score Is: "+_players[_pindex].score());
                    } else {
                        println("\t\tCarefully Note The Boundaries Of The Cookies, Player["+_pindex+"]");
                        println("\t\tYou Have Failed To Achieve Your Objective, Player["+_pindex+"]");
                        _players[_pindex].setScore(_FAILSCORE);
                    }
		    _state = _CPLACING;
                if (++_pindex == _players.length) {
                    _state = _CFINISHED;
                }
	     }
			     break;

            case _CFINISHED: {
                break;
            }
        }
        return (_state != _CFINISHED);
    }

    //********************************************
    //*
    //* View Panel
    //*
    //********************************************
    private final class ViewPanel extends JPanel implements Serializable, Scrollable {
        final int           _CWIDTH                         = 800;
        final int           _CHEIGHT                        = 200;
        final Font          _CVIEW_FONT                     = new Font("Courier", Font.BOLD, 35);
        final float         _CSTROKE_WIDTH                  = 1.0f;
        final Color         _CDIVIDER_COLOR                 = new Color(1.0f, 1.0f, 0.0f);
        final float         _CALPHA                         = 0.8f;
        final Color         _CBACKGROUND_COLOR              = new Color(0.0f, 0.0f, 0.0f);
        final Color         _COBSTACLE_COLOR                = new Color(1.0f, 0.0f, 0.0f);
        final Color         _CUSER_COLOR                    = new Color(0.0f, 1.0f, 0.0f);
        final Color         _CTRACE_COLOR                   = new Color(0.7f, 0.7f, 1.0f);
        final Color         _CLINE_COLOR                    = new Color(.7f, .7f, .4f);

        //********************************************
        //*
        //* Constructor
        //*
        //********************************************
        public ViewPanel() throws Exception {
            super();

            setLayout(new BorderLayout());
            setPreferredSize(new Dimension(10*_CWIDTH, _CHEIGHT));
//            setMinimumSize(new Dimension(_CWIDTH, _CHEIGHT));
            setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
//            setSize(new Dimension(2*_CWIDTH, _CHEIGHT));
        }

	public Dimension getPreferredScrollableViewportSize()
	{
		return new Dimension(_CWIDTH, _CHEIGHT);
	}
	
	public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction)
	{
		return 100;
	}

	public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction)
	{
		return 500;
	}

	public boolean getScrollableTracksViewportWidth()
	{
		return false;
	}

	public boolean getScrollableTracksViewportHeight()
	{
		return false;
	}

	private GeneralPath GetCookiePolygon(double startboundary, Vertex[] original, Vertex center, double rotation_angle) throws Exception
	{
		Vertex[] converted;
		Vertex converted_center;
		double xtrans, ytrans;
		GeneralPath path;
		int _MAXI = original.length;
		// Rotation not done yet...

		converted = new Vertex[original.length];

		for(int i=0;i < _MAXI;i++)
			converted[i] = original[i];
//			converted[i] = convertToScreen(original[i]);
		converted_center = convertToScreen(center);

		Vertex orig_center = centroid(converted);
		xtrans = converted_center.xpos() - orig_center.xpos();
		ytrans = converted_center.ypos() - orig_center.ypos();

		path = new GeneralPath(GeneralPath.WIND_EVEN_ODD, _MAXI);
		path.moveTo((float) (startboundary + converted[0].xpos() + xtrans), (float) (converted[0].ypos() + ytrans));
//		System.out.println(converted[0].xpos() + xtrans + ", " +converted[0].ypos() + ytrans);
		for(int i=1;i < _MAXI;i++)
		{
			path.lineTo((float) (startboundary + converted[i].xpos() + xtrans), (float) (converted[i].ypos() + ytrans));
//		System.out.println(converted[i].xpos() + xtrans + ", " +converted[i].ypos() + ytrans);
		}
		path.closePath();
		return path;
	}

	private double DrawCookieCopies(int cookieindex, Graphics2D g, double startboundary) throws Exception
	{
		GeneralPath path;
		double endboundary;

		for(int i=0;i < _cookiecopies;i++)
		{
//			path = GetCookiePolygon(convertXTranslation(startboundary), _cookieshapes[cookieindex], _moves[cookieindex].GetCookieCenter(i), _moves[cookieindex].GetCookieAngle(i));
			path = _paths[cookieindex][i];
			//g.setPaint(new Color(0.0f, (float)0.5*(1 + _random.nextFloat()), 0.0f));

			g.setPaint(_CUSER_COLOR);
			if(path != null)
				g.fill(path);
		}
                g.setPaint(_CDIVIDER_COLOR);
		endboundary = startboundary + _moves[cookieindex].GetRightBoundary();
		//System.out.println("cookieindex = " + cookieindex + ", end = " + endboundary + ", right = " + _moves[cookieindex].GetRightBoundary());
                g.draw(new Line2D.Double(convertXTranslation(endboundary), 0.0, convertXTranslation(endboundary), 200.0));

		return endboundary;
	}

        public void paintComponent(Graphics __g) {
            try {
                super.paintComponent(__g);

                Graphics2D g = (Graphics2D) __g;
                int _MAXI;
                int _MAXJ;
		double boundary = 0.0;
                Vertex[] prev;
                Vertex[] curr;
                GeneralPath path;

                g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                g.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
                g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
                g.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
                g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
                g.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
                g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, _CALPHA));
                g.setStroke(new BasicStroke(_CSTROKE_WIDTH, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));

                g.setPaint(_CBACKGROUND_COLOR);
                g.fill(new Rectangle.Double(0.0, 0.0, (double) getWidth(), (double) getHeight()));

/*
                if (_paths != null) {
                    _MAXI = _paths.length;
                    g.setPaint(_COBSTACLE_COLOR);
                    for (int i=0; i < _MAXI; i++) {
                        g.fill(_paths[i]);
                    }
                }
*/


                g.setPaint(_CDIVIDER_COLOR);
                g.draw(new Line2D.Double(0.0, 0.0, (double) getWidth(), 0.0));
	    // Kamra - Cookie Change
//                g.draw(new Line2D.Double(800.0, 0.0, 800.0, 200.0));
                g.draw(new Line2D.Double((double) getWidth(), 200.0, 0.0, 200.0));
                g.draw(new Line2D.Double(0.0, 200.0, 0.0, 0.0));
	    // Kamra - Cookie Change
//                g.draw(new Line2D.Double(200.0, 0.0, 200.0, 200.0));
//                g.draw(new Line2D.Double(600.0, 0.0, 600.0, 200.0));

// Kamra - Cookie Change
/* 
                if (_polygon != null) {
                    _MAXI = _polygon.length;
                    path = new GeneralPath(GeneralPath.WIND_EVEN_ODD, _MAXI);
                    path.moveTo((float) _polygon[0].xpos(), (float) _polygon[0].ypos());
                    for (int i=1; i < _MAXI; i++) {
                        path.lineTo((float) _polygon[i].xpos(), (float) _polygon[i].ypos());
                    }
                    path.closePath();
                    g.setPaint(_CUSER_COLOR);
                    g.fill(path);
                }
*/

		// Draw the Cookies
		if(_paths != null)
		{
			for(int i=0;i < _cookieshapes.length;i++)
			{
				if(_paths[i] != null)
					boundary = DrawCookieCopies(i, g, boundary);
			}
		}

                if ((_MAXI = _lines.size()) > 0) {
                    g.setPaint(_CLINE_COLOR);
                    for (int i=0; i < _MAXI; i++) {
                        curr = (Vertex[]) _lines.get(i);
                        g.draw(new Line2D.Double(curr[0].xpos(), curr[0].ypos(), curr[1].xpos(), curr[1].ypos()));
                    }
                }

                if (_tracing && (_MAXI = _trace.size()) > 1) {
                    g.setPaint(_CTRACE_COLOR);
                    prev = (Vertex[]) _trace.get(0);
                    _MAXJ = prev.length;
                    for (int i=1; i < _MAXI; i++) {
                        curr = (Vertex[]) _trace.get(i);
                        for (int j=0; j < _MAXJ; j++) {
                            g.draw(new Line2D.Double(prev[j].xpos(), prev[j].ypos(), curr[j].xpos(), curr[j].ypos()));
                        }
                        prev = curr;
                    }
                }

            } catch (Exception EXC) {
                System.out.println(EXC.getMessage());
                EXC.printStackTrace();
            }
        }
    }

    //********************************************
    //*
    //* Control Panel
    //*
    //********************************************
    private final class ControlPanel extends JPanel implements ActionListener, ItemListener, Serializable {
        JTabbedPane  _tab;
        JPanel       _conf;
        JPanel       _info;
        final        int         _CWIDTH = 400;
        final        int         _CHEIGHT = 250;
        final        int         _CPANEL_WIDTH = _CWIDTH;
        final        int         _CPANEL_HEIGHT = 30;
        final        int         _CPLAYER_NAME_LENGTH = 20;
        final        ImageIcon   _CSTEP_ICON  = new ImageIcon("Images/marble_step.gif");
        final        ImageIcon   _CPLAY_ICON  = new ImageIcon("Images/marble_play.gif");
        final        ImageIcon   _CRESET_ICON = new ImageIcon("Images/marble_reset.gif");
        final        Color       _CDISABLED_FIELD_COLOR = new Color(1.0f, 1.0f, 1.0f);
        final Font   _CCONTROL_FONT = new Font("Courier", Font.BOLD, 16);
        final Font   _CCOMBO_FONT = new Font("Courier", Font.BOLD, 10);

        JTextField   _rounds;
        JTextField[] _scores;
        JTextField   _numplayersfield;
        JTextField   _cookiecopiesfield;
        JTextField   _numroundsfield;
        JTextField   _gamefilefield;
        JComboBox[]  _classes;
        JComboBox    _gamefilesbox;
        JPanel       _infobox;
        JPanel       _confbox;
        JButton      _play;
        JButton      _step;
        JButton      _reset;
        NumberFormat _nf;

        //********************************************
        //*
        //* Constructor
        //*
        //********************************************
        public ControlPanel() throws Exception {
            super();

            SlotPanel       slot;
            JPanel          box;
            JLabel          label;
            int             _MAX;
            StringBuffer    SB;
            String          name;

            setLayout(new BorderLayout());
            setBorder(BorderFactory.createCompoundBorder(BorderFactory.createRaisedBevelBorder(),
                                                         BorderFactory.createLoweredBevelBorder()));
            setPreferredSize(new Dimension(_CWIDTH, _CHEIGHT));
            setMinimumSize(new Dimension(_CWIDTH, _CHEIGHT));
            setFont(_CCONTROL_FONT);

            _info = new JPanel();
            _info.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createRaisedBevelBorder(),
                                                         BorderFactory.createLoweredBevelBorder()));
            _info.setLayout(new BorderLayout());
            _info.setPreferredSize(new Dimension(_CWIDTH, _CHEIGHT));
            _info.setMinimumSize(new Dimension(_CWIDTH, _CHEIGHT));
            _info.setFont(_CCONTROL_FONT);

            _reset = new JButton(_CRESET_ICON);
            _reset.addActionListener(this);
            _step = new JButton(_CSTEP_ICON);
            _step.addActionListener(this);
            _play = new JButton(_CPLAY_ICON);
            _play.addActionListener(this);

            box = new JPanel();
            box.setLayout(new BoxLayout(box, BoxLayout.Y_AXIS));

            slot = new SlotPanel(_CPANEL_WIDTH, _CPANEL_HEIGHT);
            _input = new JTextField();
            _input.setFont(_CCONTROL_FONT);
            _input.addActionListener(this);
            label = new JLabel("Input:    ");
            label.setFont(_CCONTROL_FONT);
            slot.add(label, _input);
	    // Kamra - Cookie Change
//            box.add(slot);

            slot = new SlotPanel(_CPANEL_WIDTH, _CPANEL_HEIGHT);
            _rounds = new JTextField();
            _rounds.setEditable(false);
            _rounds.setFont(_CCONTROL_FONT);
            label = new JLabel("Rounds:   ");
            label.setFont(_CCONTROL_FONT);
            slot.add(label, _rounds);
	    // Kamra - Cookie Change
//            box.add(slot);
            
            slot = new SlotPanel(_CPANEL_WIDTH, _CPANEL_HEIGHT);
            _gamefilefield = new JTextField();
            _gamefilefield.setFont(_CCONTROL_FONT);
            _gamefilefield.setText(_gamefile);
            _gamefilefield.setEditable(false);
            label = new JLabel("GameFile: ");
            label.setFont(_CCONTROL_FONT);
            slot.add(label, _gamefilefield);
            box.add(slot);

            _MAX = numPlayers();
            _scores = new JTextField[_MAX];
            for (int i=0; i < _MAX; i++) {
                _scores[i] = new JTextField();
                _scores[i].setEditable(false);
                _scores[i].setFont(_CCONTROL_FONT);
                slot = new SlotPanel(_CPANEL_WIDTH, _CPANEL_HEIGHT);
                label = new JLabel(Util.adjustString(_players[i].name(), _CPLAYER_NAME_LENGTH));
                label.setFont(_CCONTROL_FONT);
                slot.add(label, _scores[i]);
                box.add(slot);
            }
            _info.add(box, BorderLayout.CENTER);
            _infobox = box;

            _conf = new JPanel();
            _conf.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createRaisedBevelBorder(),
                                                         BorderFactory.createLoweredBevelBorder()));
            _conf.setLayout(new BorderLayout());
            _conf.setPreferredSize(new Dimension(_CWIDTH, _CHEIGHT));
            _conf.setMinimumSize(new Dimension(_CWIDTH, _CHEIGHT));
            _conf.setFont(_CCONTROL_FONT);
            
            box = new JPanel();
            box.setLayout(new BoxLayout(box, BoxLayout.Y_AXIS));            

            slot = new SlotPanel(_CPANEL_WIDTH, _CPANEL_HEIGHT);
            _numplayersfield = new JTextField();
            _numplayersfield.setFont(_CCONTROL_FONT);
            _numplayersfield.setText(Integer.toString(_MAX));
            _numplayersfield.addActionListener(this);
            label = new JLabel("NumPlayers:  ");
            label.setFont(_CCONTROL_FONT);
            slot.add(label, _numplayersfield);
            box.add(slot);

            slot = new SlotPanel(_CPANEL_WIDTH, _CPANEL_HEIGHT);
            _numroundsfield = new JTextField();
            _numroundsfield.setFont(_CCONTROL_FONT);
            _numroundsfield.setText(Integer.toString(maxRounds()));
            _numroundsfield.addActionListener(this);
            label = new JLabel("NumRounds:   ");
            label.setFont(_CCONTROL_FONT);
            slot.add(label, _numroundsfield);
            box.add(slot);

            slot = new SlotPanel(_CPANEL_WIDTH, _CPANEL_HEIGHT);
            _cookiecopiesfield = new JTextField();
            _cookiecopiesfield.setFont(_CCONTROL_FONT);
            _cookiecopiesfield.setText(Integer.toString(cookieCopies()));
            _cookiecopiesfield.addActionListener(this);
            label = new JLabel("CookieCopies:  ");
            label.setFont(_CCONTROL_FONT);
            slot.add(label, _cookiecopiesfield);
            box.add(slot);

            slot = new SlotPanel(_CPANEL_WIDTH, _CPANEL_HEIGHT);
            _gamefilesbox = new JComboBox(_gamefilelist);
            _gamefilesbox.setFont(_CCOMBO_FONT);
            _gamefilesbox.setSelectedItem(_gamefile);
            _gamefilesbox.addItemListener(this);
            label = new JLabel("Files: ");
            label.setFont(_CCONTROL_FONT);
            slot.add(label, _gamefilesbox);
            box.add(slot);
            
            _classes = new JComboBox[_MAX];
            for (int i=0; i < _MAX; i++) {               
                _classes[i] = new JComboBox(_classlist);
                _classes[i].setSelectedItem(_players[i].playerClass());
                _classes[i].addItemListener(this);
                _classes[i].setFont(_CCOMBO_FONT);
                slot = new SlotPanel(_CPANEL_WIDTH, _CPANEL_HEIGHT);
                label = new JLabel("["+i+"]:   ");
                label.setFont(_CCONTROL_FONT);
                slot.add(label, _classes[i]);
                box.add(slot);                                    
            }
            _conf.add(box, BorderLayout.CENTER);
            _confbox = box;

            _tab = new JTabbedPane();
            _tab.add("Information", _info);                  
            _tab.add("Configuration", _conf);
            add(_tab, BorderLayout.CENTER);            
            
            _nf = NumberFormat.getInstance();
            _nf.setMinimumFractionDigits(2);
            _nf.setMaximumFractionDigits(4);
        }        

        //********************************************
        //*
        //* ActionListener Interface
        //*
        //********************************************            
        public void actionPerformed(ActionEvent __event) {
            try { 
                Object source = __event.getSource();
                ParseValue pv = null;
                JComboBox[] tmp;
                Class[] tmpcls;
                int prev;
                int curr;
                int _MAX;
                SlotPanel slot;
                JLabel label;

                if (source == _step) {
			_playmode = false;
                    step();
                    this.refresh();
                    CookieCutter.this.refresh();
                    return;
                }
                else
                if (source == _play) {
			_playmode = true;
                    while (step()) { }
                    this.refresh();
                    CookieCutter.this.refresh();
                    return;
                }
                else
                if (source == _reset) {
                    reset();
                    return;
                }
                else
                if (source == _input) {
                    return;
                }
                else
                if (source == _gamefilesbox) {
                    _config.setGameFile((String) _gamefilesbox.getSelectedItem());
                    _ui.configure(_config);
                }
                else
                if (source == _numroundsfield) {
                    pv = ParseValue.parseIntegerValue(_numroundsfield.getText(), _CMIN_ROUNDS, _CMAX_ROUNDS);
                    if (pv.isValid()) {
                        _config.setNumRounds(((Integer) pv.value()).intValue());
                        _ui.configure(_config);
                    } else {
                        println("Invalid Input");
                    }
                }
		else
                if (source == _cookiecopiesfield) {
                    pv = ParseValue.parseIntegerValue(_cookiecopiesfield.getText(), _CMIN_COPIES, _CMAX_COPIES);
                    if (pv.isValid()) {
                        _config.setCookieCopies(((Integer) pv.value()).intValue());
                        _ui.configure(_config);
                    } else {
                        println("Invalid Input");
                    }
                }
                else
                if (source == _numplayersfield) {
                    pv = ParseValue.parseIntegerValue(_numplayersfield.getText(), _CMIN_PLAYERS, _CMAX_PLAYERS);
                    if (pv.isValid()) {
                        prev = _config.numPlayers();
                        curr = ((Integer) pv.value()).intValue();
                        if (prev == curr) {
                            return;
                        }
                        if (curr > prev) {
                            tmp = _classes;
                            _classes = new JComboBox[curr];
                            System.arraycopy(tmp, 0, _classes, 0, prev);
                            tmpcls = new Class[curr];
                            System.arraycopy(_config.playerList(), 0, tmpcls, 0, prev);
                            for (int i=prev; i < curr; i++) {
                                _classes[i] = new JComboBox(_classlist);
                                _classes[i].addItemListener(this);
                                _classes[i].setFont(_CCOMBO_FONT);
                                slot = new SlotPanel(_CPANEL_WIDTH, _CPANEL_HEIGHT);
                                label = new JLabel("["+i+"]:   ");
                                label.setFont(_CCONTROL_FONT);
                                slot.add(label, _classes[i]);
                                _confbox.add(slot);
                                tmpcls[i] = (Class) _classes[i].getSelectedItem();
                            }
                            _config.setPlayerList(tmpcls);
                        }
                        if (curr < prev) {
                            tmp = new JComboBox[curr];
                            System.arraycopy(_classes, 0, tmp, 0, curr);
                            tmpcls = new Class[curr];
                            System.arraycopy(_config.playerList(), 0, tmpcls, 0,  curr);
                            for (int i=curr; i < prev; i++) {
                                _confbox.remove(_confbox.getComponents().length - 1);
                            }
                            _classes = tmp;
                            _config.setPlayerList(tmpcls);
                        }
                        _ui.configure(_config);
                        repaint();
                        CookieCutter.this.refresh();
                    } else {
                        println("Invalid Input");
                    }
                }                                                                                
            } catch (Exception EXC) {
                System.out.println(EXC.getMessage());
                EXC.printStackTrace();
            }
        }        
        
        //********************************************
        //*
        //* ItemListener Interface
        //*
        //********************************************            
        public void itemStateChanged(ItemEvent __event) {
            Object source = __event.getSource();                        
            int _MAX = _classes.length;
            
            try {
                for (int i=0; i < _MAX; i++) {
                    if (source == _classes[i]) {
                        _config.setPlayer(i, (Class) _classes[i].getSelectedItem());
                        _ui.configure(_config);
                        return;
                    }
                }
                if (source == _gamefilesbox) {
                    _config.setGameFile((String) _gamefilesbox.getSelectedItem());
                    _ui.configure(_config);
                    return;
                }
            } catch (Exception EXC) {
                System.out.println(EXC.getMessage());
                EXC.printStackTrace();
            }
        }

        //********************************************
        //*
        //* Score Updater
        //*
        //********************************************
        public void refresh() throws Exception {
            int _MAX = numPlayers();
            
            for (int i=0; i < _MAX; i++) {
                _scores[i].setText(_nf.format(_players[i].score()));
            }
            _rounds.setText(Integer.toString(remainingRounds()));
            _gamefilefield.setText(_gamefile);
        }                            
        
        //********************************************
        //*
        //* Action Tool Exporter
        //*
        //********************************************            
        public JButton[] exportTools() throws Exception {
            JButton[] ret = new JButton[3];
            ret[0] = _reset;
            ret[1] = _step;
            ret[2] = _play;
            return ret;
        }
    }

    //********************************************
    //*
    //* Human Panel
    //*
    //********************************************
    private final class HumanPanel extends JPanel implements ActionListener, Serializable {
        JPanel       _conf;
        final        int         _CWIDTH = 800;
        final        int         _CHEIGHT = 100;
        final        int         _CPANEL_WIDTH = _CWIDTH;
        final        int         _CPANEL_HEIGHT = 20;
        final        ImageIcon   _CDONE_ICON  = new ImageIcon("Images/marble_step.gif");
        final        Color       _CDISABLED_FIELD_COLOR = new Color(1.0f, 1.0f, 1.0f);
        final Font   _CCONTROL_FONT = new Font("Courier", Font.BOLD, 16);
        final Font   _CCOMBO_FONT = new Font("Courier", Font.BOLD, 10);

        JLabel       _toplabel;
        JTextField   _centerX;
        JTextField   _centerY;
        JTextField   _rotangle;
        JButton      _done;
        NumberFormat _nf;
	double _xvalue;
	double _yvalue;
	double _anglevalue;

	double _humanboundary;


        //********************************************
        //*
        //* Constructor
        //*
        //********************************************
        public HumanPanel() throws Exception {
            super();

            SlotPanel       slot;
            JPanel          box;
            JPanel          _button;
            JLabel          label;
            JSplitPane      _splitv;
            int             _MAX;
            StringBuffer    SB;
            String          name;

	    _humanboundary = 0.0;

            setLayout(new BorderLayout());
            setBorder(BorderFactory.createCompoundBorder(BorderFactory.createRaisedBevelBorder(),
                                                         BorderFactory.createLoweredBevelBorder()));
            setPreferredSize(new Dimension(_CWIDTH, _CHEIGHT));
            setMinimumSize(new Dimension(_CWIDTH, _CHEIGHT));
            setFont(_CCONTROL_FONT);

	    _splitv = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
	    //_splitv = new JSplitPane(JSplitPane.VERTICAL_SPLIT);

            _conf = new JPanel();
            _conf.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createRaisedBevelBorder(),
                                                         BorderFactory.createLoweredBevelBorder()));
            _conf.setLayout(new BorderLayout());
            _conf.setPreferredSize(new Dimension(_CWIDTH/2, _CHEIGHT));
            _conf.setMinimumSize(new Dimension(_CWIDTH/2, _CHEIGHT));
            _conf.setFont(_CCONTROL_FONT);

            //_done = new JButton(_CDONE_ICON);
            _done = new JButton("Done");
            _done.addActionListener(this);

            box = new JPanel();
            box.setLayout(new BoxLayout(box, BoxLayout.Y_AXIS));

            slot = new SlotPanel(_CPANEL_WIDTH, _CPANEL_HEIGHT);
	    String str = new String("Placement of Cookie " + _cookieindex + ", Copy " + (_copyindex+1) + ":");
            _toplabel = new JLabel(str);
            _toplabel.setFont(_CCONTROL_FONT);
            slot.add(_toplabel);
            box.add(slot);

            slot = new SlotPanel(_CPANEL_WIDTH, _CPANEL_HEIGHT);
            _centerX = new JTextField();
            _centerX.setFont(_CCONTROL_FONT);
            _centerX.addActionListener(this);
            label = new JLabel("X Coordinate of Centroid:    ");
            label.setFont(_CCONTROL_FONT);
            slot.add(label, _centerX);
            box.add(slot);

            slot = new SlotPanel(_CPANEL_WIDTH, _CPANEL_HEIGHT);
            _centerY = new JTextField();
            _centerY.setFont(_CCONTROL_FONT);
            _centerY.addActionListener(this);
            label = new JLabel("Y Coordinate of Centroid:    ");
            label.setFont(_CCONTROL_FONT);
            slot.add(label, _centerY);
            box.add(slot);

            slot = new SlotPanel(_CPANEL_WIDTH, _CPANEL_HEIGHT);
            _rotangle = new JTextField();
            _rotangle.setFont(_CCONTROL_FONT);
            _rotangle.addActionListener(this);
            label = new JLabel("       Angle of Rotation:    ");
            label.setFont(_CCONTROL_FONT);
            slot.add(label, _rotangle);
            box.add(slot);

            _conf.add(box, BorderLayout.CENTER);

	    _button = new JPanel();
            _button.setLayout(new BorderLayout());
	    _button.add(_done);

            _splitv.setLeftComponent(_conf);
            _splitv.setRightComponent(_button);

	    add(_splitv);
            
            _nf = NumberFormat.getInstance();
            _nf.setMinimumFractionDigits(2);
            _nf.setMaximumFractionDigits(2);
        }        

        //********************************************
        //*
        //* ActionListener Interface
        //*
        //********************************************            
        public void actionPerformed(ActionEvent __event) {
            try { 
                Object source = __event.getSource();
                ParseValue pv = null;
                JComboBox[] tmp;
                Class[] tmpcls;
                int prev;
                int curr;
                int _MAX;
                SlotPanel slot;
                JLabel label;

		if(!_humanplaying)
			return;

		if((_cookieindex == 0)&&(_copyindex == 0))
		{
			_paths = new GeneralPath[_cookieshapes.length][];
			_paths[0] = new GeneralPath[_cookiecopies];
		}
		
                if (source == _done) {
			_moves[_cookieindex].setCookiePosition(_copyindex, new Vertex(_xvalue, _yvalue), _anglevalue);

			_paths[_cookieindex][_copyindex] = MakePathFromMoveElement(_cookieshapes[_cookieindex], _moves[_cookieindex], _copyindex, _humanboundary);
			_moves[_cookieindex].setRightBoundary(_moves[_cookieindex].GetRightBoundary());
			
			_copyindex++;
			if(_copyindex >= _cookiecopies)
			{
				_copyindex = 0;
				_cookieindex++;
	    			String str = new String("Placement of Cookie " + _cookieindex + ", Copy " + (_copyindex+1) + ":");
				_toplabel.setText(str);

				if(_cookieindex < _cookieshapes.length)
				{
					_humanboundary += _moves[_cookieindex - 1].GetRightBoundary();
					_paths[_cookieindex] = new GeneralPath[_cookiecopies];
				}
			}
			if(_cookieindex >= _cookieshapes.length)
			{
				_cookieindex = 0;
				_humanplaying = false;
				_state = _CHUMANPLACING;
	    			String str = new String("Placement of Cookie " + _cookieindex + ", Copy " + (_copyindex+1) + ":");
				_toplabel.setText(str);
				if(_playmode)
					play();
				else
					step();
			}
			refresh();
                    return;
                }
                else
                if (source == _centerX) {
                    pv = ParseValue.parseDoubleValue(_centerX.getText(), 0.0, 1000.0);
                    if (pv.isValid()) {
			_xvalue = ((Double) pv.value()).doubleValue();
			_moves[_cookieindex].setCookiePosition(_copyindex, new Vertex(_xvalue, _yvalue), _anglevalue);
			_paths[_cookieindex][_copyindex] = MakePathFromMoveElement(_cookieshapes[_cookieindex], _moves[_cookieindex], _copyindex, _humanboundary);
			refresh();
                    } else {
                        println("Invalid Input");
                    }
                    return;
                }
                else
                if (source == _centerY) {
                    pv = ParseValue.parseDoubleValue(_centerY.getText(), 0.0, 1.0);
                    if (pv.isValid()) {
			_yvalue = ((Double) pv.value()).doubleValue();;
			_moves[_cookieindex].setCookiePosition(_copyindex, new Vertex(_xvalue, _yvalue), _anglevalue);
			_paths[_cookieindex][_copyindex] = MakePathFromMoveElement(_cookieshapes[_cookieindex], _moves[_cookieindex], _copyindex, _humanboundary);
			refresh();
                    } else {
                        println("Invalid Input");
                    }
                }
		else
                if (source == _rotangle) {
                    pv = ParseValue.parseDoubleValue(_rotangle.getText(), 0.0, 2*_PI);
                    if (pv.isValid()) {
			_anglevalue = ((Double) pv.value()).doubleValue();;
			_moves[_cookieindex].setCookiePosition(_copyindex, new Vertex(_xvalue, _yvalue), _anglevalue);
			_paths[_cookieindex][_copyindex] = MakePathFromMoveElement(_cookieshapes[_cookieindex], _moves[_cookieindex], _copyindex, _humanboundary);
			refresh();
                    } else {
                        println("Invalid Input");
                    }
                }
            } catch (Exception EXC) {
                System.out.println(EXC.getMessage());
                EXC.printStackTrace();
            }
        }        
        
        //********************************************
        //*
        //* ItemListener Interface
        //*
        //********************************************            
	/*
        public void itemStateChanged(ItemEvent __event) {
            Object source = __event.getSource();                        
            int _MAX = _classes.length;
            
            try {
                for (int i=0; i < _MAX; i++) {
                    if (source == _classes[i]) {
                        _config.setPlayer(i, (Class) _classes[i].getSelectedItem());
                        _ui.configure(_config);
                        return;
                    }
                }
                if (source == _gamefilesbox) {
                    _config.setGameFile((String) _gamefilesbox.getSelectedItem());
                    _ui.configure(_config);
                    return;
                }
            } catch (Exception EXC) {
                System.out.println(EXC.getMessage());
                EXC.printStackTrace();
            }
        }
*/
        //********************************************
        //*
        //* Score Updater
        //*
        //********************************************
        public void refresh() throws Exception {
		_ui.refresh();
//		repaint();
//		CookieCutter.this.refresh();
        }                            
        
        //********************************************
        //*
        //* Action Tool Exporter
        //*
        //********************************************            
	/*
        public JButton[] exportTools() throws Exception {
            JButton[] ret = new JButton[3];
            ret[0] = _reset;
            ret[1] = _step;
            ret[2] = _play;
            return ret;
        }
	*/
    }
}


