/*******************************************************************************
* 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.                       *
*******************************************************************************/
/*
 * Created on 02-Nov-2004
 *
 */
package uk.ac.ic.doc.kevlar.hial;

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

import uk.ac.ic.doc.kevlar.api.programs.IProgramInfoNode;
import uk.ac.ic.doc.kevlar.api.programs.arguments.ArgumentModelProvider;
import uk.ac.ic.doc.kevlar.api.programs.arguments.IArgumentInstance;
import uk.ac.ic.doc.kevlar.api.programs.arguments.IBasicArgumentInfo;
import uk.ac.ic.doc.kevlar.api.programs.arguments.IBasicArgumentValue;
import uk.ac.ic.doc.kevlar.api.programs.arguments.IInputNode;
import uk.ac.ic.doc.kevlar.api.programs.arguments.IInputOutputNodeTuple;
import uk.ac.ic.doc.kevlar.api.programs.arguments.IOutputNode;
import uk.ac.ic.doc.kevlar.core.framework.IContext;
import uk.ac.ic.doc.kevlar.core.framework.IExecutionEndedListener;
import uk.ac.ic.doc.kevlar.core.framework.IPipe;
import uk.ac.ic.doc.kevlar.core.framework.IPipelineProgram;
import uk.ac.ic.doc.kevlar.publicInterfaces.INodeID;
import uk.ac.ic.doc.kevlar.publicInterfaces.IProgramDescription;
import uk.ac.ic.doc.kevlar.publicUtil.dom.XMLUtil;

/**
 * @author Daniel Burke
 *
 */
public class HialProgram implements IHialProgram, IExecutionEndedListener {

	private IPipelineProgram program;
	private IContext context;
	private HialContext hialContext;
	private long refID;
	private boolean executing;
	
	public HialProgram(HialContext hialContext, IContext context, long refID)
	{
		this.hialContext = hialContext;
		this.context = context;
		this.refID = refID;
		this.program = context.getProgram(refID);
	}
	
	private void check()
	{
	    if(program.isDestroyed())
	    {
	        program = context.getProgram(refID);
	    }
	    
	    if(program == null)
	    {
	        throw new IllegalStateException(
	                "This program has been destroyed and may no longer be used");
	    }
	}
	
	public IBasicArgumentInfo getArgumentInfo(String argName)
	{
	    synchronized(context)
	    {
	        check();
	        return program.getArgumentInfo(argName);
	    }
	}
	
	public Collection<IArgumentValuePair> getArgumentValues() 
	{
	    synchronized(context)
	    {
	        check();
	        IArgumentInstance instance = program.getArgumentInstance();
	        
	        return convertArgumentPairs(instance);
	    }
	}
	
	public Collection<IArgumentValuePair> convertArgumentPairs(IArgumentInstance instance)
	{
        Collection<IArgumentValuePair> pairs = new HashSet<IArgumentValuePair>();
        for(Map.Entry<String, IBasicArgumentValue> entry : 
        	instance.getAssigmentsMap().entrySet())
        {
        	pairs.add( new ArgumentValuePair(
        			program.getArgumentInfo(entry.getKey()),
        			entry.getValue()
        					));
        }
        
        return pairs; 
	}
	
	private Collection<IBasicArgumentInfo> convertArgs(Set<String> args)
	{
        Set<IBasicArgumentInfo> argInfos = new HashSet<IBasicArgumentInfo>();
        for(String arg : args)
        {
        	argInfos.add( program.getArgumentInfo(arg));
        }
        
        return argInfos;
	}
	
	public Collection<IBasicArgumentInfo> getAvailableArguments() 
	{
	    synchronized(context)
	    {
	        check();
	        Set<String> args = program.getAvailableArguments();
	        return convertArgs(args);
	    }
	}
	
	public Collection<IBasicArgumentInfo> getAvailableButNotVisibleArguments()
	{
	    synchronized(context)
	    {
	        check();
			Set<String> args = program.getAvailableArguments();
			args.removeAll(
					program.getArgumentInstance().getAssigmentsMap().keySet());
	        return convertArgs(args);
	    }
	}
	
	public IProgramDescription getDescription()
	{
	    synchronized(context)
	    {
	        check();
	        return program.getDescription();
	    }
	}
	
