package com.grkj.iscs.modbus

import android.content.Context
import com.clj.fastble.BleManager
import com.grkj.iscs.BusinessManager
import com.grkj.iscs.R
import com.grkj.iscs.extentions.removeLeadingZeros
import com.grkj.iscs.extentions.toHexStrings
import com.grkj.iscs.model.DeviceConst.DEVICE_TYPE_KEY
import com.grkj.iscs.model.DeviceConst.DEVICE_TYPE_LOCK
import com.grkj.iscs.model.DeviceConst.DOCK_TYPE_ELEC_LOCK_BOARD
import com.grkj.iscs.model.DeviceConst.DOCK_TYPE_KEY
import com.grkj.iscs.model.DeviceConst.DOCK_TYPE_LOCK
import com.grkj.iscs.model.DeviceConst.DOCK_TYPE_PORTABLE
import com.grkj.iscs.util.Executor
import com.grkj.iscs.util.NetApi
import com.grkj.iscs.util.ToastUtils
import com.grkj.iscs.util.log.LogUtil
import java.util.concurrent.Executors
import java.util.stream.Collectors


/**
 * ModBus 主控板控制器
 */
object ModBusController {

    var isInitReady = false

    /**
     * 底座列表
     */
    var dockList: MutableList<DockBean> = mutableListOf()

    private const val LISTENER_TYPE_STATUS = 3

    // 主控板管理器
    var modBusManager: ModBusManager? = null
    private var slaveCount: Int = 0

    private val threadPool = Executors.newScheduledThreadPool(4)

    private val listeners = ArrayList<StatusListener>()

    // 是否中断读取状态
    private var interruptReadStatus: ArrayList<Boolean> = ArrayList()

    var shouldStopUpgrade = false

    // TODO 临时改成5s
    const val REPEAT_FREQUENCY = 800L

    fun setSlaveCount(count: Int) {
        modBusManager?.slaveCount = count
        slaveCount = count
    }

    class StatusListener(
        val key: Any,
        val listener: (Any) -> Unit,
        val type: Int
    )

    fun interruptReadTrashBinStatus(interrupt: Boolean) {
        interruptReadStatus.clear()
        interruptReadStatus.add(interrupt)
    }

    @ExperimentalUnsignedTypes
    fun start(ctx: Context) {
        modBusManager?.stop()
        PortManager.openCtrlBord(ctx)
            ?.let { pm ->
                return@let ModBusManager(slaveCount, pm, true)
            }
            // 间隔 1 秒读一遍桶的状态
            ?.repeatSendToAll(MBFrame.READ_STATUS, {
                interruptReadStatus
            }, { res ->
//                // Logger.d("ModbusController", "res: ${res.map { it.toHexString() }}")
                LogUtil.i("****************************************************************************")
                // 过滤非空的数据，重置slaveCount
                // 不再使用slaveCount，改用地址池
//                val onlineCount = res.filter { it.isNotEmpty() }.size
//                setSlaveCount(onlineCount)
                for (l in listeners) {
                    if (l.type == LISTENER_TYPE_STATUS) {
                        l.listener(res)
                    }
                }
            }, REPEAT_FREQUENCY)
            ?.also {
                modBusManager = it
                Executor.runOnIO {
//                    refreshAllowOpenDoorUnidentified(ctx, it)
                }
            }
            ?.start()
    }

    fun registerStatusListener(key: Any, listener: (Any) -> Unit) {
        listeners.add(StatusListener(key, listener, LISTENER_TYPE_STATUS))
    }

    fun unregisterListener(key: Any) {
        val it = listeners.iterator()
        while (it.hasNext()) {
            if (it.next().key == key) {
                it.remove()
            }
        }
    }

    fun stop() {
        modBusManager?.stop()
    }

    fun isRunning(): Boolean? {
        return modBusManager?.isRunning()
    }

    /*****************************************************************************************/

