package com.rdiot.yx485.device.ble

import android.bluetooth.BluetoothGatt
import android.util.Log
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import com.clj.fastble.BleManager
import com.clj.fastble.callback.BleGattCallback
import com.clj.fastble.callback.BleMtuChangedCallback
import com.clj.fastble.callback.BleNotifyCallback
import com.clj.fastble.callback.BleScanCallback
import com.clj.fastble.callback.BleWriteCallback
import com.clj.fastble.data.BleDevice
import com.clj.fastble.data.BleScanState
import com.clj.fastble.exception.BleException
import com.clj.fastble.scan.BleScanRuleConfig
import com.google.gson.Gson
import com.king.zxing.util.LogUtils
import com.rdiot.yx485.BuildConfig
import com.rdiot.yx485.util.LocalCache
import com.rdiot.yx485.util.byteArrayToHexStr
import com.rdiot.yx485.util.hexToByteArray
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine
import java.nio.charset.Charset
import java.util.concurrent.atomic.AtomicBoolean
import kotlin.coroutines.resume

/**
 * 蓝牙配网流程
 * @author mR2hao
 * @date 2022/11/24
 */
object BleCtrl : DefaultLifecycleObserver {
    private lateinit var deviceName: String
    private lateinit var roomName: String
    private lateinit var homeId: String
    private lateinit var wifiSsid: String
    private lateinit var wifiPwd: String
    private lateinit var deviceTypeId: String
    private var callBack: ((BLECtrlEvent) -> Unit)? = null

    private const val TAG = "BleCtrl"

    /** 设备Service ID */
    private const val SERVICE_UUID = "000000ff-0000-1000-8000-00805f9b34fb"

    /** 读写 特征值 */
    private const val READ_CHA_UUID = "0000ff01-0000-1000-8000-00805f9b34fb"

    /** 取消绑定操作 */
    fun cancelBind() {
        bindJob?.cancel()
        bindJob = null
        BleManager.getInstance().cancelScan()
        BleManager.getInstance().disconnectAllDevice()
    }

    /** 当前连接蓝牙设备 */
    private var bleDevice: BleDevice? = null
    private var isConnecting = AtomicBoolean(false)
    private var bindJob: Job? = null
    private var split: Boolean = true

    /** 初始化BLE */
    private fun initBleManager() {
        val scanRuleConfig = BleScanRuleConfig.Builder()
            .setDeviceName(false, deviceName)
            .setCanRepeatFound(false)
            .setAutoConnect(true)
            .setScanTimeOut(5000)
            .build()
        BleManager.getInstance().initScanRule(scanRuleConfig)
    }

