package com.grkj.iscs.modbus

import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import com.grkj.iscs.MyApplication
import com.grkj.iscs.extentions.crc16
import com.grkj.iscs.extentions.toHexStrings
import com.grkj.iscs.util.Executor
import com.grkj.iscs.util.SPUtils
import com.grkj.iscs.util.jvmSeconds
import com.grkj.iscs.util.log.LogUtil
import com.grkj.iscs.view.fragment.DockTestFragment
import java.util.concurrent.LinkedBlockingQueue

/**
 * modbus 最小发送间隔（150豪秒）
 */
// TODO 超时的可能也是用的这个，看情况是否增加到500
//const val MODBUS_MIN_SEND_INTERVAL = 150_000_000
const val MODBUS_MIN_SEND_INTERVAL = 500_000_000

/**
 * 最大从机数量
 */
const val MODBUS_MAX_SLAVE_COUNT = 16

/**
 * 从机状态：未变化
 */
const val MODBUS_SLAVE_STATUS__NO_CHANGE = -1

/**
 * 从机状态：已经满溢
 */
const val MODBUS_SLAVE_STATUS__FULL = 0b00100000


/**
 * ModBus 协议管理器
 */
class ModBusManager(
    // 从机数量
    var slaveCount: Int,
    // 串口管理器
    val portManager: PortManager,
    // 是否输出详细信息
    val verbose: Boolean = false
) {

    @Volatile
    private var running = true

    /**
     * 正在发送的任务
     */
    @Volatile
    private var sending: FrameTask? = null

    /**
     * 等待发送队列
     */
    private val pendings = LinkedBlockingQueue<FrameTask>()

    /**
     * 线程锁
     */
    private val lock = Any()


    private var thread: Thread? = null

    var mSlaveAddressList = mutableListOf<Byte>()


    init {
        portManager.listen { res ->
            if (verbose) {
                LogUtil.i("接收：${res.toHexStrings()}")
            }
            synchronized(lock) {
                sending?.run {
                    if (match(res) && running) {
                        done?.let { it(res) }
                        sending = null
                    } else {
                        LogUtil.w("响应: ${res.toHexStrings()}未匹配, running：${running}")
                    }
                }
            }
        }
        // 初始化地址池
        val dockConfigJson = SPUtils.getDockConfig(MyApplication.instance?.applicationContext!!)
        if (!dockConfigJson.isNullOrEmpty()) {
            val tempList: MutableList<DockTestFragment.DockTestBean> =
                Gson().fromJson(
                    dockConfigJson,
                    object : TypeToken<MutableList<DockTestFragment.DockTestBean>>() {}.type
                )
            mSlaveAddressList.addAll(tempList.map { it.address })
        }
    }

    /**
     * 发送队列的消息
     */
    private fun takePendingToSend() {
        if (sending == null) {
            sending = pendings.take()
        }
        if (!running) {
            return
        }
        sending?.run {
            waitIfNecessary()
        }
        synchronized(lock) {
            sending?.run {
                if (shouldSend()) {
                    if (portManager.send(req)) {
                        afterSent()
                        if (verbose) {
                            LogUtil.i("发送：${req.toHexStrings()}")
                        }
                    } else {
//                        Tip.toast("无法与主控板通讯")
                        LogUtil.w("无法与主控板通讯")
                    }
                } else {
                    LogUtil.i("未响应: ${req.toHexStrings()}")
                    // 放弃处理，回调空数据
                    done?.let { it(byteArrayOf()) }
                    sending = null
//                    onFrameTimeout()
                }
            }
        }
    }

    private var timeouts = 0
    private var lastTimeout = 0L

    private fun onFrameTimeout() {
        val now = jvmSeconds()
        if (now - lastTimeout > 10) {
            timeouts = 0
        }
        // 如果连续超时达到 15 次，则重建 Modbus 连接
        if (++timeouts > 15) {
//            EventBus.getDefault().post(ConfigEvent())
        }
        lastTimeout = now
    }

    /**
     * 循环发送给所有从机
     * @param frame 发送报文
     * @param listener 每轮发送完后的数据监听
     * @param delayMills 每轮发送的间隔
     */
    fun repeatSendToAll(
        frame: MBFrame,
        interrupt: (() -> List<Boolean>)? = null,
        listener: (res: List<ByteArray>) -> Unit,
        delayMills: Long
    ): ModBusManager {
        val keep = interrupt?.invoke()?.run { !this[0] } ?: false
        if (keep) {
            sendToAll(frame) {
                if (running) {
                    listener(it)
                    Executor.delayOnIO({
                        if (running) {
                            repeatSendToAll(frame, interrupt, listener, delayMills)
                        }
                    }, delayMills)
                }
            }
        } else {
            Executor.delayOnIO({
                if (running) {
                    repeatSendToAll(frame, interrupt, listener, delayMills)
                }
            }, delayMills)
        }
        return this
    }


    /**
     * 发送给所有从机
     * @param frame 发送报文
     * @param done 所有从机都发送完成后的回调
     */
    fun sendToAll(frame: MBFrame, done: ((res: List<ByteArray>) -> Unit)? = null) {
//        if (slaveCount == 0) {
        if (mSlaveAddressList.size == 0) {
            done?.let { it(listOf()) }
            return
        }
        sendUp(0, frame, done, ArrayList())
    }

    private fun sendUp(
        index: Int,
        frame: MBFrame,
        done: ((res: List<ByteArray>) -> Unit)?,
        resList: ArrayList<ByteArray>
    ) {
        sendTo(mSlaveAddressList[index], frame) { res ->
            resList.add(res)
//            if (index >= slaveCount - 1) {
            if (index >= mSlaveAddressList.size - 1) {
                // 已经发送完
                if (running) {
                    done?.let { it(resList) }
                }
            } else {
                // 发送给下一个从机
                sendUp(index + 1, frame, done, resList)
            }
        }
    }

    /**
     * 发送给序号为 index 的从机
     * @param slaveAddress 从机地址
     * @param frame 发送报文
     * @param done 完成回调
     */
    fun sendTo(
        slaveAddress: Byte,
        frame: MBFrame,
        allowRetransmission: Boolean = true,
        minSendIntervalNanoSeconds: Int = MODBUS_MIN_SEND_INTERVAL,
        done: ((res: ByteArray) -> Unit)? = null
    ) {
//        if (slaveCount <= 0) {
        if (mSlaveAddressList.size <= 0) {
            LogUtil.i("sendTo($slaveAddress), 地址池大小为0, 返回空数据")
            done?.invoke(byteArrayOf())
            return
        }
//        if (index < 0 || index >= slaveCount) {
//            throw IllegalArgumentException("index [${index}] out of bound [${slaveCount}]")
//        }
        if (mSlaveAddressList.none { it == slaveAddress }) {
            throw IllegalArgumentException("slaveAddress [${slaveAddress}] is not configed")
        }

//        val task = FrameTask(frame.compile(index), done)
        val task = FrameTask(frame.compile(slaveAddress), done)
        task.allowRetransmission = allowRetransmission
        task.minSendInterval = minSendIntervalNanoSeconds
        pendings.add(task)
    }

    fun start() {
        thread = Thread {
            while (running) {
                try {
                    takePendingToSend()
                } catch (e: InterruptedException) {
                }
            }
        }
        thread?.isDaemon = true
        thread?.start()
    }

    fun isRunning(): Boolean {
        return running
    }

    fun stop() {
        running = false
        thread?.interrupt()
        portManager.close()
    }

    /**
     * 生成锁具/便携式底座 单个锁具卡扣开关指令
     */
    fun generateLockBuckleCmd(isOpen: Boolean, lockIndex: Int): MBFrame {
        var str = ""
        val idx = lockIndex - (lockIndex / 8) * 8
        for (i in 7 downTo 0) {
            str += if (i == idx) {
                "1"
            } else {
                "0"
            }
        }

        // 第三位是 是否响应，第四位是操作哪个，操作默认全是0或者1，使用第三位响应来进行操作
        return MBFrame(
            FRAME_TYPE_WRITE,
            byteArrayOf(0x00,
                if (lockIndex in 0..7) 0x11 else 0x12,
                str.toInt(2).toByte(),
                if (isOpen) 0x00 else 0xFF.toByte()
            )
        )
    }

    /**
     * 生成多个锁具卡扣开关指令（只能针对同一个底座，不能跨底座）
     */
    fun generateLockBuckleCmd(isOpen: Boolean, lockIndexList: MutableList<Int>): MutableList<MBFrame> {
        var str = ""
        val rstList = mutableListOf<MBFrame>()
        val smallIdxList = lockIndexList.filter { it in 0..7 }
        val bigIdxList = lockIndexList.filter { it in 8..15 }
        if (smallIdxList.isNotEmpty()) {
            for (i in 7 downTo 0) {
                str += if (smallIdxList.contains(i)) {
                    "1"
                } else {
                    "0"
                }
            }
            rstList.add(MBFrame(FRAME_TYPE_WRITE, byteArrayOf(0x00, 0x11, str.toInt(2).toByte(), if (isOpen) 0x00 else 0xFF.toByte())))
        }
        if (bigIdxList.isNotEmpty()) {
            str = ""
            for (i in 7 downTo 0) {
                str += if (bigIdxList.contains(i + 8)) {
                    "1"
                } else {
                    "0"
                }
            }
            rstList.add(MBFrame(FRAME_TYPE_WRITE, byteArrayOf(0x00, 0x12, str.toInt(2).toByte(), if (isOpen) 0x00 else 0xFF.toByte())))
        }
        return rstList
    }



    /**
     * 生成钥匙底座灯光指令
     *
     * @param leftAction、rightAction 0：保持当前状态 1：点亮 2：熄灭 默认0
     */
    @Deprecated("已取消")
    fun generateKeyLightCmd(leftAction: Int = 0, rightAction: Int = 0): MBFrame {
        return MBFrame(
            FRAME_TYPE_WRITE,
            // TODO 第三个是高位，第四个是低位
            byteArrayOf(0x00, 0x15, rightAction.toByte(), leftAction.toByte())
        )
    }

    /**
     * 操作钥匙/便携式底座钥匙卡扣，一次只操作一个卡扣
     *
     * @param isOpen true：开操作 false：关操作
     * @param index 0：左 1：右 便携式底座钥匙传0
     */
    fun generateKeyBuckleCmd(isOpen: Boolean, index: Int): MBFrame {
        return MBFrame(
            FRAME_TYPE_WRITE,
            byteArrayOf(0x00, 0x11, if (index == 1) 0b00010000.toByte() else 0b0000001, if (isOpen) 0x00 else 0xFF.toByte())
        )
    }

    /**
     * 生成RFID 读指令
     *
     * @param idx 锁具底座锁具：0-9  钥匙底座钥匙：0(left)-1(right)  便携式底座锁具：0-2  便携式底座钥匙：4   便携式底座卡：8
     */
    fun generateRfidCmd(idx: Int): MBFrame {
        return MBFrame(
            FRAME_TYPE_READ,
            byteArrayOf(0x00, (0x20 + idx * 4).toByte(), 0x00, 0x04)
        )
    }
}


