/////////////////////////////////////////////////////////////////////////
//
// © 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:             Darren Marvin
//      Created date:           2003/09/27
//      Created for project:    GEMSS
//
/////////////////////////////////////////////////////////////////////////
//
//      Dependencies: None
//
/////////////////////////////////////////////////////////////////////////
//
//      Last commit info:       $Author: $
//                              $Date: $
//                              $Revision: $
//
/////////////////////////////////////////////////////////////////////////

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

import eu.gemss.components.transport.*;
import eu.gemss.components.transport.servicedescription.*;
import eu.gemss.signals.*;
import java.util.*;
import java.util.logging.*;
import uk.ac.soton.itinnovation.gemss.transportmessaging.configuration.*;
import uk.ac.soton.itinnovation.gemss.transportmessaging.messaging.*;
import uk.ac.soton.itinnovation.gemss.utils.configuration.*;
import eu.gemss.components.transport.payload.DataItemDOMTranslator;
import uk.ac.soton.itinnovation.gemss.transportmessaging.payload.serialisation.TranslatorRegistry;

/**
 * TransportMessaging enables a client to make invocations on web services without
 * understanding the underlying details of how to do that. It can apply flexible policies
 * for encoding of messages and support various transport protocols including HTTP and HTTPS
 */
public class TransportMessaging {

    private Logger mLogger = Logger.getLogger("uk.ac.soton.itinnovation.gemss.transportmessaging.TransportMessaging");
    private static final String TEMPORARY_FILE_DIR = "gemss.transport.messaging.temp.file.dir";
    private TransportMessagingConfiguration mConfiguration = null;
    private SignalHandler mSignalHandler = null;

    /**
     * Creates a standard transport and messaging component without any configuration information,
     * default values will be used.
     * @throws TransportMessagingException
     */
    public TransportMessaging() throws TransportMessagingException {
        try{
            init();
        }
        catch(Exception ex) {
            mLogger.log(Level.SEVERE,"Unexplained failure, please send a bug report including all log files",ex);
            throw new TransportMessagingException("Unexplained failure, please send a bug report including all log files");
        }
    }

    /**
     * Creates a standard transport and messaging component without any configuration and using
     * the passed signal processor for processing signals generated from providers linked to the
     * component.
     * @param handler signal handler
     * @throws TransportMessagingException
     */
    public TransportMessaging(SignalHandler handler) throws TransportMessagingException {
        init(handler);

    }

    /**
     * Creates a standard transport and messaging component that will use the supplied configuration
     * @param config configuration
     * @throws TransportMessagingException
     */
    public TransportMessaging(TransportMessagingConfiguration config) throws TransportMessagingException {
        init(config);
    }

    /**
     * Creates a standard transport and messaging component that will use the supplied configuration
     * @param config configuration
     * @param handler signal handler
     * @throws TransportMessagingException
     */
    public TransportMessaging(TransportMessagingConfiguration config,SignalHandler handler) throws TransportMessagingException {
        init(config,handler);
    }

    /**
     * Invokes the service described by the passed service description using input data
     * contained in the passed input container
     * @param serviceDesc service description
     * @param input input data container
     * @param fileHandles map from return item names to file destination paths
     * @return Invocation Output Container
     */
    public InvocationOutput invokeService(ServiceDescription serviceDesc, InvocationInput input, Map fileHandles) throws uk.ac.soton.itinnovation.gemss.transportmessaging.messaging.MessagingException,
    uk.ac.soton.itinnovation.gemss.transportmessaging.connection.ConnectionException, uk.ac.soton.itinnovation.gemss.transportmessaging.messaging.ServiceException {

        //for now this simply uses a messenger instance to undertake the call, later versions might
        //coodinate messengers and streamers to make an invocation
        Messenger messenger = new Messenger(mConfiguration,mSignalHandler);
        return messenger.invokeService(serviceDesc,input,fileHandles,false);

    }

    /**
     * Invokes the service described by the passed service description using input data
     * contained in the passed input container
     * @param serviceDesc service description
     * @param input input data container
     * @return Invocation Output Container
     */
    public InvocationOutput invokeService(ServiceDescription serviceDesc, InvocationInput input) throws uk.ac.soton.itinnovation.gemss.transportmessaging.messaging.MessagingException,
    uk.ac.soton.itinnovation.gemss.transportmessaging.connection.ConnectionException, uk.ac.soton.itinnovation.gemss.transportmessaging.messaging.ServiceException  {

        //for now this simply uses a messenger instance to undertake the call, later versions might
        //coodinate messengers and streamers to make an invocation
        Messenger messenger = new Messenger(mConfiguration,mSignalHandler);
        return messenger.invokeService(serviceDesc,input,new HashMap(),false);

    }

