import java.io.IOException;
import java.math.BigInteger;

public class GameConnection extends GameShell {

    public static int clientVersion = 1;
    public static int maxReadTries;
    private static BigInteger rsaExponent = new BigInteger("58778699976184461502525193738213253649000149147835990136706041084440742975821");
    //private static BigInteger rsaModulus = new BigInteger("7162900525229798032761816791230527296329313291232324290237849263501208207972894053929065636522363163621000728841182238772712427862772219676577293600221789");
    private static BigInteger rsaModulus = new BigInteger("7656522762491711741880224224809835569769759737077076094091609307381193032090602256314159126169417567841597729801408692196383745596665658895073411749475443");
    private final int maxSocialListSize = 100;
    public String server;
    public int port;
    public ClientStream clientStream;
    public int friendListCount;
    public long friendListHashes[];
    public int friendListOnline[];
    public int ignoreListCount;
    public long ignoreList[];
    public int settingsBlockChat;
    public int settingsBlockPrivate;
    public int settingsBlockTrade;
    public int settingsBlockDuel;
    public long sessionID;
    public int worldFullTimeout;
    public int moderatorLevel;
    String username;
    String password;
    byte incomingPacket[];
    int autoLoginTimeout;
    long packetLastRead;
    private int anIntArray629[];
    private int anInt630;

    public GameConnection() {
        server = "127.0.0.1";
        port = 43594;
        username = "";
        password = "";
        incomingPacket = new byte[5000];
        friendListHashes = new long[200];
        friendListOnline = new int[200];
        ignoreList = new long[maxSocialListSize];
        anIntArray629 = new int[maxSocialListSize];
    }

