/
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.tools.guitool;

import net.sourceforge.pain.network.guitool.*;
import net.sourceforge.pain.tools.guitool.dialog.*;

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

public class GTClientConnection {
    private int sequence_id = 0;

    private long lastEventTime;
    private String host;
    private int port;
    private String login;
    private String password;

    private Socket socket;
    private ObjectInputStream is;
    private ObjectOutputStream os;

    private ConnectionPinger checker;
    private Receiver receiver;
    private Sender sender;


    public GTClientConnection(String host, int port, String login, String password) throws Exception {
        this.host = host;
        this.port = port;
        this.login = login;
        this.password = password;
        connect();
        checker = new ConnectionPinger();
        checker.start();
    }

    private void connect() throws Exception {
        socket = new Socket();
        socket.setSoTimeout(10000);
        socket.connect(new InetSocketAddress(host, port));
        boolean ok = false;
        try {
            os = new ObjectOutputStream(socket.getOutputStream());
            is = new ObjectInputStream(socket.getInputStream());
            receiver = new Receiver();
            sender = new Sender();
            login();
            receiver.start();
            sender.start();
            ok = true;
        } finally {
            if (!ok) {
                disconnect();
            }

        }
    }

    private void login() throws Exception {
        GTNetPacket request = new GTNetPacket("Login", new String[]{login, password}, nextSequenceId());
        sendPacket(request);
        GTNetPacket reply = (GTNetPacket) is.readObject();
        if (!"INFO".equals(reply.eventClassName)) {
            throw new RuntimeException("Login error:" + reply.data);
        }
    }


    private synchronized void sendPacket(GTNetPacket packet) throws IOException {
        os.writeObject(packet);
        os.flush();
        lastEventTime = System.currentTimeMillis();
    }

    public void close() {
        if (socket != null) {
            disconnect();
        }
    }

    private void onRemoteDisconnect() {
        disconnect();
        GlobalPerformer.onRemoteDisconnect();
    }

    private void disconnect() {
        try {
            Socket tmp = socket;
            socket = null;
            is = null;
            os = null;
            tmp.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (checker != null) {
            checker.die();
            checker = null;
        }

        if (receiver != null) {
            receiver.die();
            receiver = null;
        }
        if (sender != null) {
            sender.die();
            sender = null;
        }
    }


    public void addGTConnectionEventListener(GTConnectionListener listener) {
        receiver.addListener(listener);
    }

    public void removeGTConnectionEventListener(GTConnectionListener listener) {
        receiver.removeListener(listener);
    }

    public synchronized int nextSequenceId() {
        return ++sequence_id;
    }

    /** temporary method impl*/
    public GTNetPacket sendBlocking(final GTNetPacket p) throws IOException, InterruptedException {
        final ArrayList result = new ArrayList();
        GTConnectionListener l = new GTConnectionListener() {
            public Object onPacketReceived(GTNetPacket packet) {
                if (packet.sequence_id == p.sequence_id) {
                    result.add(packet);
                    removeGTConnectionEventListener(this);
                    synchronized (result) {
                        result.notify();
                    }
                }
                return null;
            }
        };
        addGTConnectionEventListener(l);
        synchronized (result) {
            sendPacket(p);
            result.wait();
        }
        return (GTNetPacket) result.get(0);
    }


    private final class ConnectionPinger extends Thread {
        private boolean isAlive = true;

        public ConnectionPinger() {
            setDaemon(true);
        }

        public void run() {
            while (isAlive) {
                try {
                    synchronized (this) {
                        wait(5000L);
                    }
                    if (lastEventTime + 5000L > System.currentTimeMillis()) {
                        continue;
                    }
                    sendPacket(new GTNetPacket("Ping", "", nextSequenceId()));
                } catch (InterruptedException e) {
                    break;
                } catch (Exception e) {
                    if (socket != null) {
                        e.printStackTrace();
                        onRemoteDisconnect();
                    }
                    break;
                }
            }
        }

        public void die() {
            isAlive = false;
            synchronized (this) {
                notify();
            }
        }
    }

    private final class Receiver extends Thread {
        private boolean isAlive = true;
        final HashSet listeners = new HashSet();
        final HashSet toRemove = new HashSet();
        final HashSet toAdd = new HashSet();

        public Receiver() {
            setDaemon(true);
        }

        public void run() {
            while (isAlive) {
                try {
                    checkAddRemove();
                    GTNetPacket p = (GTNetPacket) is.readObject();
                    checkAddRemove();
                    for (Iterator it = listeners.iterator(); it.hasNext();) {
                        GTConnectionListener l = (GTConnectionListener) it.next();
                        try {
                            l.onPacketReceived(p);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                } catch (Exception e) {
                    if (socket != null) {
                        e.printStackTrace();
                        onRemoteDisconnect();
                    }
                    break;
                }
            }
        }

        private void checkAddRemove() {
            synchronized (listeners) {
                if (!toAdd.isEmpty()) {
                    listeners.addAll(toAdd);
                    toAdd.clear();
                }
                if (!toRemove.isEmpty()) {
                    listeners.removeAll(toRemove);
                    toRemove.clear();
                }
            }
        }

        public void addListener(GTConnectionListener l) {
            synchronized (listeners) {
                toAdd.add(l);
            }
        }

        public void removeListener(GTConnectionListener l) {
            synchronized (listeners) {
                toRemove.add(l);
            }
        }

        public void die() {
            isAlive = false;
            synchronized (this) {
                notify();
            }
        }
    }

    private final class Sender extends Thread {
        private boolean isAlive = true;
        private LinkedList outQueue = new LinkedList();

        public Sender() {
            setDaemon(true);
        }

        public void run() {
            while (isAlive) {
                try {
                    if (outQueue.isEmpty()) {
                        synchronized (outQueue) {
                            outQueue.wait(50);
                        }
                        continue;
                    }

                    synchronized (outQueue) {
                        while (!outQueue.isEmpty()) {
                            GTNetPacket p = (GTNetPacket) outQueue.removeFirst();
                            os.writeObject(p);
                        }
                    }
                    os.flush();
                } catch (Exception e) {
                    if (socket != null) {
                        e.printStackTrace();
                        onRemoteDisconnect();
                    }
                    break;
                }
            }
        }

        public void send(GTNetPacket p) {
            synchronized (outQueue) {
                outQueue.add(p);
                outQueue.notify();
            }
        }

        public void die() {
            isAlive = false;
            synchronized (this) {
                notify();
            }
        }
    }

}