package condor.gahp.gt3;

import condor.gahp.*;
import condor.gahp.gsi.GSIUtils;

import org.globus.ogsa.impl.base.gram.client.*;
import org.globus.ogsa.impl.security.authentication.Constants;
import org.globus.ogsa.impl.security.authorization.Authorization;
import org.globus.ogsa.impl.security.authorization.SelfAuthorization;
import org.globus.ogsa.base.gram.types.JobStateType;

import org.globus.ogsa.base.gram.service.ManagedJobServiceGridLocator;
import org.globus.ogsa.impl.security.authorization.SelfAuthorization;
import org.gridforum.ogsi.GridService;
import org.globus.ogsa.impl.base.gram.client.GramRsl2XmlConverter;
import org.globus.rsl.ParseException;
import org.gridforum.ogsi.GridService;
import org.gridforum.ogsi.OGSIServiceLocator;
import org.globus.ogsa.handlers.GrimProxyPolicyHandler;
import org.globus.ogsa.impl.security.authentication.Constants;
import org.globus.ogsa.utils.AnyHelper;
import org.gridforum.ogsi.ExtensibilityType;
import org.globus.ogsa.types.properties.PropertiesDetailType;
import org.gridforum.ogsi.EntryType;
import org.globus.ogsa.base.gram.service.ManagedJobServiceLocator;
import org.gridforum.ogsi.EntryContentType;
import org.gridforum.ogsi.ServiceDataValuesType;
import org.gridforum.ogsi.Factory;
import org.globus.ogsa.utils.QueryHelper;
import org.globus.ogsa.impl.security.authorization.Authorization;
import org.globus.ogsa.ServiceData;
import org.gridforum.ogsi.TerminationTimeType;
import org.gridforum.ogsi.ExtendedDateTimeType;
import org.globus.ogsa.base.gram.ManagedJobPortType;
import org.gridforum.ogsi.LocatorType;
import org.globus.ogsa.utils.GridServiceFactory;
import org.globus.ogsa.impl.base.gram.utils.rsl.RslParser;
import org.globus.ogsa.impl.base.gram.utils.rsl.RslParseException;
import org.globus.ogsa.impl.base.gram.utils.rsl.RslParserFactory;
import org.globus.ogsa.base.gram.types.JobStatusType;
import org.globus.axis.gsi.GSIConstants;
import org.globus.ogsa.base.gram.types.JobStateType;
import org.globus.ogsa.impl.base.gram.client.*;
import org.globus.ogsa.NotificationSinkCallback;
import org.globus.ogsa.client.managers.NotificationSinkManager;
import org.globus.ogsa.impl.core.service.ServicePropertiesImpl;
import org.globus.ogsa.ServicePropertiesException;
import org.w3c.dom.Element;
import javax.xml.namespace.QName;
import org.ietf.jgss.GSSCredential;
import org.ietf.jgss.GSSException;
import org.gridforum.jgss.ExtendedGSSManager;
import org.gridforum.ogsi.HandleType;
import javax.xml.rpc.Stub;
import java.net.URL;
import java.io.StringReader;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;

public  class Gt3GramJobCreateHandler implements CommandHandler {
    private GahpInterface gahp;
    
    public void setGahp (GahpInterface g) {
        gahp = g;
    }

    public CommandHandlerResponse handleCommand (String[] cmd) {
        Integer reqId;
        String contactString = null;
        String callbackContact = null;
        boolean fullDelegation = false;
        String RSL = null;

        try {
            // cmd[0] = GRAM_JOB_REQUEST
            reqId = new Integer(cmd[1]);
            contactString = cmd[2];
            callbackContact = cmd[3];
            //fullDelegation = new Integer(cmd[4]).intValue() == 1;
            RSL = cmd[4];
        }
        catch (Exception e) {
            e.printStackTrace(System.err);
            return CommandHandlerResponse.SYNTAX_ERROR;
        }

        // Check that callbackcontact is valid
        CallbackSink callbackSink = CallbackSink.getCallbackSink(gahp, callbackContact);
        if (callbackSink == null) {
            // TODO: is this right? What if GAHP was restarted? maybe we would create a new one?
            System.err.println ("Unable to find notification sink manager "+callbackContact);
            return CommandHandlerResponse.SYNTAX_ERROR;
        }

        MyGramJob toRun = new MyGramJob (reqId, 
                                         RSL, 
                                         contactString, 
                                         callbackSink, 
                                         //fullDelegation,
                                         gahp);

        return new CommandHandlerResponse (CommandHandlerResponse.SUCCESS,
                                           toRun);
    }

    class MyGramJob implements Runnable {
            
        //public final GramJob job;
        public final String factoryAddr;
        public final String jobRSL;
        //public final boolean autoDestruct;
        //public final condor.gt3.gahp.UniqueID id;
        public final Integer requestID;
        private String callbackContact;
        private CallbackSink callbackSink;
        private GahpInterface gahp;
        private boolean fullDelegation;
        private String jobHandle = null;

        public MyGramJob(Integer requestID, 
                         String rsl, 
                         String factoryURL, 
                         CallbackSink callbackSink,
                         //boolean fullDelegation,
                         GahpInterface gahp) {
            this.factoryAddr = factoryURL;
            this.jobRSL = rsl;
            this.requestID = requestID;
            this.gahp = gahp;
            this.fullDelegation = true;
            this.callbackSink = callbackSink;
        }
            
