/************************************************
* Columbia University CS Department
* Systems and Security Group
*
* Open and Survivable Embedded Systems (OASES)
*
* Michael E. Locasto
*
* Protocol For Code Exchange
*  in Survivable Embedded Systems (PCXSES)
*
* This software is provided as is without any
* warranty or guarantee of merchantibility
* or fitness for a particular purpose. This
* software is released under the terms of the
* GNU General Public License (GPL).
*
* Last updated: October 31, 2002
*
***********************************************/

package locasto.pcxses.protocol;

import java.util.Vector;

/**
* This class will mostly contain the control info
* and state info. Actual transport of object code
* will be accomplished in a separate stream.
*
* The Bottle contains mostly headers and small
* data to set up any streams. Usually, the Bottle
* itself is enough to complete the information
* transfer. However, in the case of a Bottle of
* type MORPH_MESSAGE, the Bottle becomes a header
* for the streams that will follow.
* <p>
* The message type of each message determines how
*  it is handled and is available in the <code>
*  messageType</code> attribute.
* <p>
* The client GOOD_MORNING_MESSAGE MUST contain the
*  client <code>mode</code> and the client <code>
*  name</code>. It MAY additionally
*  contain values for the <code>helloInterval</code>
*  and the <code>deadInterval</code>. The server
*  will send back an ack GOOD_MORNING_MESSAGE
*  confirming or overriding these values. Subsequent
* messages MAY contain the <code>clientName</code> value
* and MUST NOT contain the <code>mode, helloInterval,</code>
* or <code>deadInterval</code>
* <p>
* The GOOD_BYE_MESSAGE (in either direction) MUST contain
* the client <code>name</code> and nothing else.
* <p>
* The HELLO_MESSAGE MUST be sent only in the
*  client-->server direction and MAY contain state
*  information. If it does not contain state information,
*  the server simply hangs on to the last known good state
*  (LKGS) information it was sent. It MUST contain the
*  <code>name</code>
*  of the client.
* <p>
* The STATE_MESSAGE requirements are the same as the
*  HELLO_MESSAGE requirements, except that a STATE_MESSAGE
*  MUST NOT be sent if the client is operating in JOINED_MODE.
* <p>
* The MORPH_MESSAGE is the most complicated message because
* it must inform the client about the incoming computation.
*
*
*
*
* @author locasto@cs.columbia.edu
*/
public class Bottle implements java.io.Serializable{

	//------------------------ MESSAGE TYPES

	/** Type of message for joining (and ack'ing the join request) the network. */
	public static final int GOOD_MORNING_MESSAGE = 100;

	/** Type of message for gracefully leaving (and ack'ing the request) the network. */
	public static final int GOOD_BYE_MESSAGE = 105;

	/** Type of message sent from client to AC to say &quot;Hey, I'm alive.&quot;*/
	public static final int HELLO_MESSAGE = 110;

	/** Optional message type for sending state information separate from the HELLO_MESSAGE.*/
	public static final int STATE_MESSAGE = 115;

	/** Used to set up the transfer of Object Definitions (OD) or .class files and ack back to the server. */
	public static final int MORPH_MESSAGE = 120;

	/** Messages of this type should be dropped on the floor. */
	public static final int DEAD_MESSAGE = -127;

	//------------------------ CONTROL SIGNALS

	/** The basic operation; load the classes in. */
	public static final byte LOAD_CLASSES = 0x0F;

	/** The client sends this back to the AC if the MORPH Bottle was malformed. */
	public static final byte CLIENT_IGNORED_MORPH = 0x01;

	public static final byte CLIENT_ACK_MORPH = 0x21;

	public static final byte CLIENT_ACK_STATE_MORPH = 0x32;


	//------------------------ variables


	/** The message type. See the constants for this class. */
	private int messageType=DEAD_MESSAGE;

