package kr.co.icontrols.ilp300_700.ifconverter;

import android.content.Context;
import android.os.Handler;
import android.util.Log;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.InvalidParameterException;

import android_serialport_api.SerialPort;
import kr.co.icontrols.ilp300_700.global.Configure.ConfigValue;
import kr.co.icontrols.ilp300_700.global.Declare.MultiReaderPacket;
import kr.co.icontrols.ilp300_700.global.Declare.SmartAutoDoor;
import kr.co.icontrols.ilp300_700.global.Global;
import kr.co.icontrols.ilp300_700.rfreader.RFReaderManager;

public class SmartAutoDoorIFManager {
    final static String TAG = "SmartAutoDoorIFManager";
    protected static OutputStream mOutputStream;
    static boolean bLOG = true;   // true, false
    static boolean bLOG_DEBUG = true;   // true, false
    static boolean bLOG_INFO = true;   // true, false
    static Context mContext;
    static long lSocketConnectionPeriod = 0;
    static int TIMEOUT_iMAP_WAIT = 1000;   // iMAP 타임아웃은 실제 3초 이지만, 마진을 두어 4초로 한다.
    static int SOCKET_BUFFER_SIZE_READ = 60;
    public final int HANDLE_MSG_DONE = 100;
    public final int HANDLE_MSG_ING = 200;
    public final int HANDLE_MSG_ERROR_TIMEOUT = 800;
    public final int HANDLE_MSG_ERROR_TIMEOUT_READ = 801;
    public final int HANDLE_MSG_ERROR = 900;
    public final int HANDLE_MSG_ERROR_READ = 901;
    public final int HANDLE_MSG_ERROR_MESSAGE = 902;
    public final int HANDLE_MSG_NONE = 1000;
    private final String SERIALPORTINFO = "COM3_ttyS3_Baudrate_9600";
    protected InputStream mInputStream;
    int SERIAL_RECEIVE_TIMEOUT = 80;   // Packet receiving duration from STX to ETX
    int SERIAL_PACKET_EXPECTED_LENGTH_MIN = 8;   // data packet length
    int SERIAL_PACKET_EXPECTED_LENGTH = 8;   // Cardreader data packet length
    int SERIAL_PACKET_MAX_LENGTH = 112;   // MAX data packet length
    boolean bSTART_DATA = false;
    int nHandlerResult = HANDLE_MSG_NONE;
    byte[] hRcvData = new byte[SERIAL_PACKET_MAX_LENGTH];
    byte[] hSamplePacket = {(byte) 0x28, (byte) 0x31, (byte) 0x42, (byte) 0xA0, (byte) 0x07, (byte) 0x12, (byte) 0x11, (byte) 0x22, (byte) 0x33, (byte) 0x44, (byte) 0x55, (byte) 0x66, (byte) 0x77, (byte) 0x28, (byte) 0x29};
    boolean bCheckPacket = false;
    RS485EventListener mRS485EventListener;
    private SerialPort mSerialPort = null;
    private SerialDataReceiver mSerialDataReceiver;

    public SmartAutoDoorIFManager(Context context) {
        mContext = context;
    }

    private static void LOG(String log) {
        if (bLOG) Log.v(TAG, log);
    }

    private static void LOG_DEBUG(String log) {
        if (bLOG_DEBUG) Log.d(TAG, log);
    }

    private static void LOG_INFO(String log) {
        if (bLOG_INFO) Log.i(TAG, log);
    }

