import struct
from time import sleep
from typing import List, Literal
from qlsdk.core.serial import SerialDevice
from loguru import logger

from qlsdk.core.utils import to_channels
from qlsdk.lse.config import EStimConfig, LightSoundStimConfig
from qlsdk.lse.message import ArsLSEMessage, ArsLSEParser, ArsLSEResponse
from qlsdk.lse.type import ArsLSECommand, LseControlMode
        
class ArsLse(object):
    def __init__(self, device: SerialDevice):
        if not device:
            raise ValueError("不能指定空设备")    
        # 串口设备
        self.device:SerialDevice = device
        # 消息解析器
        self._parser = ArsLSEParser()
        
        # 设备信息
        self._desc = None
        # 音频列表
        self._audio_list = None
        # 电刺激状态
        self._e_stim_status = False
        # 声光刺激状态
        self._sound_light_status = False
        # 开机持续时间（ms）
        self._time = 0
        
        # 回调函数
        self._current_callback = None
        self._impedance_callback = None
        
        # log status
        self._current_log = False
        
    # 断开设备连接
    def disconnect(self):
        self.device.disconnect()
        
    # 设备连接
    def connect(self):
        if self.device.is_connected():
            logger.warning(f"设备[{self.device.port}]已连接，请勿重复连接")
        else:
            self.device.connect()
            
        # 处理设备返回的数据
        self.device.start_reading(self._response_handler)  # 确保读取线程正在运行
        
    def _response_handler(self, response: bytes):
        result = self._parser.feed_data(response)
        if result and len(result) > 0:
            for resp in result:
                # 更新运行时长
                self._time = resp.time
                if resp.command == ArsLSECommand.HAND_SHAKE.value:
                    self._deal_hand_shake(resp)
                elif resp.command == ArsLSECommand.E_STIM_STATE_NOTIFY.value:
                    logger.debug("刺激状态通知: " + resp.custom_data.hex())
                elif resp.command == ArsLSECommand.IMPEDANCE_DATA.value:
                    self._deal_impedance_data(resp.custom_data)
                elif resp.command == ArsLSECommand.E_CURRENT_DATA.value:
                    self._deal_current_data(resp.custom_data)
                elif resp.command == ArsLSECommand.GET_E_REMAIN_TIME.value:
                    logger.debug("剩余电刺激时间: " + resp.custom_data.hex())
                elif resp.command == ArsLSECommand.GET_AL_REMAIN_TIME.value:
                    logger.debug("剩余声光刺激时间: " + resp.custom_data.hex())
                elif resp.command == ArsLSECommand.GET_E_PATTERN.value:
                    logger.debug("当前电刺激范式: " + resp.custom_data.hex())
                elif resp.command == ArsLSECommand.SET_AL_STIM.value:
                    logger.debug("声光刺激配置响应: " + resp.result_desc)
                elif resp.command == ArsLSECommand.STOP_AL_STIM.value:
                    logger.debug("声光刺激停止响应: " + resp.result_desc)
                elif resp.command == ArsLSECommand.STOP_E_STIM.value:
                    logger.debug("停止电刺激响应: " + resp.result_desc)
                elif resp.command == ArsLSECommand.START_E_STIM.value:
                    logger.debug("开始电刺激响应: " + resp.result_desc)
                elif resp.command == ArsLSECommand.READY_FOR_UI_STIM.value:
                    logger.debug("准备刺激（使用UI参数）响应: " + resp.result_desc)
                elif resp.command == ArsLSECommand.START_UI_STIM.value:
                    logger.debug("开始刺激（使用UI参数）响应: " + resp.result_desc)
                elif resp.command == ArsLSECommand.STOP_UI_STIM.value:
                    logger.debug("停止刺激（使用UI参数）响应: " + resp.result_desc)
                elif resp.command in [ArsLSECommand.SYNC_START_ALE.value, ArsLSECommand.SYNC_STOP_ALE.value]:
                    logger.debug("光声电同步响应: " + resp.custom_data.hex())
                elif resp.command == ArsLSECommand.GET_AUDIO_LIST.value:
                    self._deal_audio_list(resp)
                elif resp.command == 0xA003:
                    print(resp.custom_data)
                else:
                    logger.info(f"响应: {resp.command_desc}-{resp.result_desc}")
        
    # 处理握手响应消息
    def _deal_hand_shake(self, resp: ArsLSEResponse):
        if resp.custom_data:
            self._desc = resp.custom_data.decode('utf-8')
        else:
            self._desc = None
        logger.debug(f"握手响应{resp.result_desc}: {self._desc}")
        
    # 处理实时阻抗数据包
    def _deal_impedance_data(self, data: bytes):
        # 解析依据协议文档
        if len(data) < 8:
            return
        
        # 阻抗通道
        channels = to_channels(data[0:4])
        
        # 阻抗值（单位：欧姆）
        impedance_values = []
        for i in range(8, len(data), 4):
            if i + 4 <= len(data):
                impedance = int.from_bytes(data[i:i+4], 'little')
                impedance_values.append(impedance)
        # logger.debug(f"接收到阻抗数据，通道 {channels}，阻抗值: {impedance_values}")
        
        # 回调函数
        if self._impedance_callback:
            packet = {"channels": channels, "impedance": impedance_values}
            self._impedance_callback(packet)
        
    # 处理实时电流数据包
    def _deal_current_data(self, data: bytes):
        # 解析刺激电流数据的具体格式，根据设备协议文档进行解析
        if len(data) < 8:
            return
        
        channels = to_channels(data[0:4])
        
        # 电流值（单位：uA)
        current_values = []
        for i in range(8, len(data), 12):
            if i + 12 <= len(data):
                current = int.from_bytes(data[i:i+4], 'little', signed=True)
                current_values.append(current)
                
        if self._current_log:
            logger.info(f"接收到电流数据, 通道: {channels}, 电流值: {current_values}")
        
        # 回调函数
        if self._current_callback:
            packet = {"channels": channels, "current": current_values}
            self._current_callback(packet)
        
    # 准备刺激参数（使用UI参数）
    def ready_for_ui_stim(self):
        logger.debug("发送准备刺激指令（使用UI参数）")
        return self.device.send(ArsLSEMessage.ready_for_ui_stim())
        
    # 开始刺激（使用UI参数）
    def start_ui_stim(self):
        logger.debug("发送开始刺激指令(使用UI参数)")
        return self.device.send(ArsLSEMessage.start_ui_stim())
        
    # 停止刺激（使用UI参数）
    def stop_ui_stim(self):
        logger.debug("发送停止刺激指令(使用UI参数)")
        return self.device.send(ArsLSEMessage.stop_ui_stim())
    
    # 握手指令，mode=0使用API参数刺激，mode=1使用UI参数刺激
    def hand_shake(self, mode: Literal[LseControlMode.API, LseControlMode.UI] = LseControlMode.API):
        logger.debug("发送握手指令")
        return self.device.send(ArsLSEMessage.handshake(mode=mode))
    
    # 开始电刺激
    def start_e_stim(self, config:list, mode:Literal[0, 1, 2] = 0, log_realtime_current=False):
        logger.debug("发送开始电刺激指令")
        self._current_log = log_realtime_current
        
        # 参数过滤及长度控制
        # config是json字符串列表，每个字符串是一个json对象，包含刺激参数
        # 在同一次配置中，json中的多个通道不能重复
        paradigms = []
        for cfg in config:      
            # 通过配置对象，管控输入参数的合法性，避免直接使用用户输入的json字符串导致设备异常  
            wave_form=cfg.get("wave_form")    
            paradigm = EStimConfig(
                    wave_form=wave_form,
                    anode_channels=cfg.get("anode_channels"),
                    cathode_channels=cfg.get("cathode_channels"),
                    current_intensity=cfg.get("current_intensity"),
                    stim_duration_ms=cfg.get("group_duration"),
                    sham=cfg.get("sham", False),
                    delay_ms=cfg.get("delay_ms", -1),
                    delay_us=cfg.get("delay_us", -1),
                    ramp_up_ms=cfg.get("ramp_up_ms", 0),
                    ramp_down_ms=cfg.get("ramp_down_ms", 0),
                    freq=cfg.get("frequency", -1),
                    phase=cfg.get("phase", 0),
                    duty_cycle=cfg.get("duty_cycle", 0),
                    high_freq_1=cfg.get("freq_band_max", 0),
                    low_freq_1=cfg.get("freq_band_min", 0),
                    seed=cfg.get("duty_cycle", 1),
                    pos_current=cfg.get("pos_current"),
                    pos_pulse_us=cfg.get("pos_pulse_us"),
                    neg_current=cfg.get("neg_current"),
                    neg_pulse_us=cfg.get("neg_pulse_us"),
                    dead_us=cfg.get("dead_us"),
                    pulse_num=cfg.get("pulse_num", 1),
                    pulse_interval_us=cfg.get("pulse_interval_us")
            )
            
            
            paradigms.append(paradigm.to_json())
        
        logger.debug(f"电刺激配置: {paradigms}")
        msg = ArsLSEMessage.start_e_stim(paradigms, mode=mode)
        print(msg)
        return self.device.send(msg)

    def stop_e_stim(self):
        logger.debug("发送停止电刺激指令")
        return self.device.send(ArsLSEMessage.stop_e_stim())
    
    # 非数组
    def start_light_sound_stim(self, config: List):
        logger.debug("发送开始声光刺激指令")
        paradigms = []
        for cfg in config:      
            # 通过配置对象，管控输入参数的合法性，避免直接使用用户输入的json字符串导致设备异常      
            paradigm = LightSoundStimConfig(
                    audio_name=cfg.get("audio_name"),
                    audio_volume=cfg.get("audio_volume"),
                    light_freq=cfg.get("light_freq"),
                    light_intensity=cfg.get("light_intensity"),
                    light_duty_cycle=cfg.get("light_duty_cycle"),
                    sound_freq=cfg.get("sound_freq"),
                    sound_duty_cycle=cfg.get("sound_duty_cycle"),
                    duration=cfg.get("duration")
            )
            paradigms.append(paradigm.to_json())
        return self.device.send(ArsLSEMessage.start_light_sound_stim(paradigms))
    
    def config_light_sound_stim(self, config: List):
        logger.debug("发送声光刺激配置指令")
        
        return self.device.send(ArsLSEMessage.start_light_sound_stim())
    
    def get_audio_list(self, timeout=2):
        logger.debug("发送获取可用音频列表指令")
        self._audio_list = None
        self.device.send(ArsLSEMessage.get_audio_list())
        wait_count = timeout * 100
        while wait_count > 0:
            if self._audio_list is None:
                sleep(0.01)
                continue
            else:
                break
            
        return self._audio_list
    
    def _deal_audio_list(self, resp: ArsLSEResponse):
        logger.info(f"获取可用音频列表{resp.result_desc}")
        # 音频个数
        audio_num = struct.unpack('<I', resp.custom_data[:4])[0]
        
        # 跳过数量字段
        offset = 4
        audio_list = []
        
        for i in range(audio_num):
            # 找到字符串结束位置（null字符）
            end_pos = offset
            while end_pos < len(resp.custom_data) and resp.custom_data[end_pos] != 0:
                end_pos += 1
            
            # 提取字符串
            if end_pos > offset:
                string_bytes = resp.custom_data[offset:end_pos]
                try:
                    # 尝试UTF-8解码
                    string = string_bytes.decode('utf-8')
                except UnicodeDecodeError:
                    # 尝试其他编码或使用原始字节
                    string = repr(string_bytes)
            else:
                string = ""  # 空字符串
            
            audio_list.append(string)
            
            # 移动到下一个字符串开始位置
            offset = end_pos + 1
            # 跳过可能的填充（连续null字符）
            while offset < len(resp.custom_data) and resp.custom_data[offset] == 0:
                offset += 1
            
        # 音频列表
        self._audio_list = audio_list
    
    def stop_light_sound_stim(self):
        logger.debug("发送停止声光刺激指令")
        return self.device.send(ArsLSEMessage.stop_light_sound_stim())
    
    def start_impedance_measure(self):
        logger.debug("发送开始阻抗采集指令")
        return self.device.send(ArsLSEMessage.start_impedance_measure())
    
    def stop_impedance_measure(self):
        logger.debug("发送停止阻抗采集指令")
        return self.device.send(ArsLSEMessage.stop_impedance_measure())
    
    # 
    def set_current_callback(self, callback):   
        self._current_callback = callback
        
    def set_impedance_callback(self, callback):
        self._impedance_callback = callback
        