/*******************************************************************************
* 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 05-Nov-2004 by toa02
 *
 */
package uk.ac.ic.doc.kevlar.core.framework.internal.programs;

import java.io.File;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;

import uk.ac.ic.doc.kevlar.api.programs.XMLModelLoadingException;
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.descriptions.ProgramDescriptionProvider;
import uk.ac.ic.doc.kevlar.api.programs.returnValues.ReturnModelProvider;
import uk.ac.ic.doc.kevlar.core.framework.exceptions.InvalidProgramException;
import uk.ac.ic.doc.kevlar.core.framework.internal.IProgram;
import uk.ac.ic.doc.kevlar.core.framework.internal.IProgramInstance;
import uk.ac.ic.doc.kevlar.core.framework.internal.IReturnValue;
import uk.ac.ic.doc.kevlar.core.framework.internal.execute.ExecutionEngineProvider;
import uk.ac.ic.doc.kevlar.core.framework.internal.loaders.LoaderProvider;
import uk.ac.ic.doc.kevlar.core.framework.internal.loaders.xmlparse.IProgramXMLFile;
import uk.ac.ic.doc.kevlar.core.framework.internal.loaders.xmlparse.ProgramXMLFileProvider;
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;

/**
 * ChangeDirectory shell builtin.
 * @author toa02
 *
 */
class ChangeDirectory implements IProgram {

    final ArgumentModelProvider amp;
    final ProgramDescriptionProvider pdp;
    final ReturnModelProvider rmp; 
    
    ChangeDirectory(){
        
        IProgramXMLFile ipxf = ProgramXMLFileProvider.getProgramXMLFile(
                getClass().getResourceAsStream("ChangeDirectory.xml"));
        try {
            amp = new ArgumentModelProvider(ipxf.getArgumentModel(),LoaderProvider.getLoader().getTypeLoader());
            pdp = new ProgramDescriptionProvider(ipxf.getDescriptionModel());
            rmp = new ReturnModelProvider(ipxf.getReturnModel());
        } catch (XMLModelLoadingException e) {
            throw new InternalError("ChangeDirectory Builtin Failed: " + e);
        } catch (ClassNotFoundException e) {
            throw new InternalError("ChangeDirectory Builtin Failed: " + e);
        }
                
    }
    
    
    public ArgumentModelProvider getArgumentModelProvider()
            throws InvalidProgramException {
        return amp;
    }

    public ProgramDescriptionProvider getDescription()
            throws InvalidProgramException {
        return pdp;
    }

    public ReturnModelProvider getReturnModelProvider() {
        return rmp;
    }

    public IProgramInstance run(IArgumentInstance args, IJVMEnvironment env,
            Map<INodeID, IInputPipe> ins, Map<INodeID, IOutputPipe> outs) {
        
        final String target = args.getAssigmentsMap().get("Directory-To-Go-To").toString();
        
        final AtomicBoolean retOk = new AtomicBoolean(true);
        
        return new IProgramInstance(){

            final Set<IProgramExitListener> notifiers = new HashSet<IProgramExitListener>();
            
            
            final Thread t = new Thread(){
                public void run(){
                    if ( target == null ) {
                        retOk.set(false);
                        tellNotifiers();
                        
                        return;
                    }
                    
                    File f = new File(target).getAbsoluteFile();
                    if ( f.exists() && f.isDirectory() ) {
                        ExecutionEngineProvider.getExecutionEngine().setWorkingDirectory(f);
                    } else {
                        retOk.set(false);
                    }
                    
                    tellNotifiers();
                }
            };
            
            {
            t.start();
            }
            
            void tellNotifiers() {
                
                synchronized ( notifiers ) {
                    for ( IProgramExitListener list : notifiers ) {
                        list.programExited(this);
                    }
                }
                
            }
            
            public boolean isRunning() {
                return t.isAlive();
            }

            public void kill() {
                //ok so you can't actually do this.
                if( t.isAlive() ) {
                    // deb02: Tris, check that this is right, it seems right to me
                    retOk.set(false);
                    try {
                        t.join();
                    } catch ( InterruptedException ie ) {
                        /* ignore it and leave */
                    }
                }
            }

            /**
             * @see uk.ac.ic.doc.kevlar.core.framework.internal.IProgramInstance#addExitNotifier(uk.ac.ic.doc.kevlar.core.framework.internal.programs.IProgramExitListener)
             */
            public void addExitNotifier(IProgramExitListener notifer) {
                synchronized ( notifiers ) {
                    if( t.isAlive() ) {
                        notifiers.add(notifer);
                        return;
                    }
                }
               notifer.programExited(this);
            }
            
            public IReturnValue getReturnValue() {
                if( t.isAlive() ) {
                    try {
                        t.join();
                    } catch ( InterruptedException ie ) {
                        /* ignore it and leave */
                    }
                }
                
                return new IReturnValue(){

                    public int getLiteralReturnValue() {
                        if( retOk.get() ) {
                            return 0;
                        } else {
                            return 1;
                        }
                    }

                    public String getReturnDescription() {
                        if ( retOk.get() ) {
                            return rmp.getReturnModel().getReturnValueMap().get(Integer.valueOf(0)).getMessage();
                        } else {
                            return rmp.getReturnModel().getReturnValueMap().get(Integer.valueOf(1)).getMessage();
                        }
                    }

                    public boolean isErroredReturn() {
                        return !retOk.get();
                    }
                    
                };
            }
            
        };
    }

    public String toString() {
        return "IProgram: ChangeDirectory";
    }

    
    /**
     * @see uk.ac.ic.doc.kevlar.core.framework.internal.IProgram#getCanonicalName()
     */
    public String getCanonicalName() {
        return getClass().getCanonicalName();
    }

    /**
     * @see uk.ac.ic.doc.kevlar.core.framework.internal.IProgram#isBuiltin()
     */
    public boolean isBuiltin() {
        return true;
    }
}