    public static void sendReplySmartKeyAccess(boolean bOpen, byte[] hData) {
        try {
            LOG("[sendReplySmartKeyAccess] bOpen = " + bOpen + ", hData.length = " + hData.length);
            if (hData.length != 22) {
                Log.e(TAG, "[sendReplySmartKeyAccess] hData.length is out of range!!");
                return;
            }
            byte[] hMsg = new byte[hData.length + 6];

            hMsg[0] = MultiReaderPacket.STX;
            hMsg[1] = MultiReaderPacket.ID_RFREADER;   // Dst ID : ICRD-50LP = 0x11, LobbyPhone = 0x1A
            hMsg[2] = MultiReaderPacket.ID_LOBBY;   // Src ID : ICRD-50LP = 0x11, LobbyPhone = 0x1A
            if (bOpen) {
                hMsg[3] = MultiReaderPacket.CMD_SMARTKEY_DOOROPEN;   // CMD : SmartKey 응답
            } else {
                hMsg[3] = MultiReaderPacket.CMD_SMARTKEY_INVALID;   // CMD : SmartKey 응답
            }

            for (int i = 0; i < hData.length; i++) {
                hMsg[i + 4] = hData[i];
            }

            hMsg[hMsg.length - 2] = makeChecksum(hMsg);
            hMsg[hMsg.length - 1] = MultiReaderPacket.ETX;   // ETX

            mOutputStream.write(hMsg);
            mOutputStream.flush();
            LOG_INFO("[sendReplySmartKeyAccess] hMsg = " + Global.printHexs(hMsg));
        } catch (Exception e) {
            Log.e(TAG, "[Exception] sendReplySmartKeyAccess(bOpen, hData)");
            e.printStackTrace();
        }
    }

    public static byte makeChecksum(byte[] hData) {
        try {
            byte hCS = 0x00;

            for (int i = 1; i < hData.length - 2; i++) {
                hCS ^= hData[i];
                hCS++;
            }
            LOG_DEBUG("[makeChecksum] hData = " + Global.HextoString(hCS));
            return hCS;
        } catch (Exception e) {
            Log.e(TAG, "[Exception] makeChecksum(hData)");
            e.printStackTrace();
            return (byte) 0xFF;
        }
    }

    public boolean startSmartAutoDoorManager() {
        try {
            if (!startSerialPort()) return false;
            if (!startIFConvertSVR()) return false;
            return true;
        } catch (Exception e) {
            Log.e(TAG, "[Exception] startSmartAutoDoorManager()");
            e.printStackTrace();
            return false;
        }
    }

    public boolean startSerialPort() {
        try {
            LOG_INFO("[startSerialPort] ===== START =====");

            mSerialPort = openSerialPort(SERIALPORTINFO);
            LOG_INFO("[startSerialPort] Open FD : " + mSerialPort.get_FD());

            if (mSerialPort == null) {
                Log.e(TAG, "[startSerialPort] mSerialPort is null!!");
                return false;
            }

            LOG_INFO("[startSerialPort] ReceiveSerialDataThread Create : " + mSerialPort.toString());
            mOutputStream = mSerialPort.getOutputStream();
            mInputStream = mSerialPort.getInputStream();
            mSerialDataReceiver = new SerialDataReceiver(RFReaderManager.RFReaderManagerHandler);
            mSerialDataReceiver.start();
            return true;

        } catch (Exception e) {
            Log.e(TAG, "[Exception] startCardReaderSerial()");
            e.printStackTrace();
            return false;
        }
    }

    public SerialPort openSerialPort(String strSerialInfo) throws SecurityException, IOException, InvalidParameterException {
        try {
            LOG_INFO("[openSerialPort] ===== START =====");
            if (mSerialPort == null) {
                // Read serial port parameters
                String[] aSerialInfo = strSerialInfo.split("_");
                int nBaudrate = Integer.parseInt(aSerialInfo[3], 10);
                String strPath = "/dev/" + aSerialInfo[1];
                LOG_INFO("[openSerialPort] nBaudrate [" + nBaudrate + "], strPath [" + strPath + "]");

                // Check parameters
                if ((strPath.length() < 0) || (nBaudrate < 0)) {
                    throw new InvalidParameterException();
                }

                LOG_INFO("[openSerialPort] Serialport will be open though " + aSerialInfo[0]);
                mSerialPort = new SerialPort(new File(strPath), nBaudrate, 0);
                LOG_INFO("[openSerialPort] " + aSerialInfo[0] + " opened!");
                LOG_INFO("[openSerialPort] mSerialPort : " + mSerialPort);
                LOG_INFO("[openSerialPort] mSerialPort FD : " + mSerialPort.get_FD());
            }
            return mSerialPort;
        } catch (Exception e) {
            Log.e(TAG, "[Exception] openSerialPort(SerialInfo)");
            e.printStackTrace();
            return null;
        }
    }

