package aicare.net.cn.sdk.ailinksdkdemoandroid.modules.ble_speed_test

import aicare.net.cn.sdk.ailinksdkdemoandroid.R
import aicare.net.cn.sdk.ailinksdkdemoandroid.base.KBleBaseNewActivity
import aicare.net.cn.sdk.ailinksdkdemoandroid.databinding.ActivityBleSpeedTestBinding
import aicare.net.cn.sdk.ailinksdkdemoandroid.utils.CoroutineUtils
import aicare.net.cn.sdk.ailinksdkdemoandroid.utils.TimeUtils
import aicare.net.cn.sdk.ailinksdkdemoandroid.utils.toUnsignedInt
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothGattCharacteristic
import android.net.Uri
import android.os.Build
import android.os.Handler
import android.os.Looper
import android.os.Message
import android.text.TextUtils
import android.view.View
import android.widget.ArrayAdapter
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.RequiresApi
import com.pingwang.bluetoothlib.config.BleConfig
import com.pingwang.bluetoothlib.device.AiLinkBleCheckUtil
import com.pingwang.bluetoothlib.device.BleDevice
import com.pingwang.bluetoothlib.device.SendDataBean
import com.pingwang.bluetoothlib.listener.OnBleMtuListener
import com.pingwang.bluetoothlib.listener.OnBleOtherDataListener
import com.pingwang.bluetoothlib.listener.OnCallbackBle
import com.pingwang.bluetoothlib.listener.OnCharacteristicListener
import com.pingwang.bluetoothlib.utils.BleLog
import com.pingwang.bluetoothlib.utils.UuidUtils
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.nio.charset.Charset
import java.nio.charset.StandardCharsets
import java.util.UUID

/**
 * ble速度测试界面
 * @author xing
 * @date 2025/09/12
 * @constructor 创建[BleSpeedTestActivity]
 */
class BleSpeedTestActivity : KBleBaseNewActivity<ActivityBleSpeedTestBinding>(), View.OnClickListener, OnCallbackBle, OnBleOtherDataListener {
    private val REFRESH_DATA = 3
    private var mList: MutableList<String> = mutableListOf()
    private var listAdapter: ArrayAdapter<String>? = null
    private var mBleDevice: BleDevice? = null
    private var sendUuid: UUID? = null
    private var sendUuidServer: UUID? = null
    private var sendUuidNotify: UUID? = null
    private var mCount = 0L

    // 指定每个分组的字节大小
    private val headMax = 12 // 1KB
    private var mMtu = 514 // 1KB
    private var mSendDataMax = 514 - headMax // 1KB
    private val filePickerLauncher = registerForActivityResult(ActivityResultContracts.GetContent()) { uri ->
        uri?.let { readFileChunks(it) }
    }
    private var mSelectFilePath = ""
    private var mAddress: String? = ""
    private var mSendList: MutableList<ByteArray> = mutableListOf()
    private var mReadList: MutableList<ByteArray> = mutableListOf()
    private var mWriteBytes: Long = 0
    private var mReadBytes: Long = 0
    private var mStartTime: Long = 0
    private var mStartWriteTime: Long = 0
    private var mStartReadTime: Long = 0

    private val mHandler = object : Handler(Looper.getMainLooper()) {
        override fun handleMessage(msg: Message) {
            super.handleMessage(msg)
            when (msg.what) {
                REFRESH_DATA -> {
                    listAdapter?.notifyDataSetChanged()
                }
            }
        }
    }

    override fun getLayoutView(): ActivityBleSpeedTestBinding {
        return ActivityBleSpeedTestBinding.inflate(layoutInflater)
    }

    override fun initListener() {
        binding.btnUuid.setOnClickListener(this)
        binding.btnUuidServer.setOnClickListener(this)
        binding.btnNotify.setOnClickListener(this)
        binding.btnSelectFile.setOnClickListener(this)
        binding.btnWriteTest.setOnClickListener(this)
        binding.btnReadTest.setOnClickListener(this)

    }

