/
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.telnet;


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

import java.io.*;
import java.net.*;


public class TelnetConsoleAdapter extends ConsoleAdapter implements Runnable {

	/** IAC - init sequence for telnet negotiation. */
	private final static int IAC = 255;

	/** [IAC] End Of Record */
//	private final static int EOR = 239; not used

	/** [IAC] WILL */
	private final static int WILL = 251;

	/** [IAC] WONT */
	private final static int WONT = 252;

	/** [IAC] DO */
	private final static int DO = 253;

	/** [IAC] DONT */
	private final static int DONT = 254;

	/** [AYT] are you there?*/
	private final static int AYT = 246;

	private final byte[] IACWONT = {(byte) IAC, (byte) WONT, 0};
	private final byte[] IACDONT = {(byte) IAC, (byte) DONT, 0};
	private final byte[] IACAYT = {(byte) IAC, (byte) AYT, 0};

	TelnetConsoleServer server;
	Socket socket;
	InputStream is;
	OutputStream os;

	boolean forcedClose;
	boolean lostLink;

	Object monitor = new Object();

	int currentColor = AnsiColor.COLOR_CLEAR;

	StringBuffer out = new StringBuffer();

	protected TelnetConsoleAdapter(TelnetConsoleServer server, Socket socket) throws Exception {
		super();
		this.server = server;
		this.socket = socket;

		socket.setSoTimeout(10000);
		is = socket.getInputStream();
		os = socket.getOutputStream();
		init();
	}

	public void run() {
		startService();
	}

	public void startService() {
		forcedClose = false;
		try {
			StringBuffer buf = new StringBuffer();
			int c = 0;

			do {
				try {
					c = read(is);
					if (c > 0) {
						if (c == IAC) { //Interpret As Command
							c = read(is);
							if (c != IAC) { // its really command
								if (c == WILL) { // if command is WILL echo DON'T
									Log.debug("will");
									c = read(is);
									IACDONT[2] = (byte) c;
									sendRaw(IACDONT); // we do not want any command :)
								} else if (c == DO) {// if command is DO echo WON'T
									Log.debug("do");
									c = read(is);
									IACWONT[2] = (byte) c;
									sendRaw(IACWONT);
								} else {
									Log.debug("telnet command:" + c);
								}
								continue;
							}
						}
						if (c == '\r') {
							continue;
						}
						if (c == '\n') {
							if (buf.length() > 0) {
								lineReceived(buf.toString());
								buf.delete(0, buf.length());
							} else {
								lineReceived("");
							}
						} else {
							buf.append((char) c);
						}
					}
				} catch (InterruptedIOException e) {
					break;
				} catch (IOException e) {
					if (!forcedClose) {
						Log.error(e.getMessage(), e);
						lostLink = true;
					}
					break;
				} catch (Exception e) {
					Log.error(e.getMessage(), e);
				}
			} while (c > -1 && !forcedClose && !lostLink);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			server.onClose(this);
			try {
				socket.close();
			} catch (Exception ignored) {
			}
			if (!forcedClose) {
				closedRemote();
			}
		}
	}

	private int read(InputStream is) throws Exception {
		if (is.available() > 0) {
			return is.read();
		}
		do {
			synchronized (monitor) {
				monitor.wait(10);
			}
			if (forcedClose) {
				throw new IOException("forced close!");
			}
			if (lostLink) {
				throw new InterruptedIOException("lost link!");
			}
		} while (is.available() <= 0);
		return is.read();
	}

	public void outText(String text) {
		// special symbol color parsing
		for (int i = 0; i < text.length(); i++) {
			char c = text.charAt(i);

			if (c == '{') {
				i++;
				if (i < text.length()) {
					// explicit mapping inherited from old version -> todo: do more clear color model
					int newColor = AnsiColor.toColor(text.charAt(i));

					if (newColor != currentColor) {
						appendColorMask(newColor);
						currentColor = newColor;
					}
				}
			} else if (c == '\n') {
				out.append("\r\n");
			} else {
				out.append(c);
			}
		}
	}

	private synchronized void sendRaw(byte[] data) throws IOException {
		os.write(data);
		os.flush();
	}

	public void flush() throws IOException {
		if (out.length() == 0) {
			return;
		}
		os.write(out.substring(1, out.length()).getBytes());
		os.flush();
		out.delete(1, out.length());
	}

	public void forceClose() {
		forcedClose = true;
		synchronized (monitor) {
			monitor.notify();
		}
	}

	public void appendColorMask(int colorCode) {
		String color;

		switch (colorCode) {
			case AnsiColor.COLOR_CLEAR:
				color = "\033[m";
				break;

			case AnsiColor.COLOR_BLACK:
				color = "\033[0;30m";
				break;

			case AnsiColor.COLOR_RED:
				color = "\033[0;31m";
				break;

			case AnsiColor.COLOR_GREEN:
				color = "\033[0;32m";
				break;

			case AnsiColor.COLOR_YELLOW:
				color = "\033[0;33m";
				break;

			case AnsiColor.COLOR_BLUE:
				color = "\033[0;34m";
				break;

			case AnsiColor.COLOR_MAGENTA:
				color = "\033[0;35m";
				break;

			case AnsiColor.COLOR_CYAN:
				color = "\033[0;36m";
				break;

			case AnsiColor.COLOR_WHITE:
				color = "\033[0;37m";
				break;

				// brighter
			case AnsiColor.COLOR_DARK_GREY:
				color = "\033[1;30m";
				break;

			case AnsiColor.COLOR_BRIGHT_RED:
				color = "\033[1;31m";
				break;

			case AnsiColor.COLOR_BRIGHT_GREEN:
				color = "\033[1;32m";
				break;

			case AnsiColor.COLOR_BRIGHT_YELLOW:
				color = "\033[1;33m";
				break;

			case AnsiColor.COLOR_BRIGHT_BLUE:
				color = "\033[1;34m";
				break;

			case AnsiColor.COLOR_BRIGHT_MAGENTA:
				color = "\033[1;35m";
				break;

			case AnsiColor.COLOR_BRIGHT_CYAN:
				color = "\033[1;36m";
				break;

			case AnsiColor.COLOR_BRIGHT_WHITE:
				color = "\033[1;37m";
				break;

				// special
			case AnsiColor.COLOR_BEEP:
				color = "\007";
				break;

			default:
				Log.warn("Unknown color:" + colorCode);
				return;
		}
		currentColor = colorCode;
		out.append(color);
	}

	protected void ping() {
		try {
			sendRaw(IACAYT);
		} catch (Exception e) {
			Log.error(e.getMessage(), e);
			lostLink = true;
			synchronized (monitor) {
				monitor.notify();
			}
		}
	}
}