/*******************************************************************************
* Copyright (c)                                                                *
*           2005   Tristan Allwood,                                            *
*           2005   Daniel Burke,                                               *
*           2005   Marc Hull,                                                  *
*           2005   Ekaterina Itskova,                                          *
*           2005   Steve Zymler                                                *
*                                                                              *
* Permission is hereby granted, free of charge, to any person obtaining a      *
* copy of this software and associated documentation files (the                *
* "Software"), to deal in the Software without restriction, including          *
* without limitation the rights to use, copy, modify, merge, publish,          *
* distribute, sublicense, and/or sell copies of the Software, and to           *
* permit persons to whom the Software is furnished to do so, subject to        *
* the following conditions:                                                    *
*                                                                              *
* The above copyright notice and this permission notice shall be included      *
* in all copies or substantial portions of the Software.                       *
*                                                                              *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS      *
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF                   *
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.       *
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY         *
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,         *
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE            *
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                       *
*******************************************************************************/
package uk.ac.ic.doc.kevlar.core.framework.internal.execute;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import uk.ac.ic.doc.kevlar.Kevlar;
import uk.ac.ic.doc.kevlar.api.programs.arguments.IArgumentInstance;
import uk.ac.ic.doc.kevlar.api.programs.framework.internal.PipeManagerProvider;
import uk.ac.ic.doc.kevlar.api.programs.framework.internal.execute.ISetupInformation;
import uk.ac.ic.doc.kevlar.api.programs.returnValues.IReturnModel;
import uk.ac.ic.doc.kevlar.core.framework.internal.IProgramInstance;
import uk.ac.ic.doc.kevlar.core.framework.internal.loaders.LoaderProvider;
import uk.ac.ic.doc.kevlar.publicInterfaces.AbstractProgram;
import uk.ac.ic.doc.kevlar.publicInterfaces.IInputPipe;
import uk.ac.ic.doc.kevlar.publicInterfaces.IJVMEnvironment;
import uk.ac.ic.doc.kevlar.publicInterfaces.INodeID;
import uk.ac.ic.doc.kevlar.publicInterfaces.IOutputPipe;


/**
 * An implementation of the ExecutionEngine
 * @author tora
 */
class ExecutionEngine implements IExecutionEngine{

    private static final String CLASSPATH_BASE = System.getProperty("java.class.path");
    private static final String JAVA_EXECUTABLE = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java";
    
    private File cwd;
    
    private final ClassLoader cl;
    
    ExecutionEngine(ClassLoader cl){
    	this.cl = cl;
        cwd = new File("").getAbsoluteFile();
        if( !cwd.isDirectory()) {
            throw new AssertionError("Completely blown up precondition");
        }
    }
    
    
    
    public IProgramInstance createProgramInstance(String className, 
        	String jarFileLocation,
        	IJVMEnvironment env,
        	final IReturnModel retMod,
        	IArgumentInstance args,
        	Map<INodeID, IInputPipe> ins,
        	Map<INodeID, IOutputPipe> outs, Runnable fini) {
        
        if ( ins == null ) {
            ins = new HashMap<INodeID, IInputPipe>();
        }
        
        if ( outs == null ) {
            outs = new HashMap<INodeID, IOutputPipe>();
        }
        
        // Command arg arg arg...
        List<String> cmd = new ArrayList<String>();
        
        // java AbstractProgram programToLoad
        cmd.add(JAVA_EXECUTABLE);
        cmd.add("-Ddebug=false");
        cmd.add(AbstractProgram.class.getCanonicalName());
        cmd.add(className);
        
        ProcessBuilder pb = new ProcessBuilder(cmd);
        
        String typeClassPath = LoaderProvider.getLoader().getTypeLoader().getClassPath();
        
        //Update the Classpath environment variable with our one TODO: toa02 - this needs changing
        Map<String,String> penv = pb.environment();
        if( jarFileLocation == null ) {
            //There is no jar file, how very odd
            penv.put("CLASSPATH",CLASSPATH_BASE);
        } else {
            penv.put("CLASSPATH",CLASSPATH_BASE + File.pathSeparator + jarFileLocation + typeClassPath);
        }
        
        pb.directory(getCurrentWorkingDirectory());
        
        Set<String> inNames = extractIDs(ins.keySet());
        Set<String> outNames = extractIDs(outs.keySet());
        
        ISetupInformation setup = new SetupInformation(inNames, outNames,args);
    	final Process p;
        
        ProgramInstanceImpl pi;
    	try {
            p = pb.start();
            
             pi = new ProgramInstanceImpl(p,retMod);
            
            PipeManagerProvider.getPipeManager().managePipes(
                    p.getInputStream(), p.getOutputStream(), makeMap(ins), makeMap(outs),
                    Kevlar.DEBUG ? p.getErrorStream() : null, setup,cl,pi.getTraceRunnable());
        } catch (IOException e) {
            e.printStackTrace();
            pi = null;
        }
        
        return pi;
    }

    private static<T> Map<String,T> makeMap( Map<INodeID,T> map ) {
        
        Map<String,T> outMap = new HashMap<String,T>();
        
        for( INodeID id : map.keySet() ) {
            
            T ret = map.get(id);
            outMap.put( id.getFriendlyName(), ret);
            
        }
        
        return outMap;
        
    }
    
    private static Set<String> extractIDs(Set<INodeID> ids){
        Set<String> out = new HashSet<String>();
        
        for(INodeID id : ids){
            out.add(id.getFriendlyName());
        }
        return out;
    }
    
    public File getCurrentWorkingDirectory(){
        synchronized ( cwd )  {
            return cwd;
        }
    }
    
    public void setWorkingDirectory( File cwd ) {
        if( cwd.exists() && cwd.isDirectory()){
            synchronized (this.cwd) {
                this.cwd = cwd.getAbsoluteFile();
            }
        }
    }
   
    /**
     * @see uk.ac.ic.doc.kevlar.core.framework.internal.execute.IExecutionEngine#getEnvironmentVariables()
     */
    public Map<String, String> getEnvironmentVariables() {
        throw new UnsupportedOperationException("TODO: toa02");
    }

    /**
     * @see uk.ac.ic.doc.kevlar.core.framework.internal.execute.IExecutionEngine#setEnvironmentVariables(java.util.Map)
     */
    public void setEnvironmentVariables(Map<String, String> arg0) {
        throw new UnsupportedOperationException("TODO: toa02");
    }

    /**
     * @see uk.ac.ic.doc.kevlar.core.framework.internal.execute.IExecutionEngine#updateVariable(java.lang.String, java.lang.String, boolean)
     */
    public void updateVariable(String name, String value, boolean append) {
        throw new UnsupportedOperationException("TODO: toa02");
    }  								
	 

}
