/////////////////////////////////////////////////////////////////////////
//
// © 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:           2004/04/30
//      Created for project:    GEMSS
//
/////////////////////////////////////////////////////////////////////////
//
//      Dependencies: None
//
/////////////////////////////////////////////////////////////////////////
//
//      Last commit info:       $Author: $
//                              $Date: $
//                              $Revision: $
//
/////////////////////////////////////////////////////////////////////////

package uk.ac.soton.itinnovation.gemss.security.context.proxies.gemss;

import eu.gemss.components.security.token.SecurityToken;
import eu.gemss.components.security.token.TokenDescriptor;
import eu.gemss.components.security.token.TokenDescriptor;
import eu.gemss.components.security.token.SecurityTokenException;
import eu.gemss.components.security.SecurityContextException;

import uk.ac.soton.itinnovation.gemss.security.context.token.SecurityTokenManager;
import uk.ac.soton.itinnovation.gemss.security.context.SecurityContext;
import java.util.logging.Logger;
import java.util.logging.Level;
import eu.gemss.components.Configuration;
import uk.ac.soton.itinnovation.gemss.security.context.configuration.SecurityContextConfiguration;
import eu.gemss.GEMSS;
import eu.gemss.components.ComponentManager;
import eu.gemss.signals.Signal;
import eu.gemss.signals.SignalHandler;
import eu.gemss.components.security.*;
import eu.gemss.*;

/**
 * Reference implementation for the GEMSS Security context component
 */
public class GEMSSSecurityContextImpl implements eu.gemss.components.security.GEMSSSecurityContext {

    private static Logger mLogger = Logger.getLogger("uk.ac.soton.itinnovation.gemss.security.context.proxies.gemss");
    private static SecurityContext mSecurityContext;
    private static final long SLEEP_TIME = 1000;
    private static final long WAIT_TIME = 360000;
    /**
     * Default constructor for the IT Innovation security context module implementation
     */
    public GEMSSSecurityContextImpl() throws eu.gemss.components.security.SecurityContextException {
        try{
            //get the configuration details

            Configuration config = null;
//[stuart DUMMYGEMSS removed nov03]
//            String isTest = System.getProperty("gemss.test");
//            if(isTest!=null && isTest.equals("true")) {
//			mLogger.log(Level.INFO,"Using dummyGEMSS configuration for security context component");
//			config = uk.ac.soton.itinnovation.gemss.DummyGEMSS.getConfiguration();
//            }
//            else {
			// use component manager to get config
			mLogger.log(Level.INFO,"Using proper configuration for security context component");
			config = (Configuration) GEMSS.getInstance().getInstance("eu.gemss.components.Configuration");
//            }

            if(config == null) {
                mLogger.warning("No configuration information was accessible, creating security context without any configuration");
                //creates a single SecurityContext instance in preparation for token requests
                mSecurityContext = new SecurityContext(new GEMSSSignalPump());
            }
            else {
                SecurityContextConfiguration ctxConfig = new SecurityContextConfiguration(config.getConfigDir());
                mSecurityContext = new SecurityContext(ctxConfig,new GEMSSSignalPump());
            }
        }
        catch(Exception ex) {
            mLogger.log(Level.SEVERE,"Unexpected failure occured inside security context component, please send a bug report including all log files",ex);
            throw new eu.gemss.components.security.SecurityContextException("Unexpected failure occured inside security context component, please send a bug report including all log files");
        }
    }

    /**
     * Retrieve a security token for the passed token identifier
     * @param token identifier
     * @return security token
     */
    public SecurityToken generateSecurityToken(TokenDescriptor tokenDesc) throws GridException {
        try{
            return mSecurityContext.generateSecurityToken(tokenDesc);
        }
        catch(Exception ex) {
            if(ex instanceof GridException) {
                throw (GridException) ex;
            }
            else {
                mLogger.log(Level.SEVERE,"Unexpected failure occured inside security context component, please send a bug report including all log files",ex);
                throw new eu.gemss.components.security.SecurityContextException("Unexpected failure occured inside security context component, please send a bug report including all log files");
            }
        }
    }

    /**
     * Test if the supplies security token is trusted
     * @param secToken security token
     * @return true if trusted, false otherwise
     */
    public boolean isTrusted(SecurityToken secToken) throws GridException {
        try{
           return mSecurityContext.isTrusted(secToken);
        }
        catch(Exception ex) {
            if(ex instanceof GridException) {
                throw (GridException) ex;
            }
            else {
                mLogger.log(Level.SEVERE,"Unexpected failure occured inside security context component, please send a bug report including all log files",ex);
                throw new eu.gemss.components.security.SecurityContextException("Unexpected failure occured inside security context component, please send a bug report including all log files");
            }
        }
    }

