//***********************************************************
//*
//* File:           Maps.java
//* Author:         Abhinav Kamra
//* Contact:        kamra-at-cs.columbia.edu
//* Update:         10/03/2004
//*
//* Description:    Game model for Project 2, CS4444
//*                 Fall 2004
//*
//*
//***********************************************************


package Maps;

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

public final class Maps implements IFCConstants, IFCModel {

    private Class[]                 _classlist;
    private Class[]                 _playerlist;
    String[]                	    _gamefilelist;
    private PlayerWrapper[]         _players;
    String                  	    _gamefile;
    private int                     _currRound;
    private int                     _numrounds;
    private int                     _numplayers;
    private int                     _state;
    private int                     _state_before_wait;
    private transient IFCUI         _ui;
    private IFCConfiguration        _config;
    private static Random           _random;
    private JTextField              _input;
    private ControlPanel            _control;
    private ViewPanel               _view;
    private int                     _playerindex;
    private ArrayList               _history;
    private boolean[]               _boot;
    private boolean                 _registered;
    private Move[]                  _moves;
    private static final int        _CSELECTING             = 0;
    private static final int        _CMOVING                = 1;
    private static final int        _CWAITING               = 2;
    private static final int        _CDECISION              = 3;
    private static final int        _CFINISHED              = 4;
    private static final int[]      _CSTATES                = { _CSELECTING, _CMOVING, _CWAITING, _CDECISION, _CFINISHED };
    private static final String     _CNAME                  = "Maps";
    private static final String     _CPROPERTIES_FILE       = "gamemodel.properties";
    private static final int        _CMIN_ROUNDS            = 1;
    private static final int        _CMAX_ROUNDS            = 1000;
    static final String     _CSPLIT_CHARS           = ",\t\n ";
    static final String     _CGAME_FILE_DIRECTORY   = "GameFiles/";
    private static final int        _CMIN_PLAYERS           = 1;
    private static final int        _CMAX_PLAYERS           = 30;

    class MapConfig
    {
	    ArrayList cities;
	    ArrayList links;
    }

    MapConfig _mapdata;

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

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

    public Maps(IFCTournament __tournament) throws Exception {
        run(__tournament);
    }
    
    //********************************************
    //*
    //* Constructor Delegates
    //*
    //********************************************
    public void run(IFCTournament __tournament) throws Exception {

	    /*
        IFCGameRecord[] games;
        IFCPlayer player;
        HashMap hash;
        int _MAX;
	int _numhiddencards;
	double starttime;
        
        games = __tournament.games();
        if (games == null) {
            throw new Exception("Error:  Null game record list");
        }
        _MAX = games.length;
        _random = new Random();
        hash = new HashMap();

	starttime = System.currentTimeMillis()/60000.0;
        for (int game=0; game < _MAX; game++) {
            System.err.println("[Game "+ game +"]: Starting at " + (System.currentTimeMillis()/60000.0 - starttime) + " minutes from start");

            _playerlist = games[game].players();
            if (_playerlist == null) {
                throw new Exception("Error:  Null player list in game record: "+ game);
            }

	    _cardsperplayer = games[game].cardsperplayer();
	    _numhiddencards = games[game].numHiddenCards();
	    _numplayers = _playerlist.length;
	    _N = (_numplayers * _cardsperplayer) + _numhiddencards;
	    _numrounds = games[game].numRounds();
	    _playersleft = _numplayers;

	    goodrank = 1;
	    badrank = _numplayers;

	    _random = new Random();
	    _playerindex = _random.nextInt(_numplayers);

	    // Lets create the cards
	    ArrayList tmp = new ArrayList(_N);
	    for(int i=1;i <= _N;i++)
		    tmp.add(new Integer(i));

	    _hidden = new int[_numhiddencards];
	    for(int i=0;i < _numhiddencards;i++)
		    _hidden[i] = ((Integer) tmp.remove(_random.nextInt(tmp.size()))).intValue();

	    _playercards = new int[_numplayers][];
	    for(int i=0;i < _numplayers;i++)
	    {
		    _playercards[i] = new int[_cardsperplayer];
	    	for(int j=0;j < _cardsperplayer;j++)
		{
		    _playercards[i][j] = ((Integer) tmp.remove(_random.nextInt(tmp.size()))).intValue();
		}
	    }
	    //Cards Created
	    //Lets sort them
	    Arrays.sort(_hidden);
	    for(int i=0;i < _numplayers;i++)
		    Arrays.sort(_playercards[i]);
	    //Cards Sorted
	    
	    _currRound = 0;
	    _state = _CMOVING;
	    _boot = new boolean[_numplayers];
	    for(int i=0;i < _numplayers;i++)
		    _boot[i] = false;
	    _history = new ArrayList();
        
	    
            _players = new PlayerWrapper[_numplayers];
            for (int j=0; j < _numplayers; j++) {
                if (_playerlist[j] == null) {
                    throw new Exception("Error:  Null player, game record: "+game+", player: "+j);
                }
                player = (IFCPlayer) hash.get(_playerlist[j]);
                if (player != null) {
                    _players[j] = new PlayerWrapper(_playerlist[j]);
                    _players[j].register(this, player);
                } else {
                    _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");
                }

            }
	    
	    _moves = new Move[_numplayers];

            while (step()) { }
            games[game].setScores(finalScores());

            for (int j=0; j < _numplayers; j++) {
                if (_players[j].player() instanceof IFCPersistentPlayer) {
                    if (!games[game].batchComplete()) {
                        hash.put(_players[j].playerClass(), _players[j].player());
                    } else {
                        hash.put(_players[j].playerClass(), null);
                    }
                }
            }
        } // end -- all games

    */
    } // end -- run