    override fun onClick(v: View) {
        when (v.id) {
            R.id.btn_uuid -> {
                val uuid = binding.etUuid.getText().toString().trim { it <= ' ' }.uppercase()
                sendUuid = UuidUtils.getUuid(uuid)
                addLog("设置特征UUID")
            }

            R.id.btn_uuid_server -> {
                val uuid = binding.etUuidServer.getText().toString().trim { it <= ' ' }.uppercase()
                sendUuidServer = UuidUtils.getUuid(uuid)
                addLog("设置服务UUID")
            }

            R.id.btn_notify -> {
//                val notifyStr = binding.etUuid.getText().toString().trim { it <= ' ' }.uppercase()
                sendUuidNotify = UuidUtils.getUuid("FFE2")
                readNotify(sendUuidNotify)
                readNotify(UuidUtils.getUuid("FFE3"))
                addLog("打开notify")
            }

            R.id.btnSelectFile -> {
                filePickerLauncher.launch("application/octet-stream")
            }

            R.id.btnWriteTest -> {
                if (TextUtils.isEmpty(mSelectFilePath)) {
                    addLog("请选择文件")
                    return
                }
                if (mBleDevice == null) {
                    addLog("请先连接设备")
                    return
                }
                if (sendUuid == null) {
                    addLog("请先设置特征UUID")
                    return
                }
                if (sendUuidServer == null) {
                    addLog("请先设置服务UUID")
                    return
                }

//                sendStartWriteFile()
                sendA5DataStart(mWriteBytes.toInt())

            }

            R.id.btnReadTest -> {
                sendStartReadFile()
            }


        }
    }