    protected void login(String u, String p, boolean reconnecting) {
        if (worldFullTimeout > 0) {
            showLoginScreenStatus("Please wait...", "Connecting to server");
            try {
                Thread.sleep(2000L);
            } catch (Exception Ex) {
            }
            showLoginScreenStatus("Sorry! The server is currently full.", "Please try again later");
            return;
        }
        try {
            username = u;
            u = Utility.formatAuthString(u, 20);
            password = p;
            p = Utility.formatAuthString(p, 20);
            if (u.trim().length() == 0) {
                showLoginScreenStatus("You must enter both a username", "and a password - Please try again");
                return;
            }
            if (reconnecting)
                drawTextBox("Connection lost! Please wait...", "Attempting to re-establish");
            else
                showLoginScreenStatus("Please wait...", "Connecting to server");
            clientStream = new ClientStream(createSocket(server, port), this);
            clientStream.maxReadTries = maxReadTries;
            long l = Utility.username2hash(u);
            clientStream.newPacket((Command.CL_SESSION));
            clientStream.putByte((int) (l >> 16 & 31L));
            clientStream.flushPacket();
            long sessid = clientStream.getLong();
            sessionID = sessid;
            if (sessid == 0L) {
                showLoginScreenStatus("Login server offline.", "Please try again in a few mins");
                return;
            }
            System.out.println("Verb: Session id: " + sessid);
            int limit30 = 0;
            /*
            try {
                if (getStartedAsApplet()) {
                    String s2 = getParameter("limit30");
                    if (s2.equals("1"))
                        limit30 = 1;
                }
            } catch (Exception Ex) {
            }*/
            int ai[] = new int[4];
            ai[0] = (int) (Math.random() * 99999999D);
            ai[1] = (int) (Math.random() * 99999999D);
            ai[2] = (int) (sessid >> 32);
            ai[3] = (int) sessid;

            // TODO maybe re-enable RSA later
            /*
            Buffer buffer = new Buffer(new byte[500]);
            buffer.offset = 0;
            buffer.putByte(10);
            buffer.putInt(ai[0]);
            buffer.putInt(ai[1]);
            buffer.putInt(ai[2]);
            buffer.putInt(ai[3]);
            buffer.putInt(getLinkUID());
            buffer.putString(u);
            buffer.putString(p);
            buffer.encrypt(rsaExponent, rsaModulus);
            */

            clientStream.newPacket((Command.CL_LOGIN));

            if (reconnecting)
                clientStream.putByte(1);
            else
                clientStream.putByte(0);
            clientStream.putShort(clientVersion);
            clientStream.putByte(limit30);

            clientStream.putByte(10);
            clientStream.putInt(ai[0]);
            clientStream.putInt(ai[1]);
            clientStream.putInt(ai[2]);
            clientStream.putInt(ai[3]);
            clientStream.putInt(getLinkUID());
            clientStream.putString(u);
            clientStream.putString(p);

            //clientStream.putBytes(buffer.buffer, 0, buffer.offset);

            clientStream.flushPacket();
            clientStream.seedIsaac(ai);
            int resp = clientStream.readStream();
            System.out.println("login response:" + resp);
            if (resp == 25) {
                moderatorLevel = 1;
                autoLoginTimeout = 0;
                resetGame();
                return;
            }
            if (resp == 0) {
                moderatorLevel = 0;
                autoLoginTimeout = 0;
                resetGame();
                return;
            }
            if (resp == 1) {
                autoLoginTimeout = 0;
                method37();
                return;
            }
            if (reconnecting) {
                u = "";
                p = "";
                resetLoginVars();
                return;
            }
            if (resp == -1) {
                showLoginScreenStatus("Error unable to login.", "Server timed out");
                return;
            }
            if (resp == 3) {
                showLoginScreenStatus("Invalid username or password.", "Try again, or create a new account");
                return;
            }
            if (resp == 4) {
                showLoginScreenStatus("That username is already logged in.", "Wait 60 seconds then retry");
                return;
            }
            if (resp == 5) {
                showLoginScreenStatus("The client has been updated.", "Please reload this page");
                return;
            }
            if (resp == 6) {
                showLoginScreenStatus("You may only use 1 character at once.", "Your ip-address is already in use");
                return;
            }
            if (resp == 7) {
                showLoginScreenStatus("Login attempts exceeded!", "Please try again in 5 minutes");
                return;
            }
            if (resp == 8) {
                showLoginScreenStatus("Error unable to login.", "Server rejected session");
                return;
            }
            if (resp == 9) {
                showLoginScreenStatus("Error unable to login.", "Loginserver rejected session");
                return;
            }
            if (resp == 10) {
                showLoginScreenStatus("That username is already in use.", "Wait 60 seconds then retry");
                return;
            }
            if (resp == 11) {
                showLoginScreenStatus("Account temporarily disabled.", "Check your message inbox for details");
                return;
            }
            if (resp == 12) {
                showLoginScreenStatus("Account permanently disabled.", "Check your message inbox for details");
                return;
            }
            if (resp == 14) {
                showLoginScreenStatus("Sorry! This world is currently full.", "Please try a different world");
                worldFullTimeout = 1500;
                return;
            }
            if (resp == 15) {
                showLoginScreenStatus("You need a members account", "to login to this world");
                return;
            }
            if (resp == 16) {
                showLoginScreenStatus("Error - no reply from loginserver.", "Please try again");
                return;
            }
            if (resp == 17) {
                showLoginScreenStatus("Error - failed to decode profile.", "Contact customer support");
                return;
            }
            if (resp == 18) {
                showLoginScreenStatus("Account suspected stolen.", "Press 'recover a locked account' on front page.");
                return;
            }
            if (resp == 20) {
                showLoginScreenStatus("Error - loginserver mismatch", "Please try a different world");
                return;
            }
            if (resp == 21) {
                showLoginScreenStatus("Unable to login.", "That is not an RS-Classic account");
                return;
            }
            if (resp == 22) {
                showLoginScreenStatus("Password suspected stolen.", "Press 'change your password' on front page.");
                return;
            } else {
                showLoginScreenStatus("Error unable to login.", "Unrecognised response code");
                return;
            }
        } catch (Exception exception) {
            System.out.println(String.valueOf(exception));
        }
        if (autoLoginTimeout > 0) {
            try {
                Thread.sleep(5000L);
            } catch (Exception Ex) {
            }
            autoLoginTimeout--;
            login(username, password, reconnecting);
        }
        if (reconnecting) {
            username = "";
            password = "";
            resetLoginVars();
        } else {
            showLoginScreenStatus("Sorry! Unable to connect.", "Check internet settings or try another world");
        }
    }

    protected void closeConnection() {
        if (clientStream != null)
            try {
                clientStream.newPacket((Command.CL_CLOSE_CONNECTION));
                clientStream.flushPacket();
            } catch (IOException Ex) {
            }
        username = "";
        password = "";
        resetLoginVars();
    }

    protected void lostConnection() {
        try {
            throw new Exception("");
        } catch (Exception ex) {
            System.out.println("loast connection: ");
            ex.printStackTrace();
        }
        System.out.println("Lost connection");
        autoLoginTimeout = 10;
        login(username, password, true);
    }

