/////////////////////////////////////////////////////////////////////////
//
// © University of Southampton IT Innovation Centre, 2004
//
// Copyright in this library belongs to the IT Innovation Centre of
// 2 Venture Road, Chilworth Science Park, Southampton, SO16 7NP, UK.
//
// This software may not be used, sold, licensed, transferred, copied
// or reproduced in whole or in part in any manner or form or in or
// on any media by any person other than in accordance with the terms
// of the Licence Agreement supplied with the software, or otherwise
// without the prior written consent of the copyright owners.
//
// This software is distributed WITHOUT ANY WARRANTY, without even the
// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE, except where stated in the Licence Agreement supplied with
// the software.
//
//      Created by:             Stuart E. Middleton
//      Created date:           2004/04/30
//      Created for project:    GEMSS
//
/////////////////////////////////////////////////////////////////////////
//
//      Dependencies: None
//
/////////////////////////////////////////////////////////////////////////
//
//      Last commit info:       $Author: $
//                              $Date: $
//                              $Revision: $
//
/////////////////////////////////////////////////////////////////////////

package uk.ac.soton.itinnovation.gemss.infrastructure;

import java.util.*;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
import java.io.*;
import java.awt.*;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JFileChooser;

import eu.gemss.GEMSS;
import eu.gemss.GridException;
import eu.gemss.components.transport.NetworkException;
import eu.gemss.components.ComponentManager;
import eu.gemss.signals.*;
import eu.gemss.signals.negotiation.*;
import eu.gemss.signals.security.*;
import eu.gemss.components.proxies.*;

import uk.ac.soton.itinnovation.gemss.*;
import uk.ac.soton.itinnovation.gemss.utils.signalhandlers.*;
import uk.ac.soton.itinnovation.gemss.utils.configuration.ConfigurationException;

// adv security signal and handler
import de.nece.ccrl.advsec.signals.*;
import eu.gemss.signals.advsecurity.*;

/*
* Sample client to demonstrate the phase two infrastructure capabilities. A simple app using the
* GEMSSNegotiator class, used to discover new services and negotiate access to them. In phase two
* there is no real negotiation, just service discovery and selection.
*/
public class SampleUseEndPoint {

	// constants
      private static final String mstaticServiceName = "serviceName";
	private static final String mstaticInputFilename = "inputFilename";
	private static final String mstaticRequestDescFilename = "requestDescFilename";
	private static final String mstaticOutputFilename = "outputFilename";
	private static final String mstaticProxyConfigFile = "ProxyConfigFile";
	private static final String mstaticQoSConfigFile = "QoSConfigFile";
	private static final String mstaticFastRetry = "FastRetry";
	private static final String mstaticSlowRetry = "SlowRetry";
	private static final String mstaticTimeoutNegotiation = "TimeoutNegotiation";
	private static final String mstaticTimeoutOperation = "TimeoutOperation";
	private static final String mstaticTimeoutJobRun = "TimeoutJobRun";

	// member variables
	private GEMSSNegotiator mnegotiator;
    private String mstrServiceName;
	private String mstrInputFilename;
	private String mstrRequestDescFilename;
	private String mstrOutputFilename;
	private String mstrProxyConfigFile;
	private String mstrQoSConfigFile;
	private int mnFastRetry;
	private int mnSlowRetry;
	private int mnTimeoutNegotiation;
	private int mnTimeoutJobRun;
	private int mnTimeoutOperation;

	/*
	* default constructor
	*/
	public SampleUseEndPoint()
	{
		mnegotiator = null;
            mstrServiceName = null;
		mstrInputFilename = null;
		mstrRequestDescFilename = null;
		mstrOutputFilename = null;
		mstrProxyConfigFile = null;
		mnFastRetry = 0;
		mnTimeoutNegotiation = -1;
		mnSlowRetry = 0;
		mnTimeoutJobRun = -1;
		mnTimeoutOperation = -1;
	}

