package com.suixingpay.serialsdk.api;

import android.os.CountDownTimer;
import android.util.Log;

import com.suixingpay.serialsdk.bean.ComBean;
import com.suixingpay.serialsdk.stick.AbsStickPackageHelper;
import com.suixingpay.serialsdk.stick.BaseStickPackageHelper;
import com.suixingpay.serialsdk.utils.ByteUtil;

import org.json.JSONException;
import org.json.JSONTokener;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.security.InvalidParameterException;


public abstract class SerialHelper {

    private static final String TAG = "SerialHelper";

    private SerialPort mSerialPort;
    private OutputStream mOutputStream;
    private InputStream mInputStream;
    private ReadThread mReadThread;
    private SendThread mSendThread;
    private String sPort = "/dev/ttyS0";
    private int iBaudRate = 9600;
    private int stopBits = 1;
    private int dataBits = 8;
    private int parity = 0;
    private int flowCon = 0;
    private int flags = 0;
    private boolean _isOpen = false;
    private byte[] _bLoopData = {48};
    private int iDelay = 500;

    private CountDownTimer countdownTimer;

    private int timeout = 20000; // 超时时间 20秒

    public SerialHelper() {
        Log.i(TAG, "SerialHelper init !");
    }

    public SerialHelper(String sPort, int iBaudRate) {
        Log.i(TAG, "SerialHelper init, port is " + sPort + " baudRate is " + iBaudRate);
        this.sPort = sPort;
        this.iBaudRate = iBaudRate;
    }

    public void open() throws SecurityException, IOException, InvalidParameterException {
        Log.i(TAG, "SerialHelper open: port is " + this.sPort + " iBaudRate is " + this.iBaudRate);
        this._isOpen = true;
        this.mSerialPort = new SerialPort(new File(this.sPort), this.iBaudRate, this.stopBits, this.dataBits, this.parity, this.flowCon, this.flags);
        this.mOutputStream = this.mSerialPort.getOutputStream();
        this.mInputStream = this.mSerialPort.getInputStream();
        this.mReadThread = new ReadThread();
        this.mReadThread.start();
        this.mSendThread = new SendThread();
        this.mSendThread.setSuspendFlag();
        this.mSendThread.start();

    }

    public void close() {
        if (this.mReadThread != null) {
            this.mReadThread.interrupt();
        }
        if (this.mSerialPort != null) {
            this.mSerialPort.close();
            this.mSerialPort = null;
        }
        this._isOpen = false;
    }