	/** The control signals for use with a Bottle of type MORPH_MESSAGE.
	 * The number of control signals present in this array is
	 * arbitrary. Each control signal is a different byte in the array. There is
	 * no required order to the bytes. If not control signals are present, then the client will
	 * effectively ignore the MORPH_MESSAGE and reject any subsequent streams.
	 * See the constants for this class. The client will send back the control signals
	 * it recieved with the 'ack' MORPH_MESSAGE.
	 */
	private byte [] ctrlsigs = new byte[0];

	/** The mode the client wishes to operate in,
	 * either PCXClient.SPLIT_MODE or PCXClient.JOINED_MODE
	*/
	private int mode;

	/** Initially, a client set value, or 0 if the client doesn't care.
	 * If the client sends 0, the AC will send back a server default value. If
	 * <0 or >0, then the AC will send back confirmation by way of the same
	 * value. If the AC does not confirm correctly, send a GOODBYE message and
	 * quit the protocol.
	 */
	private int helloInterval=0;

	/** Initially, a client set value, or 0 if the client doesn't care.
	 * If the client sends 0, the AC will send back a server default value. If
	 * <0 or >0, then the AC will send back confirmation by way of the same
	 * value. If the AC does not confirm correctly, send a GOODBYE message and
	 * quit the protocol.
	 */
	private int deadInterval=0;

	/**
	 * The name of the client, as determined by the client. Used to partially
	 * identify all Bottles; it should be present in all Bottles of all types.
	 * The client and the AC MUST reject a Bottle without a clientName;
	*/
	private String clientName="";

	/** The array of fully qualified class names of the objects to be transported.
	 * This array includes the classname of the SUI as the last class.
	*/
	private String [] classnames = new String[0];

	/** The current state of the object */
	private State state = null;

	/** length should be classnames.length and
	 * each position specifies the number of bytes
	 * in that object file to be transferred.
	*/
	private int [] classbytes = new int[0];

	/** specify which classes in the Vector
	 * should be instantiated. Their constructor
	 * acts as a <code>main()</code> method.
	*/
	private int [] mainIndexes = new int[0];

	/** Let x = the number of milliseconds since Jan 1st, 1970.
	 * morphRequestID = AreaControllerName.x.clientName.
	 * This should uniquely id this morph request so the client
	 * can ack that it has received all classes and morphed as requested, and
	 * also discover classes as needed.
	*/
	private String morphRequestID="";


	/**
	 * A Vector of byte arrays representing the objects to be
	 *  imported to the target VM.
	 *
	 *
	*/
	private Vector newClasses = null;

	/**
	 *
	 * Default do-nothing constructor
	*/
	public Bottle(){
	}

	/**
	 * Create with the message type. Message type is checked against
	 * the legal set. If the message type is an illegal type, it is
	 * set to Bottle.DEAD_MESSAGE and MUST be dropped by the recipient.
	*/
	public Bottle( int m ){
		setMessageType( m );
	}

	//------------- general

	/**
	 * Set the message type. Message type is checked against
	 * the legal set. If the message type is an illegal type, it is
	 * set to Bottle.DEAD_MESSAGE and MUST be dropped by the recipient.
	*/
	public void setMessageType( int m ){
		switch( m )
		{
			case Bottle.GOOD_MORNING_MESSAGE: this.messageType = m; break;
			case Bottle.GOOD_BYE_MESSAGE: this.messageType = m; break;
			case Bottle.HELLO_MESSAGE: this.messageType = m; break;
			case Bottle.MORPH_MESSAGE: this.messageType = m; break;
			case Bottle.STATE_MESSAGE: this.messageType = m; break;
			default: this.messageType = Bottle.DEAD_MESSAGE;
		}
	}

	/** Return the type of this message. */
	public int getMessageType(){
		return messageType;
	}

	//------------- GOOD MORNING negotiation

	/**
	 * Set the mode the client and AC are communicating in. The
	 * input is checked for consistancy with the constants in
	 * PCXClient and defaults to PCXClient.JOINED_MODE if an
	 * illegal value is presented.
	 *
	*/
	public void setMode( int m ){
		switch( m )
		{
			case PCXClient.JOINED_MODE: mode = m; break;
			case PCXClient.SPLIT_MODE: mode = m; break;
			default: mode = PCXClient.JOINED_MODE;
		}
	}