    /** 自动绑定 */
    fun autoBind(
        owner: LifecycleOwner,
        deviceName: String,
        homeId: String,
        roomName: String,
        wifiSsid: String,
        wifiPwd: String,
        deviceTypeId: String,
        split: Boolean,
        callBack: ((BLECtrlEvent) -> Unit)? = null
    ) {
        LogUtils.d("autoBind: $deviceName, $homeId $roomName $wifiSsid $wifiPwd $deviceTypeId")
        BleCtrl.deviceName = deviceName
        BleCtrl.homeId = homeId
        BleCtrl.roomName = roomName
        BleCtrl.wifiSsid = wifiSsid
        BleCtrl.wifiPwd = wifiPwd
        BleCtrl.deviceTypeId = deviceTypeId
        BleCtrl.callBack = callBack
        BleCtrl.split = split
        initBleManager()

        bindJob = owner.lifecycleScope.launch {
            while (isActive) {
                if (BleManager.getInstance().scanSate == BleScanState.STATE_SCANNING || isConnecting.get()) {
                    delay(1000)
                    continue
                }

                BleManager.getInstance().scan(object : BleScanCallback() {
                    override fun onScanStarted(success: Boolean) {
                        LogUtils.d("StartScan $success")
                    }

                    override fun onScanning(bleDevice: BleDevice) {
                        LogUtils.d("deviceName:${bleDevice.name}")
                        if (!bleDevice.name.isNullOrBlank() && bleDevice.name == deviceName) {
                            if (bleDevice.name != null && bleDevice.name.toString() != "") {
                                LogUtils.i("发现设备-> Mac:${bleDevice.mac}  Name:${bleDevice.name} rssi:${bleDevice.rssi}")
                            }

                            if (!isConnecting.get()) {
                                isConnecting.set(true)
                                startConnect(owner, bleDevice)
                                BleManager.getInstance().cancelScan()
                            }
                        }
                    }

                    override fun onScanFinished(scanResultList: MutableList<BleDevice>) {
//                        if (isActive) {
//                            LogUtils.w("发现设备:${scanResultList.size}个")
//                            if (scanResultList.isNotEmpty()) {
//                                scanResultList.first().let { bleDevice ->
//                                    if (!bleDevice.name.isNullOrBlank() && bleDevice.name == deviceName) {
//                                        LogUtils.i("发现设备-> Mac:${bleDevice.mac}  Name:${bleDevice.name}")
//                                        if (!isConnecting.get() && bleDevice.rssi > -70) {
//                                            isConnecting.set(true)
//                                            startConnect(owner, bleDevice)
//                                        }
//                                    }
//                                }
//                            }
//                        }
                    }
                })
            }
        }
    }

    /** 开始连接 */
    private fun startConnect(owner: LifecycleOwner, bleDevice: BleDevice) {
        owner.lifecycleScope.launch {
            if (!isActive) return@launch
            if (!connectDevice(bleDevice)) {
                return@launch
            }

            delay(500)
            if (!isActive) return@launch
            if (!startNotify(bleDevice)) {
                return@launch
            }
        }
    }

    /**
     * 连接设备
     * @param device [BleDevice]
     * @return Boolean
     */
    private suspend fun connectDevice(device: BleDevice): Boolean {
        return suspendCancellableCoroutine { cont ->

            if (BleManager.getInstance().isConnected(device)) {
                isConnecting.set(true)
                if (cont.isActive) cont.resume(true)
            } else {
                if (cont.isActive) {
                    BleManager.getInstance().connect(device, object : BleGattCallback() {
                        override fun onStartConnect() {
                            isConnecting.set(true)
                            LogUtils.i("开始连接 ${device.name} ${device.mac}")
                        }

                        override fun onConnectFail(bleDevice: BleDevice?, exception: BleException) {
                            isConnecting.set(false)
                            LogUtils.e("连接失败(${exception.description}) ${device.name} ${device.mac}")
                            BleManager.getInstance().disconnect(bleDevice)
                            if (cont.isActive) cont.resume(false)
                        }

                        override fun onConnectSuccess(
                            device: BleDevice,
                            gatt: BluetoothGatt?,
                            status: Int
                        ) {
                            isConnecting.set(true)
                            bleDevice = device
                            if (cont.isActive) cont.resume(true)
                            BleManager.getInstance()
                                .setMtu(bleDevice, 500, object : BleMtuChangedCallback() {
                                    override fun onMtuChanged(mtu: Int) {
                                        LogUtils.d("Mtu设置成功")
                                    }

                                    override fun onSetMTUFailure(exception: BleException) {
                                        // 设置MTU失败
                                        LogUtils.d("Mtu设置失败")
                                    }
                                })
                        }

                        override fun onDisConnected(
                            isActiveDisConnected: Boolean,
                            bleDevice: BleDevice,
                            gatt: BluetoothGatt?,
                            status: Int
                        ) {
                            isConnecting.set(false)
                            if (cont.isActive) cont.resume(false)
                        }
                    })
                }
            }
        }
    }


