/
codebase/
codebase/area/
codebase/doc/
codebase/etc/
codebase/src/net/sourceforge/pain/data/trigger/
codebase/src/net/sourceforge/pain/logic/
codebase/src/net/sourceforge/pain/logic/affect/
codebase/src/net/sourceforge/pain/logic/event/
codebase/src/net/sourceforge/pain/logic/event/deploy/
codebase/src/net/sourceforge/pain/logic/event/guitool/
codebase/src/net/sourceforge/pain/logic/event/guitool/event/
codebase/src/net/sourceforge/pain/logic/fn/util/
codebase/src/net/sourceforge/pain/logic/trigger/
codebase/src/net/sourceforge/pain/logic/trigger/impl/
codebase/src/net/sourceforge/pain/network/console/
codebase/src/net/sourceforge/pain/network/console/telnet/
codebase/src/net/sourceforge/pain/network/guitool/
codebase/src/net/sourceforge/pain/plugin/
codebase/src/net/sourceforge/pain/plugin/command/
codebase/src/net/sourceforge/pain/plugin/reset/
codebase/src/net/sourceforge/pain/plugin/shutdown/
codebase/src/net/sourceforge/pain/plugin/social/
codebase/src/net/sourceforge/pain/util/
db/doc/javadoc/resources/
db/src/net/sourceforge/pain/util/
gui/
gui/lib/
gui/src/net/sourceforge/pain/tools/guitool/dbbrowse/
gui/src/net/sourceforge/pain/tools/guitool/dialog/
gui/src/net/sourceforge/pain/tools/guitool/menu/
gui/src/net/sourceforge/pain/tools/guitool/resources/
gui/src/net/sourceforge/pain/tools/guitool/resources/images/
gui/src/net/sourceforge/pain/tools/guitool/resources/images/explorer/
tests/
tests/src/
tests/src/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.*;
import java.util.*;


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;

    /**
     * no op
     */
//    private final static int NOP = 241;


    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};

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

    private boolean forcedClose;
    private boolean lostLink;

    private final Object monitor = new Object();
    private final Object flushMonitor = new Object();

    private ArrayList flushBlocks = new ArrayList();
    private ArrayList flushBlocksShadow = new ArrayList();

    private int currentColor = AnsiColor.COLOR_CLEAR;

    private StringBuffer out = new StringBuffer();
    private boolean wantFlush = false;
    long lastFlushTime;

    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
//                            Log.debug("TELNET:IAC");
                            c = read(is);
                            if (c != IAC) { // its really command
                                if (c == WILL) { // if command is WILL echo DON'T
                                    c = read(is);
//                                    Log.debug("TELNET:will:" + c);
                                    IACDONT[2] = (byte) c;
                                    sendRaw((byte[]) IACDONT.clone()); // we do not want any command (do not support)
                                } else if (c == DO) {// if command is DO echo WON'T
                                    c = read(is);
//                                    Log.debug("TELNET:do:" + c);
                                    IACWONT[2] = (byte) c;
                                    sendRaw((byte[]) IACWONT.clone());
                                } else {
//                                    Log.debug("TELNET: skipping command:" + c);
                                }
                                continue;
                            }
                        }
                        if (c == '\r') {
                            continue;
                        }
                        if (c == '\n') {
                            if (buf.length() > 0) {
                                lineReceived(buf.substring(0));
                                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!");
            }
            if (wantFlush) {
                ArrayList blocks;
                synchronized (flushMonitor) {
                    blocks = flushBlocks;
                    flushBlocks = flushBlocksShadow;
                    flushBlocksShadow = blocks;
                    wantFlush = false;
                }
                int len = blocks.size();
                if (len > 0) {
                    for (int i = 0; i < len; i++) {
                        byte[] block = (byte[]) blocks.get(i);
                        os.write(block);
                    }
//                    os.write(0);
                    lastFlushTime = System.currentTimeMillis();
                    os.flush();
                    flushBlocksShadow.clear();
                }
            }
        } 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:
                    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) {
        flushBlocks.add(data);
        flush();
    }

    public void flush() {
        if (out.length() == 0) {
            wantFlush = flushBlocks.size() > 0;
            return;
        }
        synchronized (flushMonitor) {
            flushBlocks.add(out.substring(0).getBytes());
            wantFlush = true;
        }
        out.delete(0, 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() {
//        sendRaw(IACAYT);
        sendRaw(new byte[]{0});//windows telnet does not like IACs..
    }
}