    protected void drawTextBox(String s, String s1) {
        /*Graphics g = getGraphics();
        Font font = new Font("Helvetica", 1, 15);
        char c = '\u0200';
        char c1 = '\u0158';
        g.setColor(Color.black);
        g.fillRect(c / 2 - 140, c1 / 2 - 25, 280, 50);
        g.setColor(Color.white);
        g.drawRect(c / 2 - 140, c1 / 2 - 25, 280, 50);
        drawString(g, s, font, c / 2, c1 / 2 - 10);
        drawString(g, s1, font, c / 2, c1 / 2 + 10);*/
    }

    protected void checkConnection() {
        long l = System.currentTimeMillis();
        if (clientStream.hasPacket())
            packetLastRead = l;
        if (l - packetLastRead > 5000L) {
            packetLastRead = l;
            clientStream.newPacket((Command.CL_PING));
            clientStream.sendPacket();
        }
        try {
            clientStream.writePacket(20);
        } catch (IOException Ex) {
            lostConnection();
            return;
        }
        if (!method43())
            return;
        int psize = clientStream.readPacket(incomingPacket);
        if (psize > 0) {
            int ptype = clientStream.isaacCommand(incomingPacket[0] & 0xff);
            handlePacket(ptype, ptype, psize);
        }
    }

    private void handlePacket(int opcode, int ptype, int psize) {
        //ptype = clientStream.isaacCommand(ptype);
        //System.out.println(String.format("opcode:%s(%d) psize:%d", opcode.name(), ptype, psize));
        System.out.println("opcode:" + opcode + " psize:" + psize);
        if (opcode == Command.SV_MESSAGE) {
            String s = new String(incomingPacket, 1, psize - 1);
            showServerMessage(s);
        }
        if (opcode == Command.SV_CLOSE_CONNECTION)
            closeConnection();
        if (opcode == Command.SV_LOGOUT_DENY) {
            cantLogout();
            return;
        }
        if (opcode == Command.SV_FRIEND_LIST) {
            friendListCount = Utility.getUnsignedByte(incomingPacket[1]);
            for (int k = 0; k < friendListCount; k++) {
                friendListHashes[k] = Utility.getUnsignedLong(incomingPacket, 2 + k * 9);
                friendListOnline[k] = Utility.getUnsignedByte(incomingPacket[10 + k * 9]);
            }

            sortFriendsList();
            return;
        }
        if (opcode == Command.SV_FRIEND_STATUS_CHANGE) {
            long hash = Utility.getUnsignedLong(incomingPacket, 1);
            int online = incomingPacket[9] & 0xff;
            for (int i2 = 0; i2 < friendListCount; i2++)
                if (friendListHashes[i2] == hash) {
                    if (friendListOnline[i2] == 0 && online != 0)
                        showServerMessage("@pri@" + Utility.hash2username(hash) + " has logged in");
                    if (friendListOnline[i2] != 0 && online == 0)
                        showServerMessage("@pri@" + Utility.hash2username(hash) + " has logged out");
                    friendListOnline[i2] = online;
                    psize = 0; // not sure what this is for
                    sortFriendsList();
                    return;
                }

            friendListHashes[friendListCount] = hash;
            friendListOnline[friendListCount] = online;
            friendListCount++;
            sortFriendsList();
            return;
        }
        if (opcode == Command.SV_IGNORE_LIST) {
            ignoreListCount = Utility.getUnsignedByte(incomingPacket[1]);
            for (int i1 = 0; i1 < ignoreListCount; i1++)
                ignoreList[i1] = Utility.getUnsignedLong(incomingPacket, 2 + i1 * 8);

            return;
        }
        if (opcode == Command.SV_PRIVACY_SETTINGS) {
            settingsBlockChat = incomingPacket[1];
            settingsBlockPrivate = incomingPacket[2];
            settingsBlockTrade = incomingPacket[3];
            settingsBlockDuel = incomingPacket[4];
            return;
        }
        if (opcode == Command.SV_FRIEND_MESSAGE) {
            long from = Utility.getUnsignedLong(incomingPacket, 1);
            int k1 = Utility.getUnsignedInt(incomingPacket, 9); // is this some sort of message id ?
            for (int j2 = 0; j2 < maxSocialListSize; j2++)
                if (anIntArray629[j2] == k1)
                    return;

            anIntArray629[anInt630] = k1;
            anInt630 = (anInt630 + 1) % maxSocialListSize;
            String msg = WordFilter.filter(ChatMessage.descramble(incomingPacket, 13, psize - 13));
            showServerMessage("@pri@" + Utility.hash2username(from) + ": tells you " + msg);
            return;
        } else {
            handleIncomingPacket(opcode, ptype, psize, incomingPacket);
            return;
        }
    }