	/**
	 * Returns the mode the client and AC are communicating in.
	 *
	 * @see <a href="PCXClient.html">PCXClient</a>
	*/
	public int getMode(){
		return mode;
	}

	/**
	 * Set the helloInterval (the number of milliseconds before the AC
	 * must here another HELLO_MESSAGE to consider the client alive).
	*/
	public void setHelloInterval( int hi ){
		helloInterval = hi;
	}

	/**
	 * Return the helloInterval value in milliseconds.
	*/
	public int getHelloInterval(){
		return helloInterval;
	}

	/**
	 * Set the deadInterval (the number of cycles of helloIntervals the AC
	 * will wait [give the client a second chance to say hello] before it
	 * considers the device failed and ships out code).
	 * <p>
	 * So if the helloInterval is 6000 milliseconds (6 seconds) and the
	 * deadInterval is 4, the AC expects to receive a HELLO_MESSAGE every
	 * 6 seconds from the client. If 6 seconds have passed without a HELLO_MESSAGE,
	 * (a STATE_MESSAGE DOES NOT serve as a HELLO_MESSAGE) then the AC
	 * will wait
	 * <pre>
	 *   (helloInterval*deadInterval, 6000*4 or 24000 milliseconds) 24 seconds
	 * </pre>
	 * before shipping out code. So, the client effectively has
	 * <pre>
	 *   (deadInterval+1)*helloInterval
	 * </pre>
	 * before it is effectively dead.
	*/
	public void setDeadInterval( int di ){
		deadInterval = di;
	}

	/**
	 * Return the deadInterval.
	*/
	public int getDeadInterval(){
		return deadInterval;
	}

	/**
	 * Set the client name. This should be unique. Hopefully, the server
	 * can combine the name with the IP address and get an even more unique
	 * id.
	*/
	public void setClientName( String cn ){
		clientName = cn;
	}

	/**
	 * Return the client name.
	*/
	public String getClientName(){
		return clientName;
	}

	//------------- HELLO | STATE MESSAGE

	/** HELLO and STATE use the State state to send State info*/


	//------------- MORPH MESSAGE

	/** Set the control signals. */
	public void setCtrlsigs( byte [] cs ){
		ctrlsigs = cs;
	}

	/** Retrieve the control signals */
	public byte [] getCtrlsigs(){
		return ctrlsigs;
	}

	/** Identify the class names to be transported. */
	public void setClassnames( String [] s ){
		classnames = s;
	}

	/** Retrieve the class names */
	public String [] getClassnames(){
		return classnames;
	}

	public void setState( State s ){
		state = s;
	}

	public State getState(){
		return ((State)state);
	}

	/** Set the bytes per class. This data array MUST be the
	 * same length as the classnames array, and indicates the
	 * total number of bytes in each class file to be received.
	*/
	public void setClassbytes( int [] cb ){
		classbytes = cb;
	}

	/**
	 * Retrieve the classbytes information.
	*/
	public int [] getClassbytes(){
		return classbytes;
	}

	/**
	 * Indicate the main classes (which classes can act as startup threads)
	*/
	public void setMainIndexes( int [] mi ){
		mainIndexes = mi;
	}

	/**
	 * Retrieve the main classes. The array indicates a number of
	 * positions in the classnames array. There MUST be at least
	 * 1 class indicated in this array.
	*/
	public int [] getMainIndexes(){
		return mainIndexes;
	}

	/**
	 * Set the morphSessionID: ACName.mrid.clientName
	 * Does not check that .clientName matches "."+this.clientName
	*/
	public void setMorphRequestID( String rid ){
		morphRequestID = rid;
	}

	/**
	 * Retrieve the morphRequestID.
	*/
	public String getMorphRequestID(){
		return morphRequestID;
	}


	public Vector getNewclasses(){
		return newClasses;
	}

	public void setNewclasses( Vector v ){
		newClasses = v;
	}

}