    public void send(byte[] bOutArray) {
        try {
            this.mOutputStream.write(bOutArray);
            // 启动倒计时定时器
            startCountdownTimer();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void sendHex(String sHex) {
        byte[] bOutArray = ByteUtil.HexToByteArr(sHex);
        send(bOutArray);
    }

    public void sendTxt(String sTxt) {
        byte[] bOutArray = sTxt.getBytes();
        send(bOutArray);
    }

    // 倒计时定时器
    private void startCountdownTimer() {
        if (countdownTimer != null) {
            countdownTimer.cancel();
        }
        countdownTimer = new CountDownTimer(this.timeout, 120000) { // 设置倒计时为60秒，每秒触发一次
            public void onTick(long millisUntilFinished) {
                // 每秒触发
            }

            public void onFinish() {
                if (_isOpen) {
                    Log.i(TAG, "超时: 调用onError(408, Receive data timeout)");
                    // 超时处理
                    onError(408, "Receive data timeout");
                }
            }
        }.start();
    }

    // 接收到数据时重置倒计时定时器
    private void cancelCountdownTimer() {
        if (countdownTimer != null) {
            Log.i(TAG, "定时器取消");
            countdownTimer.cancel();
            countdownTimer = null;
        }
    }

    private class ReadThread extends Thread {
        private ReadThread() {
        }

        // 获取字符串中第一个JSON数据
        private String extractFirstJson(String str) {
            int braceCount = 0;
            int endIndex = -1;
            for (int i = 0; i < str.length(); i++) {
                char c = str.charAt(i);
                if (c == '{') braceCount++;
                if (c == '}') braceCount--;
                if (braceCount == 0 && i > 0) {
                    endIndex = i + 1;
                    break;
                }
            }
            if (endIndex > 0) {
                return str.substring(0, endIndex);
            }
            return null;
        }

        public void run() {
            super.run();
            StringBuilder dataBuffer = new StringBuilder();

            Log.d(TAG, "isInterrupted：" + isInterrupted());
            while (!isInterrupted() && SerialHelper.this._isOpen) {
                try {
                    if (SerialHelper.this.mInputStream == null) {
                        Log.e(TAG, "InputStream is null");
                        return;
                    }

                    BufferedInputStream bufferedInputStream = new BufferedInputStream(SerialHelper.this.mInputStream);

                    // 检查是否有数据可读
                    int available = bufferedInputStream.available();
                    if (available > 0) {
                        byte[] buffer = new byte[available];
                        int totalBytesRead = 0;

                        while (totalBytesRead < available) {
                            int bytesRead = bufferedInputStream.read(buffer, totalBytesRead, available - totalBytesRead);
                            if (bytesRead == -1) {
                                break; // 流结束
                            }
                            totalBytesRead += bytesRead;
                        }

                        if (totalBytesRead > 0) {
                            String receivedString = new String(buffer, 0, totalBytesRead, StandardCharsets.UTF_8);
                            Log.d(TAG, "receivedString：" + receivedString);

                            dataBuffer.append(receivedString);
                            Log.d(TAG, "dataBuffer：" + dataBuffer);

                            while (true) {
                                String firstJson = extractFirstJson(dataBuffer.toString());
                                if (firstJson == null) break;

                                ComBean comRecData = new ComBean(SerialHelper.this.sPort, firstJson);
                                SerialHelper.this.onDataReceived(comRecData);
                                SerialHelper.this.cancelCountdownTimer();

                                dataBuffer.delete(0, firstJson.length());
                            }
                        }
                    } else {
                        // 如果没有数据可读，短暂休眠避免过度占用CPU
                        Thread.sleep(50);
                    }
                } catch (IOException e) {
                    Log.e(TAG, "IOException in ReadThread: " + e.getMessage());
                    // 如果发生IOException，尝试重新打开串口
                    try {
                        close();
                        open();
                    } catch (Exception ex) {
                        Log.e(TAG, "Error reopening serial port: " + ex.getMessage());
                    }
                } catch (InterruptedException e) {
                    Log.e(TAG, "InterruptedException in ReadThread: " + e.getMessage());
                    // 线程被中断，退出循环
                    return;
                } catch (Throwable e) {
                    Log.e(TAG, "Unexpected error in ReadThread: " + e.getMessage());
                    // 其他异常，记录日志并继续
                }
            }
        }
    }

    private class SendThread
            extends Thread {
        public boolean suspendFlag = true;

        private SendThread() {
        }

        public void run() {
            super.run();
            while (!isInterrupted()) {
                synchronized (this) {
                    while (this.suspendFlag) {
                        try {
                            wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
                SerialHelper.this.send(SerialHelper.this.getbLoopData());
                try {
                    Thread.sleep(SerialHelper.this.iDelay);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

        public void setSuspendFlag() {
            this.suspendFlag = true;
        }

        public synchronized void setResume() {
            this.suspendFlag = false;
            notify();
        }
    }

    public int getBaudRate() {
        return this.iBaudRate;
    }

    public boolean setBaudRate(int iBaud) {
        if (this._isOpen) {
            return false;
        }
        this.iBaudRate = iBaud;
        return true;
    }

    public boolean setBaudRate(String sBaud) {
        int iBaud = Integer.parseInt(sBaud);
        return setBaudRate(iBaud);
    }

    public int getStopBits() {
        return this.stopBits;
    }

    public boolean setStopBits(int stopBits) {
        if (this._isOpen) {
            return false;
        }
        this.stopBits = stopBits;
        return true;
    }

    public int getDataBits() {
        return this.dataBits;
    }

    public boolean setDataBits(int dataBits) {
        if (this._isOpen) {
            return false;
        }
        this.dataBits = dataBits;
        return true;
    }

    public int getParity() {
        return this.parity;
    }

    public boolean setParity(int parity) {
        if (this._isOpen) {
            return false;
        }
        this.parity = parity;
        return true;
    }

    public int getFlowCon() {
        return this.flowCon;
    }

    public boolean setFlowCon(int flowCon) {
        if (this._isOpen) {
            return false;
        }
        this.flowCon = flowCon;
        return true;
    }

    public String getPort() {
        return this.sPort;
    }

    public boolean setPort(String sPort) {
        if (this._isOpen) {
            return false;
        }
        this.sPort = sPort;
        return true;
    }

    public int getTimeout() {
        return this.timeout;
    }

    public boolean setTimeout(int sTimeout) {
        if (this._isOpen) {
            return false;
        }
        this.timeout = sTimeout;
        return true;
    }

    public boolean isOpen() {
        return this._isOpen;
    }

    public byte[] getbLoopData() {
        return this._bLoopData;
    }

    public void setbLoopData(byte[] bLoopData) {
        this._bLoopData = bLoopData;
    }

    public void setTxtLoopData(String sTxt) {
        this._bLoopData = sTxt.getBytes();
    }

    public void setHexLoopData(String sHex) {
        this._bLoopData = ByteUtil.HexToByteArr(sHex);
    }

    public int getiDelay() {
        return this.iDelay;
    }

    public void setiDelay(int iDelay) {
        this.iDelay = iDelay;
    }

    public void startSend() {
        if (this.mSendThread != null) {
            this.mSendThread.setResume();
        }
    }

    public void stopSend() {
        if (this.mSendThread != null) {
            this.mSendThread.setSuspendFlag();
        }
    }

    protected abstract void onDataReceived(ComBean paramComBean);

    protected abstract void onError(int code, String msg);


    private AbsStickPackageHelper mStickPackageHelper = new BaseStickPackageHelper();  // 默认不处理粘包，直接读取返回

    public AbsStickPackageHelper getStickPackageHelper() {
        return mStickPackageHelper;
    }

    public void setStickPackageHelper(AbsStickPackageHelper mStickPackageHelper) {
        this.mStickPackageHelper = mStickPackageHelper;
    }
}