    /**
     * Invokes the service described by the passed service description using input data
     * contained in the passed input container
     * @param serviceDesc service description
     * @param input input data container
     * @param fileHandles map from return item names to file destination paths
     * @param usePolicyFile true if should use static policy file, false otherwise
     * @return Invocation Output Container
     */
    public InvocationOutput invokeService(ServiceDescription serviceDesc, InvocationInput input, Map fileHandles, boolean usePolicyFile) throws uk.ac.soton.itinnovation.gemss.transportmessaging.messaging.MessagingException,
    uk.ac.soton.itinnovation.gemss.transportmessaging.connection.ConnectionException, uk.ac.soton.itinnovation.gemss.transportmessaging.messaging.ServiceException {

        //for now this simply uses a messenger instance to undertake the call, later versions might
        //coodinate messengers and streamers to make an invocation
        Messenger messenger = new Messenger(mConfiguration,mSignalHandler);
        return messenger.invokeService(serviceDesc,input,fileHandles,usePolicyFile);

    }

    /**
     * Invokes the service described by the passed service description using input data
     * contained in the passed input container
     * @param serviceDesc service description
     * @param input input data container
     * @param usePolicyFile true if should use static policy file, false otherwise
     * @return Invocation Output Container
     */
    public InvocationOutput invokeService(ServiceDescription serviceDesc, InvocationInput input, boolean usePolicyFile) throws uk.ac.soton.itinnovation.gemss.transportmessaging.messaging.MessagingException,
    uk.ac.soton.itinnovation.gemss.transportmessaging.connection.ConnectionException, uk.ac.soton.itinnovation.gemss.transportmessaging.messaging.ServiceException  {

        //for now this simply uses a messenger instance to undertake the call, later versions might
        //coodinate messengers and streamers to make an invocation
        Messenger messenger = new Messenger(mConfiguration,mSignalHandler);
	    return messenger.invokeService(serviceDesc,input,new HashMap(),usePolicyFile);
    }

    public boolean addDataItemDOMTranslator(DataItemDOMTranslator translator) {
      try{
        if(translator==null)
          throw new IllegalArgumentException("null translator supplied");
        TranslatorRegistry.getInstance().addDataItemDOMTranslator(translator);
      }
      catch(Exception ex) {
        mLogger.log(Level.WARNING,"Unable to add translator called '" + translator.getClass().getName() + "'");
        return false;
      }
      return true;
	}


    private void init() throws TransportMessagingException {
        try{
            //grab the configuration
            mConfiguration = new TransportMessagingConfiguration();
            mSignalHandler = new NullSignalHandler();
        }
        catch(ConfigurationException ex) {
            mLogger.log(Level.SEVERE,"Could not configure the transport messaging component, more information in log files",ex);
            throw new TransportMessagingException("Could not configure the transport messaging component, more information in log files");
        }
    }

    private void init(TransportMessagingConfiguration config) throws TransportMessagingException {
        try{
            //grab the configuration
            mConfiguration = config;
            //set the temporary directory
            String tempDir = mConfiguration.getConfigurationValue(TEMPORARY_FILE_DIR);
            if(tempDir!=null) {
                System.setProperty("java.io.tmpDir",tempDir);
            }
            mSignalHandler = new NullSignalHandler();

        }
        catch(ConfigurationException ex) {
            mLogger.log(Level.SEVERE,"Could not configure the transport messaging component, more information in log files",ex);
            throw new TransportMessagingException("Could not configure the transport messaging component, more information in log files");
        }
    }

    private void init(SignalHandler handler) {
        //grab the configuration
        mSignalHandler = handler;
    }

    private void init(TransportMessagingConfiguration config,SignalHandler handler) throws TransportMessagingException {
        //grab the configuration
        init(config);
        init(handler);
    }

    private class NullSignalHandler implements SignalHandler {

        /**
         * Accepts signal and forwards them to the true signal
         * handler for the software. This should be blocking but does depend
         * on how the particular signal framework for the software is implemented.
         */
        public void process(Signal signal) {
            //this implementation does nothing.
            //it throws an exception to indicate that no response is coming
            mLogger.log(Level.SEVERE,"Using a null signal processor, this is not correct please check configuration");

        }

    }
}