    /**
         * Add a security token instance to the prevailing security context
         * @param secToken security token to add
         * @throws SecurityContextException
         */
        public void addSecurityTokenToContext(SecurityToken secToken) throws eu.gemss.GridException {
            try{
                mSecurityContext.addSecurityTokenToContext(secToken);
            }
            catch(Exception ex) {
                if(ex instanceof GridException) {
                    throw (GridException) ex;
                }
                else {
                    mLogger.log(Level.SEVERE,"Unexpected failure occured inside security context component, please send a bug report including all log files",ex);
                    throw new eu.gemss.components.security.SecurityContextException("Unexpected failure occured inside security context component, please send a bug report including all log files");
                }
            }
        }

        /**
         * Remove from the prevailing security context the security token identified by the supplied token descriptor
         * @param secToken token to remove
         * @throws SecurityContextException
         */
        public void removeSecurityTokenFromContext(SecurityToken secToken) throws eu.gemss.GridException {
            try{
                mSecurityContext.removeSecurityTokenFromContext(secToken);
            }
            catch(Exception ex) {
                if(ex instanceof GridException) {
                    throw (GridException) ex;
                }
                else {
                    mLogger.log(Level.SEVERE,"Unexpected failure occured inside security context component, please send a bug report including all log files",ex);
                    throw new eu.gemss.components.security.SecurityContextException("Unexpected failure occured inside security context component, please send a bug report including all log files");
                }

            }
        }

        /**
         * Retrieve from the prevailing security context a previously stored security token matching the passed token descriptor
         * @param tokenDesc token identifier
         * @return security token required
         * @throws SecurityContextException
         */
        public SecurityToken getSecurityToken(TokenDescriptor tokenDesc) throws eu.gemss.GridException{
            try{
                return mSecurityContext.getSecurityTokenFromContext(tokenDesc);
            }
            catch(Exception ex) {
                if(ex instanceof GridException) {
                    throw (GridException) ex;
                }
                else {
                    mLogger.log(Level.SEVERE,"Unexpected failure occured inside security context component, please send a bug report including all log files",ex);
                    throw new eu.gemss.components.security.SecurityContextException("Unexpected failure occured inside security context component, please send a bug report including all log files");
                }
            }
        }

    private class GEMSSSignalPump implements SignalHandler {

        public GEMSSSignalPump() {}

        public void process(Signal signal) {
            ComponentManager cManager = null;
//          String isTest = System.getProperty("gemss.test");
//            if(isTest!=null && isTest.equals("true")) {
//                cManager = uk.ac.soton.itinnovation.gemss.DummyGEMSS.getInstance();
//
//            }
//            else {
                cManager = GEMSS.getInstance();
//            }
                if(signal instanceof uk.ac.soton.itinnovation.gemss.security.context.token.providers.javakeystore.CredentialRequest) {

                    //register with component manager
                    uk.ac.soton.itinnovation.gemss.security.context.token.providers.javakeystore.CredentialRequest itCred =
                            (uk.ac.soton.itinnovation.gemss.security.context.token.providers.javakeystore.CredentialRequest) signal;
                    eu.gemss.signals.security.CredentialRequest gemssCred = new eu.gemss.signals.security.CredentialRequest(this);
                    mLogger.log(Level.INFO,"Generating Signal for component manager");
                    Signal[] returnedSignals = cManager.generateSignal(gemssCred);
                    if(returnedSignals.length==0) {
                        mLogger.log(Level.INFO,"No handlers exist for the signal '" + signal.getSignalType() + "'");
                    }
                    else {
                        mLogger.log(Level.INFO,"Signal '" + signal.getSignalType() + "' being handled by " + returnedSignals.length);
                    }
                    //there should be only one signal handler for this
                    Signal response = returnedSignals[0];
                    //cast to credential
                    eu.gemss.signals.security.CredentialRequest respCred =
                            (eu.gemss.signals.security.CredentialRequest) response;
                    long commenceWait = System.currentTimeMillis();
                        while(respCred.getKeystorePassword()==null ||
                              respCred.getPrivateKeyAlias()==null ||
                              respCred.getPrivateKeyPassword()==null) {
                            if(WAIT_TIME > System.currentTimeMillis()-commenceWait) {
                                try{
                                    Thread.sleep(SLEEP_TIME);
                                }
                                catch(InterruptedException ex) {
                                    //fine
                                }
                                mLogger.log(Level.INFO,"Waiting for authentication credentials");
                            }
                        }
                    //set
                    itCred.setKeystorePassword(respCred.getKeystorePassword());
                    itCred.setPrivateKeyAlias(respCred.getPrivateKeyAlias());
                    itCred.setPrivateKeyPassword(respCred.getPrivateKeyPassword());
                }


                cManager.generateSignal(signal);
            }

    }

}