/************************************************
* 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: December 11, 2002
*
***********************************************/

package locasto.pcxses.ac;

import locasto.pcxses.protocol.*;

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


/**
 * The MorphSender.
 *
 *
 *
 *
 *
 *
 * @author locasto@cs.columbia.edu
*/
public class MorphSender implements Runnable{


	private AreaController parent;
	private RepositoryManager repositoryManager;
	private RepositoryWorker repositoryWorker;

	private DeviceStub device;
	private DeviceStub newDevice;


	public MorphSender(){
	}


	public void configure( AreaController parent, DeviceStub device, DeviceStub newDevice ){
		this.parent = parent;
		this.repositoryManager = parent.getRepositoryManager();
		this.device = device;
		this.newDevice = newDevice;

	}

	//----------- interface Runnable

	public void run(){

		if( this.newDevice == null ){
			parent.log( "MorphSender received a null target device and could not begin the transfer." );
			return;
		}

		Socket s=null;
		ObjectOutputStream oout=null;
		ObjectInputStream oin = null;
		//DataOutputStream dout = null;
		Bottle bottle = new Bottle( Bottle.MORPH_MESSAGE );
		Bottle stateBottle = new Bottle( Bottle.MORPH_MESSAGE );
		Bottle response = null;

		try{

			s = new Socket( InetAddress.getByName( newDevice.getClientIPAddress() ), parent.getPort() );
			oout =
				new ObjectOutputStream(
						new BufferedOutputStream(
							s.getOutputStream()
						)
				);
			oout.flush();

			oin = new ObjectInputStream( new BufferedInputStream( s.getInputStream() ) );

			//dout = new DataOutputStream( new BufferedOutputStream( s.getOutputStream() ) );

			String [] classnames = device.getClassNames();
			String clientName = newDevice.getClientName();
			String morphRequestID = parent.getName()+"."+(new java.util.Date().getTime())+"."+clientName;
			byte [] ctrlsigs = new byte[0];
			int [] classBytes = new int[ classnames.length ];

			for( int i=0;i<classBytes.length;i++ ){
				classBytes[i] = repositoryManager.getClassBytes( classnames[i] );
			}

			bottle.setMorphRequestID( morphRequestID );
			bottle.setCtrlsigs( ctrlsigs );
			bottle.setClientName( clientName );
			//bottle.setState( device.getState() );
			bottle.setClassnames( classnames );
			bottle.setClassbytes( classBytes );
			bottle.setMainIndexes( device.getMainIndexes() );

			//repositoryWorker = new RepositoryWorker( repositoryManager, classnames, dout );
			repositoryWorker = new RepositoryWorker( repositoryManager, classnames );

			bottle.setNewclasses( repositoryWorker.retrieve() );

			parent.log( "MorphSender finished creating the initial MORPH_MESSAGE." );

			//need to write all the classes, then send over the specific state object,
			//b/c the specific state object is not yet present in the virtual machine
			//of the reciever

			oout.writeObject( bottle );
			oout.flush();

			parent.log( "MorphSender wrote the initial MORPH_MESSAGE." );


			//get response indicating ok loading
			response = (Bottle)oin.readObject();
			parent.log( "MorphSender got the first response MORPH_MESSAGE." );

			switch( response.getMessageType() ){

				case Bottle.MORPH_MESSAGE :

					byte [] sigs = response.getCtrlsigs();
					if( sigs==null )
						break;
					for( int j=0;j<sigs.length;j++ ){
						if( sigs[j]==Bottle.CLIENT_IGNORED_MORPH ){
							parent.log( "MorphSender: The client ignored the morph message, possibly b/c it was malformed." );
							throw new Exception( "Client ignored the first MORPH" );
					}else if( sigs[j]==Bottle.CLIENT_ACK_MORPH ){
						parent.log( "MorphSender: The client obeyed the morph message." );
						break;
					}
				}

				break;
				default:
					parent.log( "MorphSender got non-legal message type ["+response.getMessageType()+"] back as a reply." );
			}


			stateBottle.setMorphRequestID( morphRequestID );
			stateBottle.setClientName( clientName );
			stateBottle.setState( device.getState() );

			parent.log( "MorphSender finished creating the state MORPH_MESSAGE." );

			oout.writeObject( stateBottle );
			oout.flush();

			parent.log( "MorphSender wrote the state MORPH_MESSAGE." );

			//Thread kicker = new Thread( ((Runnable)repositoryWorker) );
			//kicker.start();
			//try{
			//	kicker.join();
			//}catch( Exception ie ){
			//	parent.log( "Problem waiting for the repositoryWorker to finish: "+ie.getMessage() );
			//}

			response = (Bottle)oin.readObject();
			parent.log( "MorphSender got the second response MORPH_MESSAGE." );

			switch( response.getMessageType() ){

				case Bottle.MORPH_MESSAGE :

					byte [] sigs = response.getCtrlsigs();
					if( sigs==null )
						break;
					for( int j=0;j<sigs.length;j++ ){
						if( sigs[j]==Bottle.CLIENT_IGNORED_MORPH ){
							parent.log( "MorphSender: The client ignored the morph message, possibly b/c it was malformed." );
							break;
						}else if( sigs[j]==Bottle.CLIENT_ACK_STATE_MORPH ){
							parent.log( "MorphSender: The client obeyed the morph message." );
							break;
						}
					}

					break;

				default:
					parent.log( "MorphSender got non-legal message type ["+response.getMessageType()+"] back as a reply." );
			}

			//are we not closing the socket properly? either here or morphReciever
			//get morphreceiver to be more verbose

		}catch( Exception e ){
			parent.log( "MorphSender had a problem sending the MORPH_MESSAGE: "+e.getMessage() );
			e.printStackTrace();
		}finally{
			try{
				if( oout!=null ){
					oout.flush();
					oout.close();
				}
				if( oin!=null )
					oin.close();
				if( s!=null )
					s.close();
			}catch( Exception ex ){
				parent.log( "MorphSender had a problem closing the streams. "+ex.getMessage() );
			}
		}
	}


}
