// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package net.sourceforge.eclipsejetty.starter.console;

import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;

import net.sourceforge.eclipsejetty.starter.console.util.Arguments;

/**
 * Each {@link Command} or list of {@link Command}s will be executed within a process. The process holds the in/out
 * streams, the arguments and so on.
 * 
 * @author <NAME>
 */
public class Process
{

    private final ConsoleAdapter consoleAdapter;
    public final Process parent;
    public final Arguments args;
    public final InputStream in;
    public final PrintStream out;
    public final PrintStream err;

    private final int result = 0;

    public Process(ConsoleAdapter consoleAdapter, Process parent, Arguments args, InputStream in, PrintStream out,
        PrintStream err)
    {
        super();

        this.consoleAdapter = consoleAdapter;
        this.parent = parent;
        this.args = args;
        this.in = in;
        this.out = out;
        this.err = err;
    }

    /**
     * Creates a new process with the same properties, but different streams.
     * 
     * @param in the in stream
     * @param out the out stream
     * @param err the error stream
     * @return the new process
     */
    public Process redirect(InputStream in, PrintStream out, PrintStream err)
    {
        return new Process(consoleAdapter, parent, args, in, out, err);
    }

    /**
     * Execute the process. Parses the arguments and launches the command in the same or a dedicated child process.
     * 
     * @return the result of the command
     */
    public int execute()
    {
        try
        {
            int index = args.lastIndexOf(">", ">>", "&");

            if (index > 0)
            {
                String arg = args.consumeString(index);

                return new Process(consoleAdapter, this, args.consume(index).add(0, arg), in, out, err).execute();
            }

            return execute(this);
        }
        finally
        {
            if (err != System.err)
            {
                err.close();
            }

            if (out != System.out)
            {
                out.close();
            }

            if (in != System.in)
            {
                try
                {
                    in.close();
                }
                catch (IOException e)
                {
                    // ignore
                }
            }
        }
    }

    /**
     * The result of the command.
     * 
     * @return the result
     */
    public int getResult()
    {
        return result;
    }

    /**
     * Executes the process, handles errors.
     * 
     * @param process the process to be executed
     * @return the result of the command
     */
    private static int execute(Process process)
    {
        if (process.args.isEmpty())
        {
            return 0;
        }

        int result = 0;

        String commandName = process.args.consumeString();

        try
        {
            result = execute(process, commandName);
        }
        catch (ArgumentException e)
        {
            process.err.println(e);
        }
        catch (Exception e)
        {
            process.err.println("An exception occured:");

            e.printStackTrace(process.err);

            result = -1;
        }

        return result;

    }

    /**
     * Executes the specified command using the specified process.
     * 
     * @param process the process
     * @param commandName the name of the command
     * @return the result of the command
     * @throws Exception on occasion
     */
    private static int execute(Process process, String commandName) throws Exception
    {
        Command command = process.consoleAdapter.getCommand(commandName.toLowerCase());

        if (command == null)
        {
            process.err.printf("Unknown command: %s\n", commandName);
            process.err.println("Type \"help\" to get help.");

            return -1;
        }

        return command.execute(commandName, process);
    }

}
