/**
 * @(#)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.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.MalformedURLException;
import java.net.Socket;
import java.net.SocketException;
import java.net.URL;
import java.util.Vector;

import javax.swing.ProgressMonitor;
import javax.swing.ProgressMonitorInputStream;

/**
 * This class handles ServerThread startup, operations and shutdown.
 * Once a ServerThread is created, communications between client can occur.
 * The ServerThread is passed information from the Server to communicate to the 
 * newly connected client.
 * 
 * @author 3GP01 - Hardev Bhamra, Deepak Chandarana, James Tompkin
 * @version 2.0
 * @see Client
 * @see Server
 */

public class ServerThread extends Thread
{	
	
	PrintWriter out = null;
    BufferedReader in = null;
	Socket clientSocket = null;
	ServerThread st[];
	ScrabbleProtocol sp = new ScrabbleProtocol();
	String serverDictionary;
	int numberOfHints;
	int uid;		//of server thread
	String[] clientNames;
	
    /**
     * Creates a new ServerThread
     * 
     * @param clientSocket The socket with which to connect.
     * @param st The ServerThread itself.
     * @param dictionary The dictionary to be used in the game.
     * @param hints The number of hints to be allowed in the game.
     * @param id The uid of the client.
     * @param cNames A list of current clients in the game.
     */
    public ServerThread(Socket clientSocket, ServerThread[] st, String dictionary, int hints, int id, String[] cNames)
    {  	this.clientSocket = clientSocket;
        this.st = st;
        this.serverDictionary = dictionary;
        this.numberOfHints = hints;
        this.uid = id;
        this.clientNames = cNames;
    }
	
    /**
     * Gets Scrabble instance.
     * 
     * @return Scrabble
     */
    public Scrabble getScrabble()
    {	return Scrabble.scrabble;	
    }
    
	/* (non-Javadoc)
	 * @see java.lang.Thread#run()
	 */
	public void run()
	{		
        try 
		{	out = new PrintWriter(clientSocket.getOutputStream(), true);
			in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
		
			String inputLine;
			//send uid to client
			//also send number of hints (abuse of protocol, but a useful abuse, it saves an extra transaction)
			out.println(sp.processOutput(uid, 1, Integer.toString(numberOfHints)));	
			for (int i = 0; i < 4; i++)	//send to client clientNames
			{	if(clientNames[i]!=null)
					out.println(sp.processOutput(i, 2, clientNames[i]));	
			}
			out.println(sp.processOutput(uid, 3, serverDictionary));	//send dict check for client
			
			//when server receives transmission, respond
			try
			{	while ((inputLine = in.readLine()) != null)
				{  	String processedInput[] = sp.processInput(inputLine);
					int incomingUid = Integer.parseInt(processedInput[0]);
					int cs = Integer.parseInt(processedInput[1]);
					
					switch (cs)
					{	case 4: 		//if dict send request
							sendDictionary(serverDictionary); //send dictionary
							break;
						case 2:			//name from the client
							//this creates a new player in scrabble.clientNames 
							getScrabble().clientNames[incomingUid] = processedInput[2]; //put client name in clientNames on the server
							echo(inputLine);
							break;
						case 9:			//if a client exits
							//System.out.println("Server Thread received exit call");
							echo(inputLine);
							if(incomingUid == uid)	//if client of this specific serverthread wants to leave,
							{	getScrabble().server.playerLeft = true;
								getScrabble().server.st[uid] = null;
								//getScrabble().setConnectionSuccessful(false);
								//System.out.println("Server Thread closing...");
								close();		//close this server thread
							}
							break;
						default:		//echo to all clients input received
							echo(inputLine);
							break;
					}
				}
			}
			catch(SocketException se)
			{	
				in.close();
				out.close();
				clientSocket.close();
			}
		}
        catch (IOException e) 
		{	//Stream exception, cannot write
        	System.exit(0);
		}
	}
	
	/**
	 * Sends the inputLine to all clients.
	 * 
	 * @param inputLine The String to echo to all clients.
	 */
	public void echo(String inputLine)
	{	for(int i = 0; i < 4; i++)
		{	if (st[i]!=null)  
				st[i].out.println(inputLine);
		}
	}
	
	/**
	 * Echoes a kick request from the server lobby.
	 */
	public void kick()
	{	echo(sp.processOutput(uid,9,null));
	}
	
	/**
	 * Closes the ServerThread
	 */
	public void close()
	{	try 
		{	clientSocket.shutdownInput();
			clientSocket.shutdownOutput();
			clientSocket.close();
		}
		catch (IOException e) 
		{	//exception
			System.exit(0);
		}

	}
	
	
	/**
	 * Streams a dictionary to the client, so that the client may receive the
	 * dictionary that the server wishes to use.
	 * 
	 * @param file The dictionary to send.
	 */
	public void sendDictionary(String file)
	{	BufferedReader br = null;
	    Vector res = new Vector();
	    String line;
	    boolean eof = false;
	    //new file to read, from string input
	    File file2 = new File(System.getProperty("user.dir")
								+ File.separator + "resources"
								+ File.separator + "dictionaries"
								+ File.separator + file + ".txt");
	    URL url = null;					
		try {
			url = file2.toURL();		//new url from file
		} catch (MalformedURLException e1) {
			e1.printStackTrace();
		}
		
		try	//create ProgressMonitorInputStream on file-as-url to display loading bar
	    { 	ProgressMonitorInputStream pmis = new ProgressMonitorInputStream(null, "Uploading " + file + ".txt", url.openStream());
			br = new BufferedReader(new InputStreamReader(pmis));
	    	ProgressMonitor pm = pmis.getProgressMonitor();
	    	pm.setMillisToDecideToPopup(0);
	    }	//load dictionary from string location
	    catch (FileNotFoundException e)
	    { 	Dictionary.displayExceptionMessage(file);
	    } 
	    catch (IOException ioe) 
		{ 	Dictionary.displayExceptionMessage(file);
		}

	    while (!eof)
	    { 	try { line = br.readLine(); }
	      	catch (IOException e)
			{	Dictionary.displayExceptionMessage(file);
	      		return;
			}
	      	String processedInput = sp.processOutput(uid,4,line);
	      	out.println(processedInput); 
	      	if (line == null) 
	      	{	eof = true;
	      		processedInput = sp.processOutput(uid,4,"%");
	      		out.println(processedInput); 
	      	} 
	    }
	}
}