/**
 * @(#)Server.java	11/03/05
 * 
 * Copyright 2005 3GP01, KCL.
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * The GNU General Public Licence should be contained within licence.txt;
 * if not, write to the Free Software Foundation, Inc., 51 Franklin Street,
 * Fifth Floor, Boston, MA  02110-1301, USA.
 */

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.BindException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.Random;
import java.util.ResourceBundle;

import javax.swing.JOptionPane;

import sun.audio.AudioPlayer;
import sun.audio.AudioStream;

/**
 * This class creates a server from the Scrabble lobby.
 * A server can only be created from the server lobby.  Only one server can be created
 * on any one machine.
 * The server creates new ServerThreads when a client connects.  All communications
 * from the client are then handled by the ServerThread, with the Server overseeing
 * game syncrhonization.
 * 
 * @author 3GP01 - Hardev Bhamra, Deepak Chandarana, James Tompkin
 * @version 2.0
 * @see Client
 * @see ServerThread
 */

public class Server implements Runnable
{
	Thread thread;
	int port = 7273;
	Socket clientSocket;
	ServerSocket serverSocket;
	
	boolean listening = false;
	boolean gameInProgress = false;
	boolean playerLeft = false;
	
	//Up to four scrabble connections
	ServerThread st[] = new ServerThread[4];

	String serverDictionary;
	ScrabbleProtocol sp = new ScrabbleProtocol();
	
	int[] uids = { 0, 1, 2, 3 };
	int numberOfPlayers = 0;
	int maxNumberOfPlayers;
	int numberOfHints;
	
	ResourceBundle messages = getScrabble().getI18n().getCurrentResourceBundle(); 	//localisation message bundle
	
	/**
	 * Gets the instance of the Scrabble class.
	 * 
	 * @return Scrabble
	 */
	public Scrabble getScrabble()
	{	return Scrabble.scrabble;	
	}
	
	/**
	 * Returns true/false depending on whether the game of scrabble is in progress.
	 * 
	 * @return boolean
	 */
	public boolean isGameInProgress() 
	{	return gameInProgress;
	}

	/**
	 * Returns true/false depending on whether the server is listening for new client connections.
	 * 
	 * @return boolean
	 */
	public boolean isListening() 
	{	return listening;
	}
	
	/**
	 * Sets whether the scrabble game is in progress or not.
	 * 
	 * @param gameInProgress A boolean toggle.
	 */
	public void setGameInProgress(boolean gameInProgress) 
	{	this.gameInProgress = gameInProgress;
	}
	
	/**
	 * Sets whether the server is listening for new client connections
	 * 
	 * @param listening A boolean toggle.
	 */
	public void setListening(boolean listening) 
	{	this.listening = listening;
	}
	
