//   Copyright 2012 Georg-August-Universität Göttingen, Germany
//
//   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 de.ugoe.cs.util.console;

import java.io.IOException;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.logging.Level;

import de.ugoe.cs.util.console.listener.IErrorListener;
import de.ugoe.cs.util.console.listener.IExceptionListener;
import de.ugoe.cs.util.console.listener.IOutputListener;
import de.ugoe.cs.util.console.listener.ITraceListener;

/**
 * <p>
 * Implements a simple console observer that prints normal text to {@code stdout}, errors to
 * {@code stderr} and reads from {@code stdin}.
 * </p>
 * 
 * @author Steffen Herbold
 * @version 1.0
 */
public class TextConsole implements IOutputListener, IErrorListener, ITraceListener,
    IExceptionListener
{

    /**
     * <p>
     * Defines the trace level used by this console.
     * </p>
     */
    private Level traceLevel;

    private final SimpleDateFormat ft = new SimpleDateFormat("HH:mm:ss");

    /**
     * <p>
     * Creates a new text console and automatically registers it as observer. The trace level is
     * {@link Level#WARNING}.
     * </p>
     */
    public TextConsole() {
        this(Level.WARNING);
    }

    /**
     * <p>
     * Creates a new text console and automatically registers it as observer.
     * </p>
     * 
     * @param traceLevel
     *            trace level used by this text console
     */
    public TextConsole(Level traceLevel) {
        Console.getInstance().registerOutputListener(this);
        Console.getInstance().registerErrorListener(this);
        Console.getInstance().registerTraceListener(this);
        Console.getInstance().registerExceptionListener(this);
        this.traceLevel = traceLevel;
    }

    /**
     * <p>
     * Prints messages to {@code stdout}.
     * </p>
     * 
     * @see ConsoleObserver#outputMsg(java.lang.String)
     */
    public void outputMsg(String newMessage) {
        System.out.print(newMessage);
    }

    /**
     * <p>
     * Prints messages to {@code stderr}.
     * </p>
     * 
     * @see ConsoleObserver#errorMsg(String)
     */
    @Override
    public void errorMsg(String errMessage) {
        System.err.print(errMessage);
    }

    /**
     * <p>
     * Prints the stacktrace of an exception to {@code stderr} if the log level is more or equally
     * detailed to <code>Level.FINE</code>. Otherwise, it just prints a line naming the exception
     * or only the message, if any. 
     * </p>
     * 
     * @see ConsoleObserver#logException(Exception)
     */
    @Override
    public void logException(Exception e) {
        if (traceLevel.intValue() > Level.FINE.intValue()) {
            if (e.getMessage() != null) {
                System.err.println(e.getMessage());
            }
            else {
                System.err.println(e);
            }
        }
        else {
            e.printStackTrace(System.err);
        }
    }

    /**
     * <p>
     * Prints messages to {@code stdout}. These messages are only printed, if the console is run in
     * debug mode.
     * </p>
     */
    @Override
    public void traceMsg(String traceMessage, Level level) {
        if (level.intValue() >= traceLevel.intValue()) {
            System.out.print("[" + level.toString() + "] [" + ft.format(new Date()) + "] " +
                traceMessage);
        }
    }

    /**
     * <p>
     * Starts a new TextConsole. If the text console is started, it can be used not only to print
     * message, but also to execute commands by reading {@code stdin}.
     * </p>
     */
    public void run() {
        CommandExecuter exec = CommandExecuter.getInstance();
        while (true) {
            System.out.print("> ");
            String command = getCommand().trim();
            if (!command.equals("")) {
                exec.exec(command);
            }
        }
    }

    /**
     * <p>
     * Reads a new command from {@code stdin}.
     * </p>
     * 
     * @return a string with a command
     */
    protected String getCommand() {
        byte[] buffer = new byte[1024];
        int bytesRead = 0;
        String command;
        try {
            bytesRead = System.in.read(buffer);
        }
        catch (IOException e) {

        }
        if (bytesRead == 0) {
            command = "";
        }
        else {
            command = new String(buffer, Charset.defaultCharset());
        }
        return command;
    }

}