	/*
	* Check if a flag has been set in the negotiator flag set
	* @param strFlag The flag to check
	* @return true if the flag is now set
	*/
	public boolean checkFlag( String strFlag )
	{
		boolean bOk;
		Vector vectorFlags;
		long ntimeStart, ntimeNow;
		int i;
		String strText;

		try {
			// check proxy is ok, if not flag cannot be set
			if( mnegotiator == null ) {
				return false;
			}

			// get the negotiator flags
			vectorFlags = mnegotiator.getNegotiatorFlags();

			// check for the flag
			if( vectorFlags.contains( strFlag ) ) return true;

			// nope
			return false;
		}
		catch( Exception ex ) {
			// report failure details
			System.out.println("Exception checking for flag "+strFlag+" = "+ex.getMessage());
			ex.printStackTrace( System.out );
			return false;
		}
	}

	/*
	* Run method to start the sample negotiator. The config file holds most of the
	* parameters such as the service endpoint information and input / output
	* filenames.
	* @param strConfigFilename configuration filename
	*/
	public void run( String strConfigFilename )
	{
		uk.ac.soton.itinnovation.gemss.utils.configuration.Configuration config;
		eu.gemss.components.ComponentManager componentManager;
		SignalHandler handler;
		GEMSSNegotiatorRequest negotiatorRequest;
		GridException gridException;
		String strStatus;
		Vector vectorFlags;
		boolean bOk;
		long nTimeNow, nTimeStart;
		int i;

		try{
			// This sample negotiator uses a configuration file for obtaining certain values needed to run
			config = new uk.ac.soton.itinnovation.gemss.utils.configuration.Configuration( strConfigFilename );

			// get the config parameters
            // Note - no discovery is done since we just load the config file with the
            // service settings
            mstrServiceName = config.getConfigurationValue( mstaticServiceName );
			mstrProxyConfigFile = config.getConfigurationValue( mstaticProxyConfigFile );
			mstrQoSConfigFile = config.getConfigurationValue( mstaticQoSConfigFile );
			mstrInputFilename = config.getConfigurationValue( mstaticInputFilename );
			mstrRequestDescFilename = config.getConfigurationValue( mstaticRequestDescFilename );
			mstrOutputFilename = config.getConfigurationValue( mstaticOutputFilename );
			mnFastRetry = Integer.valueOf( config.getConfigurationValue( mstaticFastRetry ) ).intValue();
			mnTimeoutNegotiation = Integer.valueOf( config.getConfigurationValue( mstaticTimeoutNegotiation ) ).intValue();
			mnSlowRetry = Integer.valueOf( config.getConfigurationValue( mstaticSlowRetry ) ).intValue();
			mnTimeoutJobRun = Integer.valueOf( config.getConfigurationValue( mstaticTimeoutJobRun ) ).intValue();
			mnTimeoutOperation = Integer.valueOf( config.getConfigurationValue( mstaticTimeoutOperation ) ).intValue();

			// print parameters so user knows what is going on
                  System.out.println("Service name = "+mstrServiceName);
			System.out.println("QoS config file = "+mstrQoSConfigFile);
			System.out.println("Proxy config file = "+mstrProxyConfigFile);
			System.out.println("Input archive file = "+mstrInputFilename);
			System.out.println("Request desc file = "+mstrRequestDescFilename);
			System.out.println("Output archive file  = "+mstrOutputFilename);
			System.out.println("Fast retry delay [negotiation] = "+mnFastRetry+" ms");
			System.out.println("Slow retry delay [job run] = "+mnSlowRetry+" ms");
			System.out.println("Timeout delay for negotiation = "+mnTimeoutNegotiation+" ms");
			System.out.println("Timeout delay for operation invocation = "+mnTimeoutOperation+" ms");
			System.out.println("Timeout delay for job runs = "+mnTimeoutJobRun+" ms");

			// make a negotiator
			// get the framework's instance of the GEMSS component manager.
			System.out.println("Starting the component manager");
			componentManager = GEMSS.getInstance();
			if( componentManager == null ) {
				System.out.println("Failed: Component manager object null");
				return; // end
			}

			// At this point it is a good idea to register any signal handlers you have implemented for dealing with
			// signals. Initially the only signal that is necessary to handle is 'eu.gemss.signals.CredentialRequest'.
			// This signal is used to obtain credential information. A sample signal handler will be registered here.
			System.out.println("Registering signal handlers");
			handler = new uk.ac.soton.itinnovation.gemss.utils.signalhandlers.CredentialRequestGUIHandler();
			componentManager.registerSignalHandler( handler,eu.gemss.signals.security.CredentialRequest.SIGNAL_TYPE );

			// Adv security handler
			handler = new de.nece.ccrl.advsec.signals.AdvSecSignonSigHandler();
			componentManager.registerSignalHandler( handler,AdvSecSignonSig.SIGNAL_TYPE );

			// the GEMSS negotiator object needs a description of the job - metadata about the job. This metadata will
			// be used to construct a server side performance model of the job we require to run. Scheduler reservations
			// will be based on this performance model. It is in the interest of the client to make the input data metadata
			// as accurate as possibloe since if the job take too much resources the scheduler will kill it, and the client
			// will still have to pay.


			// ask the component manager for a GEMSS negotiator instance. Each GEMSSNegotiator instance
			// will initially not be bound to a service, and will in fact ask the service discovery component
			// for the set of possible services. It will hold a GEMSSProxy instance for each potential service, internally,
			// and negotiate with them and select a single GEMSSProxy instance. This GEMSSProxy instance will be used to
			// bind the negotiator instance to a single service, and thus enable the job handling functions to be called.
			// You can create many negotiators to run many jobs simultaniously.
			// The GEMSSProxyRequest class holds the init data about the target service, without
			// this info you would need the GEMSSNegotiator.
			// in phase two no negotiation will happen, just service discovery and a simple service selection rule.
			System.out.println("Asking for a GEMSSNegotiator to run the job");
			negotiatorRequest = new GEMSSNegotiatorRequest( mstrRequestDescFilename, mstrServiceName, mstrQoSConfigFile, mstrProxyConfigFile, "eu.gemss.components.proxies.GEMSSNegotiator", null, null );
			mnegotiator = (eu.gemss.components.proxies.GEMSSNegotiator) componentManager.getInstance( negotiatorRequest );
			if( mnegotiator == null ) {
				System.out.println("Failed: GEMSSNegotiator object null");
				return; // end
			}

			// we now have to wait until the negotiator has finished it's negotiation, and selected a single GEMSSProxy
			// to bind to. This might take a little while, since multiple services will be requested to bid in a
			// simple aution. The winning bidder will be asked to reserve space on thier system for our job.
			System.out.println("Waiting for negotiation to finish");
			bOk = false;
			nTimeStart = System.currentTimeMillis();
			while( !bOk ) {
				// check the negotiator flags
				if( checkFlag( GEMSSNegotiator.mstaticNegotiatorHaltedFlag ) ) {
					// an error has occured during negotiation
					gridException = mnegotiator.getNegotiatorException();
					if( gridException != null ) {
						System.out.println("Failed: Negotiation failed, "+gridException.getUserMessage());
					}
					else {
						System.out.println("Failed: Negotiation failed, check logs since no GridException was returned");
					}
					return; // end
				}
				else if( checkFlag( GEMSSNegotiationProxy.mstaticNegCompleteFlag ) &&
					checkFlag( GEMSSNegotiationProxy.mstaticJobPrepFlag ) ) {
					// both the negotiation AND job preperation are complete, so now is the time to move on
					// and do the job handling thing.
					bOk = true;
				}
				else {
					// check for timeout
					nTimeNow = System.currentTimeMillis();
					if( mnTimeoutNegotiation != -1 && nTimeNow - nTimeStart > mnTimeoutNegotiation ) {
						System.out.println("Timeout waiting for negotiation to finish");
						return; // end
					}

					// wait a short time
					try { Thread.sleep( mnFastRetry ); } catch( InterruptedException ie ) { ; }
				}
			}


// upload the input archive file to the service
			System.out.println("Input data uploading");
			bOk = false;
			nTimeStart = System.currentTimeMillis();
			while( !bOk ) {
				// check for timeout
				nTimeNow = System.currentTimeMillis();
				if( mnTimeoutOperation != -1 && nTimeNow - nTimeStart > mnTimeoutOperation ) {
					System.out.println("Failed: Timeout waiting for the uploadData operation to successfully be invoked");
					return; // end
				}

				// try to upload data
				try {
					mnegotiator.uploadData( mstrInputFilename );
					bOk = true;
				}
				catch( GridException gridex ) {
					// abort if anything other than a network problem
					// otherwise keep trying again and again until timeout
					if( !(gridex instanceof NetworkException) ) throw gridex;
					System.out.println("Retry: Retrying after a network exception");
				}
			} // loop until successful

// start the job
			System.out.println("Starting job");
			bOk = false;
			nTimeStart = System.currentTimeMillis();
			while( !bOk ) {
				// check for timeout
				nTimeNow = System.currentTimeMillis();
				if( mnTimeoutOperation != -1 && nTimeNow - nTimeStart > mnTimeoutOperation ) {
					System.out.println("Failed: Timeout waiting for the start operation to successfully be invoked");
					return; // end
				}

				// try to upload data
				try {
					mnegotiator.start();
					bOk = true;
				}
				catch( GridException gridex ) {
					// abort if anything other than a network problem
					// otherwise keep trying again and again until timeout
					if( !(gridex instanceof NetworkException) ) throw gridex;
					System.out.println("Retry: Retrying after a network exception");
				}
			} // loop until successful

// wait for the job to finish, calling getStatus regularly to get an idea on the
// progress so far
			bOk = false;
			nTimeStart = System.currentTimeMillis();
			do {
				System.out.println("Checking status");

				// get status (will also set the finished flag etc if appropriate)
				try {
					strStatus = mnegotiator.getStatus();
				}
				catch( GridException gridex ) {
					// abort if anything other than a network problem
					// otherwise keep trying again and again until timeout
					if( !(gridex instanceof NetworkException) ) throw gridex;
					System.out.println("Retry: Retrying after a network exception");
					strStatus = "unavailable due to network connection problems";
				}

				// get the negotiator flags
				vectorFlags = mnegotiator.getNegotiatorFlags();

				// has the job finished, failed or been killed?
				if( vectorFlags.contains( GEMSSJobHandlingProxy.mstaticJobFinishedFlag ) ) {
					System.out.println("Job has finished");
					bOk = true;
				}
				else if( vectorFlags.contains( GEMSSJobHandlingProxy.mstaticJobFailedFlag ) ) {
					System.out.println("Failed: Job has failed");
					return; // end
				}
				else if( vectorFlags.contains( GEMSSJobHandlingProxy.mstaticJobKilledFlag ) ) {
					System.out.println("Failed: Job has been killed");
					return; // end
				}
				else {
					// The job is on-going, so report the status
					System.out.println("Status report:\n\n"+strStatus);

					// check for timeout
					nTimeNow = System.currentTimeMillis();
					if( mnTimeoutJobRun != -1 && nTimeNow - nTimeStart > mnTimeoutJobRun ) {
						System.out.println("Failed: Timeout waiting for job to finish");
						return; // end
					}

					// wait a short time
					try { Thread.sleep( mnSlowRetry ); } catch( InterruptedException ie ) { ; }
				}
			} while( !bOk );

// download the results
			System.out.println("Downloading data");
			bOk = false;
			nTimeStart = System.currentTimeMillis();
			while( !bOk ) {
				// check for timeout
				nTimeNow = System.currentTimeMillis();
				if( mnTimeoutOperation != -1 && nTimeNow - nTimeStart > mnTimeoutOperation ) {
					System.out.println("Failed: Timeout waiting for the downloadData operation to successfully be invoked");
					return; // end
				}

				// try to upload data
				try {
					mnegotiator.downloadData( "",mstrOutputFilename );
					bOk = true;
				}
				catch( GridException gridex ) {
					// abort if anything other than a network problem
					// otherwise keep trying again and again until timeout
					if( !(gridex instanceof NetworkException) ) throw gridex;
					System.out.println("Retry: Retrying after a network exception");
				}
			} // loop until successful

// acknowledge a successful download
			System.out.println("Acknowledging results");
			bOk = false;
			nTimeStart = System.currentTimeMillis();
			while( !bOk ) {
				// check for timeout
				nTimeNow = System.currentTimeMillis();
				if( mnTimeoutOperation != -1 && nTimeNow - nTimeStart > mnTimeoutOperation ) {
					System.out.println("Failed: Timeout waiting for the acknowledgeResults operation to successfully be invoked");
					return; // end
				}

				// try to upload data
				try {
					mnegotiator.acknowledgeResults();
					bOk = true;
				}
				catch( GridException gridex ) {
					// abort if anything other than a network problem
					// otherwise keep trying again and again until timeout
					if( !(gridex instanceof NetworkException) ) throw gridex;
					System.out.println("Retry: Retrying after a network exception");
				}
			} // loop until successful

// all done
			System.out.println("Job processing complete, data saved to "+mstrOutputFilename);
			return; // success :)
		}
		catch( GridException gridex ) {
			System.out.println("Failed: Grid job failure : "+gridex.getUserMessage());
			return; // end
		}
		catch( Exception ex ) {
			System.out.println("Failed: Unrecognised failure reason: ");
			ex.printStackTrace();
			return; // end
		}
	} // end of run

