/* ***************************************************************************+
 * ITX package (cnrg.itx) for telephony application programming.              *
 * Copyright (c) 1999  Cornell University, Ithaca NY.                         *
 * A copy of the license is distributed with this package.  Look in the docs  *
 * directory, filename GPL.  Contact information: bergmark@cs.cornell.edu     *
 ******************************************************************************/

package cnrg.itx.signal;

import cnrg.itx.signal.SignalEvent.*;
import cnrg.itx.datax.*;
import cnrg.itx.datax.devices.*;
import cnrg.itx.ds.*;

import java.net.*;
import java.io.*;

/** This class represents a thread that handles each request that arrives at the DesktopSignalingServer.
 *  The thread reads and decodes the SigPacket and calls the appropriate method in DesktopSignaling
 *  to complete handling the request.
 */
class SigPacketHandler extends Thread
{
	/** Communication Socket **/
	private Socket mSocket = null;
	/** Request Number of current request **/
	private int mRequestNumber = -1;
	/** InputStream to read the SigPacket **/
	private ObjectInputStream myIn = null;
	/** SigPacket received from peer **/
	private SigPacket mySP = null;
	/** DesktopSignaling handle **/
	private DesktopSignaling myDS = null;
	/** To run or not to run **/
	private boolean flag = true;
	/** Whether or not DesktopSignaling implements SIP **/
	private boolean useSIP = false;

	/** Default Constructor
	 * 
	 * @param sock the socket for communication
	 * @param rN the request number
	 * @param ds the DesktopSignaling handle
	 */
	public SigPacketHandler(Socket sock, int rN, DesktopSignaling ds)
	{
		mSocket = sock;
		mRequestNumber = rN;
		myDS = ds;
		
		System.out.println("New request #" + mRequestNumber + " on local port " + sock.getLocalPort() + 
						   " connected to port " + sock.getPort());
		try {
			myIn  = new ObjectInputStream(mSocket.getInputStream());			
		} 
		catch(IOException ioe) {			
			System.out.println("Socket created and then closed by peer.");
			flag = false;
		}
	}
	
	/** Constructor used to state if DesktopSignaling implements SIP
	 * 
	 * @param sock the socket for communication
	 * @param rN the request number
	 * @param ds the DesktopSignaling handle
	 * @param ifSIP true if DesktopSignaling implements SIP; false otherwise
	 */
	public SigPacketHandler(Socket sock, int rN, DesktopSignaling ds, boolean ifSIP)
	{
		this(sock,rN,ds);
		useSIP = ifSIP;
	}

	/**
	 * Method called when the thread is started.
	 * 
	 * @param   None.
	 * @return  void
	 */
	public void run(){
		if (flag){
			readPacket();
			if (flag){
				try{
					handlePacket();
				}
				catch(SigPacketHandlerException sphe){
					System.out.println("Error handling SigPacket, request ignored.");
					sphe.printStackTrace();
				}
			}
		}
		System.out.println("SigPacketHandler Thread exiting for request number: " + mRequestNumber);
	}
			
	/**
	 * Reads the SigPacket object from the ObjectInputStream.
	 * 
	 * @param   None.
	 * @return  void
	 */
	private void readPacket(){
		if (useSIP == true)
		{
			String sipPacket = null;
			try{
				sipPacket = (String)myIn.readUTF();
			}
			catch (IOException ioe){
				flag = false;
				ioe.printStackTrace();
			}
			if (flag == true)
				try
				{
					mySP = SIPInterface.makeSigPacket(sipPacket);
				}
				catch (SIPException se)
				{
					InvitePacket ip = new InvitePacket(myDS.myUID,myDS.myLoc,myDS.myDesc,SignalID.DIAL);
					
					ip.setPacketType(SignalID.RESULT);
					ip.setDestination(myDS.myUID);
					ip.reject(null);
					try
					{
						myDS.sendPacket(ip,mSocket,se.toString(),null);
					}
					catch (ConnectException ce)
					{
						ce.printStackTrace();
					}
					flag = false;
				}
		}
		else
		{
			try{
				mySP = (SigPacket) myIn.readObject();
			}
			catch (OptionalDataException ode){
				flag = false;
				ode.printStackTrace();
			}
			catch (ClassNotFoundException cnfe){
				flag = false;
				cnfe.printStackTrace();
			}
			catch (IOException ioe){
				flag = false;
				ioe.printStackTrace();
			}
		}
	}
		
	/**
	 * This method decodes a SignalPacket and calls the required method in DesktopSignaling.
	 * The thread passes the socket handle to DesktopSignaling for further communication.
	 * 
	 * @param None
	 * @return void
	 * @exception SigPacketHandlerException
	 * 
	 * @see cnrg.itx.signal.SigPacketHandlerException
	 */
	private void handlePacket() throws SigPacketHandlerException{
		if (mySP == null){
			throw new SigPacketHandlerException("Received a null object.  Dropping object...  Dropped\n");
		}

		if(mySP.isInvitePacket()){
			switch(mySP.getMethodID()){
				case SignalID.DIAL:
					InviteSignalEvent ise = new InviteSignalEvent((InvitePacket)mySP);
					ise.setDesktopSignaling(myDS);
					try{
						myDS.handleDialInvite(ise, mSocket);
					}
					catch (DesktopSignalingException dse){
						dse.printStackTrace();
					}
					catch (DataException de){
						de.printStackTrace();
					}
					break;
				case SignalID.HANGUP:
					HangupSignalEvent hse = new HangupSignalEvent((InvitePacket)mySP);
					hse.setDesktopSignaling(myDS);
					myDS.handleHangupInvite(hse, mSocket);
					break;
				case SignalID.SENDDTMF:
					DTMFSignalEvent dse = new DTMFSignalEvent((InvitePacket)mySP, (String) mySP.getCustomObject());
					dse.setDesktopSignaling(myDS);
					myDS.handleSendDTMFInvite(dse, mSocket);
					break;
				case SignalID.ALIVEQUERY:
					AliveSignalEvent ase = new AliveSignalEvent((InvitePacket)mySP);
					ase.setDesktopSignaling(myDS);
					myDS.handleAliveInvite(ase, mSocket);
					break;
			}
		}
		else
			throw new SigPacketHandlerException("Received an unknown SigPacket.  Dropping packet...\nDropped.\n");
	}
}