    /**
     * 初始化所有设备的状态
     */
    fun initDevicesStatus() {
        readDeviceType { res ->
            res.forEach { bytes ->
                if (bytes.size < 5) return@forEach
                // 设备具体数据由0x0011寄存器提供
                updateDeviceType(bytes[0], bytes[4])
                val type = when (bytes[4]) {
                    DOCK_TYPE_KEY -> "钥匙底座"
                    DOCK_TYPE_LOCK -> "锁具底座"
                    DOCK_TYPE_ELEC_LOCK_BOARD -> "电磁锁控制板"
                    DOCK_TYPE_PORTABLE -> "便携式底座"
                    else -> "未知"
                }
                LogUtil.i("initDevicesStatus 设备（${bytes[0].toInt()}）类型：$type")
            }
            // TODO 待完善
            Executor.repeatOnMain({
                if (isInitReady) {
                    initLock()    // 打开所有无锁的卡扣、关闭所有有锁的卡扣、读取所有锁的RFID
                    initKey()     // 打开所有无钥匙的卡扣、关闭所有有钥匙的卡扣、读取所有钥匙的RFID
                    return@repeatOnMain false
                } else {
                    return@repeatOnMain true
                }
            }, REPEAT_FREQUENCY, true)
        }
    }

    /**
     * 初始化锁具——打开所有无锁的卡扣、读取RFID
     */
    private fun initLock() {
        LogUtil.i("initLock : $dockList")
        dockList.filter { it.type == DOCK_TYPE_LOCK || it.type == DOCK_TYPE_PORTABLE }.forEach { dockBean ->
            val hasLockIdxList = dockBean.getLockList().filter { it.isExist }.map { it.idx } as MutableList<Int>
            val noLockIdxList = dockBean.getLockList().filter { !it.isExist }.map { it.idx } as MutableList<Int>

            hasLockIdxList.forEach { idx ->
                readLockRfid(dockBean.addr, idx) { res ->
                    if (res.size < 11) {
                        LogUtil.e("Lock rfid error")
                        return@readLockRfid
                    }
                    val rfid = res.copyOfRange(3, 11).toHexStrings(false).removeLeadingZeros()
                    LogUtil.i("初始化锁具 RFID : $rfid")
                    updateLockRfid(dockBean.addr, idx, rfid)
                }
            }
            controlLockBuckle(false, dockBean.addr, hasLockIdxList)
            controlLockBuckle(true, dockBean.addr, noLockIdxList)
        }
    }

    /**
     * 初始化钥匙
     */
    private fun initKey() {
        LogUtil.i("initKey : $dockList")
        dockList.filter { it.type == DOCK_TYPE_KEY || it.type == DOCK_TYPE_PORTABLE }.forEach { dockBean ->
            dockBean.getKeyList().forEach { key ->
                if (key.isExist) {
                    LogUtil.i("initKey : ${dockBean.addr} : ${key.isLeft}")
                    readKeyRfid(dockBean.addr, if (key.isLeft) 0 else 1) { isLeft, res ->
                        if (res.size < 11) {
                            LogUtil.e("Key rfid error")
                            return@readKeyRfid
                        }
                        val rfid = res.copyOfRange(3, 11).toHexStrings(false).removeLeadingZeros()
                        LogUtil.i("初始化钥匙 RFID : $rfid")
                        // 更新rfid
                        updateKeyRfid(dockBean.addr, isLeft, rfid)
                        // 蓝牙准备操作
                        NetApi.getKeyInfo(rfid) {
                            LogUtil.i("getKeyInfo : $rfid - ${it?.macAddress}")
                            if (it != null && !it.macAddress.isNullOrEmpty()) {
                                // 更新mac
                                updateKeyMac(dockBean.addr, key.isLeft, it.macAddress)
                                BusinessManager.registerConnectListener(it.macAddress) { isDone, bleBean ->
                                    if (isDone && bleBean?.bleDevice != null) {
                                        Executor.delayOnMain(500) {
                                            BusinessManager.getCurrentStatus(3, bleBean.bleDevice)
                                        }
                                    }
                                }
                            } else {
                                ToastUtils.tip(R.string.get_key_info_fail)
                            }
                        }
                    }
                    controlKeyBuckle(false, key.isLeft, dockBean.addr)
                } else {
                    controlKeyBuckle(true, key.isLeft, dockBean.addr)
                }
            }
        }
    }

    /**
     * 更新状态
     */
    fun updateStatus(byteArray: ByteArray): DockBean? {
        if (byteArray.isEmpty()) {
            return null
        }
        val dockB = dockList.find { it.addr == byteArray[0] }
        return dockB?.parseStatus(byteArray)
    }

    /**
     * 读取设备类型
     */
    private fun readDeviceType(done: ((res: List<ByteArray>) -> Unit)? = null) {
        modBusManager?.sendToAll(MBFrame.READ_DEVICE_TYPE) { res ->
            done?.invoke(res)
        }
    }