    override fun initData() {
        mAddress = intent.getStringExtra("mac")
        if (mAddress.isNullOrEmpty()) {
            finish()
            return
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            onServiceSuccess()
        }
        listAdapter = ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, mList)
        binding.listview.setAdapter(listAdapter)

    }


    @RequiresApi(Build.VERSION_CODES.O)
    override fun onServiceSuccess() {
        if (mBleManager != null && !TextUtils.isEmpty(mAddress)) {
            mBleDevice = mBleManager.getBleDevice(mAddress)
            mBleManager.setOnCallbackBle(this)
            mBleDevice?.let {
                it.setPreferredPhy(BluetoothDevice.PHY_LE_2M, BluetoothDevice.PHY_LE_2M)
                it.setMtu(517)
                it.setOnBleMtuListener(object : OnBleMtuListener {
                    override fun onMtuAvailable(mtu: Int) {
                        mMtu = mtu
                        mSendDataMax = mtu - headMax
                    }
                })
                it.setOnBleOtherDataListener(this)
                it.setOnCharacteristicListener(object : OnCharacteristicListener {

                    override fun onCharacteristicWriteOK(characteristic: BluetoothGattCharacteristic) {
                        BleLog.i("写入成功回调时间戳:${System.currentTimeMillis()}")
                        if (characteristic.uuid.toString() == sendUuid.toString()) {
                            val str = characteristic.value.toString(Charset.forName("UTF-8"))
                            if (str.startsWith("rate")) {
                                return
                            }
                            mCount += characteristic.value.size
                            runOnUiThread {
                                binding.tvSendNumber.text = (mCount).toString()
                                //发送完成
                                val currentTimeMillis = System.currentTimeMillis()
                                val caT = (currentTimeMillis - mStartTime) / 1000F
                                val noCaT = (currentTimeMillis - mStartWriteTime) / 1000F
                                val kb = mCount / 1024
                                binding.tvSpeedWrite.text =
                                    "写速度:${kb / (caT)}KB/s(擦)  ${kb / (noCaT)}KB/s  时间差(擦):${currentTimeMillis - mStartTime}"
                            }
                        }
                    }


                })
            }
        }
    }

    override fun onNotifyOtherData(uuid: String, data: ByteArray) {
        val cmdStr = String(data, Charset.forName("UTF-8"))
        if (cmdStr.startsWith("rateTx_ready") || checkA5StartData(data)) {
            //写开始
            binding.tvSendNumber.text = "0"
            mStartWriteTime = System.currentTimeMillis()
            addLog("开始发送:${mStartWriteTime - mStartTime}")
            val total = mSendList.size
            for ((index, bytes) in mSendList.withIndex()) {
                val a5Data = getA5Data(index, total, bytes)
                sendData(a5Data)
            }


            sendA5DataEnd()
        } else if (cmdStr.startsWith("rateRx_ready")) {
            //读开始
            mStartReadTime = System.currentTimeMillis()
            mReadBytes = 0
            binding.tvReceiveNumber.text = "0"
        } else {
            mReadBytes += data.size
            mReadList.add(data)
            CoroutineUtils.coroutineScopeIo {

                withContext(Dispatchers.Main) {
                    if (mReadBytes >= mWriteBytes) {
                        val equal = areByteArrayListsEqual(mReadList, mSendList)
                        addLog("内容是否相等:$equal")
                    }
                    val currentTimeMillis = System.currentTimeMillis()
                    val lng = currentTimeMillis - mStartReadTime
                    val kb = mReadBytes / 1024
                    binding.tvSpeedRead.text = "读速度:${kb / ((lng / 1000).toFloat())}KB/s  时间差:${lng}"
                    binding.tvReceiveNumber.text = (mReadBytes).toString()
                }
            }

        }
    }

    fun areByteArrayListsEqual(list1: MutableList<ByteArray>, list2: MutableList<ByteArray>): Boolean {
        // 先比较列表大小
        if (list1.size != list2.size) return false

        // 使用索引逐个比较字节数组
        return list1.indices.all { index ->
            list1[index].contentEquals(list2[index])
        }
    }

    override fun onServiceErr() {
        addLog("服务绑定失败")
    }

    override fun unbindServices() {
        addLog("服务解绑")
    }


    override fun onDisConnected(mac: String?, code: Int) {
        super.onDisConnected(mac, code)
        addLog("断开连接:$mac  错误码:$code")
    }

    private fun readFileChunks(uri: Uri) {

        CoroutineScope(Dispatchers.Main).launch {
            val path = uri.toString()
            addLog("正在读取文件:$path")
            mSelectFilePath = path
//            binding.tvFileName.text = path.substring(path.lastIndexOf("test"))
            val result = withContext(Dispatchers.IO) {
                try {
                    contentResolver.openInputStream(uri)?.use { inputStream ->
                        // 创建 MutableList<ByteArray> 来存储分块数据
                        mSendList.clear()
                        // 使用缓冲区读取文件，避免一次性加载大文件到内存
                        val buffer = ByteArray(mSendDataMax)
                        var bytesRead: Int
                        mWriteBytes = 0
                        while (inputStream.read(buffer).also { bytesRead = it } != -1) {
                            // 创建正确大小的字节数组副本
                            val chunk = if (bytesRead < mSendDataMax) {
                                buffer.copyOf(bytesRead)
                            } else {
                                buffer.copyOf() // 创建副本，避免重用缓冲区
                            }
                            mSendList.add(chunk)
                            mWriteBytes += bytesRead
                        }
                        // 构建结果字符串
                        val stringBuilder = StringBuilder()
                        stringBuilder.append("文件读取成功！共 $mWriteBytes 字节，分成 ${mSendList.size} 个组\n\n")
                        stringBuilder.toString()
                    } ?: "无法打开文件流"
                } catch (e: Exception) {
                    "读取文件时出错: ${e.message}"
                }
            }
            addLog(result)
        }
    }

    private fun sendStartWriteFile() {
        binding.tvSendNumber.text = ""
        binding.tvReceiveNumber.text = ""
        mCount = 0
        mStartTime = System.currentTimeMillis()
        val cmdStr = "rateTx_start:$mWriteBytes"
        sendData(cmdStr.toByteArray(StandardCharsets.US_ASCII))
        mHandler.postDelayed({
            //0x7261746554785F7265616479
            val byteArray = byteArrayOf(0x72, 0x61, 0x74, 0x65, 0x54, 0x78, 0x5F, 0x72, 0x65, 0x61, 0x64, 0x79)
            onNotifyOtherData("FFE2", byteArray)

        }, 1000)
    }

    private fun sendStartReadFile() {
        binding.tvSendNumber.text = ""
        binding.tvReceiveNumber.text = ""
        mReadList.clear()
        val cmdStr = "rateRx_start:$mWriteBytes"
        sendData(cmdStr.toByteArray(StandardCharsets.US_ASCII))
    }

    private fun sendData(hex: ByteArray) {
        val sendDataBean = SendDataBean(hex, sendUuid, BleConfig.WRITE_DATA, sendUuidServer)
        mBleDevice?.sendDataNow(sendDataBean)
    }

    private fun sendA5DataStart(fileSize: Int) {
        binding.tvSendNumber.text = ""
        binding.tvReceiveNumber.text = ""
        mCount = 0
        mStartTime = System.currentTimeMillis()
        //包头+cid+长度+sum+包尾=7
        val a5Data = ByteArray(12)
        val len = 0x07
        a5Data[0] = 0xA5.toByte()
        a5Data[1] = 0x00
        a5Data[2] = len.toByte()
        a5Data[3] = 0x01
        a5Data[4] = 0x02
        a5Data[5] = 0x00
        a5Data[6] = (fileSize shr 24).toByte()
        a5Data[7] = (fileSize shr 16).toByte()
        a5Data[8] = (fileSize shr 8).toByte()
        a5Data[9] = fileSize.toByte()
        val crcData = ByteArray(9)
        System.arraycopy(a5Data, 1, crcData, 0, crcData.size)
        val crc16 = AiLinkBleCheckUtil.getCrc16(crcData)
        a5Data[a5Data.size - 2] = (crc16 shr 8).toByte()
        a5Data[a5Data.size - 1] = crc16.toByte()
        sendData(a5Data)
    }

    private fun checkA5StartData(data: ByteArray): Boolean {
        return data[0].toUnsignedInt() == 0xA5 && data[3].toUnsignedInt() == 0x01
    }

    /**
     * 获取a5数据
     *
     * @param cid  cid
     * @param data 数据
     * @return [byte[]]
     */
    private fun getA5Data(count: Int, total: Int, data: ByteArray): ByteArray {
        //包头+cid+长度+sum+包尾=7
        val a5Data = ByteArray(data.size + headMax)
        val len = data.size + (headMax - 5)
        a5Data[0] = 0xA5.toByte()
        a5Data[1] = (len shr 8).toByte()
        a5Data[2] = len.toByte()
        a5Data[3] = 0x02
        a5Data[4] = 0x02
        a5Data[5] = (count shr 8).toByte()
        a5Data[6] = count.toByte()
        a5Data[7] = (total shr 8).toByte()
        a5Data[8] = total.toByte()
        val crcData = ByteArray(a5Data.size -3)
        System.arraycopy(data, 0, a5Data, 9, data.size)
        System.arraycopy(a5Data, 1, crcData, 0, crcData.size)
        val crc16 = AiLinkBleCheckUtil.getCrc16(crcData)
        a5Data[a5Data.size - 2] = (crc16 shr 8).toByte()
        a5Data[a5Data.size - 1] = crc16.toByte()
        return a5Data
    }

    private fun sendA5DataEnd() {
        //包头+cid+长度+sum+包尾=7
        val a5Data = ByteArray(8)
        val len = 0x03
        a5Data[0] = 0xA5.toByte()
        a5Data[1] = 0x00
        a5Data[2] = len.toByte()
        a5Data[3] = 0x03
        a5Data[4] = 0x02
        a5Data[5] = 0x00
        val crcData = ByteArray(5)
        System.arraycopy(a5Data, 1, crcData, 0, crcData.size)
        val crc16 = AiLinkBleCheckUtil.getCrc16(crcData)
        a5Data[a5Data.size - 2] = (crc16 shr 8).toByte()
        a5Data[a5Data.size - 1] = crc16.toByte()
        sendData(a5Data)
    }

    /**
     * 设置通知
     */
    private fun readNotify(uuidNotify: UUID?, notifyOpen: Boolean = true) {
        if (uuidNotify == null) {
            return
        }
        if (mBleDevice != null && sendUuidServer != null) {
            if (notifyOpen) {
                mBleDevice!!.setNotify(sendUuidServer, uuidNotify)
            } else {
                mBleDevice!!.setCloseNotify(sendUuidServer, uuidNotify)
            }
        } else {
            mList.add(TimeUtils.getTime() + "请先设置特征UUID和服务UUID")
            mHandler.sendEmptyMessage(REFRESH_DATA)
        }
    }

    private var mShowLog = true
    private fun addLog(log: String?) {
        if (mShowLog) {
            BleLog.i(log)
            mList.add(TimeUtils.getTimeSSS() + log)
            mHandler.sendEmptyMessage(REFRESH_DATA)
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        mBleDevice?.disconnect()
    }
}