	/**
	 * Creates a server.
	 * Only one server can exist on any one machine at any one time.
	 * The server controls up to four ServerThreads.
	 * The server controls the start of the game based on the number of players desired.
	 * The server creates a random seed to send to all clients for random bag synchronisation.
	 */
	public void createServer()
	{	try
		{	while (numberOfPlayers < maxNumberOfPlayers && listening)
			{	//accept incoming clients using blocking socket, catch exceptions
				try
				{	clientSocket = serverSocket.accept();	}
				catch(SocketException se)
				{	//se.printStackTrace();
					serverSocket.close();
					return;
				}
				for(int i = 0; i < maxNumberOfPlayers; i++)
				{	if(st[i]==null)
					{	(st[i] = new ServerThread(clientSocket,st,serverDictionary, numberOfHints, uids[i], getScrabble().clientNames)).start();
						break;
					}
				}
				numberOfPlayers++;
				getScrabble().setChatLog(messages.getString("Server.7") + numberOfPlayers + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
			}
			listening = false;
			getScrabble().setStartGameButtonEnabled(true);
			
			getScrabble().setChatLog(messages.getString("Server.9")); //$NON-NLS-1$
			while (!gameInProgress)		
			{	Thread.sleep(100);
				if(playerLeft)
				{	numberOfPlayers--;
					playerLeft = false;
					//System.out.println("Server returning to listening...");
					return;		
				}
			}
			
			long seed = System.currentTimeMillis();		//get system time in milliseconds for generating a seed
			
		  	Random r = new Random();
	  		int turn = r.nextInt(numberOfPlayers);
			
	  		getScrabble().setChatLog(messages.getString("Server.10")); //$NON-NLS-1$
			String processedOutput = sp.processOutput(-1,5,Integer.toString(turn)); //message to all clients, telling 'game start', giving in message 'turn'!
			st[0].echo(processedOutput);	//use first serverthread as relay for echo
			
			processedOutput = sp.processOutput(-1,6,Long.toString(seed));	//send seed to clients
			st[0].echo(processedOutput);	//echo seed
			
			getScrabble().setChatLog(messages.getString("Server.11")); //$NON-NLS-1$
			while(gameInProgress)
			{	Thread.sleep(100);
			}
			return;
		}
		catch (Exception e)
		{	//System.out.println("Server error");
			//e.printStackTrace();
		}
	}
	
    /* (non-Javadoc)
     * @see java.lang.Runnable#run()
     */
    public void run()
    {   listening = true;	//server is supposed to be listening
        try 
		{	try
        	{	serverSocket = new ServerSocket(port);
        	}
			catch (BindException be)	//if port is already in use
			{	//play a sound, as per deepak's request
			    try
			    {       InputStream in = new FileInputStream("resources/sounds/error.wav");
			            AudioStream as = new AudioStream(in);
			            AudioPlayer.player.start(as);
			    }
			    catch (FileNotFoundException fnf)
			    {	//no need to resolve this exception
					//if exception is triggered, audio will not play
			    	//fnf.printStackTrace();
			    } 
			    catch (IOException ioe) 
				{ 	//no need to resolve this exception
					//if exception is triggered, audio will not play
			        //ioe.printStackTrace();
			    }
				
				JOptionPane.showMessageDialog(null, messages.getString("Server.0") + Integer.toHexString(port) + System.currentTimeMillis() + ")." + //$NON-NLS-1$ //$NON-NLS-2$
												messages.getString("Server.2") + //$NON-NLS-1$
												messages.getString("Server.3"),  //$NON-NLS-1$
												messages.getString("Server.4"),  //$NON-NLS-1$
												JOptionPane.ERROR_MESSAGE);
				System.exit(0);
			}
			getScrabble().setChatLog(messages.getString("Server.5")); //$NON-NLS-1$
			
			while(!gameInProgress)
			{	createServer();
				//System.out.println("loopyloop");
				listening = true;
				gameInProgress = false;
				getScrabble().resetLobby();
			}
        }
    	catch (IOException e) 
		{	//Could not listen on port 7273 - server creation impossible
    		System.exit(0);
    	} 
    }
    
    /**
     * Called from the lobby when 'create server' is pressed.  Takes input from lobby
     * and creates a server based on the requirements of the game.
     * 
     * @param dict The dictionary to use in the remote game.
     * @param players The number of players required in the remote game.
     * @param hints The number of hints required in the remote game.
     */
    public void start(String dict, int players, int hints)
    {   serverDictionary = dict;
    	maxNumberOfPlayers = players;
    	numberOfHints = hints;
        thread = new Thread(this,"main"); //$NON-NLS-1$
        thread.start();
    }
    
    /**
     * Closes all server sockets, and attempts to close all ServerThreads.
     */
    public void close()
    {  	try 
		{	for(int i = 0; i < 4; i++)
			{	if(st[i]!=null)
				{	st[i].close();
				}
			}
			serverSocket.close();
		}
    	catch (IOException e) 
		{	System.exit(0);
		}
    }
}