	/*
	* set the shutdown hook
	*/
	public void setShutdownHook() {
		//add shutdown hook
		ShutdownThread shutdownHook = new ShutdownThread();
		Runtime.getRuntime().addShutdownHook(shutdownHook);
	} // end of setShutdownHook

	/*
	* shutdown class to respond to a kill request
	*/
	class ShutdownThread extends Thread {
		/*
		* constructor
		*/
		public ShutdownThread() {
			super();
		}

		/*
		* shutdown the component manager
		*/
		public void run() {
			try{
				System.out.println("[Shutdown thread] Shutting down");
				if( mnegotiator != null ) GEMSS.getInstance().terminate( mnegotiator );
				System.out.println("[Shutdown thread] Shutdown complete");
			}
			catch(Exception ex) {
				System.out.println("[Shutdown thread] Shutdown incomplete : "+ex.getMessage());
				ex.printStackTrace();
			}
		}
	} // end of ShutdownThread

	/*
	* Main method to start the sample negotiator application. Call this app with no
	* args to get the argument list help text.
	* @param args argument string
	*/
	public static void main(String[] args) {
		SampleUseEndPoint gemssNegotiator;

		try{
			// read in the configuration file name
			if( args.length!=2 || !args[0].equals("-configFile") ) {
				System.out.println("Command line incorrect, format should be: 'java uk.ac.soton.itinnovation.gemss.infrastructure.SampleNegotiatorPhaseTwo -configFile <config-file-path>'");
				System.exit(1);
			}

			// create a client instance
			gemssNegotiator = new SampleUseEndPoint();
			if( gemssNegotiator == null ) {
				System.out.println("Unrecognised failure, could not create negotiator");
				System.exit(1);
			}
			else {
				//set shutdown hook
				gemssNegotiator.setShutdownHook();
			}

			// run the sample negotiator
			gemssNegotiator.run( args[1] );

			// do an exit for force close AWT
			System.exit(0);
		}
		catch( Throwable ex ) {
			System.out.println("Unrecognised failure, reason:");
			ex.printStackTrace();
			System.exit(1);
		}
	} // end of main
} // end of SampleNegotiatorPhaseTwo