    void create(IFCConfiguration __config) throws Exception
    {
        _config = __config;
        _classlist = _config.classList();
        _playerlist = _config.playerList();
        _numplayers = _config.numPlayers();
        _gamefilelist = _config.gameFileList();
        _gamefile = _config.gameFile();
        _random = new Random();
        _state = _CMOVING;
        parseGameFile(_gamefile);

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


    //********************************************
    //*
    //* Initial Configuration
    //*
    //********************************************
    public 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());

        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;
    }

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

	_mapdata = new MapConfig();

        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;
		    _mapdata.cities = new ArrayList();
                    for (int i=0; i < _MAX; i+=3) {
			    _mapdata.cities.add(new City( Double.parseDouble(toks[i]), Double.parseDouble(toks[i+1]), toks[i+2] ));
                        //vlist.add(convertToScreen(new Vertex(Double.parseDouble(toks[i]), Double.parseDouble(toks[i+1]))));
                    }
                    sb.delete(0, sb.length() - 1);
                }
                continue;
            }
            sb.append(str);
            sb.append("\n");
        }
        if (sb.length() > 0) {
            toks = Util.split(",\t\n ", new String(sb));
            _MAX = toks.length;
	    _mapdata.links = new ArrayList();
            for (int i=0; i < _MAX; i+=5) {
		    _mapdata.links.add(new Link( toks[i], toks[i+1], toks[i+2], Double.parseDouble(toks[i+3]), Double.parseDouble(toks[i+4]) ));
//                vlist.add(convertToScreen(new Vertex(Double.parseDouble(toks[i]), Double.parseDouble(toks[i+1]))));
            }
        }

    }


    //********************************************
    //*
    //* Exposed Methods
    //*
    //********************************************
    public ArrayList Cities()
    {
	    return _mapdata.cities;
    }

    public ArrayList Links()
    {
	    return _mapdata.links;
    }
    
    public int numPlayers() throws Exception {
        return _numplayers;
    }
    
    public int indexOf(IFCPlayer __player) throws Exception {
        int _MAX = numPlayers();

        for (int i=0; i < _MAX; i++) {
            if (_players[i].player() == __player) {
                return i;
            }
        }
        throw new Exception("Player not found: "+__player);
    }

    public int[] getPlayerList(int index, IFCPlayer __player)
    {
	    /*
	    int[] RET = new int[_cardsperplayer];
	    if(index < 0 || index >= _numplayers)
		    return null;
	    int playerindex = -1;
	    for(int i=0;i < _numplayers;i++)
	    {
		    if(_players[i].player() == __player)
		    {
			    playerindex = i;
			    break;
		    }
	    }
	    int finalindex = (playerindex == index)?(index):(_boot[index] ? index : -1 );
	    if(finalindex < 0)
		    return null;
	    System.arraycopy(_playercards[finalindex], 0, RET, 0, _cardsperplayer);
	    return RET;
	    */
	    return null;
    }

    int[] scores() throws Exception
    {
	    int[] RET = new int[numPlayers()];
	    for(int i=0;i < numPlayers();i++)
	    {
		    RET[i] = (int)(_players[i].score());
	    }
	    return RET;
    }

    double[] finalScores() throws Exception
    {
	    double[] RET = new double[numPlayers()];
	    for(int i=0;i < numPlayers();i++)
	    {
		    RET[i] = _players[i].score();
	    }
	    return RET;
    }

    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();
        }
    }

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

