//   Course Number: COSC 650 Data Comm/Networks
//    Program Name: JavaChat
//  Project Number: 2
//         Authors: Brian Hoffman
//                  Mike McKinney
//                  Javier Carrasco
//     Description:  This class creates a thread used by the server to listen 
//                   for and process incomming messages.

import java.net.*;   //package required for socket connection
import java.io.*;    //package required for data streams and stdout

public class SocketThread extends Thread {
    
    private ObjectInputStream objIn;
    private ObjectOutputStream objOut;
    Socket clientSocket;
    ChatClient client;
    ThreadedChatServer server;

    /**
     * ------------------------------------------------------------------------
     * Creates a new instance of SocketThread
     * ------------------------------------------------------------------------
     */
    public SocketThread(Socket socket, ThreadedChatServer srv)
    {
        System.out.println("Thread " + this.getName() + " created.");
        clientSocket = socket;
        server = srv;
        
        /* method will create socket streams from socket in
         * the constructor */
        this.createSocketStreams(clientSocket);
        
        /* method to register user.  After completion, a new 
         * ChatClient object will have been added to the clientMap */
        this.registerUser();
        System.out.println("userRegistered");
    }
    
    /**
     * ------------------------------------------------------------------------
     * Guts of the SocketThread.  It will loop infinitely processing requests
     * ------------------------------------------------------------------------
     */
    public void run()
    {
        boolean streamErr = false;
        
        while(!streamErr)
        {
            try 
            {
                ChatMessage message = (ChatMessage)objIn.readObject();
                String mesType = message.getType();


                /* Logic for processing the messages;  Iwillmost likely implement 
                 * with else statements at some point to improve performance. Will 
                 * also be removing print statements although could change to write
                 * to file for logging purposes. */
                
                if(mesType.equals("default"))
                {
                    System.out.println("default message received");
                    server.sendChat(message.getRoomName(), message);
                }
                
                if(mesType.equals("createRoom"))
                {
                    System.out.println("createRoom received");
                    server.insertRoom(message.getRoomName(), new 
                      ChatRoom(message.getRoomName(), message.getSender(), server));
                }
                
                if(mesType.equals("joinRoom"))
                {
                    System.out.println("joinRoom received");
                    server.addUserToRoom(message.getSender(), message.getRoomName());
                }
                
                if(mesType.equals("leaveRoom"))
                {
                    System.out.println("leaveRoom received");
                    server.removeUserFromRoom(message.getSender(), message.getRoomName());
                }
                
                if(mesType.equals("requestPrivate"))
                {
                    System.out.println("requestPrivate received");
                    server.requestPrivate(message);
                }
                
                if(mesType.equals("acceptPrivate"))
                {
                    System.out.println("acceptPrivate received");
                    String roomName = message.getMsg() + " & "  + message.getSender();
                    server.insertPrivRoom(roomName, new ChatRoom(roomName, 
                        message.getMsg(), message.getSender(), server));
                }
                
                if(mesType.equals("denyPrivate"))
                {
                    System.out.println("denyPrivate received");
                    server.denyPrivate(message);
                }
                
                if(mesType.equals("endClient"))
                {
                    server.cleanUser(message.getSender());
                    streamErr = true;
                }
                
                if(mesType.equals("bootUser"))
                {
                    System.out.println("bootUser recevied");
                    server.bootUser(message.getMsg(), message);
                }
           }
           catch (Exception e) 
           {
               System.out.println(e);
               System.out.println(e.getMessage());
               System.out.println("Detected Error communication with " + client.getUser());
               System.out.println("Cleaning user");
               server.cleanUser(client.getUser());
               streamErr = true;
            }
                   
        }
    }
    
    /**
     * ------------------------------------------------------------------------
     * Creates object streams for input and output to and from the socket
     * ------------------------------------------------------------------------
     */
    private void createSocketStreams(Socket clientSocket)
    {
        try 
        {
            objOut = new ObjectOutputStream(clientSocket.getOutputStream());
            objOut.flush();
            objIn = new ObjectInputStream(clientSocket.getInputStream());
            System.out.println("Streams created for " + clientSocket.getInetAddress());
        }
        catch (Exception e) 
        {
            System.out.println("Error creating streams");
        }
    }
    
    /**
     * ------------------------------------------------------------------------
     * Used to register a user.  The method will loop infinitely  until it has
     * successfully registered a user.
     *
     * It will read a message from the object stream and attempt to create a new
     * client.  If the username in the message  already exists, it will send a
     * message back to the client advising of failure and wait for another. This
     * will continue until an unused username is received and the user has been
     * registered.
     * ------------------------------------------------------------------------
     */
    private void registerUser()
    {
        
        boolean regSuccess = false;  /*used to control looping; will
                                      *be updated to true if client
                                      *is successfully registered */ 
        while(!regSuccess)
        {
            try 
            {
                //message read from input stream
                ChatMessage message = (ChatMessage)objIn.readObject();
                
                //new ChatClient created using message
                client = new ChatClient(message.getSender(), clientSocket, objOut, objIn);
                
                //method call to insert the client into clientMap; return value whether successful
                regSuccess = server.insertClient(message.getSender(), client);
                
                if(!regSuccess)   
                {
                    //writes a deny message to client
                    objOut.writeObject(new ChatMessage("denyClient", "", "", ""));
                    objOut.flush();
                    client = null;
                }
                else
                {
                    //writes a accept message including the names of chatrooms
                    objOut.writeObject(new ChatMessage("acceptClient", "", "", server.getRoomList()));
                    objOut.flush();  
                    
                }
            }
            catch (Exception e) { }
        }
        System.out.println("Client Accepted");
    }
}