	public IInputOutputNodeTuple getIONodes()
	{
	    synchronized(context)
	    {
	        check();
	        return program.getIONodes();
	    }
	}
	
	public boolean isDestroyed()
	{
	    synchronized(context)
	    {
	        return program.isDestroyed();
	    }
	}
	
    public void updateArgumentValues(Collection<IArgumentValuePair> values)
    {
	    synchronized(context)
	    {
	        check();
	        Map<String, IBasicArgumentValue> argMap = new HashMap<String, IBasicArgumentValue>();
	        for(IArgumentValuePair argPair : values)
	        {
	        	argMap.put(argPair.getArgInfo().getName(), argPair.getArgValue());
	        }
	        
	        IArgumentInstance instance = ArgumentModelProvider.makeArgumentInstance(argMap);
	        updateArgumentInstance(instance);
	    }
    }
    
    void updateArgumentInstance(IArgumentInstance instance)
    {
	    synchronized(context)
	    {
	        check();
	        context.updateArgumentInstance(instance, refID );
	    }
    }

    public void addArgument(IBasicArgumentInfo arg)
    {
	    synchronized(context)
	    {
	        check();
	        context.addArgument(arg.getName(), refID );
	    }
    }
    
    

    public boolean inputHasConnectedPipe(INodeID id) {
	    synchronized(context)
	    {
	        check();
	        return program.inputHasConnectedPipe(id);
	    }
    }

    public boolean outputHasConnectedPipe(INodeID id) {
	    synchronized(context)
	    {
	        check();
	        return program.outputHasConnectedPipe(id);
	    }
    }

    public IHialPipe getInputPipe(INodeID id) {
	    synchronized(context)
	    {
	        check();
	        IPipe pipe = program.getInputPipe(id);
	        if(pipe == null)
	        {
	            return null;
	        }
	        
	        return hialContext.getPipe(
	                hialContext.convert( pipe.getID() ) );
	    }
    }

    public IHialPipe getOutputPipe(INodeID id) {
	    synchronized(context)
	    {
	        check();
	        IPipe pipe = program.getOutputPipe(id);
	        if(pipe == null)
	        {
	            return null;
	        }
	        
	        return hialContext.getPipe(
	                hialContext.convert( pipe.getID() ) );
	    }
    }
    
    
    
	
	void putProgram(
			StringBuilder text, 
			IGUIContextSaver guiCallback, 
			int depth)
	{
		depth = XMLUtil.putOpenElem(text, "program", depth);
		
		XMLUtil.putElement(text, "name", 
				program.getCanonicalName(), depth);
		
		XMLUtil.putElement(text, "id", Long.toString(refID), depth);
		
		program.getArgumentInstance().putInstance(text, depth);
		
		depth = XMLUtil.putOpenElem(text, "gui-program", depth);
		guiCallback.putGUIProgram(text, hialContext.convert(refID), depth);
		depth = XMLUtil.putCloseElem(text, "gui-program", depth);
		
		depth = XMLUtil.putCloseElem(text, "program", depth);
	}

    public SortedSet<IProgramInfoNode> getAllProgramInfoNodes()
    {
        SortedSet<IProgramInfoNode> union = new TreeSet<IProgramInfoNode>();
        union.addAll(this.getArgumentValues());
        union.addAll(this.getAvailableButNotVisibleArguments());
        IInputOutputNodeTuple ioNodes = this.getIONodes();
        for(IInputNode node : ioNodes.getInputs())
        {
            union.add(node);
        }
        
        for(IOutputNode node : ioNodes.getOutputs())
        {
            union.add(node);
        }
        
        return union;
    }
    
    public void kill()
    {
	    synchronized(context)
	    {
	        check();
	        program.kill();
	    }
    }
    
    public synchronized void setExecutingState(boolean executing)
    {
        this.executing = executing;
	    synchronized(context)
	    {
	        check();
	        if(executing)
	        {
	            program.addExecutionEndedListener(this);
	        }
	    }
    }
    
    public void programExecutionEnded()
    {
        hialContext.programCompleted(this);
    }
    
    public Collection<IBasicArgumentInfo> getInvalidArguments()
	{
	    synchronized(context)
	    {
	        check();
	        Set<String> args = program.getInvalidArguments();
	        return convertArgs(args);
	    }
	}
}