    public void sendRequestStatus() {
        try {
            LOG("[sendRequestStatus] ===== START =====");
            byte[] hMsg = new byte[28];
            hMsg[0] = MultiReaderPacket.STX;
            hMsg[1] = MultiReaderPacket.ID_RFREADER;
            hMsg[2] = MultiReaderPacket.ID_LOBBY;
            hMsg[3] = MultiReaderPacket.CMD_STATUS_REQUEST;
            for (int i = 4; i < hMsg.length - 3; i++) {
                hMsg[i] = 0x00;
            }
            hMsg[3] = makeChecksum(hMsg);
            hMsg[4] = MultiReaderPacket.ETX;

            LOG_INFO("[sendRequestStatus] hMsg = " + Global.printHexs(hMsg));
            mOutputStream.write(hMsg);
            mOutputStream.flush();
        } catch (Exception e) {
            Log.e(TAG, "[Exception] sendRequestStatus()");
            e.printStackTrace();
        }
    }

    public void sendRequestFWVer() {
        try {
            LOG("[sendRequestFWVer] ===== START =====");
            byte[] hMsg = new byte[28];
            hMsg[0] = MultiReaderPacket.STX;
            hMsg[1] = MultiReaderPacket.ID_RFREADER;
            hMsg[2] = MultiReaderPacket.ID_LOBBY;
            hMsg[3] = MultiReaderPacket.CMD_FW_VER_REQUEST;
            for (int i = 4; i < hMsg.length - 2; i++) {
                hMsg[i] = 0x00;
            }
            hMsg[hMsg.length - 2] = makeChecksum(hMsg);
            hMsg[hMsg.length - 1] = MultiReaderPacket.ETX;

            LOG_INFO("[sendRequestFWVer] hMsg = " + Global.printHexs(hMsg));
            mOutputStream.write(hMsg);
            mOutputStream.flush();
        } catch (Exception e) {
            Log.e(TAG, "[Exception] sendRequestFWVer()");
            e.printStackTrace();
        }
    }

    public boolean sendPacketMsg(byte[] hData) {
        try {
            LOG_INFO("[sendPacketMsg] hData = " + Global.printHexs(hData));
            mOutputStream.write(hData);
            mOutputStream.flush();
            return true;
        } catch (Exception e) {
            Log.e(TAG, "[Exception] sendPacketMsg(hData)");
            e.printStackTrace();
            return false;
        }
    }

    public boolean sendSamplePacketMsg() {
        try {
            LOG_INFO("[sendSamplePacketMsg] hData = " + Global.printHexs(hSamplePacket));
            mOutputStream.write(hSamplePacket);
            mOutputStream.flush();
            return true;
        } catch (Exception e) {
            Log.e(TAG, "[Exception] sendSamplePacketMsg(hData)");
            e.printStackTrace();
            return false;
        }
    }

    public boolean startIFConvertSVR() {
        try {
            IFConvertServerStarter mIFConvertServerStarter = new IFConvertServerStarter(mContext, true);
            mIFConvertServerStarter.start();
            return true;
        } catch (Exception e) {
            Log.e(TAG, "[Exception] startIFConvertSVR()");
            e.printStackTrace();
            return false;
        }
    }

    public void sendRS485toEthernet(byte[] msg, String targetip, int targetport) {
        try {
            IFConvertClient mIFConvertClient = new IFConvertClient(msg, targetip, targetport);
            mIFConvertClient.start();
        } catch (Exception e) {
            Log.e(TAG, "[Exception] sendRS485toEthernet(byte[] msg, String targetip, int targetport)");
            e.printStackTrace();
        }
    }

    public void sendRS485toEthernet(byte[] msg) {
        try {
            IFConvertClient mIFConvertClient = new IFConvertClient(msg, ConfigValue.strMonitoringPCIPAddress, ConfigValue.nSmartAutodoorServerPortNum);
            mIFConvertClient.start();
        } catch (Exception e) {
            Log.e(TAG, "[Exception] sendRS485toEthernet(byte[] msg)");
            e.printStackTrace();
        }
    }

    public void setRS485EventListener(RS485EventListener EventListener) {
        mRS485EventListener = EventListener;
    }

