//   Course Number: COSC 645 Applied Cryptography
//    Program Name: ClientThread.java
//         Authors: Brian Hoffman
//                  Derek Waby
//                  Kypros Ioannou
//                  Harsha V. Reddy            
//     Description: This class inherits from class thread and its run method is
//                  used to monitor requests sent by a client. It accepts messages 
//                  from the input stream (encapsulated inside a ServerConnection 
//                  object) and based upon their type field calls methods in its
//                  associated server as defined in the constructor. In order to
//                  fully represent a client connection, this class also includes
//                  a standard write method that can be called to send a message
//                  to the client through a SeverConnection object. Thus, the 
//                  ClientThreads class is actually responsible for all communication
//                  with the client. The encapuslation of client oriented data is 
//                  completed by the addition of private fields that keep track of 
//                  the single share any one client is allowed to provide. These 
//                  values may all be accessed using provided get methods.

// Java Core Packages
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;
import java.math.BigInteger;

// Jave Extension Packages 
import javax.swing.*;

public class ClientThread extends Thread
{
    private ShamirServer myServer;
    private ServerConnection myConnection;
    private boolean haveShare;
    private BigInteger myX;
    private BigInteger myY;
    private BigInteger myPrime;
  
    /**
     * ------------------------------------------------------------------------
     * Constructor - sets the server with whom this thread is associated and 
     *               whose methods will be used to handle the actual processing
     *               of messages
     * ------------------------------------------------------------------------
     */
    public ClientThread(ShamirServer inServer, ServerConnection inConnect)
    {
       myServer = inServer;
       myConnection = inConnect;
       haveShare = false;
    }
    
    /**
     * ------------------------------------------------------------------------
     * getMethods
     * ------------------------------------------------------------------------
     */
    public boolean hasShare() { return haveShare; }
    public BigInteger getX() { return myX; }
    public BigInteger getY() { return myY; } 
    public BigInteger getPrime() { return myPrime; }
    
    /**
     * ------------------------------------------------------------------------
     * writeMsg
     * ------------------------------------------------------------------------
     */
    public void writeMsg(ShamirMsg msg)
    {
        try
        { myConnection.sendObject(msg); }
        catch (IOException ioe)
        { ioe.printStackTrace(); }
    }
    
    /**
     * ------------------------------------------------------------------------
     * run - overridden method of the thread class that executes the task to 
     *       be performed by the thread. In this case, the thread listens to 
     *       input messages and dipatches them to the proper methods of the 
     *       ShamirServer class.
     * ------------------------------------------------------------------------
     */    
    public void run()
    {
        ShamirMsg currentMsg;
        String currentType;
        
        try 
        {
            currentMsg = (ShamirMsg) myConnection.readObject();
            currentType = currentMsg.getType();

            while (!interrupted() && !currentType.equals("terminate"))
            {
               if (currentType.equals("requestShare"))
               {
                  ShamirMsg shareMsg = myServer.getShareMsg();
                  myServer.postOutput(myConnection.getSocket().getInetAddress() +
                                      " requested a share.");
                  myConnection.sendObject(shareMsg);
                  myServer.postOutput("Share sent to " + myConnection.getSocket().getInetAddress() +
                                      ".");
               }
               else if (currentType.equals("share"))
               {
                   if (haveShare)
                   {
                       myServer.postOutput(myConnection.getSocket().getInetAddress() +
                                           " sent another share");
                       break;
                   }
                   else if (myServer.validateShareMsg(currentMsg, this))
                   {
                     myServer.postOutput(myConnection.getSocket().getInetAddress() +
                                         " sent a valid share."); 
                      haveShare = true; 
                      myX = currentMsg.getX();
                      myY = currentMsg.getY();
                      myPrime = currentMsg.getPrime();
                      myServer.checkComplete();
                   }
                   else
                   {
                       myServer.postOutput(myConnection.getSocket().getInetAddress() +
                                           " sent in an invalid share.");
                       myServer.postOutput("X: " + currentMsg.getX());
                       myServer.postOutput("Y: " + currentMsg.getY());
                       myServer.postOutput("Prime: " + currentMsg.getPrime());
                       myConnection.sendObject(new ShamirMsg("invalidShare"));
                       break;
                   }    
               }
               else
               {
                   System.out.println("I got somewhere I shouldn't");
               }
               currentMsg = (ShamirMsg) myConnection.readObject();
               currentType = currentMsg.getType();
            }
            myServer.postOutput("Connection to " +
                                myConnection.getSocket().getInetAddress()+
                                " has been terminated.");
        }
        
        // handle any IO exceptions including EOFExceptions
        catch(IOException ioException)
        {
            ioException.printStackTrace();
        }

        // handle ClassNotFound Exception
        catch (ClassNotFoundException cnf)
        {
            myServer.postOutput("Connection to " +
                           myConnection.getSocket().getInetAddress()+
                           " has submitted unknown object and has been closed.");            
        }
        
        // no matter what happens, close the connection I am listening to 
        finally
        {
            myConnection.close();
            myServer.removeClient(this);
        }
    } 
}