    /**
     * 更新设备类型
     */
    private fun updateDeviceType(idx: Byte, type: Byte?) {
        val dock = dockList.find { it.addr == idx }
        dock?.let {
            it.type = type
        } ?: let {
            dockList.add(DockBean(idx, type, mutableListOf()))
        }
    }

    /**
     * 读取卡扣状态
     *
     * @param isLock true：读锁具底座 false：读钥匙底座
     * @param type 0：钥匙底座 1：锁具底座1-8 2：锁具底座9、10
     */
    fun readBuckleStatus(isLock: Boolean, slaveAddress: Byte?, doneSingle: ((type: Int, res: ByteArray) -> Unit)? = null) {
        // TODO 电磁锁控制板可能不是，并且锁和钥匙的读取不一样
        slaveAddress?.let {
            modBusManager?.sendTo(it, MBFrame.READ_BUCKLE_STATUS) { res ->
                doneSingle?.invoke(if (isLock) 1 else 0, res)
            }
            if (isLock) {
                modBusManager?.sendTo(it, MBFrame.READ_LOCK_BUCKLE_EXTRA_STATUS) { res ->
                    doneSingle?.invoke(2, res)
                }
            }
        }
    }

    /**
     * 开/关锁具卡扣
     */
    fun controlLockBuckle(isOpen: Boolean, slaveAddress: Byte?, lockIdx: Int, done: ((res: ByteArray) -> Unit)? = null)  {
        slaveAddress?.let {
            modBusManager?.generateLockBuckleCmd(isOpen, lockIdx)?.let { cmd ->
                modBusManager?.sendTo(it, cmd) { res ->
                    done?.invoke(res)
                }
            }
        }
    }

    fun controlLockBuckle(isOpen: Boolean, slaveAddress: Byte?, lockIdxList: MutableList<Int>, done: ((res: ByteArray) -> Unit)? = null)  {
        slaveAddress?.let {
            modBusManager?.generateLockBuckleCmd(isOpen, lockIdxList)?.let { cmdList ->
                cmdList.forEach { cmd ->
                    modBusManager?.sendTo(it, cmd) { res ->
                        done?.invoke(res)
                    }
                }
            }
        }
    }

    /**
     * 读取钥匙RFID
     */
    fun readKeyRfid(slaveAddress: Byte?, idx: Int, done: ((isLeft: Boolean, res: ByteArray) -> Unit)? = null) {
        slaveAddress?.let {
            modBusManager?.generateRfidCmd(idx)?.let { cmd ->
                modBusManager?.sendTo(it, cmd) {
                    done?.invoke(idx == 0, it)
                }
            }
        }
    }

    /**
     * 读取锁具RFID
     */
    fun readLockRfid(slaveAddress: Byte?, lockIdx: Int, done: ((res: ByteArray) -> Unit)? = null) {
        slaveAddress?.let {
            modBusManager?.generateRfidCmd(lockIdx)?.let { cmd ->
                modBusManager?.sendTo(it, cmd) { res ->
                    done?.invoke(res)
                }
            }
        }
    }

    /**
     * 读便携式底座卡RFID
     */
    fun readPortalCaseCardRfid(slaveAddress: Byte?, done: ((res: ByteArray) -> Unit)? = null) {
        slaveAddress?.let {
            modBusManager?.generateRfidCmd(8)?.let { cmd ->
                modBusManager?.sendTo(it, cmd) { res ->
                    done?.invoke(res)
                }
            }
        }
    }

    /**
     * 更新钥匙RFID
     */
    fun updateKeyRfid(slaveAddress: Byte, isLeft: Boolean, rfid: String) {
        dockList.find { it.addr == slaveAddress }?.getKeyList()?.find { it.isLeft == isLeft }?.rfid = rfid
    }

    /**
     * 更新钥匙MAC
     */
    fun updateKeyMac(slaveAddress: Byte, isLeft: Boolean, mac: String) {
        dockList.find { it.addr == slaveAddress }?.getKeyList()?.find { it.isLeft == isLeft }?.mac = mac
    }

    /**
     * 通过RFID更新对应的Mac
     */
    fun updateKeyMacByRfid(rfid: String, mac: String) {
        dockList.find { it.type == DOCK_TYPE_KEY }?.getKeyList()?.find { it.rfid == rfid }?.mac = mac
    }

    /**
     * 更新锁具RFID
     */
    fun updateLockRfid(slaveAddress: Byte, lockIdx: Int, rfid: String) {
        dockList.find { it.addr == slaveAddress }?.getLockList()?.find { it.idx == lockIdx }?.rfid = rfid
    }

