package kamicharserver;

import java.net.*;
import java.io.*;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.Logger;

public class KamiUser {

    Socket sSocket;
    DataInputStream isInput;
    DataOutputStream osOutput;
    KamiProcessor kt_Process;
    KamiCommand kc_Command;
    KamiChar[] kc_Chars;
    byte nCurrent;
    int nID;
    long nTimeout;

    //
    // Constructor
    public KamiUser(Socket socket, KamiProcessor proc) throws IOException {
        kt_Process = proc;
        sSocket = socket;
        osOutput = new DataOutputStream(sSocket.getOutputStream());
        isInput = new DataInputStream(sSocket.getInputStream());

        nCurrent = 0;
        kc_Chars = new KamiChar[5];
        //kc_Command = new KamiCommand();
        nTimeout = System.currentTimeMillis();
    }

    // =========================================================================
    // Main Methods
    //
    public boolean process() {

        try {

            if (!sSocket.isConnected() || sSocket.isClosed() || sSocket.isInputShutdown() || sSocket.isOutputShutdown()) {
                close();
                return true;
            }

            nID = -1;

            // Command was not processed correctly
            if(kc_Command!=null) {
                System.out.println("[KamiUser::process] Reparsing command : " + kc_Command.toString());
                kt_Process.respond(kc_Command, this);
                osOutput.flush();
                kc_Command = null;
                nTimeout = System.currentTimeMillis();
            }

            if (isInput.available() > 0) {
                nID = isInput.readInt();
                if (nID == -1) {
                    close();
                    return true;
                }
                readCommand();
            } else if (System.currentTimeMillis() > nTimeout + KamiCharServer.nTimeout) {
                osOutput.writeInt(20);
                osOutput.flush();
                nID = -1;
                nID = isInput.readInt();
                if (nID == -1) {
                    close();
                    return true;
                } else if (nID == 21) {
                    nTimeout = System.currentTimeMillis();
                } else {
                    readCommand();
                }
            }
            kc_Command = null;


        } catch (IOException ex) {
            System.out.println("Failed to read or write to Stream. Message: " + ex);
            close();
            return true;

        } catch (java.sql.SQLException ex) {
            System.out.println("Failed to process requested command. Message: "+ ex);
            //ex.printStackTrace();
            KamiCharServer.reconnect();
            process();
            //close();
            //return true;

        } catch (Exception ex) {
            System.out.println("Unknow error. Message: " + ex);
            close();
            return true;
        }

        return false;
    }

    private void readCommand() throws java.sql.SQLException, IOException {
        kc_Command = kt_Process.parse(nID, isInput);
        System.out.println("Command recived: " + kc_Command.toString());
        kt_Process.respond(kc_Command, this);
        osOutput.flush();
        kc_Command = null;
        nTimeout = System.currentTimeMillis();
    }

    public void sendCommand(int id, byte[] b, int p) throws IOException {
        if (!sSocket.isConnected() || sSocket.isClosed() || sSocket.isInputShutdown() || sSocket.isOutputShutdown()) {
            return;
        }

        osOutput.writeInt(id);
        /*
        //fake lag
        int i = 0;
        while(i<p) {
        osOutput.write(b[i]);
        try {
        Thread.sleep( (int)Math.floor(Math.random()*10));
        } catch (InterruptedException ex) {
        Logger.getLogger(KamiUser.class.getName()).log(Level.SEVERE, null, ex);
        }
        ++i;
        }
         */
        osOutput.write(b, 0, p);
        osOutput.flush();
    }

    public boolean checkCID(int cid) {

        for (KamiChar kc : kc_Chars) {
            if (kc != null && kc.getCID() == cid) {
                return true;
            }
        }
        return false;
    }

    public OutputStream getStream() {
        return osOutput;
    }

    public void addChar(KamiChar kc) {
        kc_Chars[nCurrent] = kc;
        ++nCurrent;
    }

    public void removeChar(byte cid) {

        byte i = 0;
        while (i < kc_Chars.length) {
            if (kc_Chars[i] != null && kc_Chars[i].getCID() == cid) {
                break;
            }
            i++;
        }

        while (i + 1 < kc_Chars.length) {
            kc_Chars[i] = kc_Chars[i + 1];
            i++;
        }
        kc_Chars[i] = null;
        nCurrent--;
    }

    public KamiChar[] getChars() {
        return kc_Chars;
    }

    private void close() {
        try {
            isInput.close();
        } catch (Exception ec) {
            System.out.println("Failed to close Input");
        }

        try {
            osOutput.close();
        } catch (Exception ec) {
            System.out.println("Failed to close Output");
        }

        try {
            sSocket.close();
        } catch (Exception ec) {
            System.out.println("Failed to close Socket");
        }

        System.out.println("User disconnected.");
        KamiCharServer.main.removeUser(this);
    }
}
