package com.suixingpay.serialsdk.api;

import android.os.Build;
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 com.suixingpay.serialsdk.utils.JsonBufferHelper;

import org.json.JSONObject;

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;
import java.util.List;


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 = 57600;
    private int stopBits = 1;
    private int dataBits = 8;
    private int parity = 0;
    private int flowCon = 0;
    private final int flags = 0;
    private boolean _isOpen = false;
    private byte[] _bLoopData = {48};
    private int iDelay = 500;

    private CountDownTimer countdownTimer;

    private int timeout = 0; // 超时时间 默认不开启

    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);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void sendHex(String sHex) {

        if (this.timeout > 0) {
            // 启动倒计时定时器
            startCountdownTimer(sHex);
        }

        byte[] bOutArray = ByteUtil.HexToByteArr(sHex);
        send(bOutArray);
    }

    public void sendTxt(String sTxt) {

        if (this.timeout > 0) {
            // 启动倒计时定时器
            startCountdownTimer(sTxt);
        }
        byte[] bOutArray = sTxt.getBytes();
        send(bOutArray);
    }

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

            public void onFinish() {
                if (_isOpen) {
                    // 超时处理
                    onError(408, "Receive data timeout, message is " + jsonString);
                }
            }
        }.start();
    }

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


    private class ReadThread extends Thread {

        private final JsonBufferHelper jsonBufferHelper = new JsonBufferHelper();


        private ReadThread() {
        }

        public void run() {
            super.run();
            StringBuilder tmpData = new StringBuilder();
            while (!isInterrupted() && SerialHelper.this._isOpen) {
                try {
                    if (SerialHelper.this.mInputStream == null) {
                        Log.e(TAG, "InputStream is null");
                        return;
                    }
                    byte[] readBytes = getStickPackageHelper().execute(SerialHelper.this.mInputStream);
                    if (readBytes != null && readBytes.length > 0) {
                        String incoming = new String(readBytes, StandardCharsets.UTF_8);
                        Log.i(TAG, "response data:" + incoming);
                        tmpData.append(incoming);

                        // 交由 JsonBufferHelper 组装 JSON
                        List<JSONObject> jsonList = jsonBufferHelper.append(readBytes);

                        // 输出所有完整 JSON
                        for (JSONObject json : jsonList) {
                            SerialHelper.this.onDataReceived(json.toString());
                        }
                        if (!jsonList.isEmpty()) {
                            tmpData.setLength(0);
                        }
                        SerialHelper.this.cancelCountdownTimer();
                    } else {
                        //判断是否为乱码
                        if (isGarbled(tmpData.toString())) {
                            JSONObject jsonObject = new JSONObject();
                            jsonObject.put("code", "99");
                            jsonObject.put("msg", "The data is garbled. Please check the baud rate and other configurations.");
                            jsonObject.put("data", tmpData.toString());
                            SerialHelper.this.onDataReceived(jsonObject.toString());
                        }
                        tmpData.setLength(0);
                    }
                } catch (Throwable e) {
                    if (e.getMessage() != null) {
                        Log.e("error", e.getMessage(), e);
                    }
                    return;
                }
            }
        }
    }

    /**
     * 判断字符串是否为乱码
     *
     * @param data 接收到的字符串
     * @return true如果是乱码
     */
    private boolean isGarbled(String data) {
        // 检查异常字符比例(如问号、替换字符)
        long invalidChars = 0;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            invalidChars = data.chars().filter(c -> c == '�' || c == '?').count();
        }
        // 超过25%的无效字符
        return invalidChars > data.length() / 4;
    }

    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 void setTimeout(int sTimeout) {
        if (this._isOpen) {
            return;
        }
        this.timeout = sTimeout;
    }

    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 onDataReceived(String jsonString);

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

    private AbsStickPackageHelper mStickPackageHelper = new BaseStickPackageHelper();

//    private AbsStickPackageHelper mStickPackageHelper = new StaticLenStickPackageHelper(2048);

//    private AbsStickPackageHelper mStickPackageHelper = new VariableLenStickPackageHelper(
//            ByteOrder.BIG_ENDIAN, // 大端
//            2,                    // len字段长度
//            2,                    // len字段位置
//            12                    // offset
//    );

    public AbsStickPackageHelper getStickPackageHelper() {
        return mStickPackageHelper;
    }

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