package com.dlc.chemical.serial.usb.steelyard

import android.content.Context
import android.hardware.usb.UsbDevice
import com.dlc.chemical.serial.usb.AbsUsbSerialManager
import com.dlc.chemical.serial.HexDump
import com.dlc.chemical.serial.listener.OnSerialListener
import com.dlc.chemical.serial.usb.SerialParam
import com.dlc.chemical.sp.UsbDevicesSp
import com.dlc.chemical.sp.UsbDevicesType
import com.hoho.android.usbserial.driver.UsbSerialPort
import com.rc.core.log.RcLog
import java.math.BigDecimal
import java.math.RoundingMode
import kotlin.experimental.inv

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

    companion object {
        const val DEVICE_VID = 0x0403 // 0x067B
        const val DEVICE_PID = 0x6001 // 0x23A3

        private const val BAUD_RATE = 38400 // 波特率
        private const val DATA_BITS = UsbSerialPort.DATABITS_8 // 数据位
        private const val STOP_BITS = UsbSerialPort.STOPBITS_1 // 停止位
        private const val PARITY = UsbSerialPort.PARITY_EVEN // 校验码

        const val FN_WEIGHING = "04"
        const val FN_PEEL = "05"

        private val CMD_WEIGHING = byteArrayOf(0x01, 0x04, 0x00, 0x04, 0x00, 0x03, 0xF1.toByte(), 0xCA.toByte())
        private val CMD_PEEL = byteArrayOf(0x01, 0x05, 0x00, 0x09, 0xFF.toByte(), 0x00, 0x5C, 0x38)
    }

    private val mWeight = mutableListOf<String>()

    override fun findUsbDevice(context: Context, device: UsbDevice): Boolean {
        val deviceConfig = UsbDevicesSp.loadConfig(
            context = context,
            type = UsbDevicesType.STEELYARD,
            defVID = DEVICE_VID,
            defPID = DEVICE_PID
        )
        return device.vendorId == deviceConfig.vendorId && device.productId == deviceConfig.productId
    }

    override fun findUsbDevice(device: UsbDevice): Boolean {
//        return device.deviceId != SCAN_DEVICE_ID
        return true
    }

    override fun intentAction() = INTENT_ACTION_STEELYARD_USB

    override fun createSerialParam(): SerialParam {
        return SerialParam(BAUD_RATE, DATA_BITS, STOP_BITS, PARITY)
    }

    override fun dispatchReceiveData(data: ByteArray) {
        mWeight.add(HexDump.toHexString(data))
    }

    override fun handleReceiveData() {
        RcLog.info("UsbSerial receive data:${mWeight}")
        val tempData = convertData()
        RcLog.info("UsbSerial convert data:${tempData}")
        if (tempData.isNotEmpty() && tempData.size > 5) {
            // 01 04 06 00 00 00 CC 11 06 2C FE
            if (tempData[0] == "01") {
                if (tempData[1] == FN_WEIGHING) {
                    // 称重返回
                    val dataLen = /*tempData[2].toInt()*/ 4

                    val weightHex = tempData.subList(3, 3 + dataLen).reduce { acc, s -> acc + s }
                    val weightDec = if ((HexDump.charToDecimal(weightHex[0]) and 0x8) == 0x8) {
                        // 负数
                        val byteArray = HexDump.hexStringToByteArray(weightHex)
                        val invByteArray = byteArray.map { it.inv() }.toByteArray()
                        HexDump.hexToDecimal(HexDump.toHexString(invByteArray)).inv()
                    } else {
                        HexDump.hexToDecimal(weightHex)
                    }
                    val weightBd = BigDecimal(weightDec)
                        .divide(BigDecimal("10"))
                        .setScale(1, RoundingMode.HALF_UP)
                    val igWeight = if (weightBd.abs() < BigDecimal("0.5")) "0.0" else weightBd.toString()
                    RcLog.info("hex weight:${weightHex}, dec weight:${weightDec}, weightBd=${weightBd}g, igWeight=${igWeight}g")
                    listener?.onReceive(igWeight)
                } else if (tempData[1] == FN_PEEL || tempData[1] == "85") {
                    // 置零返回
                    listener?.onReceiveCmd(FN_PEEL)
                }
            }

            if (tempData.size > 11) {
                // 称重和置零同时返回，忽略称重数据，重新称重
                listener?.onReceiveCmd(FN_PEEL)
            }
        }
        mWeight.clear()
    }

    fun sendWeighingCMD() {
        send(CMD_WEIGHING)
    }

    fun sendPeelCMD() {
        send(CMD_PEEL)
    }

    /**
     * 数据变换
     *
     * [01, 04060000, 16D211, 00C8B2]
     * -> 010406000016D21100C8B2
     * -> [01, 04, 06, 00, 00, 16, D2, 11, 00, C8, B2]
     */
    private fun convertData(): MutableList<String> {
        val reduce = mWeight.reduce { acc, s -> acc + s }
        return mutableListOf<String>().also {
            for (idx in reduce.indices step 2) {
                it.add(reduce.substring(idx, idx + 2))
            }
        }
    }

}