//        create(createDefaultConfiguration());

        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 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 void refresh() throws Exception {
        if (_registered) {
            _ui.refresh();
        }
    } 
    //********************************************
    //*
    //* Private Methods
    //*
    //********************************************


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

    private boolean processMoves(Move _move) throws Exception
    {
	    boolean RET = true;
	    /*
	    if(_move == null)
		    return false;
	    responseindex = -1;
	    switch(_move.type())
	    {
		case _CGUESS:
			RET = processGuess(_move.cardlist());
			if(RET == true)
				ExitPlayer(_playerindex, _CRIGHTGUESS);
			else
				ExitPlayer(_playerindex, _CWRONGGUESS);
			return true;
		case _CINTERROGATION:
			RET = processInterrogation(_move.interrogatee(), _move.cardlist());
			return true;
		default:
			System.out.println("Unknown Move Type : " + _move.type());
			System.exit(-1);
			return false;
	    }
	    */
	    return RET;
    }

    Move parseMove(String __str) throws Exception
    {
	    /*
        try{
            if (__str == null) {
                return null;
            }

            StringTokenizer st = new StringTokenizer(__str, " \t\n;");
            int count = 0;
	    char movetype;
	    int[] Cards;
	    int interrogatee = -1;

	    if(st.hasMoreElements())
	    {
		    movetype = Character.toUpperCase(st.nextToken().charAt(0));
		    if(movetype == 'G')
		    {
			    int morecards = st.countTokens();
			    if(morecards <= 0)
				    return null;
			    Cards = new int[morecards];
		    }
		    else if(movetype == 'I')
		    {
			    int morecards = st.countTokens() - 1;
			    if(morecards <= 0)
				    return null;
			    Cards = new int[morecards];
			    interrogatee = (new Integer(st.nextToken())).intValue();
		    }
		    else return null;
	    }
	    else return null;

            while (st.hasMoreElements())
	    {
                Cards[count] = (new Integer(st.nextToken())).intValue();
                count++;
            }
	   if(movetype == 'G')
	   {
		   return new Move(_CGUESS, Cards);
	   }
	   else
	   {
		   return new Move(_CINTERROGATION, Cards, interrogatee);
	   }
        } catch (Exception EXC) {
            println("Error:  "+EXC.getMessage());
            return null;
        }
	*/
	    return null;
    }


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

    private boolean step() throws Exception {

        double[] scores;
        int[] winners = null;
        int _MAX;
        int _MAXJ;
        long stime;
        long etime;
        StringBuffer SB = new StringBuffer();

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

            case _CMOVING:  {
                if (!_players[_playerindex].interactive())
		{
			System.err.println("Inside the case");
			_moves[_playerindex] = _players[_playerindex].move();
			System.err.println("After move");
			if(processMoves(_moves[_playerindex]) == true)
			{
			System.err.println("processMoves true");
                        	SB.append("\tPlayer[");
                        	SB.append(Integer.toString(_playerindex));
                        	SB.append("]: \n");
				SB.append(_players[_playerindex].toString());
			}
			else
			{
                        	SB.append("Player[");
                        	SB.append(Integer.toString(_playerindex));
                        	SB.append("] has made an invalid move\n");
                        	SB.append("\tPlayer[");
                        	SB.append(Integer.toString(_playerindex));
                        	SB.append("] has been given the boot.\n");
			}
                    _playerindex++;
		    if(_playerindex == _players.length)
			    _state = _CFINISHED;
		    _view.repaint();
		}
		else
		{
			_state_before_wait = _state;
                	_state = _CWAITING;
                    	break;
		}
				    
                println(new String(SB));
                break;
            }

        }
	/* amg2006 -- added if condition for when the stop button is hit */
	/*
        if (_lastchange >= _numrounds && _state < _CDECISION) {
            _state = _CDECISION;
            step();
        };
	*/
        return (_state != _CFINISHED);
    }
    
    private final class FilledRectangle {
        
        public int _xa;
        public int _xb;
        public int _ya;
        public int _yb;
        
        public FilledRectangle(int __xa, int __xb, int __ya, int __yb) {
            _xa = __xa;
            _xb = __xb;
            _ya = __ya;
            _yb = __yb;

        }
    
        public int hashCode() {
            return _xa + _xb + _ya + _yb;
        }

        public boolean equals(Object __obj) {
            try {
                FilledRectangle _rect = (FilledRectangle) __obj;
                
                if (_xa == _rect._xa &&
                    _xb == _rect._xb &&
                    _ya == _rect._ya &&
                    _yb == _rect._yb) {
                    
                    return true;
                }
                return false;

            } catch (Exception EXC) {
                return false;
            }
        }
    }

    //********************************************
    //*
    //* View Panel
    //*
    //********************************************
    private final class ViewPanel extends JPanel implements Serializable {
        final int           _CWIDTH                         = 460;
        final int           _CHEIGHT                        = 400;
        final Color         _CBACKGROUND_COLOR              = new Color(1.0f, 1.0f, 1.0f);
        final Color         _CDEFAULT_COLOR_1               = new Color(0.4f, 0.4f, 0.8f);
        final Color         _CDEFAULT_COLOR_2               = new Color(0.8f, 0.4f, 0.4f);
        final Color         _CTEXT_COLOR                    = new Color(0.0f, 0.0f, 0.0f);
        final int           _CX_START                       = 100;
        final int           _CY_START                       = 50;
        final Font          _CVIEW_FONT                     = new Font("Courier", Font.BOLD, 25);
        final int           _CHORIZONTAL_STRING_OFFSET      = 5;
        final int           _CVERTICAL_STRING_OFFSET        = 30;
        final int           _CDOUBLE_DIGIT_OFFSET           = (int) (_CVIEW_FONT.getSize() * 1.3);
        final Color[]       _CDEFAULT_COLORS                = { _CDEFAULT_COLOR_1, _CDEFAULT_COLOR_2 }; 

        //********************************************
        //*
        //* Constructor
        //*
        //********************************************            
        public ViewPanel() throws Exception {
            super();                                        
            setLayout(new BorderLayout());
            //setBackground(_CBACKGROUND_COLOR);
            setBorder(BorderFactory.createCompoundBorder(BorderFactory.createRaisedBevelBorder(), BorderFactory.createLoweredBevelBorder()));
            setPreferredSize(new Dimension(2*_CWIDTH, 2*_CHEIGHT));
            //setMinimumSize(new Dimension(_CWIDTH, _CHEIGHT));
            setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
            //setFont(_CVIEW_FONT);
        }       
        
        //********************************************
        //*
        //* paint() Override
        //*
        //********************************************            
        public void paintComponent(Graphics __g)
	{
                super.paintComponent(__g);


		try
		{
			int xstart = _CX_START;
			int ystart = _CY_START;

			File file = new File("NY.jpg");
			BufferedImage bi = ImageIO.read(file);
			//__g.drawImage(bi, 10, 10, null);
			if(_playerindex > 0)
			{
				if(_moves[_playerindex - 1] != null)
				{
					__g.drawImage(_moves[_playerindex - 1].getImage(0), 10, 10, null);
					String str = _moves[_playerindex - 1].getName();
					int len = str.length();
					__g.drawString(str, 10, 550);
				}
				else
				{
					__g.drawString("No Image Returned", 10, 550);
				}
			}

//			System.out.println("cpp = " + numCardsPerPlayer() +  ", p = " + numPlayers() + ", (w,h) = " + _CCARD_WIDTH + ", " + _CCARD_HEIGHT);

			// Draw Hidden Cards
			//__g.drawString("Hidden", _CHORIZONTAL_STRING_OFFSET, ystart + _CVERTICAL_STRING_OFFSET);
			/*
			for(int i=0;i < numHiddenCards();i++)
			{
				DrawCard(__g, xstart + i*_CCARD_WIDTH, ystart, _CHIDDENCARD_COLOR, _hidden[i]);
			}
			ystart += _CVERTICAL_CARD_PLACEMENT_INCREMENT;
			// Draw Guess/Query Cards
			int _round = currRound();
			if(_round > 0)
			{
				MoveResult result = history(_round - 1);
				if(result.type != _CINVALID)
				{
					interrogator = result.player;
					if(result.type == _CGUESS)
					{
						responseindex = -1;
						__g.drawString("G", _CHORIZONTAL_STRING_OFFSET, ystart + _CVERTICAL_STRING_OFFSET);
					}
					if(result.type == _CINTERROGATION)
					{
						__g.drawString("I", _CHORIZONTAL_STRING_OFFSET, ystart + _CVERTICAL_STRING_OFFSET);
						interrogatee = result.interrogatee;
						response = result.response;
					}
					
					int _LEN = (result.cardlist == null)?(0):(result.cardlist.length);
					for(int j=0;j < _LEN;j++)
					{
						DrawCard(__g, xstart + j*_CCARD_WIDTH, ystart, _CGUESS_COLOR, result.cardlist[j]);
					}
				}
			}
			
			ystart += _CVERTICAL_CARD_PLACEMENT_INCREMENT;
			// Draw Player Cards
			for(int i=0;i < numPlayers();i++)
			{
				for(int j=0;j < numCardsPerPlayer();j++)
				{
					if(_boot[i])
						DrawCard(__g, xstart + j*_CCARD_WIDTH, ystart, new Color(1.0f, 1.0f, 1.0f), _playercards[i][j]);
					else
						DrawCard(__g, xstart + j*_CCARD_WIDTH, ystart, _players[i].color(), _playercards[i][j]);
				}
				if(i == interrogator)
				{
					int xpoint = xstart - 10;
					int ypoint = ystart + _CVERTICAL_CARD_PLACEMENT_INCREMENT/2;
					__g.setColor(_CDEFAULT_COLORS[0]);
					__g.drawLine(xpoint, ypoint, 20, ypoint);
					__g.drawLine(xpoint, ypoint, xpoint - 10, ypoint - 10);
					__g.drawLine(xpoint, ypoint, xpoint - 10, ypoint + 10);
				}
				if(i == interrogatee)
				{
					int xpoint = 20;
					int ypoint = ystart + _CVERTICAL_CARD_PLACEMENT_INCREMENT/2;
					__g.setColor(_CDEFAULT_COLORS[1]);
					__g.drawLine(xpoint, ypoint, xstart - 10, ypoint);
					__g.drawLine(xpoint, ypoint, xpoint + 10, ypoint - 10);
					__g.drawLine(xpoint, ypoint, xpoint + 10, ypoint + 10);
					if(response)
					{
						__g.drawLine(xstart - 10, ypoint, xstart - 20, ypoint - 10);
						__g.drawLine(xstart - 10, ypoint, xstart - 20, ypoint + 10);
						DrawCard(__g, xstart + responseindex*_CCARD_WIDTH, ystart, _CGUESS_COLOR, _playercards[i][responseindex]);
					}
				}
				__g.drawString(Integer.toString(i), _CHORIZONTAL_STRING_OFFSET, ystart + _CVERTICAL_CARD_PLACEMENT_INCREMENT/2 + _CVIEW_FONT.getSize()/2);
				ystart += _CVERTICAL_CARD_PLACEMENT_INCREMENT;
			}
				*/
            } catch (Exception EXC) {
                EXC.printStackTrace();
            }
	}
    }    
    
    //********************************************
    //*
    //* Control Panel
    //*
    //********************************************
    private final class ControlPanel extends JPanel implements ActionListener, ItemListener, Serializable {
        JTabbedPane  _tab;
        JPanel       _conf;
        JPanel       _info;
        final        int         _CWIDTH = 300;
        final        int         _CHEIGHT = 350;
        final        int         _CPANEL_WIDTH = _CWIDTH;
        final        int         _CPANEL_HEIGHT = 21;
        final        int         _CPLAYER_NAME_LENGTH = 20;
        final        ImageIcon   _CSTEP_ICON  = new ImageIcon("Images/marble_step.gif");
        final        ImageIcon   _CSTOP_ICON  = new ImageIcon("Images/marble_stop.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, 12);
        final Font   _CCOMBO_FONT = new Font("Courier", Font.BOLD, 10);

        JTextField[] _scores;
        JTextField   _numplayersfield;
        JTextField   _gamefilefield;
        JComboBox[]  _classes;
        JComboBox    _gamefilesbox;
        JPanel       _infobox;
        JPanel       _confbox;
        JButton      _play;
        JButton      _step;
        JButton      _reset;
	JButton      _stop;
        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));
            setMaximumSize(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);

	    /* amg2006 adding stop button */
	    _stop = new JButton(_CSTOP_ICON);
	    _stop.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);
            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.setForeground(_players[i].color());
                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);
            _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(2);

        }        

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

            try {
                if (source == _step) {
                    step();
                    this.refresh();
                    Maps.this.refresh();
                    return;
                }
                if (source == _play) {
		    new StopListener(this).start();
                    return;
                }
                if (source == _reset) {
                    reset();
                    return;
                }
		if (source == _stop) {
		    _state = _CFINISHED;
		}

                if (source == _input) {
			/*
                    if (_state == _CFINISHED) {
                        return;
                    }
		    _moves[_playerindex] = parseMove(_input.getText());
		    if(_moves[_playerindex] != null)
		    {
			    if((_moves[_playerindex].type() == _CINTERROGATION) && (_state_before_wait == _CDECISION))
			    {
                        	println("Game Closing: Must Guess !!");
				return;
			    }
        		StringBuffer SB = new StringBuffer();
			if(processMoves(_moves[_playerindex]) == true)
			{
                        	SB.append("Player[");
                        	SB.append(Integer.toString(_playerindex));
                        	SB.append("]: \n");
				SB.append(_moves[_playerindex].toString());
			}
			else
			{
                        	SB.append("Player[");
                        	SB.append(Integer.toString(_playerindex));
                        	SB.append("] has made an invalid move\n");
                        	SB.append("Player[");
                        	SB.append(Integer.toString(_playerindex));
                        	SB.append("] has been given the boot.\n");
				ExitPlayer(_playerindex, _CEXCEPTION);
                        //	_boot[_playerindex] = true;
				result = new MoveResult();
				result.setType(_CINVALID);
				_history.add(_currRound, result);
			}
			println(new String(SB));
                    	_playerindex = NextPlayer(_playerindex);
		    	_currRound++;
			_state = _CMOVING;
		    	if(_playersleft <= 1)
			    _state = _CDECISION;
		    	if(_currRound >= maxRounds())
			    _state = _CDECISION;
		    }
		    else
		    {
                        println("Invalid Input");
		    }
                    return;
		    */
                }

                if (source == _gamefilesbox) {
                    _config.setGameFile((String) _gamefilesbox.getSelectedItem());
                    _ui.configure(_config);
                }

                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();
                        Maps.this.refresh();
                    } else {
                        println("Invalid Input");
                    }
		    validate(); // Lets reset the bounds
                }                                                                                
            } 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);
                    }                                
                }                        
                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(Integer.toString((int)_players[i].score()));
                _scores[i].setText(Double.toString(0.0));
            }
        }                            

        
        //********************************************
        //*
        //* Action Tool Exporter
        //*
        //********************************************
        public JButton[] exportTools() {
	    /* amg2006 changed to 4 */
            JButton[] ret = new JButton[4];
            ret[0] = _reset;
            ret[1] = _step;
            ret[2] = _play;
	    ret[3] = _stop;
            return ret;
        }
    }

    class StopListener extends Thread
    {
	private ControlPanel controlPanel;	
	public StopListener(ControlPanel cp) { 
	    controlPanel=cp;
	} 
	public void run ()
	{
		try
		{
		    while (GUI._maps.step()) {
			GUI._maps.refresh();
			controlPanel.refresh();
		    }		
			GUI._maps.refresh();
			controlPanel.refresh();
		}
		catch (Exception e)
		{
		    System.out.println("unexpected exception caught in run");
	                e.printStackTrace();
		}
	}
    }

}