    private void sortFriendsList() {
        boolean flag = true;
        while (flag) {
            flag = false;
            for (int i = 0; i < friendListCount - 1; i++)
                if (friendListOnline[i] != 255 && friendListOnline[i + 1] == 255 || friendListOnline[i] == 0 && friendListOnline[i + 1] != 0) {
                    int j = friendListOnline[i];
                    friendListOnline[i] = friendListOnline[i + 1];
                    friendListOnline[i + 1] = j;
                    long l = friendListHashes[i];
                    friendListHashes[i] = friendListHashes[i + 1];
                    friendListHashes[i + 1] = l;
                    flag = true;
                }

        }
    }

    protected void sendPrivacySettings(int chat, int priv, int trade, int duel) {
        clientStream.newPacket((Command.CL_SETTINGS_PRIVACY));
        clientStream.putByte(chat);
        clientStream.putByte(priv);
        clientStream.putByte(trade);
        clientStream.putByte(duel);
        clientStream.sendPacket();
    }

    protected void ignoreAdd(String s) {
        long l = Utility.username2hash(s);
        clientStream.newPacket((Command.CL_IGNORE_ADD));
        clientStream.putLong(l);
        clientStream.sendPacket();
        for (int i = 0; i < ignoreListCount; i++)
            if (ignoreList[i] == l)
                return;

        if (ignoreListCount >= maxSocialListSize) {
            return;
        } else {
            ignoreList[ignoreListCount++] = l;
            return;
        }
    }

    protected void ignoreRemove(long l) {
        clientStream.newPacket((Command.CL_IGNORE_REMOVE));
        clientStream.putLong(l);
        clientStream.sendPacket();
        for (int i = 0; i < ignoreListCount; i++)
            if (ignoreList[i] == l) {
                ignoreListCount--;
                for (int j = i; j < ignoreListCount; j++)
                    ignoreList[j] = ignoreList[j + 1];

                return;
            }

    }

    protected void friendAdd(String s) {
        clientStream.newPacket((Command.CL_FRIEND_ADD));
        clientStream.putLong(Utility.username2hash(s));
        clientStream.sendPacket();
        long l = Utility.username2hash(s);
        for (int i = 0; i < friendListCount; i++)
            if (friendListHashes[i] == l)
                return;

        if (friendListCount >= maxSocialListSize) {
            return;
        } else {
            friendListHashes[friendListCount] = l;
            friendListOnline[friendListCount] = 0;
            friendListCount++;
            return;
        }
    }

    protected void friendRemove(long l) {
        clientStream.newPacket((Command.CL_FRIEND_REMOVE));
        clientStream.putLong(l);
        clientStream.sendPacket();
        for (int i = 0; i < friendListCount; i++) {
            if (friendListHashes[i] != l)
                continue;
            friendListCount--;
            for (int j = i; j < friendListCount; j++) {
                friendListHashes[j] = friendListHashes[j + 1];
                friendListOnline[j] = friendListOnline[j + 1];
            }

            break;
        }

        showServerMessage("@pri@" + Utility.hash2username(l) + " has been removed from your friends list");
    }

    protected void sendPrivateMessage(long u, byte buff[], int len) {
        clientStream.newPacket((Command.CL_PM));
        clientStream.putLong(u);
        clientStream.putBytes(buff, 0, len);
        clientStream.sendPacket();
    }

    protected void sendChatMessage(byte buff[], int len) {
        clientStream.newPacket((Command.CL_CHAT));
        clientStream.putBytes(buff, 0, len);
        clientStream.sendPacket();
    }

    protected void sendCommandString(String s) {
        clientStream.newPacket((Command.CL_COMMAND));
        clientStream.putString(s);
        clientStream.sendPacket();
    }

    protected void showLoginScreenStatus(String s, String s1) {
    }

    protected void method37() {
    }

    protected void resetGame() {
    }

    protected void resetLoginVars() {
    }

    protected void cantLogout() {
    }

    protected void handleIncomingPacket(int opcode, int ptype, int len, byte data[]) {
    }

    protected void showServerMessage(String s) {
    }

    protected boolean method43() {
        return true;
    }

    protected int getLinkUID() {
        return 0;
    }

}
