/
area/
classes/net/sourceforge/pain/logic/
classes/net/sourceforge/pain/logic/event/
classes/net/sourceforge/pain/logic/fn/util/
classes/net/sourceforge/pain/network/console/
classes/net/sourceforge/pain/plugin/
classes/net/sourceforge/pain/plugin/reset/
classes/net/sourceforge/pain/plugin/shutdown/
classes/net/sourceforge/pain/plugin/social/
classest/net/sourceforge/pain/db/data/
doc/
doc/paindb/resources/
src/net/sourceforge/pain/logic/
src/net/sourceforge/pain/logic/event/
src/net/sourceforge/pain/logic/fn/util/
src/net/sourceforge/pain/network/console/
src/net/sourceforge/pain/network/console/telnet/
src/net/sourceforge/pain/plugin/
src/net/sourceforge/pain/plugin/command/
src/net/sourceforge/pain/plugin/reset/
src/net/sourceforge/pain/plugin/shutdown/
src/net/sourceforge/pain/plugin/social/
src/net/sourceforge/pain/util/
tests/
tests/net/sourceforge/pain/db/data/
package net.sourceforge.pain.network.console;


import net.sourceforge.pain.*;
import net.sourceforge.pain.network.console.telnet.*;
import net.sourceforge.pain.util.*;

import java.util.*;

public final class ConsoleManager extends PeriodOfTimeListener {

	public static final int DEFAULT_PORT = 7777;

	private static final String EVENT_CONSOLE_INPUT = "console.ConsoleInputEvent";
	private static final String EVENT_CONSOLE_EXPIRED = "console.ConsoleExpiredEvent";
	private static final String EVENT_NEW_CONNECTION = "console.LoginEvent";
	private static final String EVENT_CONSOLE_DISCONNECT = "console.ConnectionLostEvent";
	public static final String EVENT_PROMPT = "console.ShowPrompt";

	private static final int PERIOD = 10;

	private ArrayList consoles = new ArrayList();
	private TelnetConsoleServer server = null;
	private int port = DEFAULT_PORT;

	public ConsoleManager() {
		this(DEFAULT_PORT);
	}

	public ConsoleManager(int port) {
		super(PERIOD, PERIOD_IN_PULSE);
		this.port = port;
	}

	public void init() {
		if (server != null) {
			throw new RuntimeException("CONSOLE_MANAGER:already inited");
		}
		server = new TelnetConsoleServer(port);
	}

	protected synchronized Console register(ConsoleAdapter adapter)  {
		Console console = new Console(adapter);
		consoles.add(console);
		return console;
	}

	protected synchronized void onPeriod(int time) {
		Console console;
		int nCons = consoles.size();
		for (int i = 0; i < nCons; i++) {
			console = (Console) consoles.get(i);
			boolean wasPrompt = false;
			if (console.state == Console.STATE_ACTIVE) {
				if (console.input.size() > 0) {
					try {
						console.newLineProcessingStarted = true;
						Core.processEvent(EVENT_CONSOLE_INPUT, console);
					} catch (Exception e) {
						Log.error(e);
						console.popInputLine();// removing this error line from out queue
					}
					if (console.state == Console.STATE_ACTIVE) { // Note: console could be marked inactive during event processing!
						console.expireTime = time + Console.MAX_INACTIVE_TIME;
						if (console.isCommandMode()) {
							actPrompt(console);
							wasPrompt = true;
							if (console.input.size() > 0 && console.isCommandMode()) {
								console.out(ConsoleAdapter.NEW_LINE);
							}
						}
					}
				} else {
					if (time > console.expireTime) {
						try {
							Core.processEvent(EVENT_CONSOLE_EXPIRED, console);
						} catch (Exception e) { // if error occured we will force to close console
							Log.error(e.getMessage(), e);
						} finally {
							if (console.state != Console.STATE_SYSTEM_CLOSED) {
								closeConsole(console);
							}
						}
					}
				}
				if (console.state == Console.STATE_ACTIVE) {
					if (console.hasBufferedOutput) {
						if (!wasPrompt && console.isCommandMode()) {
							actPrompt(console);
						}
						console.flushOutput();
					}
				}
			} else if (console.state == Console.STATE_NEW) {
				try {
					Core.processEvent(EVENT_NEW_CONNECTION, console);
				} catch (Exception e) {
					Log.error(e);
				}
				console.expireTime = time + Console.MAX_INACTIVE_TIME;
				console.state = Console.STATE_ACTIVE;
			} else if (console.state == Console.STATE_SYSTEM_CLOSED) {
				if (console.adapter != null) { // if was not closed remotely (disconnected)
					console.adapter.forceClose();
				}
				consoles.remove(i);
				i--;
				nCons--;
			} else if (console.state == Console.STATE_REMOTE_CLOSED_1) {
				console.state = Console.STATE_REMOTE_CLOSED_2;
				try {
					Core.processEvent(EVENT_CONSOLE_DISCONNECT, console);
				} catch (Exception e) {
					Log.error(e.getMessage(), e);
					closeConsole(console);
				}
			} else {
				// we do nothing with REMOTE closed consoles here
				// system MUST close it from it's side -> console should become STATE_SYSTEM_CLOSED!!
			}
		}
	}

	public static synchronized void closeConsole(Console console) {
		Log.debug("CONSOLE_MANAGER:manual close!");
		if (console.state == Console.STATE_NEW) {
			// This state is possible since we allow
			// to get a consoles Set copy by consoles() method for outer code
			console.flushOutput();
		} else if (console.state == Console.STATE_ACTIVE) {
			console.flushOutput();
		} else {
			// console also could have STATE_REMOTE_CLOSE_2 here
			// we should not do any special in this case
		}
		console.state = Console.STATE_SYSTEM_CLOSED;
	}

	protected synchronized void onRemoteClose(Console console) {
		Log.debug("CONSOLE_MANAGER:remote close!");
		if (console.state == Console.STATE_NEW) {
			consoles.remove(console);
		} else if (console.state == Console.STATE_ACTIVE) {
			console.adapter = null;
			console.state = Console.STATE_REMOTE_CLOSED_1;
		}
	}

	private static void actPrompt(Console console) {
		try {
			Core.processEvent(ConsoleManager.EVENT_PROMPT, console);
		} catch (Exception e) {
			Log.error(e.getMessage(), e);
		}
	}

	public synchronized Set consoles() {
		return new HashSet(consoles);
	}


}