class FrameTask(
    val req: ByteArray,
    val done: ((res: ByteArray) -> Unit)?    // 响应回调
) {

    /**
     * 是否允许重发
     */
    var allowRetransmission: Boolean = true

    /**
     * 上次发送时间
     */
    var lastSent: Long = 0

    /**
     * 已发送次数
     */
    var sentCount = 0

    /**
     * 最小发送间隔
     */
    var minSendInterval: Int = MODBUS_MIN_SEND_INTERVAL


    fun waitIfNecessary() {
        val interval = System.nanoTime() - lastSent
        if (interval < minSendInterval) {
            Thread.sleep((minSendInterval - interval) / 1000_000)
        }
    }

    fun shouldSend(): Boolean {
        return if (allowRetransmission) {
            sentCount < 3
        } else {
            sentCount < 1
        }
    }

    fun afterSent() {
        sentCount++
        lastSent = System.nanoTime()
    }

    /**
     * 判断 res 是否是 frame 的响应
     */
    fun match(res: ByteArray): Boolean {
        // 从机地址 和 功能码 必须相同
        if (res.size < 5 || req[0] != res[0] || req[1] != res[1]) {
            return false
        }
        // 报文2 的 CRC校验得正确
        val crc16 = res.crc16(0, res.size - 2)
        return crc16[0] == res[res.size - 2] && crc16[1] == res[res.size - 1]
    }

}

