//GameMaster.java

/** 
 *
 * @author  Sean Bridges
 * @version 1.0.1
 * 
 * The game master controls the state of the game, and is responsible for 
 * making the players move in sequence.
 *
 * If a subclass overrides start, restart or stop, it should call super.start,
 * restart or stop respectively.  
 *
 * Whenever start or restart are called a new thread is created which 
 * asks the players for their moves in sequence.  
 * If there was an old thread, the old thread will be interrupted, and discarded.
 */

import java.util.*;

public class GameMaster 
{

//--------------------------------------------------------
	//instance variables
	private Vector listeners = new Vector(); //listeners to the state,
                                             //use a vector for jdk1.0 compatability
	private Player[] players; //the players of the game
	private Board board;
	private int currentPlayerIndex = 0;
	private GameThread gameThread;
    
  
//--------------------------------------------------------
	//constructor
  
	/** 
	 * Creates new GameMaster.
	 * The PlayerArray should have at least 1 player in it.
	 * The order the players occur in the array is the order in which the 
	 * players will move.
	 * If the array is empty, an ArrayOutOfBoundsException will be thrown.
	 */
	public GameMaster(Board board, Player[] players ) 
	{
    
		if(players.length == 0)
		{
			System.err.println("Error, player array is empty, GameMaster() ");
			throw new ArrayIndexOutOfBoundsException("Game Master created with empty array");
		}
    
		this.board = board;
		this.players = players;
    
	}

//--------------------------------------------------------
	//instance methods

//------------------------------------------------------
	//listeners
  
	/**
	 * Add a game event listener.
	 */
	public synchronized void addListener(GameEventListener l)
	{
		listeners.addElement(l);
	}	
  
	public synchronized void removeListener(GameEventListener l)
	{
		listeners.removeElement(l);
	}
  
	/**
	 * Clone the listener vector in case the list of listeners 
	 * is modified while informing the listeners.
	 * This means the enumeration enumerates a copy of the list,
	 * and additions can be made to the list while informing listeners.
	 */
	private synchronized Enumeration enumerateListeners()
	{
		return ((Vector) listeners.clone() ).elements();
	}

  
	private void notifyListenersMoveMade(Move aMove)
	{
		Enumeration e = enumerateListeners();
		while(e.hasMoreElements())
		{
			((GameEventListener) e.nextElement()).moveMade(aMove);
		}
	}  
  
	private void notifyListenersGameReStarted()
	{
		Enumeration e = enumerateListeners();
		while(e.hasMoreElements())
		{
			((GameEventListener) e.nextElement()).gameRestarted();
		}
	}  
  
  
	private void notifyListenersGameStarted()
	{
		Enumeration e = enumerateListeners();
		while(e.hasMoreElements())
		{
			((GameEventListener) e.nextElement()).gameStarted();
		}
	}

	private void notifyListenersGameStoped()
	{
		Enumeration e = enumerateListeners();
		while(e.hasMoreElements())
		{
			((GameEventListener) e.nextElement()).gameStoped();
		}
	}
  
//--------------------------------------------------------
	//player management
  
	/**
	 * The players whose move it currently is.
	 */
	private synchronized Player getCurrentPlayer()
	{
		return players[currentPlayerIndex];
	}
  
	/**
	 * Advance the player
	 */
	private synchronized void advancePlayer()
	{
		currentPlayerIndex ++;
		if(currentPlayerIndex >= players.length)
		{
			currentPlayerIndex = 0;
		}
	}



//------------------------------------------------------
	//state

	/** 
	 *  Start the game.
	 *  Tells the board to start, starts the game loop thread,
	 *  then notifies listeners.
	 */
	public synchronized void startGame()
	{
		if(gameThread != null)
		{
			gameThread.stopThread();
			gameThread = null;
		}
    
		board.gameStarted();
		currentPlayerIndex = 0;
		gameThread = new GameThread();
		gameThread.start();
		notifyListenersGameStarted();
    
	}


	/**
	 * Stop the game.
	 * Stops the game thread, tells the board the game is stopped, and
	 * then notifies listeners.
	 */
	public synchronized void stopGame()
	{
		if(gameThread != null)
		{
			gameThread.stopThread();
			gameThread = null;
		}
    
		board.gameStoped();
		notifyListenersGameStoped();
	}

	/**
	 * Restart the game.
	 * Stops the current game loop, tells the board to restart,
	 * starts a new game loop, and then notifies listeneres.
	 */
	public synchronized void restartGame()
	{
		if(gameThread != null)
		{
			gameThread.stopThread();
			gameThread = null;
		}
    
		board.gameRestarted();
		currentPlayerIndex = 0;
		gameThread = new GameThread();
		gameThread.start();
		notifyListenersGameReStarted();
    
	}   
  
  
//--------------------------------------------------------
	//inner classes

	/** 
	 * The game thread class does the work of 
	 * making the players move.
	 * Exits after stop is called.
     */
	class GameThread extends Thread
	{    
    
		public boolean active = true;
    
		public void stopThread()
		{
			if(active)
			{
				active = false;
				this.interrupt();
			}
		}
    
		public  void run()
		{
			while( active && ! board.isGameOver() )
			{
        
				//get the players move.
				Player player = getCurrentPlayer();
				Move move = player.getMove((Board) board.clone());
				if(move == null)
				{
					//ignore null moves
					continue;
				}
        
				//if we are still active, make the move
				boolean moveMade = false;
  
				if(active)  
				{
					moveMade = board.move(move); 
				}
        
				//if we are still active, and the move was made, notify listeners
				if(active && moveMade)
				{
					advancePlayer();
					notifyListenersMoveMade(move);
				}
        
        
			}//end while
			
			if(active && board.isGameOver())
			{
				notifyListenersGameStoped();
			}

			active = false;
		}//end run
    
  
	}//end class GameThread
  
}//end class GameMaster