    public void releaseRS485EventListener() {
        mRS485EventListener = null;
    }

    public interface RS485EventListener {
        public void on485RcvEvent(byte[] hData);
    }

    public class SerialDataReceiver extends Thread {
        Handler CRHandler;
        long lRcvTimeLimit = 0;
        int nRcvDataCnt = 0;
        byte hChecksum = 0x00;
        int nAvailable = 0;
        byte[] hRcvBuffer;
        boolean bRcvDone = false;   // 데이터 수신완료 여부 (true: 수신 완료, false: 수신 미완료)

        public SerialDataReceiver(Handler Handle) {
            LOG_INFO("[SerialDataReceiver(Handle)] === START ===");
            CRHandler = Handle;
        }

        public void run() {
            try {
                LOG_INFO("[SerialDataReceiver.run()] === START ===");
                while (!isInterrupted()) {
                    try {
                        if (mInputStream == null) {
                            LOG_INFO("[SerialDataReceiver.run()] mInputStream is null!!");
                            return;
                        }

                        if (mInputStream.available() > 0) {
//                        	LOG_INFO("[SerialDataReceiver.run()] mInputStream.available() = " + mInputStream.available());
                            lRcvTimeLimit = System.currentTimeMillis() + SERIAL_RECEIVE_TIMEOUT;   // Time limit for receiving data
                            if (bCheckPacket) {
                                while (lRcvTimeLimit > System.currentTimeMillis()) {
                                    if (mInputStream.available() > 0) {
                                        hRcvBuffer = new byte[mInputStream.available()];
                                        mInputStream.read(hRcvBuffer);
//                                        LOG_DEBUG("[SerialDataReceiver.run()] hRcvBuffer.length = " + hRcvBuffer.length);
//                                        LOG_DEBUG("[SerialDataReceiver.run()] hRcvBuffer = " + Global.printHexs(hRcvBuffer));
                                        for (int i = 0; i < hRcvBuffer.length; i++) {
                                            if (nRcvDataCnt == 0) {
                                                if (hRcvBuffer[i] == SmartAutoDoor.STX) {
                                                    // STX
                                                    hRcvData[nRcvDataCnt] = hRcvBuffer[i];
                                                    nRcvDataCnt++;
//                                                    LOG_DEBUG("[SerialDataReceiver.run()] hRcvData 1 = " + Global.printHexs(hRcvData, nRcvDataCnt));
                                                } else {
                                                    // No STX
                                                    nRcvDataCnt = 0;
                                                    SERIAL_PACKET_EXPECTED_LENGTH = SERIAL_PACKET_EXPECTED_LENGTH_MIN;
                                                }
                                            } else if (nRcvDataCnt == 1) {
                                                // DevID
                                                hRcvData[nRcvDataCnt] = hRcvBuffer[i];
                                                nRcvDataCnt++;
                                            } else if (nRcvDataCnt == 2) {
                                                // ID
                                                hRcvData[nRcvDataCnt] = hRcvBuffer[i];
                                                nRcvDataCnt++;
                                            } else if (nRcvDataCnt == 3) {
                                                // Type
                                                hRcvData[nRcvDataCnt] = hRcvBuffer[i];
                                                nRcvDataCnt++;
                                            } else if (nRcvDataCnt == 4) {
                                                // Length
//                                            	LOG_DEBUG("[SerialDataReceiver.run()] Length = " + hRcvBuffer[i]);
                                                hRcvData[nRcvDataCnt] = hRcvBuffer[i];
                                                SERIAL_PACKET_EXPECTED_LENGTH = SERIAL_PACKET_EXPECTED_LENGTH_MIN + hRcvData[nRcvDataCnt];
//                                            	LOG_DEBUG("[SerialDataReceiver.run()] SERIAL_PACKET_EXPECTED_LENGTH = " + SERIAL_PACKET_EXPECTED_LENGTH);
                                                nRcvDataCnt++;
                                            } else if (4 < nRcvDataCnt && nRcvDataCnt < SERIAL_PACKET_EXPECTED_LENGTH - 1) {
                                                hRcvData[nRcvDataCnt] = hRcvBuffer[i];
                                                nRcvDataCnt++;
                                            } else if (nRcvDataCnt == SERIAL_PACKET_EXPECTED_LENGTH - 1) {
                                                // ETX
                                                if (hRcvBuffer[i] == SmartAutoDoor.ETX) {
                                                    // ETX
                                                    hRcvData[nRcvDataCnt] = hRcvBuffer[i];
                                                    nRcvDataCnt++;

                                                    byte[] hRcvPacket = new byte[nRcvDataCnt];
                                                    for (int j = 0; j < hRcvPacket.length; j++) {
                                                        hRcvPacket[j] = hRcvData[j];
                                                    }
                                                    LOG_DEBUG("[SerialDataReceiver.run()] [DONE] hRcvData = " + Global.printHexs(hRcvPacket));
                                                    sendRS485toEthernet(hRcvPacket, ConfigValue.strMonitoringPCIPAddress, ConfigValue.nSmartAutodoorServerPortNum);
                                                    if (mRS485EventListener != null)
                                                        mRS485EventListener.on485RcvEvent(hRcvPacket);

                                                    nRcvDataCnt = 0;
                                                    SERIAL_PACKET_EXPECTED_LENGTH = SERIAL_PACKET_EXPECTED_LENGTH_MIN;
                                                    bRcvDone = true;
                                                    lRcvTimeLimit = System.currentTimeMillis() - 1;   // 타이머 루프를 빠져나온다.
                                                } else {
                                                    // No ETX
                                                    nRcvDataCnt = 0;
                                                    SERIAL_PACKET_EXPECTED_LENGTH = SERIAL_PACKET_EXPECTED_LENGTH_MIN;
                                                }
                                            } else {
                                                nRcvDataCnt = 0;
                                                SERIAL_PACKET_EXPECTED_LENGTH = SERIAL_PACKET_EXPECTED_LENGTH_MIN;
                                            }
                                        }
                                    }
                                }   // End of "while (lRcvTimeLimit > System.currentTimeMillis())"
                                nRcvDataCnt = 0;
                                SERIAL_PACKET_EXPECTED_LENGTH = SERIAL_PACKET_EXPECTED_LENGTH_MIN;
                                if (bRcvDone) {
                                    bRcvDone = false;
                                    LOG_DEBUG("[SerialDataReceiver.run()] Receive done!!");
                                } else {
                                    LOG_DEBUG("[SerialDataReceiver.run()] Receive timeout!!");
                                }
                            } else {
                                while (lRcvTimeLimit > System.currentTimeMillis()) {
//                            		Log.d(TAG, "Wating!! lRcvTimeLimit [" + lRcvTimeLimit + "]");
                                    Thread.sleep(10);
                                }
                                if (mInputStream.available() > 0) {
                                    hRcvBuffer = new byte[mInputStream.available()];
                                    mInputStream.read(hRcvBuffer);
                                    LOG_DEBUG("[SerialDataReceiver.run()] hRcvBuffer.length = " + hRcvBuffer.length);
                                    LOG_DEBUG("[SerialDataReceiver.run()] hRcvBuffer = " + Global.printHexs(hRcvBuffer));
                                    sendRS485toEthernet(hRcvBuffer, ConfigValue.strMonitoringPCIPAddress, ConfigValue.nSmartAutodoorServerPortNum);
                                    if (mRS485EventListener != null)
                                        mRS485EventListener.on485RcvEvent(hRcvBuffer);
                                }
                            }
                        }  // After "if (mInputStream.available() > 0)"
                    } catch (Exception e) {
                        Log.e(TAG, "[Exception] @while (!isInterrupted())");
                        e.printStackTrace();
                    }
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        Log.e(TAG, "[InterruptedException] (SerialDataReceiver.run()) Thread.sleep(10)");
                        e.printStackTrace();
                    }
                }
                LOG_DEBUG("[SerialDataReceiver.run()] After !! [while (!isInterrupted())] !!");
            } catch (Exception e) {
                Log.e(TAG, "[Exception] (SerialDataReceiver.run())");
                e.printStackTrace();
            }
        }
    }

    public class IFConvertServerStarter extends Thread {
        ServerSocket SvrSock;
        IFConvertSVRProc mIFConvertSVRProc;
        boolean bServerRun = true;

        public IFConvertServerStarter(Context context, boolean bRun) {
            try {
                Log.w(TAG, "[IFConvertServerStarter] bRun [" + bRun + "]");
                mContext = context;
                bServerRun = bRun;
                SvrSock = new ServerSocket(ConfigValue.nSmartAutodoorServerPortNum);
            } catch (IOException e) {
                Log.e(TAG, "[IOException] IFConvertServerStarter()");
                e.printStackTrace();
            }
        }

        @Override
        public void run() {
            try {
                while (bServerRun) {
                    try {
                        Socket Sock = null;
                        if (!SvrSock.isClosed()) Sock = SvrSock.accept();
                        mIFConvertSVRProc = new IFConvertSVRProc(Sock);
                        mIFConvertSVRProc.start();
                    } catch (IOException e) {
                        Log.e(TAG, "[IOException] IFConvertServerStarter().run() : NonEncrypted");
                        e.printStackTrace();
                    }
                }

                try {
                    Log.w(TAG, "[IFConvertServerStarter().run()] ========== Server sockets close!! ==========");
                    if (SvrSock != null) SvrSock.close();
                } catch (Exception e) {
                    Log.e(TAG, "[Exception] IFConvertServerStarter.run() : Socket close!!");
                    e.printStackTrace();
                }
            } catch (Exception e) {
                Log.e(TAG, "[Exception] IFConvertServerStarter().run()");
                e.printStackTrace();
            }
        }
    }

    public class IFConvertSVRProc extends Thread {
        Socket IFCvtSock;

        public IFConvertSVRProc(Socket Sock) {
            IFCvtSock = Sock;
        }

        @Override
        public void run() {
            try {
                LOG_INFO("[IFConvertSVRProc().run()] Received something!!");
                if (!Global.getEnableDeviceInput()) {
                    Log.e(TAG, "[IFConvertSVRProc().run()] Global.getEnableDeviceInput() = " + Global.getEnableDeviceInput());
                    return;
                }
                InputStream InStm = IFCvtSock.getInputStream();
                IFCvtSock.setSoLinger(true, 3000);
                lSocketConnectionPeriod = System.currentTimeMillis() + TIMEOUT_iMAP_WAIT;   // 타임아웃시간을 설정한다.

                if (InStm.available() > 0) {
                    byte[] hRcvData = new byte[SOCKET_BUFFER_SIZE_READ];
                    int nCnt = InStm.read(hRcvData);

                    byte[] hRcvPacket = new byte[nCnt];
                    for (int j = 0; j < hRcvPacket.length; j++) {
                        hRcvPacket[j] = hRcvData[j];
                    }
                    Log.e(TAG, "hRcvPacket = " + Global.printHexs(hRcvPacket));
                    sendPacketMsg(hRcvPacket);

                    IFCvtSock.close();
                }

//                while (System.currentTimeMillis() < lSocketConnectionPeriod) {
//                    try {
//                        Log.e(TAG, "[IFConvertSVRProc().run()] InStm.available() = " + InStm.available());
//                        if (InStm.available() > 0) {
//                        	byte[] hRcvData = new byte[SOCKET_BUFFER_SIZE_READ];
//                        	int nCnt = InStm.read(hRcvData);
//                        	Log.e(TAG, "hRcvData = " + Global.printHexs(hRcvData, nCnt));
//                        }
//                        else {
//                            Thread.sleep(100);
//                        }
//                    } catch (Exception e) {
//                    	Log.e(TAG, "[Exception] IFConvertSVRProc.run() ===== 2 =====");
//                        IFCvtSock.close();
//                        e.printStackTrace();
//                        return;
//                    }
//                }

                if (!IFCvtSock.isClosed()) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        Log.e(TAG, "[Exception] IFCvtSock.isBound()");
                        e.printStackTrace();
                    }
                    Log.e(TAG, "[Exception] IFCvtSock still opened!!");
                    IFCvtSock.close();
                }
            } catch (IOException e) {
                Log.e(TAG, "[Exception] IFConvertSVRProc.run() ===== 5 =====");
                e.printStackTrace();
            }
        }
    }


}