package de.ugoe.cs.util.console;

import java.util.ArrayList;
import java.util.List;

import de.ugoe.cs.util.StringTools;

/**
 * <p>
 * This class provides an interface for communication with the user without have
 * to rely on a specific user interface. Thus, it can be used to decouple the
 * programs logic from its user interface.
 * </p>
 * <p>
 * {@link Command} objects can be used to execute behavior.
 * </p>
 * <p>
 * To send output to the user interface, the Observer pattern is used. The
 * Console is an observable, the concrete user interfaces are the observers. The
 * interface for the observers is {@link ConsoleObserver}.
 * </p>
 * 
 * @author Steffen Herbold
 * @version 1.0
 */
public final class Console {

	/**
	 * <p>
	 * List of observers.
	 * </p>
	 */
	private List<ConsoleObserver> observers;

	/**
	 * <p>
	 * Handle of the Console instance.
	 * </p>
	 */
	private static Console theInstance = null;

	/**
	 * <p>
	 * Returns the instance of Console. If no instances exists yet, a new one is
	 * created.
	 * </p>
	 * 
	 * @return instance of this class
	 */
	public static Console getInstance() {
		if (theInstance == null) {
			theInstance = new Console();
		}
		return theInstance;
	}

	/**
	 * <p>
	 * Creates a new Console. Private to prevent multiple instances (Singleton).
	 * </p>
	 */
	private Console() {
		observers = new ArrayList<ConsoleObserver>();
	}

	/**
	 * <p>
	 * Register a new observer.
	 * </p>
	 * 
	 * @param observer
	 *            observer to be added
	 */
	public void registerObserver(ConsoleObserver observer) {
		observers.add(observer);
	}

	/**
	 * <p>
	 * Remove an observer. If the observer is not found, nothing is done.
	 * </p>
	 * 
	 * @param observer
	 *            observer to be removed
	 */
	public void deleteObserver(ConsoleObserver observer) {
		observers.remove(observer);
	}

	/**
	 * <p>
	 * Sends a message to all observers containing the message that was passed
	 * to this function.
	 * </p>
	 * 
	 * @param msg
	 *            message that is send to the console
	 */
	public static void print(String msg) {
		if (theInstance == null) {
			getInstance();
		}
		for (ConsoleObserver observer : theInstance.observers) {
			observer.updateText(msg);
		}
	}

	/**
	 * <p>
	 * Sends a message to all observers containing the message that was passed
	 * to this function and adds an endline to the message.
	 * </p>
	 * 
	 * @param msg
	 *            message that is send to the observers
	 */
	public static void println(String msg) {
		if (theInstance == null) {
			getInstance();
		}
		for (ConsoleObserver observer : theInstance.observers) {
			observer.updateText(msg + StringTools.ENDLINE);
		}
	}

	/**
	 * <p>
	 * Sends an error message to all observers containing the message that was
	 * passed to this function.
	 * </p>
	 * 
	 * @param errMsg
	 *            message that is send to the observers
	 */
	public static void printerr(String errMsg) {
		if (theInstance == null) {
			getInstance();
		}
		for (ConsoleObserver observer : theInstance.observers) {
			observer.errStream(errMsg);
		}
	}

	/**
	 * <p>
	 * Sends an error message to all observers containing the message that was
	 * passed to this function and adds an endline to the message.
	 * </p>
	 * 
	 * @param errMsg
	 *            message that is send to the observers
	 */
	public static void printerrln(String errMsg) {
		if (theInstance == null) {
			getInstance();
		}
		for (ConsoleObserver observer : theInstance.observers) {
			observer.errStream(errMsg + StringTools.ENDLINE);
		}
	}

	/**
	 * <p>
	 * Sends an exception to all observers to print its stack trace.
	 * </p>
	 * 
	 * @param e
	 *            exception whose stack trace is to be printed
	 */
	public static void printStacktrace(Exception e) {
		if (theInstance == null) {
			getInstance();
		}
		for (ConsoleObserver observer : theInstance.observers) {
			observer.printStacktrace(e);
		}
	}

	/**
	 * <p>
	 * Sends a debug message to all observers containing the message that was
	 * passed to this function.
	 * </p>
	 * 
	 * @param traceMsg
	 *            message that is send to the observers
	 */
	public static void trace(String traceMsg) {
		if (theInstance == null) {
			getInstance();
		}
		for (ConsoleObserver observer : theInstance.observers) {
			observer.trace(traceMsg);
		}
	}

	/**
	 * <p>
	 * Sends a debug message to all observers containing the message that was
	 * passed to this function and adds an {@link StringTools#ENDLINE} to the
	 * message.
	 * </p>
	 * 
	 * @param traceMsg
	 *            message that is send to the observers
	 */
	public static void traceln(String traceMsg) {
		if (theInstance == null) {
			getInstance();
		}
		for (ConsoleObserver observer : theInstance.observers) {
			observer.trace(traceMsg + StringTools.ENDLINE);
		}
	}

	/**
	 * <p>
	 * Called by {@link CommandExecuter#exec(String)}.
	 * </p>
	 * 
	 * @param command
	 *            command that is executed
	 */
	static void commandNotification(String command) {
		if (theInstance == null) {
			getInstance();
		}
		for (ConsoleObserver observer : theInstance.observers) {
			observer.commandNotification(command);
		}
	}

}