    /** 打开设备Notify */
    private suspend fun startNotify(device: BleDevice): Boolean {
        return suspendCancellableCoroutine { cont ->
            if (cont.isActive && BleManager.getInstance().isConnected(bleDevice)) {
                BleManager.getInstance().notify(
                    device,
                    SERVICE_UUID,
                    READ_CHA_UUID,
                    object : BleNotifyCallback() {
                        override fun onNotifySuccess() {
                            LogUtils.i("打开Notify成功")
                            if (cont.isActive) cont.resume(true)
                        }

                        override fun onNotifyFailure(exception: BleException?) {
                            BleManager.getInstance().disconnect(bleDevice)
                            isConnecting.set(false)
                            if (cont.isActive) cont.resume(false)
                            LogUtils.e("打开Notify失败(${exception?.description})")
                        }

                        override fun onCharacteristicChanged(data: ByteArray) {
                            val realData =
                                data.byteArrayToHexStr().replace("00", "").hexToByteArray()
                            val dataString = realData.toString(Charset.defaultCharset())

                            val bindBean = Gson().fromJson(dataString, BindBean::class.java)
                            LogUtils.d("收到数据 $dataString")
                            if (bindBean.c != 0) {
                                cancelBind()
                                LogUtils.e("设备绑定失败 ${bindBean.s} - ${bindBean.c}")
                            } else {
                                if (bindBean.s == 1) {
                                    sendWifiInfo()
                                }
                            }
                            callBack?.invoke(
                                BLECtrlEvent(
                                    Triple(
                                        bindBean.s,
                                        bindBean.c,
                                        bindBean.id
                                    )
                                )
                            )

                        }
                    })
            } else {
                BleManager.getInstance().disconnect(bleDevice)
                isConnecting.set(false)

                if (cont.isActive) cont.resume(false)
            }

        }
    }

    /** 发送Wifi信息 */
    private fun sendWifiInfo() {
        val userData = LocalCache.getUserInfo()
        val bindInfoString = BindInfo(
            userData.record_id,
            homeId,
            roomName,
            wifiSsid,
            wifiPwd,
            0,
            deviceTypeId
        ).toJsonString()
        LogUtils.d(":Json${bindInfoString}")
        sendData(bindInfoString.toByteArray())
    }

    private fun sendData(data: ByteArray) {
        if (BleManager.getInstance().allConnectedDevice.isNotEmpty()) {
            if (bleDevice?.name == "ESP_GATTS_DEMO") {
                split = false
            }
            BleManager.getInstance()
                .writeData(
                    bleDevice,
                    SERVICE_UUID,
                    READ_CHA_UUID,
                    data,
                    split,
                    object : BleWriteCallback() {
                        override fun onWriteSuccess(
                            current: Int,
                            total: Int,
                            justWrite: ByteArray
                        ) {
                            Log.i(
                                TAG,
                                "onWriteSuccess: ${current}/${total} ${justWrite.toString(Charset.defaultCharset())}"
                            )
                        }

                        override fun onWriteFailure(exception: BleException) {
                            LogUtils.e("发送数据失败 $exception")
                            if (exception.code != BleException.ERROR_CODE_TIMEOUT) {
                                BleManager.getInstance().disconnectAllDevice()
                            }

                        }
                    })
        }
    }

    override fun onDestroy(owner: LifecycleOwner) {
        super.onDestroy(owner)
        cancelBind()
        bleDevice = null
    }

    override fun onCreate(owner: LifecycleOwner) {
        super.onCreate(owner)
        BleManager.getInstance().enableLog(BuildConfig.DEBUG)
        BleManager.getInstance().enableBluetooth()
    }

}

/** Ble事件
 * first Step
 * second Code
 * */
class BLECtrlEvent(private val e: Triple<Int, Int, String>) {
    companion object {
        val CONNECTING = 1 to 0
        val CONNECT_FAILED = 1 to -1001
    }

    /** 获取步骤 */
    fun getStep() = e.first


    /** 获取Code */
    fun getCode() = e.second


    /** 获取Id */
    fun getId() = e.third

    /** 是否成功 */
    fun isSuccess() = this.getCode() == 0
}