package com.rdiot.yx485.ui.bind.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.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.king.zxing.util.LogUtils
import com.rdiot.yx485.BuildConfig
import com.rdiot.yx485.base.ApiHost
import com.rdiot.yx485.base.LocalData
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 kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
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 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


    /** 初始化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,
        roomName: String,
        wifiSsid: String,
        wifiPwd: String,
        deviceTypeId: String,
        callBack: ((BLECtrlEvent) -> Unit)? = null
    ) {
        this.deviceName = deviceName
        this.roomName = roomName
        this.wifiSsid = wifiSsid
        this.wifiPwd = wifiPwd
        this.deviceTypeId = deviceTypeId
        this.callBack = callBack
        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) {
                        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)
                                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)
                        }

                        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 = Json.decodeFromString<BindBean>(dataString)
                            if (bindBean.c != 0) {
                                cancelBind()
                                LogUtils.e("设备绑定失败 ${bindBean.s} - ${bindBean.c}")
                            } else {
                                if (bindBean.s == 1) {
                                    sendWifiInfo()
                                }
                            }
                            callBack?.invoke(BLECtrlEvent(bindBean.s to bindBean.c))

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

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

        }
    }


    /** 发送Wifi信息 */
    private fun sendWifiInfo() {
        val userData = LocalData.userData.value ?: return
        val bindInfoString = BindInfo(
            userData.recordId,
            LocalData.selFamilyId.value.toString(),
            roomName,
            wifiSsid,
            wifiPwd,
            if (LocalData.apiHost == ApiHost.TEST_HOST) 1 else 0,
            deviceTypeId
        ).toJsonString()
        LogUtils.d(":Json${bindInfoString}")
        sendData(bindInfoString.toByteArray())
    }

    private fun sendData(data: ByteArray) {
        if (BleManager.getInstance().allConnectedDevice.size >= 1) {
            BleManager.getInstance()
                .write(bleDevice, SERVICE_UUID, READ_CHA_UUID, data, 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: Pair<Int, Int>) {
    companion object {
        val CONNECTING = 1 to 0
        val CONNECT_FAILED = 1 to -1001
    }

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


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


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