        public void run () {
            try {
                jobHandle = createJob ();
            } catch (Exception e) {
                System.err.println ("ERROR Creating Job" + e.getMessage());
                e.printStackTrace(System.err);
                // TODO: We gota be more specific than that, yo...
                String errorString = e.toString();
                if (errorString == null) errorString = "unknown";
                gahp.addResult (
                        requestID.intValue(),
                        new String[] { "1", "NULL", errorString}
                        ); 
                return;
            }

            // Announce the result
            gahp.addResult (
                            requestID.intValue(),
                            new String[] { "0", this.jobHandle, "NULL"}
                            );

        } // run

        private String createJob() throws Exception
	{
            // parse the rsl
            ExtensibilityType rsl = null;

            String xml_rsl = new GramRsl2XmlConverter(jobRSL).toXml();
            RslParser rslParser = RslParserFactory.newRslParser();
            Element rslRootElement = rslParser.parse(new StringReader(xml_rsl));
            rsl = AnyHelper.getExtensibility(rslRootElement);

            // construct the factory url
            URL factoryURL = null;
            String factoryType = ContactURL.DEFAULT_FACTORY_TYPE;
            factoryURL = ContactURL.getURL( factoryAddr, factoryType );

            // create the job service
            GrimProxyPolicyHandler grimPolicyHandler = new GrimProxyPolicyHandler();
            grimPolicyHandler.reset();

            OGSIServiceLocator factoryLocator = new OGSIServiceLocator();
            Factory factory = factoryLocator.getFactoryPort(factoryURL);
            ((Stub)factory)._setProperty(Constants.GSI_SEC_MSG,
                                         Constants.SIGNATURE);
            ((Stub)factory)._setProperty(Constants.GRIM_POLICY_HANDLER,
                                         grimPolicyHandler);
            ((Stub)factory)._setProperty(Constants.AUTHORIZATION,
                                         SelfAuthorization.getInstance());

            GSSCredential cred = GSIUtils.getCredential(gahp);
            if ( cred != null ) {
                ((Stub)factory)._setProperty(GSIConstants.GSI_CREDENTIALS,cred);
            }

            grimPolicyHandler.reset(); // necessary?

            GridServiceFactory gridFactory = new GridServiceFactory(factory);
            LocatorType locator = gridFactory.createService(rsl);

            //Get a stub using the service handle
            ManagedJobServiceGridLocator serviceLocator =
                new ManagedJobServiceGridLocator();
            ManagedJobPortType service =
                serviceLocator.getManagedJobPort(locator);
            
            //Set some stub properties
            {
                org.apache.axis.client.Stub s = (org.apache.axis.client.Stub)service;
                s.setTimeout(120000);
                s._setProperty(Constants.GSI_SEC_CONV,Constants.SIGNATURE);
                s._setProperty(Constants.GRIM_POLICY_HANDLER, grimPolicyHandler);
                s._setProperty(Constants.AUTHORIZATION, SelfAuthorization.getInstance());
                s._setProperty(GSIConstants.GSI_MODE,
                               (fullDelegation) ?
                               GSIConstants.GSI_MODE_FULL_DELEG :
                               GSIConstants.GSI_MODE_LIMITED_DELEG
                               );
                if ( cred != null ) {
                    s._setProperty(GSIConstants.GSI_CREDENTIALS,cred);
                }
            }

            //set termination time
            setServiceTerminationTime(service);
                        
            // Bind this job with a NotificationSinkManager
           
            callbackSink.addJobListener (serviceLocator,
                                         service);

            return serviceLocator.getGSR().getHandle().toString();

	}
        
        private void setServiceTerminationTime(GridService gridService)
            throws Exception
        {
            ExtendedDateTimeType terminationDateTime = new ExtendedDateTimeType();
            Calendar timeout = Calendar.getInstance();
            timeout.add(Calendar.DATE, 1); //default 24 hours
            terminationDateTime.setValue(timeout);

            gridService.requestTerminationAfter(terminationDateTime);
            TerminationTimeType serviceTime =
                gridService.requestTerminationBefore(terminationDateTime);

            if (true) {
                Date timeAfter = serviceTime.getAfter().
                    getDateTimeValue().getTime();
                Date timeBefore = serviceTime.getBefore().
                    getDateTimeValue().getTime();
            }

	}
        
        public void deliverNotification(org.gridforum.ogsi.ExtensibilityType extensibilityType) {
            new Exception().printStackTrace(System.err);
        }

        // from GramJob
        private JobStatusType getJobStatusFromServiceData(
                ExtensibilityType serviceData) throws Exception {

            ServiceDataValuesType serviceDataValues = (ServiceDataValuesType)
                AnyHelper.getAsServiceDataValues(serviceData);

            checkAtLeastOne(serviceDataValues, 
                    new QName(org.globus.ogsa.base.gram.StartType.getTypeDesc().
                              getXmlType().getNamespaceURI(),
                              "ManagedJobState"));

            JobStatusType jobStatus = (JobStatusType)AnyHelper.
                getAsSingleObject(serviceDataValues, JobStatusType.class);

            return jobStatus;
        }
         
        private void checkAtLeastOne(ServiceDataValuesType serviceDataValues,
                                     QName qNameForQuery) {
            //check
            if (serviceDataValues == null || serviceDataValues.get_any() == null) {
                throw new RuntimeException(
                        "Querying by QName for\n" +
                        qNameForQuery.getNamespaceURI() + ":" +
                        qNameForQuery.getLocalPart() + "\n" +
                        "returned no such service data element (SDE), " +
                        "in violation of the schema declaration of that SDE.");
            }

        }
        
    } // class MyGramJob
    

} // class GRAM_JOB_REQUEST_Handler
