package com.dlc.eboard.serial

import android.content.Context
import android.os.Handler
import android.os.Looper
import com.dlc.eboard.sp.PortDevicesSp
import com.rc.core.log.RcLog
import eyecool_serialport_api.SerialPort
import java.io.File
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
import java.security.InvalidParameterException

/**
 * info
 *
 * @author ReiChin_
 */
class SerialPortManager(private val listener: OnSerialListener? = null) {

    companion object {
        const val BAUD_RATE = 9600
        const val PATH = "/dev/ttyS3"

        // 远程开门
        const val CMD_OPEN = "AA02000055"
        // 继电器常开
        const val CMD_ALWAYS_OPEN = "AA03000055"
        // 继电器常闭
        const val CMD_ALWAYS_CLOSE = "AA04000055"

        private const val MESSAGE_DELAY: Long = 200 // 延迟200ms，判断扫码是否完成。
    }

    private var mSerialPort: SerialPort? = null
    private var mOutputStream: OutputStream? = null
    private var mInputStream: InputStream? = null

    private var mReadThread: ReadThread? = null

    private var mKeyContent = StringBuilder()

    fun connect(context: Context) {
        try {
            val device = PortDevicesSp.loadConfig(context)
            mSerialPort = SerialPort(File(device.path), device.baudRate, 0)
            mInputStream = mSerialPort?.inputStream
            mOutputStream = mSerialPort?.outputStream
            listener?.onConnected(true, "path:${device.path}, baudRate:${device.baudRate} connected.")
            mReadThread = ReadThread(mInputStream) { buffer, size ->
                onDataReceived(buffer, size)
            }
            mReadThread?.start()
        } catch (e: SecurityException) {
            listener?.onConnected(
                false,
                "You do not have read/write permission to the serial port."
            )
        } catch (e: IOException) {
            listener?.onConnected(false, "The serial port can not be opened for an unknown reason.")
        } catch (e: InvalidParameterException) {
            listener?.onConnected(false, "Please configure your serial port first.")
        } catch (e: Exception) {
            listener?.onConnected(false, "connect port fail, 未知异常.")
        }
    }

    private fun onDataReceived(buffer: ByteArray, size: Int) {
        val hex = HexDump.toHexString(buffer, length = size)
        RcLog.info("SerialPortManager#onDataReceived() PORT buffer=${hex}, size=${size}")

        listener?.onReceive(hex)

//        mHandler.removeCallbacks(mScanningFishedRunnable)
//        mHandler.postDelayed(mScanningFishedRunnable, MESSAGE_DELAY)
    }

    private val mHandler by lazy { Handler(Looper.getMainLooper()) }
    private val mScanningFishedRunnable = Runnable {
        if (mKeyContent.isNotEmpty()) listener?.onReceive(mKeyContent.toString())
        mKeyContent.clear()
    }

    fun sendHex(sHex: String) {
        val cmd = HexDump.hexStringToByteArray(sHex)
        send(cmd)
    }

    fun send(cmd: ByteArray){
        try {
            RcLog.info("SerialPortManager#send() cmd=${HexDump.toHexString(cmd)}")
            mOutputStream?.let {
                it.write(cmd)
                it.flush()
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    fun disconnect() {
        mReadThread?.interrupt()
        mReadThread = null
        mSerialPort?.close()
        mSerialPort = null
    }

    private class ReadThread(
        private val inputStream: InputStream?,
        private val callback: ((buffer: ByteArray, size: Int) -> Unit)? = null
    ) : Thread() {
        override fun run() {
            while (!isInterrupted) {
                var size: Int
                try {
                    if (inputStream == null) return
                    if (inputStream.available() > 0) {
                        val buffer = ByteArray(512)
                        size = inputStream.read(buffer)
                        if (size > 0) {
                            callback?.invoke(buffer, size)
                        }
                    }
                } catch (e: IOException) {
                    e.printStackTrace()
                    return
                }
            }
        }
    }

}