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.rfreader.RFReaderManager;

public class HDCIPARKReaderSerial {
    protected static OutputStream mOutputStream;
    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;
    final String TAG = "HDCIPARKReaderSerial";
    private final String SERIALPORTINFO = "COM2_ttyS2_Baudrate_9600";
    protected InputStream mInputStream;
    boolean bLOG = true;   // true, false
    boolean bLOG_DEBUG = false;   // true, false
    boolean bLOG_INFO = true;   // true, false
    Context mContext;
    int SERIAL_RECEIVE_TIMEOUT = 50;   // Packet receiving duration from STX to ETX
    int SERIAL_PACKET_EXPECTED_LENGTH = 15;   // Cardreader data packet length
    int SERIAL_PACKET_MAX_LENGTH = 30;   // MAX data packet length
    boolean bSTART_DATA = false;
    int nHandlerResult = HANDLE_MSG_NONE;
    byte[] hRcvData = new byte[SERIAL_PACKET_EXPECTED_LENGTH];
    private SerialPort mSerialPort = null;
    private SerialDataReceiver mSerialDataReceiver;
    public HDCIPARKReaderSerial(Context context) {
        mContext = context;
    }

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

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

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

    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("[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 sendCMD() {
        try {
            byte[] cmd = new byte[10];
            cmd[0] = 0x00;
            cmd[1] = 0x01;
            cmd[2] = 0x02;
            cmd[3] = 0x03;
            cmd[4] = 0x04;
            cmd[5] = 0x05;
            cmd[6] = 0x06;
            cmd[7] = 0x07;
            cmd[8] = 0x08;
            cmd[9] = 0x09;
            mOutputStream.write(cmd);
        } catch (Exception e) {
            Log.e(TAG, "[Exception] sendCMD()");
            e.printStackTrace();
        }
    }

    private void sendHandlerMSG(Handler Handle, int nReaderType, int nMSGType, int nETC, byte[] hRcvData) {
        try {
//            LOG_DEBUG("[sendHandlerMSG] nReaderType [" + nReaderType + "], nMSGType [" + nMSGType + "], nETC [" + nETC + "], hRcvData.length [" + hRcvData.length + "]");
//            LOG_DEBUG("[sendHandlerMSG] Received data : " + Global.printHexs(hRcvData));
            Message Serialmsg = Handle.obtainMessage();
            Serialmsg.what = nReaderType;
            Serialmsg.arg1 = nMSGType;
            Serialmsg.arg2 = nETC;
            Serialmsg.obj = hRcvData;

            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.e(TAG, "[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 = " + Global.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 = " + Global.printHexs(hRcvData, nRcvDataCnt));
                                            } else {
                                                // No STX
                                                nRcvDataCnt = 0;
                                            }
                                        } else if (0 < nRcvDataCnt && nRcvDataCnt < SERIAL_PACKET_EXPECTED_LENGTH - 3) {
                                            // Data 1 ~ 11
                                            hRcvData[nRcvDataCnt] = hRcvBuffer[i];
                                            if (nRcvDataCnt == 1) {
                                                hChecksum = hRcvData[nRcvDataCnt];
                                            } else {
                                                hChecksum ^= hRcvData[nRcvDataCnt];
                                            }
                                            nRcvDataCnt++;
                                        } else if (nRcvDataCnt == SERIAL_PACKET_EXPECTED_LENGTH - 3) {
                                            // Checksum
//                                            LOG_DEBUG("[SerialDataReceiver.run()] hChecksum = " + Global.HextoString(hChecksum) + ", hRcvBuffer[" + i + "] = " + Global.HextoString(hRcvBuffer[i]));

                                            // 테스트를 위해 Checksum 무시
//                                            hRcvData[nRcvDataCnt] = hRcvBuffer[i];
//                                            nRcvDataCnt++;

                                            if (hRcvBuffer[i] == hChecksum) {
                                                hRcvData[nRcvDataCnt] = hRcvBuffer[i];
                                                nRcvDataCnt++;
                                            } else {
                                                nRcvDataCnt = 0;
                                            }
                                        } else if (nRcvDataCnt == SERIAL_PACKET_EXPECTED_LENGTH - 2) {
                                            // Reserved
                                            if (hRcvBuffer[i] == MultiReaderPacket.RESERVED) {
                                                hRcvData[nRcvDataCnt] = hRcvBuffer[i];
                                                nRcvDataCnt++;
                                            } else {
                                                nRcvDataCnt = 0;
                                            }
                                        } else if (nRcvDataCnt == SERIAL_PACKET_EXPECTED_LENGTH - 1) {
                                            // ETX
                                            if (hRcvBuffer[i] == MultiReaderPacket.ETX) {
                                                hRcvData[nRcvDataCnt] = hRcvBuffer[i];
                                                nRcvDataCnt++;
//                                                LOG_DEBUG("[SerialDataReceiver.run()] [DONE] hRcvData = " + Global.printHexs(hRcvData, nRcvDataCnt));
                                                sendHandlerMSG(CRHandler, RFReaderManager.HANDLE_RFREADER_HDCIPARK, RFReaderManager.HANDLE_RFPASS_STANDARD, 0, hRcvData);
                                                nRcvDataCnt = 0;
                                                bRcvDone = true;
                                                lRcvTimeLimit = System.currentTimeMillis() - 1;   // 타이머 루프를 빠져나온다.
                                            } else {
                                                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())");
            }
        }
    }


}