private const val FRAME_TYPE_READ: Byte = 0x03
private const val FRAME_TYPE_WRITE: Byte = 0x06
private const val FRAME_TYPE_SCANNER: Byte = 0x43
const val FRAME_TYPE_WRITE_MULTI: Byte = 0x10
const val FRAME_TYPE_WRITE_FILE: Byte = 0x15

/**
 * ModBus 数据帧
 */
class MBFrame(

    // 类型
    val type: Byte,
    // 数据域：D1 和 D2
    val data: ByteArray

) {

    /**
     * @param index 从机序号
     */
    fun compile(index: Int): ByteArray {
        val bytes = ByteArray(4 + data.size)
        // TODO 从机开始地址0x01
//        bytes[0] = (0x80 + index).toByte()
        bytes[0] = (1 + index).toByte()
        bytes[1] = type
        for (i in data.indices) {
            bytes[2 + i] = data[i]
        }
        val crc16 = bytes.crc16(0, bytes.size - 2)
        bytes[bytes.size - 2] = crc16[0]
        bytes[bytes.size - 1] = crc16[1]
        return bytes

//        val cmd = byteArrayOf((1 + index).toByte()) + byteArrayOf(type) + data
//        return cmd + cmd.crc16()
    }

    /**
     * @param address 从机地址
     */
    fun compile(address: Byte): ByteArray {
        val bytes = ByteArray(4 + data.size)
        bytes[0] = address
        bytes[1] = type
        for (i in data.indices) {
            bytes[2 + i] = data[i]
        }
        val crc16 = bytes.crc16(0, bytes.size - 2)
        bytes[bytes.size - 2] = crc16[0]
        bytes[bytes.size - 1] = crc16[1]
        return bytes
    }

    companion object {
        /**
         * 读取设备类型
         */
        val READ_DEVICE_TYPE = MBFrame(
            FRAME_TYPE_READ,
            byteArrayOf(0x00, 0x00, 0x00, 0x01)
        )

        /**
         * 读钥匙/锁具/便携式底座状态
         */
        val READ_STATUS = MBFrame(
            FRAME_TYPE_READ,
            byteArrayOf(0x00, 0x10, 0x00, 0x01)
        )

        /**
         * 读卡扣状态（钥匙、锁的0-7、便携式的锁和钥匙）
         */
        val READ_BUCKLE_STATUS = MBFrame(
            FRAME_TYPE_READ,
            byteArrayOf(0x00, 0x11, 0x00, 0x01)
        )

        /**
         * 读卡扣状态（锁的9、10）
         */
        val READ_LOCK_BUCKLE_EXTRA_STATUS = MBFrame(
            FRAME_TYPE_READ,
            byteArrayOf(0x00, 0x12, 0x00, 0x01)
        )
    }
}