/**
 * Copyright (c) 2010-2012 EBM WebSourcing, 2012-2013 Linagora
 * 
 * This program/library is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 2.1 of the License, or (at your
 * option) any later version.
 * 
 * This program/library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
 * for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program/library; If not, see <http://www.gnu.org/licenses/>
 * for the GNU Lesser General Public License version 2.1.
 */
package org.ow2.petals.cli.shell;

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

import org.ow2.petals.cli.api.command.exception.CommandException;
import org.ow2.petals.cli.api.exception.ShellException;
import org.ow2.petals.cli.shell.exception.UnknownCommandException;

/**
 * This class is the shell implementation for petals CLI for scripting use
 * (interactive or automatic commands flow)
 * 
 * @author Christophe DENEUX - EBM WebSourcing
 */
public abstract class PetalsScript extends AbstractShell {

    /**
     * Property name of the command execution status. This property can be used
     * in Petals script to check a command execution. Its values are:
     * {@link PetalsCli#STATUS_OK}, {@link PetalsCli#STATUS_KO}.
     */
    private static final String STATUS_PROP_NAME = "status";

    /**
     * Value of property {@value #STATUS_PROP_NAME} for a successful command
     * execution.
     */
    private static final String STATUS_OK = "0";

    /**
     * Value of property {@value #STATUS_PROP_NAME} for a failed command
     * execution.
     */
    private static final String STATUS_KO = "1";

    /**
     * 
     * @param printStream
     * @param errStream
     * @param isDebugModeEnable
     * @param isYesFlagEnable
     *            Flag to enable the automatic confirmation
     */
    public PetalsScript(final PrintStream printStream, final PrintStream errStream,
            final boolean isDebugModeEnable, final boolean isYesFlagEnable) {
        super(printStream, errStream, isDebugModeEnable, isYesFlagEnable);
    }

    /**
     * <p>
     * Callback invoked before starting script to get the command script.
     * </p>
     * <p>
     * Note: The input stream opener is responsible to close it.
     * </p>
     * 
     * @throws ShellException
     *             An error occurs getting the input stream.
     */
    protected abstract InputStream getCommandStream() throws ShellException;

    /**
     * Callback invoked before starting script
     */
    protected abstract void onStart();

    /**
     * Callback invoked before executing a script line
     */
    protected abstract void onLine();

    /**
     * Command execution error callback.
     * 
     * @throws ShellException
     */
    protected abstract void onError(final Exception e, final int lineNumber) throws ShellException;

    /**
     * <p>
     * Execute a stream of commands.
     * </p>
     * <p>
     * If the input stream containing commands is empty or <code>null</code>, no
     * command will be executed, and the script will be stopped.
     * </p>
     */
    @Override
    public void run() {

        try {
            final InputStream incomingCommandStream = this.getCommandStream();

            this.onStart();

            if (incomingCommandStream != null) {
                final LineNumberReader reader = new LineNumberReader(new InputStreamReader(
                        incomingCommandStream));
                while (this.isCommandRead) {
                    try {
                        this.onLine();

                        String line = reader.readLine();
                        if (line == null) {
                            // End of the input stream has been reached
                            break;
                        }
                        this.evaluate(line);

                    } catch (IOException ioe) {
                        throw new ShellException(ioe);
                    } catch (Exception e) {
                        this.onError(e, reader.getLineNumber());
                        this.setExitStatus(1);
                        break;
                    }
                }

                // Wait the end of asynchronous commands
                try {
                    while (this.isAsynchronousCommandInProgress()) {
                        Thread.sleep(1000);
                    }
                } catch (final InterruptedException e) {
                    // No operation to exit the shell
                }
            }
        } catch (final ShellException e) {
            this.printError(e);
            this.setExitStatus(1);
        }
    }

    @Override
    public void evaluate(String[] args) throws UnknownCommandException, CommandException {
        try {
            super.evaluate(args);
            System.setProperty(STATUS_PROP_NAME, STATUS_OK);
        } catch (CommandException e) {
            System.setProperty(STATUS_PROP_NAME, STATUS_KO);
            throw e;
        }
    }
}