    /**
     * 设备是否存在，加入deviceType防止有重复的但是不同类型的
     *
     * @param deviceType {@link [com.grkj.iscs.model.bo.DeviceTakeUpdateBO]<class>#[deviceType]}
     */
    fun isDeviceExist(rfid: String, deviceType: Int) : Boolean {
        return when (deviceType) {
            DEVICE_TYPE_KEY -> {
                dockList.find { it.type == DOCK_TYPE_KEY || it.type == DOCK_TYPE_PORTABLE }?.getKeyList()?.find { it.rfid == rfid } != null
            }
            DEVICE_TYPE_LOCK -> {
                dockList.find { it.type == DOCK_TYPE_LOCK || it.type == DOCK_TYPE_PORTABLE  }?.getLockList()?.find { it.rfid == rfid } != null
            }
            else -> {
                false
            }
        }
    }

    /**
     * 操作钥匙灯
     *
     * @param leftAction、rightAction 0：保持当前状态 1：点亮 2：熄灭 默认0
     */
    fun controlKeyLight(slaveAddress: Byte?, leftAction: Int = 0, rightAction: Int = 0, done: ((res: ByteArray) -> Unit)? = null) {
        slaveAddress?.let {
            modBusManager?.generateKeyLightCmd(leftAction, rightAction)?.let { cmd ->
                modBusManager?.sendTo(it, cmd) {
                    done?.invoke(it)
                }
            }
        }
    }

    /**
     * 开/关钥匙卡扣
     *
     * @param isOpen true：开操作 false：关操作
     * @param isLeft true：左卡扣 false：右卡扣
     */
    fun controlKeyBuckle(isOpen: Boolean, isLeft: Boolean, slaveAddress: Byte?, done: ((res: ByteArray) -> Unit)? = null)  {
        slaveAddress?.let {
            modBusManager?.generateKeyBuckleCmd(isOpen, if (isLeft) 0 else 1)?.let { cmd ->
                modBusManager?.sendTo(it, cmd) { res ->
                    done?.invoke(res)
                }
            }
        }
    }

    /**
     * 根据RFID找钥匙
     */
    fun getKeyByRfid(rfid: String): DockBean.KeyBean? {
        return dockList.filter { it.type == DOCK_TYPE_KEY || it.type == DOCK_TYPE_PORTABLE }.flatMap { it.getKeyList() }.find { it.rfid == rfid }
    }

    /**
     * 根据Mac找钥匙
     */
    fun getKeyByMac(mac: String): DockBean.KeyBean? {
        return dockList.filter { it.type == DOCK_TYPE_KEY || it.type == DOCK_TYPE_PORTABLE }.flatMap { it.getKeyList() }.find { it.mac == mac }
    }

    /**
     * 根据RFID找锁具
     */
    fun getLockByRfid(rfid: String): DockBean.LockBean? {
        return dockList.filter { it.type == DOCK_TYPE_LOCK || it.type == DOCK_TYPE_PORTABLE }.flatMap { it.getLockList() }.find { it.rfid == rfid }
    }

    /**
     * 根据钥匙Mac获取底座
     */
    fun getDockByKeyMac(mac: String): DockBean? {
        return dockList.find {
            (it.type == DOCK_TYPE_KEY || it.type == DOCK_TYPE_PORTABLE)
                    && it.getKeyList().any { it.mac == mac }
        }
    }

    /**
     * 根据类型获取底座列表
     */
    fun getDockByType(type: Byte): List<DockBean> {
        return dockList.filter { it.type == type }
    }

    fun getKeyByDockType(type: Byte): MutableList<DockBean.KeyBean>? {
        return dockList.find { it.type == type }?.let {
             it.getKeyList()
        }
    }

    fun controlAllLockBuckles(isOpen: Boolean) {
        dockList.filter { it.type == DOCK_TYPE_LOCK || it.type == DOCK_TYPE_PORTABLE }.forEach { dockBean ->
            val list = dockBean.getLockList().stream().map { it.idx }.collect(Collectors.toList())
            controlLockBuckle(isOpen, dockBean.addr, list) {
                LogUtil.i("${if (isOpen) "开启" else "关闭"}所有锁卡扣 : ${it.toHexStrings()}")
            }
        }
    }

