package kr.co.icontrols.ilp300_700.serialport;

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

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.InvalidParameterException;

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

public class HDCStandardReaderSerial {
    final static String TAG = "HDCStandardReaderSerial";
    protected static OutputStream mOutputStream;
    static boolean bLOG = true;   // true, false
    static boolean bLOG_DEBUG = false;   // true, false
    static boolean bLOG_INFO = true;   // true, false
    static Context mContext;
    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 = "COM2_ttyS2_Baudrate_115200";
    protected InputStream mInputStream;
    int SERIAL_RECEIVE_TIMEOUT = 50;   // Packet receiving duration from STX to ETX
    int SERIAL_PACKET_EXPECTED_LENGTH = 28;   // Cardreader data packet length
    int SERIAL_PACKET_BASIC_LENGTH = 28;   // Normal data Packet length
    int SERIAL_PACKET_NFC_LENGTH = 56;   // NFC 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];
    private SerialPort mSerialPort = null;
    private SerialDataReceiver mSerialDataReceiver;
    public HDCStandardReaderSerial(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 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 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 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();
        }
    }

    private void sendHandlerMSG(Handler Handle, int nReaderType, int nMSGType, int nETC, byte[] hRcvData, int nDataLength) {
        try {
            LOG_INFO("[sendHandlerMSG] nReaderType [" + nReaderType + "], nMSGType [" + nMSGType + "], nETC [" + nETC + "], nDataLength [" + nDataLength + "]");
            LOG_INFO("[sendHandlerMSG] Received data : " + Global.printHexs(hRcvData, nDataLength));
            byte[] hData = new byte[nDataLength];
            for (int i = 0; i < hData.length; i++) {
                hData[i] = hRcvData[i];
            }
            Message Serialmsg = Handle.obtainMessage();
            Serialmsg.what = nReaderType;
            Serialmsg.arg1 = nMSGType;
            Serialmsg.arg2 = nETC;
            Serialmsg.obj = hData;

            Handle.sendMessage(Serialmsg);

        } catch (Exception e) {
            Log.e(TAG, "[Exception] CRSerialDataReceiver.run()");
            e.printStackTrace();
        }
    }

    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) {
                            lRcvTimeLimit = System.currentTimeMillis() + SERIAL_RECEIVE_TIMEOUT;   // Time limit for receiving data
                            nRcvDataCnt = 0;
                            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 = " + Common.printHexs(hRcvBuffer));
                                    for (int i = 0; i < hRcvBuffer.length; i++) {
                                        if (nRcvDataCnt == 0) {
                                            if (hRcvBuffer[i] == MultiReaderPacket.STX) {
                                                // STX
                                                hRcvData[nRcvDataCnt] = hRcvBuffer[i];
                                                nRcvDataCnt++;
//                                                LOG_DEBUG("[SerialDataReceiver.run()] hRcvData 1 = " + Common.printHexs(hRcvData, nRcvDataCnt));
                                            } else {
                                                // No STX
                                                nRcvDataCnt = 0;
                                            }
                                        } else if (nRcvDataCnt == 1) {
                                            if (hRcvBuffer[i] == MultiReaderPacket.ID_LOBBY) {
                                                // Dst. ID (Lobby)
                                                hRcvData[nRcvDataCnt] = hRcvBuffer[i];
                                                hChecksum = (byte) 0x00;
                                                hChecksum ^= hRcvData[nRcvDataCnt];
                                                hChecksum = (byte) ((hChecksum + 1) % 256);
                                                nRcvDataCnt++;
//                                                LOG_DEBUG("[SerialDataReceiver.run()] hRcvData 2 = " + Common.printHexs(hRcvData, nRcvDataCnt));
                                            } else {
                                                // No Dst. ID incorrect.
                                                nRcvDataCnt = 0;
                                            }
                                        } else if (nRcvDataCnt == 2) {
                                            if (hRcvBuffer[i] == MultiReaderPacket.ID_RFREADER) {
                                                // Src. ID (RF Reader)
                                                hRcvData[nRcvDataCnt] = hRcvBuffer[i];
                                                hChecksum ^= hRcvData[nRcvDataCnt];
                                                hChecksum = (byte) ((hChecksum + 1) % 256);
                                                nRcvDataCnt++;
//                                                LOG_DEBUG("[SerialDataReceiver.run()] hRcvData 3 = " + Common.printHexs(hRcvData, nRcvDataCnt));
                                            } else {
                                                // No Src. ID incorrect
                                                nRcvDataCnt = 0;
                                            }
                                        } else if (nRcvDataCnt == 3) {
                                            if (hRcvBuffer[i] == MultiReaderPacket.CMD_IPARKNFC)
                                                SERIAL_PACKET_EXPECTED_LENGTH = SERIAL_PACKET_NFC_LENGTH;
                                            else
                                                SERIAL_PACKET_EXPECTED_LENGTH = SERIAL_PACKET_BASIC_LENGTH;
                                            hRcvData[nRcvDataCnt] = hRcvBuffer[i];
                                            hChecksum ^= hRcvData[nRcvDataCnt];
                                            hChecksum = (byte) ((hChecksum + 1) % 256);
                                            nRcvDataCnt++;
//                                            LOG_DEBUG("[SerialDataReceiver.run()] hRcvData 4 = " + Common.printHexs(hRcvData, nRcvDataCnt));
                                        } else if (3 < nRcvDataCnt && nRcvDataCnt < SERIAL_PACKET_EXPECTED_LENGTH - 2) {
                                            hRcvData[nRcvDataCnt] = hRcvBuffer[i];
                                            hChecksum ^= hRcvData[nRcvDataCnt];
                                            hChecksum = (byte) ((hChecksum + 1) % 256);
                                            nRcvDataCnt++;
//                                            LOG_DEBUG("[SerialDataReceiver.run()] hRcvData 5 = " + Common.printHexs(hRcvData, nRcvDataCnt));
                                        } else if (nRcvDataCnt == SERIAL_PACKET_EXPECTED_LENGTH - 2) {
                                            // CS
//                                            LOG_DEBUG("[SerialDataReceiver.run()] hRcvBuffer[" + i + "] = " + Common.HextoString(hRcvBuffer[i]));
//                                            LOG_DEBUG("[SerialDataReceiver.run()] hChecksum = " + Common.HextoString(hChecksum));

                                            if (hRcvBuffer[i] == hChecksum) {
                                                hRcvData[nRcvDataCnt] = hRcvBuffer[i];
                                                nRcvDataCnt++;
                                            } else {
                                                nRcvDataCnt = 0;
                                            }


//                                            LOG_DEBUG("[SerialDataReceiver.run()] hRcvData 6 = " + Common.printHexs(hRcvData, nRcvDataCnt));
                                        } else if (nRcvDataCnt == SERIAL_PACKET_EXPECTED_LENGTH - 1) {
                                            // ETX
                                            if (hRcvBuffer[i] == MultiReaderPacket.ETX) {
                                                // ETX
                                                hRcvData[nRcvDataCnt] = hRcvBuffer[i];
                                                nRcvDataCnt++;

//                                                LOG_DEBUG("[SerialDataReceiver.run()] [DONE] hRcvData = " + Global.printHexs(hRcvData, nRcvDataCnt));
                                                sendHandlerMSG(CRHandler, RFReaderManager.HANDLE_RFREADER_STANDDARD, RFReaderManager.HANDLE_MSG_RECEIVED, 0, hRcvData, nRcvDataCnt);
                                                nRcvDataCnt = 0;
                                                bRcvDone = true;
                                                lRcvTimeLimit = System.currentTimeMillis() - 1;   // 타이머 루프를 빠져나온다.
                                            } else {
                                                // No ETX
                                                nRcvDataCnt = 0;
                                            }
                                        } else {
                                            nRcvDataCnt = 0;
                                        }
                                    }
                                }
                            }   // End of "while (lRcvTimeLimit > System.currentTimeMillis())"
                            nRcvDataCnt = 0;
                            if (bRcvDone) {
                                bRcvDone = false;
                                LOG_DEBUG("[SerialDataReceiver.run()] Receive done!!");
                            } else {
                                LOG_DEBUG("[SerialDataReceiver.run()] Receive timeout!!");
                            }
                        }  // 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();
            }
        }
    }


}