//  package Java.TicTacToe.Game;

// will probably want to change RemoteInterface dependency - jlr
import RemoteInterface.*; // needed for "Move" 

import java.io.*; // needed for Serializable

import java.awt.*;
import java.awt.event.*; 

import java.beans.*; //needed for Property and Vetoable-ChangeSupport

public class GameBoard 
    extends Frame   
{
 
    //----------------------------------------------------------------------
    // Private data fields:
    private PropertyChangeSupport positionChanges = new PropertyChangeSupport(this);
    private VetoableChangeSupport positionVetos = new VetoableChangeSupport(this);
    private final static int columns = 3;// changing these values doesn't work - jlr
    private final static int rows = 3; // one known problem, rows must == columns (see recalcPosition)


    //gameStatus Class invariant: -1=playing, 0=lost, 1=won, 2=draw    
    private int gameStatus = -1; 

    //----------------------------------------------------------------------
    // Protected data fields:

    protected int playerID = 2; // class Invariant: playerID > -1

    public GameBoardPosition[] position;// rows * columns

    //----------------------------------------------------------------------
    // Methods:
    public GameBoard(){ // beans need to have a default constructor
	super();
  
	setGameStatus(-1);// set Game playing

	position = new GameBoardPosition[rows*columns];
	
	// Reset the game board
	for(int i = 0; i < position.length; i++){
	    position[i] = new GameBoardPosition(2);
	}

	addMouseListener(new MouseAdapter() {
	    public void mouseClicked(MouseEvent e) {
		GameBoardMouseUp(e, e.getX(), e.getY());
	    }
	}
	);

	MenuBar menubar = new MenuBar(); 
      	this.setMenuBar(menubar); // add MenuBar to Frame
   	Menu file = new Menu("File");
   	menubar.add(file);

   	// create two new menu items, with menu short cuts
  	MenuItem quitGame;
	
   	file.add( quitGame = new MenuItem("Exit", new MenuShortcut(KeyEvent.VK_E)));
 	
   	quitGame.addActionListener ( new ActionListener() {
    	    public void actionPerformed( ActionEvent e ) { closeWindow(); }
  	});

	this.addWindowListener( new WindowAdapter() {
	    public void windowClosing( WindowEvent e ){ closeWindow(); }
	});

	this.addComponentListener( new ComponentAdapter() {
	    public void componentResized( ComponentEvent e ){ 
		recalcPositions( getWidth(), getHeight() );
	    }
	});
	
    }

    protected void closeWindow(){  
	setVisible(false);
	dispose();
	System.exit(0);
    }
    
    public void GameBoardMouseUp(MouseEvent evt, int x, int y){
	for ( int pos = 0; pos < position.length; pos++ ) {
	    if (position[pos].contains(x,y)){
		setMove( pos );
		break;
	    }
	}
    }
    
    // this assumes only one player can maintain a position and
    // that there is a equivelence between positionState and playerID.  JLR
    void setMove( int pos )
    {
	
	int oldMove = position[pos].getPositionState();
	int newMove = getPlayerID(); 

	try {
	    // First tell the vetoers about the change.  If anyone objects, then
	    // a PropertyVetoException is generated and we don't make the change
	    positionVetos.fireVetoableChange("setMove", 
					     new Move(pos, oldMove),
					     new Move(pos, newMove) );

	    // No-one vetoed, so go ahead and make the change.
	    setPositionState(pos, newMove);
	    positionChanges.firePropertyChange("setMove", 
					       new Move(pos, oldMove),
					       new Move(pos, newMove) );
	    repaint();  // show change
	}
	catch (PropertyVetoException e) {
	    System.out.println("Move to position, " + pos + " vetoed");
	}	  
	
    }

    // PreConditions: none
    // PostConditions: positon[] bounds are set to form a matrix that
    //                 covers the "screenWidth" and "screenHeight"

    public void recalcPositions(int screenWidth, int screenHeight) {
	
	Insets in = getInsets();
	
	int height = (screenHeight - (in.top + in.bottom))/rows;
	int width = (screenWidth - (in.left + in.right))/columns;
	
	
	for (int r = 0 ; r < rows ; r++ ) {
	    for ( int c = 0; c < columns ; c++ ){
		if ( (r * rows + c) > position.length )
		    System.out.println(r + " * " + rows + " + " + c );
		else
		    position[r * rows + c].setBounds(r*width + in.left,
						     c*height + in.top, 
						     width, height );
	    }
	}
	
    }

    // Preconditions:  Unknown - this method is called by update
    // Postconditions: Renders a border for each position, 
    //                 draws the image set in each position, and
    //                 displays win, loss, or tie (if necessary)
    // NOTE: If you override this method, a call to super.paint 
    // should be the first thing done in the new paint method.
    public void paint(Graphics g) {
	
	g.setColor(Color.black); // Color to drawRect in
	
	for(int i = 0; i < position.length; i++) {
	    position[i].drawRect(g);// draw the grid lines
	    position[i].drawImage(g, this); // draw any images in the position
	 
	}
	
	showStatus(g);   // displays win, loss, or tie (if necessary) 
 
    }
    // Preconditions: None
    // Postconditions:  sets the playerID currently using the game board
    public void setPlayerID( int newPlayerID ) {
	playerID = newPlayerID;
    }

    // Preconditions: None
    // Postconditions: returns PlayerId currently using the game Board
    public int getPlayerID() {
	return playerID;
    }
    //----------------------------------------------------------------------
    // Methods for setting and retrieving the gameBoardPositions: 
  
    public void setGameBoardPositions(int[] boardState){
	if (boardState.length != position.length ){
	    System.err.println("Error in GameBoard.setGameBoardPositions");
	    System.err.println("boardState.length != position.length");
	    System.exit(1);
	}
	
	for (int i = 0; i < position.length; i++) {
	    setPositionState(i, boardState[i]);
	}

	repaint();
    }

    protected void setPositionState(int pos, int newState) {
	if ( position[pos].getPositionState() != newState ) {    
	    position[pos].setPositionState(newState);   
	}
    }

    //----------------------------------------------------------------------
    // Methods for setting and retrieving the gameStatus: 
  
    public void setGameStatus(int newStatus){
	if ( (newStatus >= -1) && (newStatus <=2 )  ){
	    gameStatus = newStatus;
	    repaint();
	}
    }
    
    public int getGameStatus()
    {
	return gameStatus;
    }
    
    public void showStatus(Graphics g) {
	if( gameStatus != -1 ) {
	    try {	 
		Insets in = getInsets();
		Dimension d = getSize();
		
		// display text at 1/4 down the _viewable_ screen
		int xoff = (d.width - in.left - in.right) / 4; 
		// center text
		int yoff = (d.height - in.top - in.bottom) / 2;
		
		g.setFont(new Font("Helvetica", Font.BOLD, 18));
		
		if( gameStatus == 1) {
		    g.drawString("You Won", xoff , yoff);
		}
		else if( gameStatus == 0 ){
		    g.drawString("You Lost", xoff, yoff);
		}
		else if( gameStatus == 2) {
		    g.drawString("You Tied",xoff,yoff);
		}
	    }
	    catch (Exception e) { }	
	}
    }
    
	


    //----------------------------------------------------------------------
    // Methods for registering listeners: 
    // this section was copied and modified from the 
    // BeanBox JellyBeans example. JLR 1/30/99
    
    /**
     * The specified PropertyChangeListeners <b>propertyChange</b> method will
     * be called each time the value of any bound property is changed.
     * The PropertyListener object is addded to a list of PropertyChangeListeners
     * managed by the GameBoard, it can be removed with removePropertyChangeListener.
     * Note: the GameBoard specification does not require PropertyChangeListeners
     * to run in any particular order.
     *
     * @see #removePropertyChangeListener
     * @param l the PropertyChangeListener
     */      
    public void addPropertyChangeListener(PropertyChangeListener l) {
	positionChanges.addPropertyChangeListener(l);
    }

    /** 
     * Remove this PropertyChangeListener from the GameBoards internal list.  
     * If the PropertyChangeListener isn't on the list, silently do nothing.
     * 
     * @see #addPropertyChangeListener
     * @param l the PropertyChangeListener
     */      
    public void removePropertyChangeListener(PropertyChangeListener l) {
	positionChanges.removePropertyChangeListener(l);
    }

    /**
     * The specified VetoableChangeListeners <b>vetoableChange</b> method will
     * be called each time the value of any constrained property is changed.
     * Currently, the only constrained property is a Move Change by a gameBoardMouseUp.
     * The VetoableChangeListener object is addded to a list of VetoableChangeListeners
     * managed by the GameBoard, it can be removed with removeVetoableChangeListener.
     * Note: the GameBoard specification does not require VetoableChangeListeners
     * to run in any particular order.
     *
     * @see #removeVetoableChangeListener
     * @param l the Ve
    //toableChangeListener
     */      
    public void addVetoableChangeListener(VetoableChangeListener l) {
	positionVetos.addVetoableChangeListener(l);
    }

    /** 
     * Remove this VetoableChangeListener from the GameBoards internal list.  
     * If the VetoableChangeListener isn't on the list, silently do nothing.
     * 
     * @see #addVetoableChangeListener
     * @param l the VetoableChangeListener
     */      
    public void removeVetoableChangeListener(VetoableChangeListener l) {
	positionVetos.removeVetoableChangeListener(l);
    }


}
