package kamiworldserver.elem;

import kamiworldserver.comm.KamiCommand;
import kamiworldserver.control.KamiProcessor;

import java.net.Socket;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;

public class KamiUser {

    public static long nTimeoutDiff = 10000;
    private Socket sSocket;
    private KamiMap km_Map;
    private KamiProcessor kp_Parent;
    private DataInputStream isInput;
    private DataOutputStream osOutput;
    private KamiCommand kc_Command;
    private String sUserName;
    private int nUserID;
    private short x;
    private short y;
    private short vx;
    private short vy;
    private byte dir;
    private String sName;
    private String sFile;
    private String sColors;
    private int nID;
    private long nTime;
    private long nTimeout;

    public KamiUser(Socket s, KamiProcessor kp) throws IOException {

        sSocket = s;
        kp_Parent = kp;

        kc_Command = new KamiCommand();
        nTimeout = System.currentTimeMillis();

        osOutput = new DataOutputStream(sSocket.getOutputStream());
        isInput = new DataInputStream(sSocket.getInputStream());

    }

    public boolean process() {

        try {
            //System.out.println("User process: "+sUserName+" entered a processing function.");
            if (!sSocket.isConnected() || sSocket.isClosed() || sSocket.isInputShutdown() || sSocket.isOutputShutdown()) {
                close();
                return true;
            }

            nID = -1;

            if (isInput.available() > 0) {
                nID = isInput.readInt();
                if (nID == -1) {
                    close();
                    return true;
                }
                //System.out.println("User : read command.");
                readCommand();
            } else if (System.currentTimeMillis() > nTimeout + KamiUser.nTimeoutDiff) {
                //System.out.println("User : Ping send for "+sUserName);
                osOutput.writeInt(50);
                osOutput.flush();
                nID = -1;
                nID = isInput.readInt();
                //System.out.println("User : Respond recived for "+sUserName);
                if (nID == -1) {
                    close();
                    return true;
                } else if (nID == 51) {
                    nTimeout = System.currentTimeMillis();
                } else {
                    readCommand();
                }
            }

        } 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.toString());
            kamiworldserver.KamiServer.reconnect();
            process();
            //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 {
        KamiProcessor.getCommand(nID, kc_Command, isInput);
        kc_Command.setCaller(this);
        kp_Parent.addCommand(kc_Command);
        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);
        osOutput.write(b, 0, p);
        osOutput.flush();
    }

    // Sends raw data
    public void sendRaw(byte[] b) throws IOException {
        if (!sSocket.isConnected() || sSocket.isClosed() || sSocket.isInputShutdown() || sSocket.isOutputShutdown()) {
            return;
        }

        osOutput.write(b);
        osOutput.flush();
    }

    // Sends on empty pocket with just an ID
    public void sendID(int id) throws IOException {
        if (!sSocket.isConnected() || sSocket.isClosed() || sSocket.isInputShutdown() || sSocket.isOutputShutdown()) {
            return;
        }

        osOutput.writeInt(id);
    }

    // =========================================================================
    // Other
    //
    public void setUserName(String n) {
        sUserName = n;
    }

    public void setMap(KamiMap km) {
        km_Map = km;
        km.addPlayer(this);
    }

    public void setID(int id) {
        nUserID = id;
    }

    public void setDir(byte d) {
        dir = d;
    }

    public void setData(String name, String file, String colors) {
        sName = name;
        sFile = file;
        sColors = colors;
    }

    public void setLocation(short nx, short ny) {
        x = nx < 0 || nx > 1020 ? 500 : nx;
        y = ny < 0 || ny > 1020 ? 500 : ny;
    }

    public void setVectors(short nx, short ny) {
        vx = nx;
        vy = ny;
    }

    public void setVectors(int nx, int ny) {
        vx = (short) nx;
        vy = (short) ny;
    }

    public void setTime(long t) {
        nTime = t;
    }

    public String getUserName() {
        return sUserName;
    }

    public int getID() {
        return nUserID;
    }

    public String getName() {
        return sName;
    }

    public String getFile() {
        return sFile;
    }

    public String getColors() {
        return sColors;
    }

    public short getX() {
        return x;
    }

    public short getY() {
        return y;
    }

    public byte getDir() {
        return dir;
    }

    public long getTime() {
        return nTime;
    }

    public KamiMap getMap() {
        return km_Map;
    }

    public Socket getSocket() {
        return sSocket;
    }

    public boolean checkSame(Socket s) {
        return s.getInetAddress().equals(sSocket.getInetAddress());
    }

    public void move() {
        x += vx;
        y += vy;
    }

    public boolean isReadyToMove() {
        return vx == 0 && vy == 0;
    }

    public void checkAndSetLocation(short nx, short ny) {
        int tmin;
        int tmax;

        //x
        if (x < x - vx) {
            tmin = x;
            tmax = x - vx;
        } else {
            tmin = x - vx;
            tmax = x;
        }
        if (nx >= tmin && nx <= tmax) {
            x = nx;
        }

        //y
        if (y < y - vy) {
            tmin = y;
            tmax = y - vy;
        } else {
            tmin = y - vy;
            tmax = y;
        }
        if (ny >= tmin && ny <= tmax) {
            y = ny;
        }
    }

    public 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");
        }

        kp_Parent.removeUser(this);
        if (km_Map != null) {
            km_Map.removePlayer(this);

            //sending disconnect broadcast
            kc_Command.clear();
            kc_Command.setCaller(this);
            kc_Command.setID(12);
            kc_Command.set(0, nUserID);
            kp_Parent.addCommand(kc_Command);
        }
    }
}
