/* ***************************************************************************+
 * ITX package (cnrg.itx) for telephony application programming.              *
 * Copyright (c) 1999  Cornell University, Ithaca NY                          *
 *                                                                            *
 * This program is free software; you can redistribute it and/or modify       *
 * it under the terms of the GNU General Public Liense as published by        *
 * the Free Software Foundation, either version 2 of the License, or (at      * 
 * your option) any later version.                                            *
 *                                                                            *
 * The ITX package 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.                                                          * 
 *                                                                            *
 * A copy of the license is distributed with this package.  Look in the docs  *
 * directory, filename GPL.                                                   *
 *                                                                            * 
 * Contact information:                                                       *
 * Donna Bergmark                                                             *
 * 484 Rhodes Hall                                                            *
 * Cornell University                                                         *
 * Ithaca, NY 14853-3801                                                      *
 * bergmark@cs.cornell.edu                                                    *
 ******************************************************************************/
package client;

import shared.*;
import java.io.*;
import java.net.*;
import java.util.*;

/**
 * This class wraps around a TCP/IP connection to a SPOT server and allows
 * <code>SPOTMessage</code>s to be transfered between the client and 
 * server.
 * 
 * @version 1.0, 3/4/1999
 * @author Jason Howes
 */
public class ClientSession
{
	/**
	 * Session status, connection, and description
	 */
	private boolean mSessionActive;
	private Socket mSessionConnection;
	private String mServerName;
	private int mServerPort;

	/**
	 * Session input/output streams
	 */
	private ObjectInputStream mSessionInputStream;
	private ObjectOutputStream mSessionOutputStream;

	/**
	 * Exception messages
	 */
	private static final String SESSION_IN_PROGRESS				= "Session in progress";
	private static final String SESSION_NOT_IN_PROGRESS			= "Session not in progress";
	private static final String SESSION_INITIALIZATION_ERROR	= "Error initializing session";
	private static final String SESSION_TERMINATION_ERROR		= "Error terminating session";

	/**
	 * Class constructor.
	 */
	public ClientSession()
	{
		// Initialize the session variables
		mServerName = (String)null;
		mServerPort = -1;
		mSessionActive = false;
	}

	/**
	 * Opens a TCP/IP connection with a SPOT server.
	 * 
	 * @param serverName name or IP address of the server to which to connect
	 * @param serverPort port of the server
	 * @throws <code>ClientSessionException</code> on error 
	 */
	public void startSession(String serverName, int serverPort) throws ClientSessionException
	{
		// Make sure there isn't a session already in progress
		if (mSessionActive)
		{
			throw new ClientSessionException(SESSION_IN_PROGRESS);
		}

		// Start the session.
		try
		{
			mSessionConnection = new Socket(InetAddress.getByName(serverName), serverPort);
			mSessionOutputStream = new ObjectOutputStream(mSessionConnection.getOutputStream());
			mSessionInputStream = new ObjectInputStream(mSessionConnection.getInputStream());
		}
		catch (IOException e)
		{
			throw new ClientSessionException(SESSION_INITIALIZATION_ERROR);
		}
		
		// Notify the server
		sendMessage(SPOTMessage.START_SESSION, null);

		// We are now connected
		mServerName = new String(serverName);
		mServerPort = serverPort;
		mSessionActive = true;
	}
	
	/**
	 * Disconnects from a SPOT server.
	 * 
	 * @param alert the server of the session end
	 * @throws <code>ClientSessionException</code> on error 
	 */
	public void endSession(boolean notifyServer) throws ClientSessionException
	{
		SPOTMessage response;
		
		// Do we need to do anything?
		if (!mSessionActive)
		{
			throw new ClientSessionException(SESSION_NOT_IN_PROGRESS);
		}

		if (notifyServer)
		{
			// Close the server socket and let the server know that we're disconnecting
			try
			{
				sendMessage(SPOTMessage.END_SESSION, null);
			}
			catch (ClientSessionException e)
			{
			}
		}
		
		try
		{
			mSessionInputStream.close();
			mSessionOutputStream.close();
			mSessionConnection.close();
		}
		catch(IOException e)
		{
			throw new ClientSessionException(SESSION_TERMINATION_ERROR);
		}

		// Reset all session variables
		mServerName = (String)null;
		mServerPort = -1;
		mSessionActive = false;
		mSessionConnection = (Socket)null;
		mSessionInputStream = (ObjectInputStream)null;
		mSessionOutputStream = (ObjectOutputStream)null;
	}	

	/**
	 * Sends a <code>SPOTMessage</code> to the SPOT server.
	 * 
	 * @param type type of the SPOTMessage to send
	 * @param object object to attach to the SPOTMessage (MUST be serializable)
	 * @throws <code>ClientSessionException</code> on error
	 */
	public void sendMessage(byte type, Object object) throws ClientSessionException
	{
		SPOTMessage message = new SPOTMessage(type, object);
		try
		{
			mSessionOutputStream.writeObject(message);
		}
		catch(InvalidClassException e)
		{
			throw new ClientSessionException("Error writing object -> InvalidClassException");
		}
		catch(NotSerializableException e)
		{
			throw new ClientSessionException("Error writing object -> NotSerializableException");
		}
		catch(IOException e)
		{
			throw new ClientSessionException("Error writing object -> IOException");
		}
	}
	
	/**
	 * Receives a <code>SPOTMessage</code> from the SPOT server.
	 * 
	 * @return the received message
	 */
	public SPOTMessage receiveMessage() throws ClientSessionException
	{
		SPOTMessage message;
		try
		{
			message = (SPOTMessage)mSessionInputStream.readObject();
		}
		catch(ClassNotFoundException e)
		{
			throw new ClientSessionException("Error reading object -> ClassNotFoundException");
		}
		catch(InvalidClassException e)
		{
			throw new ClientSessionException("Error reading object -> InvalidClassException");
		}
		catch(StreamCorruptedException e)
		{
			throw new ClientSessionException("Error reading object -> StreamCorruptedException");
		}
		catch(OptionalDataException e)
		{
			throw new ClientSessionException("Error reading object -> OptionalDataException");
		}
		catch(IOException e)
		{
			throw new ClientSessionException("Error reading object -> IOException");
		}
		return message;
	}

	/**
	 * Gets the name of the server to which the client is connected.
	 * 
	 * @return server name
	 */
	String getServerName()
	{
		return new String(mServerName);
	}
	
	/**
	 * Gets the port of the server to which the client is connected.
	 * 
	 * @return server port
	 */	
	int getServerPort()
	{
		return mServerPort;
	}
	
	/**
	 * Gets the session state.
	 * 
	 * @return <code>true</code> if the session is active, <code>false</code> otherwise
	 */
	boolean sessionActive()
	{
		return mSessionActive;
	}
}

/**
 * A <code>ClientSessionException</code> is an exception thrown by a
 * <code>ClientSession</code>.
 */
class ClientSessionException extends Exception
{
	/**
	 * Class constructor.
	 * 
	 * @param msg description of exception
	 */
	public ClientSessionException(String msg)
	{
		super("<ClientSessionException> :: " + msg);
	}

	/**
	 * Class constructor.
	 */
	public ClientSessionException()
	{
		super("<ClientSessionException>");
	}
}