    fun printDockInfo() {
        LogUtil.i("当前底座列表 : $dockList")
        dockList.forEach { dockBean ->
            when (dockBean.type) {
                DOCK_TYPE_LOCK -> {
                    dockBean.getLockList().forEach { lockBean ->
                        LogUtil.i("${dockBean.addr}锁${lockBean.idx} : ${lockBean.rfid}")
                    }
                }
                DOCK_TYPE_KEY -> {
                    dockBean.getKeyList().forEach { keyBean ->
                        LogUtil.i("${dockBean.addr}钥${keyBean.idx} : ${keyBean.rfid}")
                    }
                }
                DOCK_TYPE_PORTABLE -> {
                    dockBean.getLockList().forEach { lockBean ->
                        LogUtil.i("${dockBean.addr}柜锁${lockBean.idx} : ${lockBean.rfid}")
                    }
                    dockBean.getKeyList().forEach { keyBean ->
                        LogUtil.i("${dockBean.addr}柜钥${keyBean.idx} : ${keyBean.rfid}")
                    }
                }
            }
        }
    }

    fun updateDeviceType() {
        LogUtil.i("____________________________________")
        readDeviceType { res ->
            LogUtil.i("设备类型数量 ： ${res.size}")
            LogUtil.i("设备类型 ： ${res.map { it.toHexStrings()}}")
            res.forEach { bytes ->
                if (bytes.size < 5) return@forEach
                // 设备具体数据由0x0011寄存器提供
                updateDeviceType(bytes[0], bytes[4])
                val type = when (bytes[4]) {
                    DOCK_TYPE_KEY -> "钥匙底座"
                    DOCK_TYPE_LOCK -> "锁具底座"
                    DOCK_TYPE_ELEC_LOCK_BOARD -> "电磁锁控制板"
                    DOCK_TYPE_PORTABLE -> "便携式底座"
                    else -> "未知"
                }
                LogUtil.i("设备（${bytes[0].toInt()}）类型：$type")
            }
            LogUtil.i("____________________________________")
        }
    }

    /**
     * 获取一个钥匙（存在的、有RFID、有Mac、连接的）
     *
     * @return 底座地址，钥匙
     */
    fun getOneKey(): Pair<Byte, DockBean.KeyBean?>? {
        val keyDockList = dockList.filter { it.type == DOCK_TYPE_KEY || it.type == DOCK_TYPE_PORTABLE }
        val keyList = keyDockList.flatMap { it.getKeyList() }.filter { it.isExist }
        LogUtil.i("keyList : $keyList")
        if (keyList.isEmpty()) {
            ToastUtils.tip(R.string.no_available_key)
            return null
        }

        keyList.forEach {
            LogUtil.i("keyStatus : ${it.isExist} - ${it.rfid} - ${it.mac} - ${BusinessManager.getBleDeviceByMac(it.mac!!)?.bleDevice} - ${BleManager.getInstance().isConnected(BusinessManager.getBleDeviceByMac(it.mac!!)?.bleDevice)}")
        }
        val key = keyList.find { it.isExist && it.rfid != null && it.mac != null &&
                BleManager.getInstance().isConnected(BusinessManager.getBleDeviceByMac(it.mac!!)?.bleDevice) }
        if (key == null) {
            LogUtil.e("getOneKey : no key match")
            return null
        }

        val address = keyDockList.find { it.getKeyList().any { it.rfid == key.rfid } }?.addr
        if (address == null) {
            LogUtil.e("getOneKey : no dock match")
            return null
        }
        return Pair(address, key)
    }

    /**
     * 根据数量获取锁具(基于锁柜和便携柜不存在接一起的情况)
     *
     * @param needLockCount 需要打开的锁具数量
     *
     * @return key: dock地址，value: 锁具RFID列表
     */
    fun getLocks(needLockCount: Int): MutableMap<Byte, MutableList<DockBean.LockBean>> {
        val map = mutableMapOf<Byte, MutableList<DockBean.LockBean>>()
        if (needLockCount == 0) {
            return map
        }
        val lockDockList = dockList.filter { it.type == DOCK_TYPE_LOCK || it.type == DOCK_TYPE_PORTABLE }

        var provideCount = 0
        lockDockList.forEach loop@ { lockDock ->
            val lockList = lockDock.getLockList().filter { it.isExist }.toMutableList()
            if (lockList.size < (needLockCount - provideCount)) {
                provideCount += lockList.size
                map[lockDock.addr] = lockList
            } else {
                val rfidList = lockList.subList(0, needLockCount - provideCount)
                map[lockDock.addr] = rfidList
                return@loop
            }
        }
        return map
    }
}