from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLineEdit, QCheckBox, QComboBox, QTableWidget, QHeaderView, QMessageBox, QHBoxLayout
from qfluentwidgets import TitleLabel, BodyLabel, PushButton, CheckBox, TableWidget, LineEdit, ComboBox,MessageBox,SubtitleLabel,FluentIcon
from qfluentwidgets import InfoBar
from PyQt5.QtCore import Qt
from app.tools.analyzing_ioc import analyzing_ioc
from app.tools.code_generator import CodeGenerator
import os
import csv
import re

class BspSimplePeripheral(QWidget):
    def __init__(self, project_path, peripheral_name, template_names):
        super().__init__()
        self.project_path = project_path
        self.peripheral_name = peripheral_name
        self.template_names = template_names
        # 加载描述
        describe_path = os.path.join(CodeGenerator.get_assets_dir("User_code/bsp"), "describe.csv")
        self.descriptions = CodeGenerator.load_descriptions(describe_path)
        self._init_ui()
        self._load_config()

    def _init_ui(self):
        layout = QVBoxLayout(self)

        # 顶部横向布局：左侧复选框，居中标题
        top_layout = QHBoxLayout()
        top_layout.setAlignment(Qt.AlignVCenter)

        self.generate_checkbox = CheckBox(f"启用 {self.peripheral_name}")
        top_layout.addWidget(self.generate_checkbox, alignment=Qt.AlignLeft)

        # 弹性空间
        top_layout.addStretch()

        title = SubtitleLabel(f"{self.peripheral_name} 配置             ")
        title.setAlignment(Qt.AlignHCenter)
        top_layout.addWidget(title, alignment=Qt.AlignHCenter)

        # 再加一个弹性空间，保证标题居中
        top_layout.addStretch()

        layout.addLayout(top_layout)

        desc = self.descriptions.get(self.peripheral_name.lower(), "")
        if desc:
            desc_label = BodyLabel(f"功能说明：{desc}")
            desc_label.setWordWrap(True)
            layout.addWidget(desc_label)
        layout.addStretch()
    def is_need_generate(self):
        return self.generate_checkbox.isChecked()

    def _generate_bsp_code_internal(self):
        # 检查是否需要生成
        if not self.is_need_generate():
            # 如果未勾选，检查文件是否已存在，如果存在则跳过
            for filename in self.template_names.values():
                output_path = os.path.join(self.project_path, f"User/bsp/{filename}")
                if os.path.exists(output_path):
                    return "skipped"  # 返回特殊值表示跳过
            return "not_needed"  # 返回特殊值表示不需要生成
        
        # 使用外设名称作为子文件夹名（小写）
        periph_folder = self.peripheral_name.lower()
        template_base_dir = CodeGenerator.get_assets_dir("User_code/bsp")
        
        for key, filename in self.template_names.items():
            # 先尝试从子文件夹加载
            template_path = os.path.join(template_base_dir, periph_folder, filename)
            if not os.path.exists(template_path):
                # 如果子文件夹不存在，尝试从根目录加载（向后兼容）
                template_path = os.path.join(template_base_dir, filename)
            
            template_content = CodeGenerator.load_template(template_path)
            if not template_content:
                return False
            output_path = os.path.join(self.project_path, f"User/bsp/{filename}")
            CodeGenerator.save_with_preserve(output_path, template_content)  # 使用保留用户区域的写入
        
        # 复制BSP文件夹下的其他额外文件（如 README.md, CHANGELOG.md）
        periph_template_dir = os.path.join(template_base_dir, periph_folder)
        if os.path.exists(periph_template_dir) and os.path.isdir(periph_template_dir):
            import shutil
            files_to_process = list(self.template_names.values())
            for item in os.listdir(periph_template_dir):
                # 跳过已处理的主要文件
                if item in files_to_process:
                    continue
                
                src_file = os.path.join(periph_template_dir, item)
                dst_file = os.path.join(self.project_path, f"User/bsp/{item}")
                
                # 只复制文件，不复制子目录
                if os.path.isfile(src_file):
                    os.makedirs(os.path.dirname(dst_file), exist_ok=True)
                    shutil.copy2(src_file, dst_file)
                    print(f"复制BSP额外文件: {item}")
        
        self._save_config()
        return True
        return True


    def _save_config(self):
        config_path = os.path.join(self.project_path, "User/bsp/bsp_config.yaml")
        config_data = CodeGenerator.load_config(config_path)
        config_data[self.peripheral_name.lower()] = {'enabled': True}
        CodeGenerator.save_config(config_data, config_path)

    def _load_config(self):
        config_path = os.path.join(self.project_path, "User/bsp/bsp_config.yaml")
        config_data = CodeGenerator.load_config(config_path)
        conf = config_data.get(self.peripheral_name.lower(), {})
        if conf.get('enabled', False):
            self.generate_checkbox.setChecked(True)

class BspPeripheralBase(QWidget):
    def __init__(self, project_path, peripheral_name, template_names, enum_prefix, handle_prefix, yaml_key, get_available_func):
        super().__init__()
        self.project_path = project_path
        self.peripheral_name = peripheral_name
        self.template_names = template_names
        self.enum_prefix = enum_prefix
        self.handle_prefix = handle_prefix
        self.yaml_key = yaml_key
        self.get_available_func = get_available_func
        self.available_list = []
        # 新增：加载描述
        describe_path = os.path.join(CodeGenerator.get_assets_dir("User_code/bsp"), "describe.csv")
        self.descriptions = CodeGenerator.load_descriptions(describe_path)
        self._init_ui()
        self._load_config()

    def _init_ui(self):
        layout = QVBoxLayout(self)

        top_layout = QHBoxLayout()
        top_layout.setAlignment(Qt.AlignVCenter)

        self.generate_checkbox = CheckBox(f"生成 {self.peripheral_name} 代码")
        self.generate_checkbox.stateChanged.connect(self._on_generate_changed)
        top_layout.addWidget(self.generate_checkbox, alignment=Qt.AlignLeft)

        top_layout.addStretch()

        title = SubtitleLabel(f"{self.peripheral_name} 配置             ")
        title.setAlignment(Qt.AlignHCenter)
        top_layout.addWidget(title, alignment=Qt.AlignHCenter)

        top_layout.addStretch()

        layout.addLayout(top_layout)

        desc = self.descriptions.get(self.peripheral_name.lower(), "")
        if desc:
            desc_label = BodyLabel(f"功能说明：{desc}")
            desc_label.setWordWrap(True)
            layout.addWidget(desc_label)

        self.content_widget = QWidget()
        content_layout = QVBoxLayout(self.content_widget)
        self._get_available_list()
        if not self.available_list:
            content_layout.addWidget(BodyLabel(f"在 .ioc 文件中未找到已启用的 {self.peripheral_name}"))
        else:
            content_layout.addWidget(BodyLabel(f"可用的 {self.peripheral_name}: {', '.join(self.available_list)}"))
            self.table = TableWidget()
            self.table.setColumnCount(3)
            self.table.setHorizontalHeaderLabels(["设备名称", f"{self.peripheral_name}选择", "操作"])
            self.table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
            content_layout.addWidget(self.table)
            add_btn = PushButton(f"添加 {self.peripheral_name} 设备")
            add_btn.clicked.connect(self._add_device)
            content_layout.addWidget(add_btn)
        layout.addWidget(self.content_widget)
        self.content_widget.setEnabled(False)


    def _get_available_list(self):
        self.available_list = self.get_available_func(self.project_path)

    def _on_generate_changed(self, state):
        self.content_widget.setEnabled(state == 2)

    def _add_device(self):
        row = self.table.rowCount()
        self.table.insertRow(row)
        name_edit = LineEdit()
        name_edit.setPlaceholderText(f"输入设备名称")
        self.table.setCellWidget(row, 0, name_edit)
        combo = ComboBox()  # 使用 Fluent 风格 ComboBox
        combo.addItems(self.available_list)
        self.table.setCellWidget(row, 1, combo)
        del_btn = PushButton(FluentIcon.DELETE,"删除" )  # 添加垃圾桶图标
        del_btn.clicked.connect(lambda: self._delete_device(row))
        self.table.setCellWidget(row, 2, del_btn)

    def _delete_device(self, row):
        self.table.removeRow(row)

    def _collect_configs(self):
        configs = []
        for row in range(self.table.rowCount()):
            name_widget = self.table.cellWidget(row, 0)
            sel_widget = self.table.cellWidget(row, 1)
            if name_widget and sel_widget:
                name = name_widget.text().strip()
                sel = sel_widget.currentText()
                if name and sel:
                    configs.append((name.upper(), sel))
        return configs

    def is_need_generate(self):
        return self.generate_checkbox.isChecked() and bool(self._collect_configs())

    def generate_bsp_code(self):
        if not self.is_need_generate():
            return False
        configs = self._collect_configs()
        if not configs:
            return False
        template_dir = CodeGenerator.get_template_dir()
        if not self._generate_header_file(configs, template_dir):
            return False
        if not self._generate_source_file(configs, template_dir):
            return False
        self._save_config(configs)
        InfoBar.success(
            title="任务生成成功",
            content=f"{self.peripheral_name} 代码生成成功！",
            parent=self,
            duration=2000
        )
        return True

    def _generate_header_file(self, configs, template_dir):
        # 构建模板路径，优先从子文件夹读取
        periph_folder = self.peripheral_name.lower()
        template_base_dir = CodeGenerator.get_assets_dir("User_code/bsp")
        template_path = os.path.join(template_base_dir, periph_folder, self.template_names['header'])
        
        if not os.path.exists(template_path):
            # 向后兼容：尝试从根目录读取
            template_path = os.path.join(template_base_dir, self.template_names['header'])
        
        template_content = CodeGenerator.load_template(template_path)
        if not template_content:
            return False
        enum_lines = [f"  {self.enum_prefix}_{name}," for name, _ in configs]
        content = CodeGenerator.replace_auto_generated(
            template_content, f"AUTO GENERATED {self.enum_prefix}_NAME", "\n".join(enum_lines)
        )
        output_path = os.path.join(self.project_path, f"User/bsp/{self.template_names['header']}")
        CodeGenerator.save_with_preserve(output_path, content)  # 使用保留用户区域的写入
        return True

    def _generate_source_file(self, configs, template_dir):
        # 从子文件夹加载模板（与_generate_header_file保持一致）
        periph_folder = self.peripheral_name.lower()
        template_base_dir = CodeGenerator.get_assets_dir("User_code/bsp")
        template_path = os.path.join(template_base_dir, periph_folder, self.template_names['source'])
        
        if not os.path.exists(template_path):
            # 如果子文件夹不存在，尝试从根目录加载（向后兼容）
            template_path = os.path.join(template_base_dir, self.template_names['source'])
        
        template_content = CodeGenerator.load_template(template_path)
        if not template_content:
            return False
        # Get函数
        get_lines = []
        for idx, (name, instance) in enumerate(configs):
            if idx == 0:
                get_lines.append(f"  if ({self.handle_prefix}->Instance == {instance})")
            else:
                get_lines.append(f"  else if ({self.handle_prefix}->Instance == {instance})")
            get_lines.append(f"    return {self.enum_prefix}_{name};")
        content = CodeGenerator.replace_auto_generated(
            template_content, f"AUTO GENERATED {self.enum_prefix.split('_')[1]}_GET", "\n".join(get_lines)
        )
        # Handle函数
        handle_lines = []
        for name, instance in configs:
            handle_lines.append(f"    case {self.enum_prefix}_{name}:")
            # UART/USART统一用 huart 前缀
            if self.enum_prefix == "BSP_UART":
                # 提取数字部分
                num = ''.join(filter(str.isdigit, instance))
                handle_lines.append(f"      return &huart{num};")
            else:
                handle_lines.append(f"      return &h{instance.lower()};")
        content = CodeGenerator.replace_auto_generated(
            content, f"AUTO GENERATED {self.enum_prefix}_GET_HANDLE", "\n".join(handle_lines)
        )
        output_path = os.path.join(self.project_path, f"User/bsp/{self.template_names['source']}")
        CodeGenerator.save_with_preserve(output_path, content)  # 使用保留用户区域的写入
        return True

    def _save_config(self, configs):
        config_path = os.path.join(self.project_path, "User/bsp/bsp_config.yaml")
        config_data = CodeGenerator.load_config(config_path)
        config_data[self.yaml_key] = {
            'enabled': True,
            'devices': [{'name': name, 'instance': instance} for name, instance in configs]
        }
        CodeGenerator.save_config(config_data, config_path)

    def _load_config(self):
        config_path = os.path.join(self.project_path, "User/bsp/bsp_config.yaml")
        config_data = CodeGenerator.load_config(config_path)
        conf = config_data.get(self.yaml_key, {})
        if conf.get('enabled', False):
            self.generate_checkbox.setChecked(True)
            devices = conf.get('devices', [])
            for device in devices:
                if self.available_list:
                    self._add_device()
                    row = self.table.rowCount() - 1
                    name_widget = self.table.cellWidget(row, 0)
                    sel_widget = self.table.cellWidget(row, 1)
                    if name_widget:
                        name_widget.setText(device.get('name', ''))
                    if sel_widget:
                        instance = device.get('instance', '')
                        if instance in self.available_list:
                            sel_widget.setCurrentText(instance)

    def _generate_bsp_code_internal(self):
        # 检查是否需要生成
        if not self.is_need_generate():
            # 如果未勾选，检查文件是否已存在，如果存在则跳过
            header_file = f"{self.yaml_key}.h"
            source_file = f"{self.yaml_key}.c"
            header_path = os.path.join(self.project_path, f"User/bsp/{header_file}")
            source_path = os.path.join(self.project_path, f"User/bsp/{source_file}")
            if os.path.exists(header_path) or os.path.exists(source_path):
                return "skipped"  # 返回特殊值表示跳过
            return "not_needed"  # 返回特殊值表示不需要生成
        configs = self._collect_configs()
        if not configs:
            return False
        template_dir = CodeGenerator.get_template_dir()
        if not self._generate_header_file(configs, template_dir):
            return False
        if not self._generate_source_file(configs, template_dir):
            return False
        self._save_config(configs)
        return True

# 各外设的可用列表获取函数
def get_available_i2c(project_path):
    ioc_files = [f for f in os.listdir(project_path) if f.endswith('.ioc')]
    if ioc_files:
        ioc_path = os.path.join(project_path, ioc_files[0])
        return analyzing_ioc.get_enabled_i2c_from_ioc(ioc_path)
    return []

def get_available_can(project_path):
    ioc_files = [f for f in os.listdir(project_path) if f.endswith('.ioc')]
    if ioc_files:
        ioc_path = os.path.join(project_path, ioc_files[0])
        return analyzing_ioc.get_enabled_can_from_ioc(ioc_path)
    return []

def get_available_fdcan(project_path):
    """获取可用的FDCAN列表"""
    ioc_files = [f for f in os.listdir(project_path) if f.endswith('.ioc')]
    if ioc_files:
        ioc_path = os.path.join(project_path, ioc_files[0])
        return analyzing_ioc.get_enabled_fdcan_from_ioc(ioc_path)
    return []

def get_available_spi(project_path):
    ioc_files = [f for f in os.listdir(project_path) if f.endswith('.ioc')]
    if ioc_files:
        ioc_path = os.path.join(project_path, ioc_files[0])
        return analyzing_ioc.get_enabled_spi_from_ioc(ioc_path)
    return []

def get_available_uart(project_path):
    ioc_files = [f for f in os.listdir(project_path) if f.endswith('.ioc')]
    if ioc_files:
        ioc_path = os.path.join(project_path, ioc_files[0])
        return analyzing_ioc.get_enabled_uart_from_ioc(ioc_path)
    return []

def get_available_gpio(project_path):
    ioc_files = [f for f in os.listdir(project_path) if f.endswith('.ioc')]
    if ioc_files:
        ioc_path = os.path.join(project_path, ioc_files[0])
        return analyzing_ioc.get_enabled_gpio_from_ioc(ioc_path)
    return []

def get_available_pwm(project_path):
    """获取可用的PWM通道"""
    ioc_files = [f for f in os.listdir(project_path) if f.endswith('.ioc')]
    if ioc_files:
        ioc_path = os.path.join(project_path, ioc_files[0])
        return analyzing_ioc.get_enabled_pwm_from_ioc(ioc_path)
    return []

# 具体外设类
class bsp_i2c(BspPeripheralBase):
    def __init__(self, project_path):
        super().__init__(
            project_path,
            "I2C",
            {'header': 'i2c.h', 'source': 'i2c.c'},
            "BSP_I2C",
            "hi2c",
            "i2c",
            get_available_i2c
        )


class bsp_can(BspPeripheralBase):
    def __init__(self, project_path):
        super().__init__(
            project_path,
            "CAN",
            {'header': 'can.h', 'source': 'can.c'},
            "BSP_CAN",
            "hcan",
            "can",
            get_available_can
        )

    def _generate_bsp_code_internal(self):
        """重写生成方法，直接复制can文件夹中的文件"""
        # 检查是否需要生成
        if not self.is_need_generate():
            for filename in self.template_names.values():
                output_path = os.path.join(self.project_path, f"User/bsp/{filename}")
                if os.path.exists(output_path):
                    return "skipped"
            return "not_needed"
        
        # 调用父类方法生成配置化的代码（头文件和源文件）
        return super()._generate_bsp_code_internal()

    def _generate_source_file(self, configs, template_dir):
        # 从子文件夹加载模板（与_generate_header_file保持一致）
        periph_folder = self.peripheral_name.lower()
        template_base_dir = CodeGenerator.get_assets_dir("User_code/bsp")
        template_path = os.path.join(template_base_dir, periph_folder, self.template_names['source'])
        
        if not os.path.exists(template_path):
            # 如果子文件夹不存在，尝试从根目录加载（向后兼容）
            template_path = os.path.join(template_base_dir, self.template_names['source'])
        
        template_content = CodeGenerator.load_template(template_path)
        if not template_content:
            return False
            
        # CAN_Get函数
        get_lines = []
        for idx, (name, instance) in enumerate(configs):
            if idx == 0:
                get_lines.append(f"    if (hcan->Instance == {instance})")
            else:
                get_lines.append(f"    else if (hcan->Instance == {instance})")
            get_lines.append(f"        return {self.enum_prefix}_{name};")
        content = CodeGenerator.replace_auto_generated(
            template_content, "AUTO GENERATED CAN_GET", "\n".join(get_lines)
        )
        
        # Handle函数
        handle_lines = []
        for name, instance in configs:
            num = ''.join(filter(str.isdigit, instance))  # 提取数字
            handle_lines.append(f"    case {self.enum_prefix}_{name}:")
            handle_lines.append(f"      return &hcan{num};")
        content = CodeGenerator.replace_auto_generated(
            content, f"AUTO GENERATED {self.enum_prefix}_GET_HANDLE", "\n".join(handle_lines)
        )
        
        # 生成CAN初始化代码
        init_lines = []
        
        # 先设置初始化标志
        init_lines.append("    // 先设置初始化标志，以便后续回调注册能通过检查")
        init_lines.append("    inited = true;")
        init_lines.append("")
        
        # 按照新的FIFO分配策略
        can_instances = [instance for _, instance in configs]
        can_count = len(can_instances)
        
        # 根据CAN数量分配FIFO
        if can_count == 1:
            # 只有CAN1 -> 用FIFO0
            self._generate_single_can_init(init_lines, configs, "CAN_RX_FIFO0")
        elif can_count == 2:
            # CAN1和CAN2 -> CAN1用FIFO0，CAN2用FIFO1
            self._generate_dual_can_init(init_lines, configs)
        elif can_count >= 3:
            # CAN1,2,3+ -> CAN1和CAN2用FIFO0，CAN3用FIFO1
            self._generate_multi_can_init(init_lines, configs)
        
        content = CodeGenerator.replace_auto_generated(
            content, "AUTO GENERATED CAN_INIT", "\n".join(init_lines)
        )
        
        output_path = os.path.join(self.project_path, f"User/bsp/{self.template_names['source']}")
        CodeGenerator.save_with_preserve(output_path, content)
        return True

    def _generate_single_can_init(self, init_lines, configs, fifo_assignment):
        """单个CAN的初始化（使用FIFO0）"""
        name, instance = configs[0]
        can_num = instance[-1]  # CAN1 -> 1
        
        init_lines.extend([
            f"    // 初始化 {instance} - 使用 FIFO0",
            f"    CAN_FilterTypeDef can{can_num}_filter = {{0}};",
            f"    can{can_num}_filter.FilterBank = 0;",
            f"    can{can_num}_filter.FilterIdHigh = 0;",
            f"    can{can_num}_filter.FilterIdLow = 0;",
            f"    can{can_num}_filter.FilterMode = CAN_FILTERMODE_IDMASK;",
            f"    can{can_num}_filter.FilterScale = CAN_FILTERSCALE_32BIT;",
            f"    can{can_num}_filter.FilterMaskIdHigh = 0;",
            f"    can{can_num}_filter.FilterMaskIdLow = 0;",
            f"    can{can_num}_filter.FilterActivation = ENABLE;",
            f"    can{can_num}_filter.SlaveStartFilterBank = 14;",
            f"    can{can_num}_filter.FilterFIFOAssignment = {fifo_assignment};",
            f"    HAL_CAN_ConfigFilter(&hcan{can_num}, &can{can_num}_filter);",
            f"    HAL_CAN_Start(&hcan{can_num});",
            "",
            f"    // 自动注册{instance}接收回调函数",
            f"    BSP_CAN_RegisterCallback({self.enum_prefix}_{name}, HAL_CAN_RX_FIFO0_MSG_PENDING_CB, BSP_CAN_RxFifo0Callback);",
            f"    BSP_CAN_RegisterCallback({self.enum_prefix}_{name}, HAL_CAN_TX_MAILBOX0_CPLT_CB, BSP_CAN_TxCompleteCallback);",
            f"    BSP_CAN_RegisterCallback({self.enum_prefix}_{name}, HAL_CAN_TX_MAILBOX1_CPLT_CB, BSP_CAN_TxCompleteCallback);",
            f"    BSP_CAN_RegisterCallback({self.enum_prefix}_{name}, HAL_CAN_TX_MAILBOX2_CPLT_CB, BSP_CAN_TxCompleteCallback);",
            "",
            f"    // 激活{instance}中断",
            f"    HAL_CAN_ActivateNotification(&hcan{can_num}, CAN_IT_RX_FIFO0_MSG_PENDING | CAN_IT_TX_MAILBOX_EMPTY);",
            ""
        ])

    def _generate_dual_can_init(self, init_lines, configs):
        """双CAN初始化（CAN1用FIFO0，CAN2用FIFO1）"""
        # 找到CAN1和CAN2
        can1_config = next((cfg for cfg in configs if cfg[1] == 'CAN1'), None)
        can2_config = next((cfg for cfg in configs if cfg[1] == 'CAN2'), None)
        
        if can1_config:
            name, instance = can1_config
            init_lines.extend([
                f"    // 初始化 CAN1 - 使用 FIFO0",
                f"    CAN_FilterTypeDef can1_filter = {{0}};",
                f"    can1_filter.FilterBank = 0;",
                f"    can1_filter.FilterIdHigh = 0;",
                f"    can1_filter.FilterIdLow = 0;",
                f"    can1_filter.FilterMode = CAN_FILTERMODE_IDMASK;",
                f"    can1_filter.FilterScale = CAN_FILTERSCALE_32BIT;",
                f"    can1_filter.FilterMaskIdHigh = 0;",
                f"    can1_filter.FilterMaskIdLow = 0;",
                f"    can1_filter.FilterActivation = ENABLE;",
                f"    can1_filter.SlaveStartFilterBank = 14;",
                f"    can1_filter.FilterFIFOAssignment = CAN_RX_FIFO0;",
                f"    HAL_CAN_ConfigFilter(&hcan1, &can1_filter);",
                f"    HAL_CAN_Start(&hcan1);",
                "",
                f"    // 自动注册CAN1接收回调函数",
                f"    BSP_CAN_RegisterCallback({self.enum_prefix}_{name}, HAL_CAN_RX_FIFO0_MSG_PENDING_CB, BSP_CAN_RxFifo0Callback);",
                f"    BSP_CAN_RegisterCallback({self.enum_prefix}_{name}, HAL_CAN_TX_MAILBOX0_CPLT_CB, BSP_CAN_TxCompleteCallback);",
                f"    BSP_CAN_RegisterCallback({self.enum_prefix}_{name}, HAL_CAN_TX_MAILBOX1_CPLT_CB, BSP_CAN_TxCompleteCallback);",
                f"    BSP_CAN_RegisterCallback({self.enum_prefix}_{name}, HAL_CAN_TX_MAILBOX2_CPLT_CB, BSP_CAN_TxCompleteCallback);",
                "",
                f"    // 激活CAN1中断",
                f"    HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING | ",
                f"                                        CAN_IT_TX_MAILBOX_EMPTY);  // 激活发送邮箱空中断",
                ""
            ])
        
        if can2_config:
            name, instance = can2_config
            init_lines.extend([
                f"    // 初始化 CAN2 - 使用 FIFO1",
                f"    can1_filter.FilterBank = 14;",
                f"    can1_filter.FilterFIFOAssignment = CAN_RX_FIFO1;",
                f"    HAL_CAN_ConfigFilter(&hcan2, &can1_filter);  // 通过 CAN1 配置",
                f"    HAL_CAN_Start(&hcan2);",
                "",
                f"    // 自动注册CAN2接收回调函数",
                f"    BSP_CAN_RegisterCallback({self.enum_prefix}_{name}, HAL_CAN_RX_FIFO1_MSG_PENDING_CB, BSP_CAN_RxFifo1Callback);",
                f"    BSP_CAN_RegisterCallback({self.enum_prefix}_{name}, HAL_CAN_TX_MAILBOX0_CPLT_CB, BSP_CAN_TxCompleteCallback);",
                f"    BSP_CAN_RegisterCallback({self.enum_prefix}_{name}, HAL_CAN_TX_MAILBOX1_CPLT_CB, BSP_CAN_TxCompleteCallback);",
                f"    BSP_CAN_RegisterCallback({self.enum_prefix}_{name}, HAL_CAN_TX_MAILBOX2_CPLT_CB, BSP_CAN_TxCompleteCallback);",
                "",
                f"    // 激活CAN2中断", 
                f"    HAL_CAN_ActivateNotification(&hcan2, CAN_IT_RX_FIFO1_MSG_PENDING | ",
                f"                                        CAN_IT_TX_MAILBOX_EMPTY);  // 激活发送邮箱空中断",
                ""
            ])

    def _generate_multi_can_init(self, init_lines, configs):
        """多CAN初始化（CAN1和CAN2用FIFO0，CAN3+用FIFO1）"""
        can1_config = next((cfg for cfg in configs if cfg[1] == 'CAN1'), None)
        can2_config = next((cfg for cfg in configs if cfg[1] == 'CAN2'), None)
        other_configs = [cfg for cfg in configs if cfg[1] not in ['CAN1', 'CAN2']]
        
        # CAN1 - FIFO0
        if can1_config:
            name, instance = can1_config
            init_lines.extend([
                f"    // 初始化 CAN1 - 使用 FIFO0",
                f"    CAN_FilterTypeDef can1_filter = {{0}};",
                f"    can1_filter.FilterBank = 0;",
                f"    can1_filter.FilterIdHigh = 0;",
                f"    can1_filter.FilterIdLow = 0;",
                f"    can1_filter.FilterMode = CAN_FILTERMODE_IDMASK;",
                f"    can1_filter.FilterScale = CAN_FILTERSCALE_32BIT;",
                f"    can1_filter.FilterMaskIdHigh = 0;",
                f"    can1_filter.FilterMaskIdLow = 0;",
                f"    can1_filter.FilterActivation = ENABLE;",
                f"    can1_filter.SlaveStartFilterBank = 14;",
                f"    can1_filter.FilterFIFOAssignment = CAN_RX_FIFO0;",
                f"    HAL_CAN_ConfigFilter(&hcan1, &can1_filter);",
                f"    HAL_CAN_Start(&hcan1);",
                "",
                f"    // 自动注册CAN1接收回调函数",
                f"    BSP_CAN_RegisterCallback({self.enum_prefix}_{name}, HAL_CAN_RX_FIFO0_MSG_PENDING_CB, BSP_CAN_RxFifo0Callback);",
                f"    BSP_CAN_RegisterCallback({self.enum_prefix}_{name}, HAL_CAN_TX_MAILBOX0_CPLT_CB, BSP_CAN_TxCompleteCallback);",
                f"    BSP_CAN_RegisterCallback({self.enum_prefix}_{name}, HAL_CAN_TX_MAILBOX1_CPLT_CB, BSP_CAN_TxCompleteCallback);",
                f"    BSP_CAN_RegisterCallback({self.enum_prefix}_{name}, HAL_CAN_TX_MAILBOX2_CPLT_CB, BSP_CAN_TxCompleteCallback);",
                "",
                f"    // 激活CAN1中断",
                f"    HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING | ",
                f"                                        CAN_IT_TX_MAILBOX_EMPTY);  // 激活发送邮箱空中断",
                ""
            ])
        
        # CAN2 - FIFO0  
        if can2_config:
            name, instance = can2_config
            init_lines.extend([
                f"    // 初始化 CAN2 - 使用 FIFO0",
                f"    can1_filter.FilterBank = 14;",
                f"    can1_filter.FilterFIFOAssignment = CAN_RX_FIFO0;",
                f"    HAL_CAN_ConfigFilter(&hcan2, &can1_filter);  // 通过 CAN1 配置",
                f"    HAL_CAN_Start(&hcan2);",
                "",
                f"    // 自动注册CAN1接收回调函数",
                f"    BSP_CAN_RegisterCallback({self.enum_prefix}_{name}, HAL_CAN_RX_FIFO0_MSG_PENDING_CB, BSP_CAN_RxFifo0Callback);",
                f"    BSP_CAN_RegisterCallback({self.enum_prefix}_{name}, HAL_CAN_TX_MAILBOX0_CPLT_CB, BSP_CAN_TxCompleteCallback);",
                f"    BSP_CAN_RegisterCallback({self.enum_prefix}_{name}, HAL_CAN_TX_MAILBOX1_CPLT_CB, BSP_CAN_TxCompleteCallback);",
                f"    BSP_CAN_RegisterCallback({self.enum_prefix}_{name}, HAL_CAN_TX_MAILBOX2_CPLT_CB, BSP_CAN_TxCompleteCallback);",
                "",
                f"    // 激活CAN1中断",
                f"    HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING | ",
                f"                                        CAN_IT_TX_MAILBOX_EMPTY);  // 激活发送邮箱空中断",
                ""
            ])
        
        # CAN3+ - FIFO1
        filter_bank = 20  # 从过滤器组20开始分配给CAN3+
        for config in other_configs:
            name, instance = config
            can_num = ''.join(filter(str.isdigit, instance))
            init_lines.extend([
                f"    // 初始化 {instance} - 使用 FIFO1",
                f"    can1_filter.FilterBank = {filter_bank};",
                f"    can1_filter.FilterFIFOAssignment = CAN_RX_FIFO1;",
                f"    HAL_CAN_ConfigFilter(&hcan1, &can1_filter);  // 通过 CAN1 配置",
                f"    HAL_CAN_Start(&hcan{can_num});",
                "",
                f"    // 自动注册CAN2接收回调函数",
                f"    BSP_CAN_RegisterCallback({self.enum_prefix}_{name}, HAL_CAN_RX_FIFO1_MSG_PENDING_CB, BSP_CAN_RxFifo1Callback);",
                f"    BSP_CAN_RegisterCallback({self.enum_prefix}_{name}, HAL_CAN_TX_MAILBOX0_CPLT_CB, BSP_CAN_TxCompleteCallback);",
                f"    BSP_CAN_RegisterCallback({self.enum_prefix}_{name}, HAL_CAN_TX_MAILBOX1_CPLT_CB, BSP_CAN_TxCompleteCallback);",
                f"    BSP_CAN_RegisterCallback({self.enum_prefix}_{name}, HAL_CAN_TX_MAILBOX2_CPLT_CB, BSP_CAN_TxCompleteCallback);",
                "",
                f"    // 激活CAN2中断", 
                f"    HAL_CAN_ActivateNotification(&hcan2, CAN_IT_RX_FIFO1_MSG_PENDING | ",
                f"                                        CAN_IT_TX_MAILBOX_EMPTY);  // 激活发送邮箱空中断",
                ""
            ])
            filter_bank += 1  # 为下一个CAN分配不同的过滤器组

class bsp_fdcan(BspPeripheralBase):
    def __init__(self, project_path):
        super().__init__(
            project_path,
            "FDCAN",
            {'header': 'fdcan.h', 'source': 'fdcan.c'},
            "BSP_FDCAN",
            "hfdcan",
            "fdcan",
            get_available_fdcan
        )

    def _generate_bsp_code_internal(self):
        """重写生成方法，调用父类生成FDCAN代码后复制CAN兼容层"""
        # 先调用父类方法生成FDCAN代码
        result = super()._generate_bsp_code_internal()
        if result and result not in ["skipped", "not_needed"]:
            # 成功后复制CAN兼容层文件
            self._copy_can_wrapper()
        return result

    def _copy_can_wrapper(self):
        """复制CAN兼容层文件(can.h)到项目"""
        try:
            template_base_dir = CodeGenerator.get_assets_dir("User_code/bsp")
            fdcan_folder = os.path.join(template_base_dir, "fdcan")
            bsp_output_dir = os.path.join(self.project_path, "User/bsp")
            
            # 确保输出目录存在
            os.makedirs(bsp_output_dir, exist_ok=True)
            
            # 复制can.h（CAN兼容层头文件）
            can_h_src = os.path.join(fdcan_folder, "can.h")
            can_h_dst = os.path.join(bsp_output_dir, "can.h")
            if os.path.exists(can_h_src):
                with open(can_h_src, 'r', encoding='utf-8') as f:
                    content = f.read()
                CodeGenerator.save_with_preserve(can_h_dst, content)
                print(f"✓ 已复制CAN兼容层: can.h")
                
        except Exception as e:
            print(f"复制CAN兼容层文件时出错: {e}")

    def _generate_header_file(self, configs, template_dir):
        """重写头文件生成，添加 FDCAN 使能和 FIFO 分配定义"""
        # 从子文件夹加载模板
        periph_folder = self.peripheral_name.lower()
        template_base_dir = CodeGenerator.get_assets_dir("User_code/bsp")
        template_path = os.path.join(template_base_dir, periph_folder, self.template_names['header'])
        
        if not os.path.exists(template_path):
            # 向后兼容：尝试从根目录读取
            template_path = os.path.join(template_base_dir, self.template_names['header'])
        
        template_content = CodeGenerator.load_template(template_path)
        if not template_content:
            return False
        
        # 生成枚举
        enum_lines = [f"  {self.enum_prefix}_{name}," for name, _ in configs]
        content = CodeGenerator.replace_auto_generated(
            template_content, f"AUTO GENERATED {self.enum_prefix}_NAME", "\n".join(enum_lines)
        )
        
        # 生成 FDCAN 使能宏和 FIFO 分配
        enable_lines = []
        fdcan_count = len(configs)
        
        # 根据 FDCAN 数量分配 FIFO（与 _generate_source_file 中的逻辑一致）
        if fdcan_count == 1:
            fifo_map = {0: 0}
        elif fdcan_count == 2:
            fifo_map = {0: 0, 1: 1}
        else:  # >= 3
            fifo_map = {0: 0, 1: 0, 2: 1}
        
        for idx, (name, instance) in enumerate(configs):
            num = ''.join(filter(str.isdigit, instance))
            fifo_idx = fifo_map.get(idx, 1)
            enable_lines.append(f"#define FDCAN{num}_EN")
            enable_lines.append(f"#define FDCAN{num}_RX_FIFO  {fifo_idx}")
        
        content = CodeGenerator.replace_auto_generated(
            content, "AUTO GENERATED FDCAN_ENABLE", "\n".join(enable_lines)
        )
        
        output_path = os.path.join(self.project_path, f"User/bsp/{self.template_names['header']}")
        CodeGenerator.save_with_preserve(output_path, content)
        return True

    def _generate_source_file(self, configs, template_dir):
        # 从子文件夹加载模板
        periph_folder = self.peripheral_name.lower()
        template_base_dir = CodeGenerator.get_assets_dir("User_code/bsp")
        template_path = os.path.join(template_base_dir, periph_folder, self.template_names['source'])
        
        if not os.path.exists(template_path):
            # 向后兼容：尝试从根目录读取
            template_path = os.path.join(template_base_dir, self.template_names['source'])
        
        template_content = CodeGenerator.load_template(template_path)
        if not template_content:
            return False
            
        # FDCAN_Get函数
        get_lines = []
        for idx, (name, instance) in enumerate(configs):
            if idx == 0:
                get_lines.append(f"  if (hfdcan->Instance == {instance})")
            else:
                get_lines.append(f"  else if (hfdcan->Instance == {instance})")
            get_lines.append(f"    return {self.enum_prefix}_{name};")
        content = CodeGenerator.replace_auto_generated(
            template_content, "AUTO GENERATED FDCAN_GET", "\n".join(get_lines)
        )
        
        # Handle函数
        handle_lines = []
        for name, instance in configs:
            num = ''.join(filter(str.isdigit, instance))  # 提取数字
            handle_lines.append(f"    case {self.enum_prefix}_{name}:")
            handle_lines.append(f"      return &hfdcan{num};")
        content = CodeGenerator.replace_auto_generated(
            content, f"AUTO GENERATED {self.enum_prefix}_GET_HANDLE", "\n".join(handle_lines)
        )
        
        # 生成FDCAN初始化代码（类似CAN的策略）
        init_lines = []
        fdcan_instances = [instance for _, instance in configs]
        fdcan_count = len(fdcan_instances)
        
        # 根据FDCAN数量分配FIFO
        if fdcan_count == 1:
            self._generate_single_fdcan_init(init_lines, configs, 0)
        elif fdcan_count == 2:
            self._generate_dual_fdcan_init(init_lines, configs)
        elif fdcan_count >= 3:
            self._generate_multi_fdcan_init(init_lines, configs)
        
        content = CodeGenerator.replace_auto_generated(
            content, "AUTO GENERATED FDCAN_INIT", "\n".join(init_lines)
        )
        
        output_path = os.path.join(self.project_path, f"User/bsp/{self.template_names['source']}")
        CodeGenerator.save_with_preserve(output_path, content)
        return True

    def _generate_single_fdcan_init(self, init_lines, configs, fifo_idx):
        """单个FDCAN：使用指定的FIFO"""
        for name, instance in configs:
            num = ''.join(filter(str.isdigit, instance))
            init_lines.append(f"#ifdef FDCAN{num}_EN")
            init_lines.append(f"  {{")
            init_lines.append(f"    FDCAN_HandleTypeDef hfdcan = hfdcan{num};")
            init_lines.append(f"    FDCAN_FilterTypeDef sFilterConfig = {{0}};")
            init_lines.append(f"    #define FDCANX_RX_FIFO {fifo_idx}")
            init_lines.append(f"    #ifdef FDCAN{num}_FILTER_CONFIG_TABLE")
            init_lines.append(f"      FDCAN{num}_FILTER_CONFIG_TABLE(FDCAN_CONFIG_FILTER)")
            init_lines.append(f"    #endif")
            init_lines.append(f"    #ifdef FDCAN{num}_GLOBAL_FILTER")
            init_lines.append(f"      HAL_FDCAN_ConfigGlobalFilter(&hfdcan{num}, FDCAN{num}_GLOBAL_FILTER);")
            init_lines.append(f"    #endif")
            init_lines.append(f"    HAL_FDCAN_ActivateNotification(&hfdcan{num}, FDCANx_NOTIFY_FLAGS(FDCANX_RX_FIFO), 0);")
            init_lines.append(f"    BSP_FDCAN_RegisterCallback({self.enum_prefix}_{name}, FDCANX_MSG_PENDING_CB(FDCANX_RX_FIFO), BSP_FDCAN_RxFifo{fifo_idx}Callback);")
            init_lines.append(f"    BSP_FDCAN_RegisterCallback({self.enum_prefix}_{name}, HAL_FDCAN_TX_BUFFER_COMPLETE_CB, BSP_FDCAN_TxCompleteCallback);")
            init_lines.append(f"    #undef FDCANX_RX_FIFO")
            init_lines.append(f"    HAL_FDCAN_Start(&hfdcan{num});")
            init_lines.append(f"  }}")
            init_lines.append(f"#endif")
            init_lines.append("")

    def _generate_dual_fdcan_init(self, init_lines, configs):
        """双FDCAN：FDCAN1用FIFO0，FDCAN2用FIFO1"""
        fifo_map = {0: 0, 1: 1}
        for idx, (name, instance) in enumerate(configs):
            num = ''.join(filter(str.isdigit, instance))
            fifo_idx = fifo_map[idx]
            init_lines.append(f"#ifdef FDCAN{num}_EN")
            init_lines.append(f"  {{")
            init_lines.append(f"    FDCAN_HandleTypeDef hfdcan = hfdcan{num};")
            init_lines.append(f"    FDCAN_FilterTypeDef sFilterConfig = {{0}};")
            init_lines.append(f"    #define FDCANX_RX_FIFO {fifo_idx}")
            init_lines.append(f"    #ifdef FDCAN{num}_FILTER_CONFIG_TABLE")
            init_lines.append(f"      FDCAN{num}_FILTER_CONFIG_TABLE(FDCAN_CONFIG_FILTER)")
            init_lines.append(f"    #endif")
            init_lines.append(f"    #ifdef FDCAN{num}_GLOBAL_FILTER")
            init_lines.append(f"      HAL_FDCAN_ConfigGlobalFilter(&hfdcan{num}, FDCAN{num}_GLOBAL_FILTER);")
            init_lines.append(f"    #endif")
            init_lines.append(f"    HAL_FDCAN_ActivateNotification(&hfdcan{num}, FDCANx_NOTIFY_FLAGS(FDCANX_RX_FIFO), 0);")
            init_lines.append(f"    BSP_FDCAN_RegisterCallback({self.enum_prefix}_{name}, FDCANX_MSG_PENDING_CB(FDCANX_RX_FIFO), BSP_FDCAN_RxFifo{fifo_idx}Callback);")
            init_lines.append(f"    BSP_FDCAN_RegisterCallback({self.enum_prefix}_{name}, HAL_FDCAN_TX_BUFFER_COMPLETE_CB, BSP_FDCAN_TxCompleteCallback);")
            init_lines.append(f"    #undef FDCANX_RX_FIFO")
            init_lines.append(f"    HAL_FDCAN_Start(&hfdcan{num});")
            init_lines.append(f"  }}")
            init_lines.append(f"#endif")
            init_lines.append("")

    def _generate_multi_fdcan_init(self, init_lines, configs):
        """多FDCAN：FDCAN1和FDCAN2用FIFO0，FDCAN3用FIFO1"""
        fifo_map = {0: 0, 1: 0, 2: 1}
        for idx, (name, instance) in enumerate(configs):
            num = ''.join(filter(str.isdigit, instance))
            fifo_idx = fifo_map.get(idx, 1)
            init_lines.append(f"#ifdef FDCAN{num}_EN")
            init_lines.append(f"  {{")
            init_lines.append(f"    FDCAN_HandleTypeDef hfdcan = hfdcan{num};")
            init_lines.append(f"    FDCAN_FilterTypeDef sFilterConfig = {{0}};")
            init_lines.append(f"    #define FDCANX_RX_FIFO {fifo_idx}")
            init_lines.append(f"    #ifdef FDCAN{num}_FILTER_CONFIG_TABLE")
            init_lines.append(f"      FDCAN{num}_FILTER_CONFIG_TABLE(FDCAN_CONFIG_FILTER)")
            init_lines.append(f"    #endif")
            init_lines.append(f"    #ifdef FDCAN{num}_GLOBAL_FILTER")
            init_lines.append(f"      HAL_FDCAN_ConfigGlobalFilter(&hfdcan{num}, FDCAN{num}_GLOBAL_FILTER);")
            init_lines.append(f"    #endif")
            init_lines.append(f"    HAL_FDCAN_ActivateNotification(&hfdcan{num}, FDCANx_NOTIFY_FLAGS(FDCANX_RX_FIFO), 0);")
            init_lines.append(f"    BSP_FDCAN_RegisterCallback({self.enum_prefix}_{name}, FDCANX_MSG_PENDING_CB(FDCANX_RX_FIFO), BSP_FDCAN_RxFifo{fifo_idx}Callback);")
            init_lines.append(f"    BSP_FDCAN_RegisterCallback({self.enum_prefix}_{name}, HAL_FDCAN_TX_BUFFER_COMPLETE_CB, BSP_FDCAN_TxCompleteCallback);")
            init_lines.append(f"    #undef FDCANX_RX_FIFO")
            init_lines.append(f"    HAL_FDCAN_Start(&hfdcan{num});")
            init_lines.append(f"  }}")
            init_lines.append(f"#endif")
            init_lines.append("")


class bsp_spi(BspPeripheralBase):
    def __init__(self, project_path):
        super().__init__(
            project_path,
            "SPI",
            {'header': 'spi.h', 'source': 'spi.c'},
            "BSP_SPI",
            "hspi",
            "spi",
            get_available_spi
        )


def patch_uart_interrupts(project_path, uart_instances):
    """自动修改中断文件，插入 UART BSP 相关代码（支持 F1/F4/H7 等系列）"""
    # 检测MCU型号，确定正确的中断文件
    ioc_files = [f for f in os.listdir(project_path) if f.endswith('.ioc')]
    if not ioc_files:
        return
    
    ioc_path = os.path.join(project_path, ioc_files[0])
    mcu_name = analyzing_ioc.get_mcu_name_from_ioc(ioc_path)
    
    if not mcu_name:
        return
    
    # 根据MCU型号确定中断文件名
    mcu_upper = mcu_name.upper()
    if 'STM32F1' in mcu_upper:
        it_file = "stm32f1xx_it.c"
    elif 'STM32F4' in mcu_upper:
        it_file = "stm32f4xx_it.c"
    elif 'STM32H7' in mcu_upper:
        it_file = "stm32h7xx_it.c"
    elif 'STM32F7' in mcu_upper:
        it_file = "stm32f7xx_it.c"
    elif 'STM32G4' in mcu_upper:
        it_file = "stm32g4xx_it.c"
    elif 'STM32L4' in mcu_upper:
        it_file = "stm32l4xx_it.c"
    else:
        # 通用处理：尝试从MCU名称提取系列信息
        # 格式通常为 STM32X0XXX，提取 X0 部分
        import re as regex
        match = regex.match(r'STM32([A-Z]\d)', mcu_upper)
        if match:
            series = match.group(1).lower()
            it_file = f"stm32{series}xx_it.c"
        else:
            # 默认尝试 F4
            it_file = "stm32f4xx_it.c"
    
    it_path = os.path.join(project_path, f"Core/Src/{it_file}")
    if not os.path.exists(it_path):
        return
    
    with open(it_path, "r", encoding="utf-8") as f:
        code = f.read()

    # 1. 插入 #include "bsp/uart.h"
    include_pattern = r"(/\* USER CODE BEGIN Includes \*/)(.*?)(/\* USER CODE END Includes \*/)"
    if '#include "bsp/uart.h"' not in code:
        code = re.sub(
            include_pattern,
            lambda m: f'{m.group(1)}\n#include "bsp/uart.h"{m.group(2)}{m.group(3)}',
            code,
            flags=re.DOTALL
        )

    # 2. 插入 BSP_UART_IRQHandler(&huartx);
    for instance in uart_instances:
        num = ''.join(filter(str.isdigit, instance))
        irq_pattern = (
            rf"(void\s+USART{num}_IRQHandler\s*\(\s*void\s*\)\s*\{{.*?/\* USER CODE BEGIN USART{num}_IRQn 1 \*/)(.*?)(/\* USER CODE END USART{num}_IRQn 1 \*/)"
        )
        if f"BSP_UART_IRQHandler(&huart{num});" not in code:
            code = re.sub(
                irq_pattern,
                lambda m: f"{m.group(1)}\n  BSP_UART_IRQHandler(&huart{num});{m.group(2)}{m.group(3)}",
                code,
                flags=re.DOTALL
            )
        # 兼容 UARTx 命名
        irq_pattern_uart = (
            rf"(void\s+UART{num}_IRQHandler\s*\(\s*void\s*\)\s*\{{.*?/\* USER CODE BEGIN UART{num}_IRQn 1 \*/)(.*?)(/\* USER CODE END UART{num}_IRQn 1 \*/)"
        )
        if f"BSP_UART_IRQHandler(&huart{num});" not in code:
            code = re.sub(
                irq_pattern_uart,
                lambda m: f"{m.group(1)}\n  BSP_UART_IRQHandler(&huart{num});{m.group(2)}{m.group(3)}",
                code,
                flags=re.DOTALL
            )

    # 使用save_with_preserve保存文件以保留用户区域
    CodeGenerator.save_with_preserve(it_path, code)


class bsp_uart(BspPeripheralBase):
    def __init__(self, project_path):
        super().__init__(
            project_path,
            "UART",
            {'header': 'uart.h', 'source': 'uart.c'},
            "BSP_UART",
            "huart",
            "uart",
            get_available_uart
        )
    def _generate_bsp_code_internal(self):
        if not self.is_need_generate():
            return False
        configs = self._collect_configs()
        if not configs:
            return False
        template_dir = CodeGenerator.get_template_dir()
        if not self._generate_header_file(configs, template_dir):
            return False
        if not self._generate_source_file(configs, template_dir):
            return False
        self._save_config(configs)
        # 自动补充 stm32f4xx_it.c
        uart_instances = [instance for _, instance in configs]
        patch_uart_interrupts(self.project_path, uart_instances)
        return True
class bsp_gpio(QWidget):
    def __init__(self, project_path):
        super().__init__()
        self.project_path = project_path
        self.available_list = self._get_all_gpio_list()
        # 加载描述
        describe_path = os.path.join(CodeGenerator.get_assets_dir("User_code/bsp"), "describe.csv")
        self.descriptions = CodeGenerator.load_descriptions(describe_path)
        self._init_ui()
        self._load_config()

    def _get_all_gpio_list(self):
        """获取所有GPIO配置"""
        ioc_files = [f for f in os.listdir(self.project_path) if f.endswith('.ioc')]
        if ioc_files:
            ioc_path = os.path.join(self.project_path, ioc_files[0])
            return analyzing_ioc.get_all_gpio_from_ioc(ioc_path)
        return []

    def _init_ui(self):
        layout = QVBoxLayout(self)

        # 顶部布局
        top_layout = QHBoxLayout()
        top_layout.setAlignment(Qt.AlignVCenter)

        self.generate_checkbox = CheckBox("生成 GPIO 代码")
        self.generate_checkbox.stateChanged.connect(self._on_generate_changed)
        top_layout.addWidget(self.generate_checkbox, alignment=Qt.AlignLeft)

        top_layout.addStretch()

        title = SubtitleLabel("GPIO 配置             ")
        title.setAlignment(Qt.AlignHCenter)
        top_layout.addWidget(title, alignment=Qt.AlignHCenter)

        top_layout.addStretch()

        layout.addLayout(top_layout)

        desc = self.descriptions.get("gpio", "")
        if desc:
            desc_label = BodyLabel(f"功能说明：{desc}")
            desc_label.setWordWrap(True)
            layout.addWidget(desc_label)

        # 内容区域
        self.content_widget = QWidget()
        content_layout = QVBoxLayout(self.content_widget)

        if not self.available_list:
            content_layout.addWidget(BodyLabel("在 .ioc 文件中未找到可用的 GPIO"))
        else:
            # 创建表格
            self.table = TableWidget()
            self.table.setColumnCount(4)
            self.table.setHorizontalHeaderLabels(["IOC Label", "自定义名称", "类型", "包含"])
            self.table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
            
            # 填充表格
            for row, gpio_info in enumerate(self.available_list):
                self.table.insertRow(row)
                
                # IOC Label (只读)
                from PyQt5.QtWidgets import QTableWidgetItem
                label_item = QTableWidgetItem(gpio_info['label'])
                label_item.setFlags(label_item.flags() & ~Qt.ItemIsEditable)
                self.table.setItem(row, 0, label_item)
                
                # 自定义名称 (可编辑)
                name_edit = LineEdit()
                name_edit.setText(gpio_info['label'])  # 默认使用IOC的label
                name_edit.setPlaceholderText("输入自定义名称")
                self.table.setCellWidget(row, 1, name_edit)
                
                # 类型显示
                gpio_type = "EXTI" if gpio_info['has_exti'] else ("输出" if gpio_info['is_output'] else "输入")
                type_item = QTableWidgetItem(gpio_type)
                type_item.setFlags(type_item.flags() & ~Qt.ItemIsEditable)
                self.table.setItem(row, 2, type_item)
                
                # 包含复选框
                include_checkbox = CheckBox("")
                include_checkbox.setChecked(True)  # 默认选中
                self.table.setCellWidget(row, 3, include_checkbox)

            content_layout.addWidget(self.table)

        layout.addWidget(self.content_widget)
        self.content_widget.setEnabled(False)

    def _on_generate_changed(self, state):
        self.content_widget.setEnabled(state == 2)

    def is_need_generate(self):
        return self.generate_checkbox.isChecked() and bool(self.available_list)

    def _collect_configs(self):
        """收集用户配置"""
        configs = []
        for row in range(self.table.rowCount()):
            include_widget = self.table.cellWidget(row, 3)
            if include_widget and include_widget.isChecked():
                name_widget = self.table.cellWidget(row, 1)
                if name_widget:
                    custom_name = name_widget.text().strip()
                    if custom_name:
                        gpio_info = self.available_list[row]
                        configs.append({
                            'custom_name': custom_name.upper(),
                            'ioc_label': gpio_info['label'],
                            'pin': gpio_info['pin'], 
                            'has_exti': gpio_info['has_exti']
                        })
        return configs

    def _generate_bsp_code_internal(self):
        # 检查是否需要生成
        if not self.is_need_generate():
            # 如果未勾选，检查文件是否已存在，如果存在则跳过
            gpio_h_path = os.path.join(self.project_path, "User/bsp/gpio.h")
            gpio_c_path = os.path.join(self.project_path, "User/bsp/gpio.c")
            if os.path.exists(gpio_h_path) or os.path.exists(gpio_c_path):
                return "skipped"  # 返回特殊值表示跳过
            return "not_needed"  # 返回特殊值表示不需要生成
        
        configs = self._collect_configs()
        if not configs:
            return False
            
        template_dir = CodeGenerator.get_template_dir()
        if not self._generate_header_file(configs, template_dir):
            return False
        if not self._generate_source_file(configs, template_dir):
            return False
        self._save_config(configs)
        return True

    def _generate_header_file(self, configs, template_dir):
        # 从子文件夹加载模板
        template_base_dir = CodeGenerator.get_assets_dir("User_code/bsp")
        template_path = os.path.join(template_base_dir, "gpio", "gpio.h")
        
        if not os.path.exists(template_path):
            # 向后兼容：从根目录加载
            template_path = os.path.join(template_base_dir, "gpio.h")
        
        template_content = CodeGenerator.load_template(template_path)
        if not template_content:
            return False
            
        # 生成枚举
        enum_lines = []
        for config in configs:
            enum_lines.append(f"  BSP_GPIO_{config['custom_name']},")
            
        content = CodeGenerator.replace_auto_generated(
            template_content, "AUTO GENERATED BSP_GPIO_ENUM", "\n".join(enum_lines)
        )
        
        output_path = os.path.join(self.project_path, "User/bsp/gpio.h")
        CodeGenerator.save_with_preserve(output_path, content)
        return True

    def _generate_source_file(self, configs, template_dir):
        # 从子文件夹加载模板
        template_base_dir = CodeGenerator.get_assets_dir("User_code/bsp")
        template_path = os.path.join(template_base_dir, "gpio", "gpio.c")
        
        if not os.path.exists(template_path):
            # 向后兼容：从根目录加载
            template_path = os.path.join(template_base_dir, "gpio.c")
        
        template_content = CodeGenerator.load_template(template_path)
        if not template_content:
            return False
            
        # 生成MAP数组
        map_lines = []
        for config in configs:
            ioc_label = config['ioc_label']
            map_lines.append(f"    {{{ioc_label}_Pin, {ioc_label}_GPIO_Port}},")
            
        content = CodeGenerator.replace_auto_generated(
            template_content, "AUTO GENERATED BSP_GPIO_MAP", "\n".join(map_lines)
        )
        
        # 生成EXTI使能代码 - 使用用户自定义的BSP枚举名称
        # 根据引脚编号获取正确的IRQn（适配不同MCU）
        enable_lines = []
        disable_lines = []
        for config in configs:
            if config['has_exti']:
                ioc_label = config['ioc_label']
                custom_name = config['custom_name']
                # 尝试从IOC label中提取引脚编号，优先使用IOC定义的IRQn
                enable_lines.append(f"    case BSP_GPIO_{custom_name}:")
                enable_lines.append(f"#if defined({ioc_label}_EXTI_IRQn)")
                enable_lines.append(f"      HAL_NVIC_EnableIRQ({ioc_label}_EXTI_IRQn);")
                enable_lines.append(f"#endif")
                enable_lines.append(f"      return BSP_OK;")
                
                disable_lines.append(f"    case BSP_GPIO_{custom_name}:")
                disable_lines.append(f"#if defined({ioc_label}_EXTI_IRQn)")
                disable_lines.append(f"      HAL_NVIC_DisableIRQ({ioc_label}_EXTI_IRQn);")
                disable_lines.append(f"#endif")
                disable_lines.append(f"      return BSP_OK;")
                
        content = CodeGenerator.replace_auto_generated(
            content, "AUTO GENERATED BSP_GPIO_ENABLE_IRQ", "\n".join(enable_lines)
        )
        content = CodeGenerator.replace_auto_generated(
            content, "AUTO GENERATED BSP_GPIO_DISABLE_IRQ", "\n".join(disable_lines)
        )
        
        output_path = os.path.join(self.project_path, "User/bsp/gpio.c")
        CodeGenerator.save_with_preserve(output_path, content)
        return True



    def _save_config(self, configs):
        config_path = os.path.join(self.project_path, "User/bsp/bsp_config.yaml")
        config_data = CodeGenerator.load_config(config_path)
        gpio_configs = []
        for config in configs:
            # 根据 pin 查找原始 available_list 项
            match = next((item for item in self.available_list if item['pin'] == config['pin']), None)
            gpio_type = "EXTI" if config['has_exti'] else (
                "OUTPUT" if match and match.get('is_output') else "INPUT"
            )
            gpio_configs.append({
                'custom_name': config['custom_name'],
                'ioc_label': config['ioc_label'],
                'pin': config['pin'],
                'has_exti': config['has_exti'],
                'type': gpio_type
            })
        config_data['gpio'] = {
            'enabled': True,
            'configs': gpio_configs
        }
        CodeGenerator.save_config(config_data, config_path)

    def _load_config(self):
        config_path = os.path.join(self.project_path, "User/bsp/bsp_config.yaml")
        config_data = CodeGenerator.load_config(config_path)
        conf = config_data.get('gpio', {})
        if conf.get('enabled', False):
            self.generate_checkbox.setChecked(True)
            saved_configs = conf.get('configs', [])
            # 恢复用户的自定义名称和选择状态
            for saved_config in saved_configs:
                for row in range(len(self.available_list)):
                    if (self.available_list[row]['label'] == saved_config['ioc_label'] and
                        hasattr(self, 'table')):
                        name_widget = self.table.cellWidget(row, 1)
                        if name_widget:
                            name_widget.setText(saved_config['custom_name'])

class bsp_pwm(QWidget):
    def __init__(self, project_path):
        super().__init__()
        self.project_path = project_path
        self.available_list = self._get_pwm_channels()
        # 加载描述
        describe_path = os.path.join(CodeGenerator.get_assets_dir("User_code/bsp"), "describe.csv")
        self.descriptions = CodeGenerator.load_descriptions(describe_path)
        self._init_ui()
        self._load_config()

    def _get_pwm_channels(self):
        """获取所有PWM通道配置"""
        return get_available_pwm(self.project_path)

    def _init_ui(self):
        layout = QVBoxLayout(self)

        # 顶部布局
        top_layout = QHBoxLayout()
        top_layout.setAlignment(Qt.AlignVCenter)

        self.generate_checkbox = CheckBox("生成 PWM 代码")
        self.generate_checkbox.stateChanged.connect(self._on_generate_changed)
        top_layout.addWidget(self.generate_checkbox, alignment=Qt.AlignLeft)

        top_layout.addStretch()

        title = SubtitleLabel("PWM 配置             ")
        title.setAlignment(Qt.AlignHCenter)
        top_layout.addWidget(title, alignment=Qt.AlignHCenter)

        top_layout.addStretch()

        layout.addLayout(top_layout)

        desc = self.descriptions.get("pwm", "")
        if desc:
            desc_label = BodyLabel(f"功能说明：{desc}")
            desc_label.setWordWrap(True)
            layout.addWidget(desc_label)

        # 内容区域
        self.content_widget = QWidget()
        content_layout = QVBoxLayout(self.content_widget)

        if not self.available_list:
            content_layout.addWidget(BodyLabel("在 .ioc 文件中未找到可用的 PWM 通道"))
        else:
            # 创建表格
            self.table = TableWidget()
            self.table.setColumnCount(4)
            self.table.setHorizontalHeaderLabels(["PWM通道", "自定义名称", "定时器信息", "选择"])
            
            # 设置列宽度 - 手动调整各列的初始宽度
            header = self.table.horizontalHeader()
            header.setSectionResizeMode(0, QHeaderView.Interactive)  # PWM通道列可调整
            header.setSectionResizeMode(1, QHeaderView.Stretch)      # 自定义名称列拉伸
            header.setSectionResizeMode(2, QHeaderView.Interactive)  # 定时器信息列可调整
            header.setSectionResizeMode(3, QHeaderView.Fixed)        # 选择列固定宽度
            
            # 设置初始列宽
            self.table.setColumnWidth(0, 150)  # PWM通道列宽度
            self.table.setColumnWidth(2, 120)  # 定时器信息列宽度  
            self.table.setColumnWidth(3, 80)   # 选择列宽度
            
            # 填充表格
            for row, pwm_info in enumerate(self.available_list):
                self.table.insertRow(row)
                
                # PWM通道信息 (只读) - 显示完整的通道信息
                from PyQt5.QtWidgets import QTableWidgetItem
                channel_display = f"{pwm_info['timer']} {pwm_info['channel'].replace('TIM_CHANNEL_', 'CH')}"
                channel_item = QTableWidgetItem(channel_display)
                channel_item.setFlags(channel_item.flags() & ~Qt.ItemIsEditable)
                self.table.setItem(row, 0, channel_item)
                
                # 自定义名称 (可编辑)
                name_edit = LineEdit()
                name_edit.setText(pwm_info['label'])  # 默认使用IOC的label
                name_edit.setPlaceholderText("输入自定义名称")
                self.table.setCellWidget(row, 1, name_edit)
                
                # 定时器信息 - 显示引脚和信号
                timer_info = f"{pwm_info['pin']}"
                timer_item = QTableWidgetItem(timer_info)
                timer_item.setFlags(timer_item.flags() & ~Qt.ItemIsEditable)
                self.table.setItem(row, 2, timer_item)
                
                # 选择复选框 - 放在中央
                checkbox_widget = QWidget()
                checkbox_layout = QHBoxLayout(checkbox_widget)
                checkbox_layout.setContentsMargins(0, 0, 0, 0)
                checkbox_layout.setAlignment(Qt.AlignCenter)
                
                include_checkbox = CheckBox("")
                include_checkbox.setChecked(True)  # 默认选中
                checkbox_layout.addWidget(include_checkbox)
                
                self.table.setCellWidget(row, 3, checkbox_widget)

            content_layout.addWidget(self.table)

        layout.addWidget(self.content_widget)
        self.content_widget.setEnabled(False)

    def _on_generate_changed(self, state):
        self.content_widget.setEnabled(state == 2)

    def is_need_generate(self):
        return self.generate_checkbox.isChecked() and bool(self.available_list)

    def _collect_configs(self):
        """收集用户配置"""
        configs = []
        for row in range(self.table.rowCount()):
            checkbox_widget = self.table.cellWidget(row, 3)
            if checkbox_widget:
                # 获取复选框
                include_checkbox = checkbox_widget.findChild(CheckBox)
                if include_checkbox and include_checkbox.isChecked():
                    name_widget = self.table.cellWidget(row, 1)
                    if name_widget:
                        custom_name = name_widget.text().strip()
                        if custom_name:
                            pwm_info = self.available_list[row]
                            configs.append({
                                'custom_name': custom_name.upper(),
                                'timer': pwm_info['timer'],
                                'channel': pwm_info['channel'],
                                'label': pwm_info['label']
                            })
        return configs

    def _generate_bsp_code_internal(self):
        # 检查是否需要生成
        if not self.is_need_generate():
            # 如果未勾选，检查文件是否已存在，如果存在则跳过
            pwm_h_path = os.path.join(self.project_path, "User/bsp/pwm.h")
            pwm_c_path = os.path.join(self.project_path, "User/bsp/pwm.c")
            if os.path.exists(pwm_h_path) or os.path.exists(pwm_c_path):
                return "skipped"  # 返回特殊值表示跳过
            return "not_needed"  # 返回特殊值表示不需要生成
        
        configs = self._collect_configs()
        if not configs:
            return False
            
        template_dir = CodeGenerator.get_template_dir()
        if not self._generate_header_file(configs, template_dir):
            return False
        if not self._generate_source_file(configs, template_dir):
            return False
        self._save_config(configs)
        return True

    def _generate_header_file(self, configs, template_dir):
        # 从子文件夹加载模板
        template_base_dir = CodeGenerator.get_assets_dir("User_code/bsp")
        template_path = os.path.join(template_base_dir, "pwm", "pwm.h")
        
        if not os.path.exists(template_path):
            # 向后兼容：从根目录加载
            template_path = os.path.join(template_base_dir, "pwm.h")
        
        template_content = CodeGenerator.load_template(template_path)
        if not template_content:
            return False
            
        # 生成枚举
        enum_lines = []
        for config in configs:
            enum_lines.append(f"  BSP_PWM_{config['custom_name']},")
            
        content = CodeGenerator.replace_auto_generated(
            template_content, "AUTO GENERATED BSP_PWM_ENUM", "\n".join(enum_lines)
        )
        
        output_path = os.path.join(self.project_path, "User/bsp/pwm.h")
        CodeGenerator.save_with_preserve(output_path, content)
        return True

    def _generate_source_file(self, configs, template_dir):
        # 从子文件夹加载模板
        template_base_dir = CodeGenerator.get_assets_dir("User_code/bsp")
        template_path = os.path.join(template_base_dir, "pwm", "pwm.c")
        
        if not os.path.exists(template_path):
            # 向后兼容：从根目录加载
            template_path = os.path.join(template_base_dir, "pwm.c")
        
        template_content = CodeGenerator.load_template(template_path)
        if not template_content:
            return False
            
        # 生成MAP数组
        map_lines = []
        for config in configs:
            timer = config['timer'].lower()  # tim1 -> htim1
            channel = config['channel']
            map_lines.append(f"  {{&h{timer}, {channel}}},")
            
        content = CodeGenerator.replace_auto_generated(
            template_content, "AUTO GENERATED BSP_PWM_MAP", "\n".join(map_lines)
        )
        
        output_path = os.path.join(self.project_path, "User/bsp/pwm.c")
        CodeGenerator.save_with_preserve(output_path, content)
        return True

    def _save_config(self, configs):
        config_path = os.path.join(self.project_path, "User/bsp/bsp_config.yaml")
        config_data = CodeGenerator.load_config(config_path)
        config_data['pwm'] = {
            'enabled': True,
            'configs': [
                {
                    'custom_name': config['custom_name'],
                    'timer': config['timer'],
                    'channel': config['channel'],
                    'label': config['label']
                } for config in configs
            ]
        }
        CodeGenerator.save_config(config_data, config_path)

    def _load_config(self):
        config_path = os.path.join(self.project_path, "User/bsp/bsp_config.yaml")
        config_data = CodeGenerator.load_config(config_path)
        conf = config_data.get('pwm', {})
        if conf.get('enabled', False):
            self.generate_checkbox.setChecked(True)
            saved_configs = conf.get('configs', [])
            # 恢复用户的自定义名称和选择状态
            for saved_config in saved_configs:
                for row in range(len(self.available_list)):
                    if (self.available_list[row]['label'] == saved_config['label'] and
                        hasattr(self, 'table')):
                        name_widget = self.table.cellWidget(row, 1)
                        if name_widget:
                            name_widget.setText(saved_config['custom_name'])


class bsp_flash(QWidget):
    """Flash BSP配置界面 - 自动识别MCU型号并生成对应的Flash配置"""
    def __init__(self, project_path):
        super().__init__()
        self.project_path = project_path
        self.mcu_name = None
        self.flash_config = None
        # 加载描述
        describe_path = os.path.join(CodeGenerator.get_assets_dir("User_code/bsp"), "describe.csv")
        self.descriptions = CodeGenerator.load_descriptions(describe_path)
        self._detect_mcu()
        self._init_ui()
        self._load_config()

    def _detect_mcu(self):
        """自动检测MCU型号并获取Flash配置"""
        ioc_files = [f for f in os.listdir(self.project_path) if f.endswith('.ioc')]
        if ioc_files:
            ioc_path = os.path.join(self.project_path, ioc_files[0])
            self.mcu_name = analyzing_ioc.get_mcu_name_from_ioc(ioc_path)
            if self.mcu_name:
                self.flash_config = analyzing_ioc.get_flash_config_from_mcu(self.mcu_name)

    def _init_ui(self):
        layout = QVBoxLayout(self)

        # 顶部布局
        top_layout = QHBoxLayout()
        top_layout.setAlignment(Qt.AlignVCenter)

        self.generate_checkbox = CheckBox("生成 Flash 代码")
        self.generate_checkbox.stateChanged.connect(self._on_generate_changed)
        top_layout.addWidget(self.generate_checkbox, alignment=Qt.AlignLeft)

        top_layout.addStretch()

        title = SubtitleLabel("Flash 配置             ")
        title.setAlignment(Qt.AlignHCenter)
        top_layout.addWidget(title, alignment=Qt.AlignHCenter)

        top_layout.addStretch()

        layout.addLayout(top_layout)

        desc = self.descriptions.get("flash", "自动根据MCU型号配置Flash扇区")
        if desc:
            desc_label = BodyLabel(desc)
            desc_label.setWordWrap(True)
            layout.addWidget(desc_label)

        # 内容区域
        self.content_widget = QWidget()
        content_layout = QVBoxLayout(self.content_widget)

        if not self.flash_config:
            no_config_label = BodyLabel("❌ 无法识别MCU型号或不支持的MCU")
            content_layout.addWidget(no_config_label)
        else:
            # 显示检测到的MCU信息
            mcu_info = BodyLabel(f"✅ 检测到MCU: {self.mcu_name}")
            content_layout.addWidget(mcu_info)
            
            flash_size = (self.flash_config['end_address'] - 0x08000000) // 1024
            flash_type = self.flash_config.get('type', 'sector')
            
            if flash_type == 'page':
                # F1系列 - Page模式
                page_size = self.flash_config.get('page_size', 1)
                flash_info = BodyLabel(f"Flash容量: {flash_size} KB ({len(self.flash_config['sectors'])} 个页，每页 {page_size}KB)")
                content_layout.addWidget(flash_info)
                type_info = BodyLabel(f"📄 Page模式 (F1系列)")
                content_layout.addWidget(type_info)
            else:
                # F4/H7系列 - Sector模式
                flash_info = BodyLabel(f"Flash容量: {flash_size} KB ({len(self.flash_config['sectors'])} 个扇区)")
                content_layout.addWidget(flash_info)
                
                if self.flash_config['dual_bank']:
                    max_sector = len(self.flash_config['sectors']) - 1
                    bank_info = BodyLabel(f"⚠️ 双Bank Flash (Sector 0-{max_sector})")
                else:
                    max_sector = len(self.flash_config['sectors']) - 1
                    bank_info = BodyLabel(f"单Bank Flash (Sector 0-{max_sector})")
                content_layout.addWidget(bank_info)

        layout.addWidget(self.content_widget)
        self.content_widget.setEnabled(False)

    def _on_generate_changed(self, state):
        self.content_widget.setEnabled(state == 2)

    def is_need_generate(self):
        return self.generate_checkbox.isChecked() and self.flash_config is not None

    def _generate_bsp_code_internal(self):
        if not self.is_need_generate():
            return False
        
        if not self.flash_config:
            return False
        
        # 生成头文件
        if not self._generate_header_file():
            return False
        
        # 生成源文件
        if not self._generate_source_file():
            return False
        
        self._save_config()
        return True

    def _generate_header_file(self):
        """生成flash.h"""
        periph_folder = "flash"
        template_base_dir = CodeGenerator.get_assets_dir("User_code/bsp")
        template_path = os.path.join(template_base_dir, periph_folder, "flash.h")
        
        if not os.path.exists(template_path):
            return False
        
        template_content = CodeGenerator.load_template(template_path)
        if not template_content:
            return False
        
        # 生成Sector/Page定义
        flash_type = self.flash_config.get('type', 'sector')
        sector_lines = []
        
        for item in self.flash_config['sectors']:
            addr = item['address']
            size = item['size']
            item_id = item['id']
            
            if flash_type == 'page':
                # F1系列 - Page模式
                sector_lines.append(
                    f"#define ADDR_FLASH_PAGE_{item_id} ((uint32_t)0x{addr:08X})"
                )
                sector_lines.append(
                    f"/* Base address of Page {item_id}, {size} Kbytes */"
                )
            else:
                # F4/H7系列 - Sector模式
                sector_lines.append(
                    f"#define ADDR_FLASH_SECTOR_{item_id} ((uint32_t)0x{addr:08X})"
                )
                sector_lines.append(
                    f"/* Base address of Sector {item_id}, {size} Kbytes */"
                )
        
        content = CodeGenerator.replace_auto_generated(
            template_content, "AUTO GENERATED FLASH_SECTORS", "\n".join(sector_lines)
        )
        
        # 生成结束地址
        end_addr = self.flash_config['end_address']
        end_line = f"#define ADDR_FLASH_END ((uint32_t)0x{end_addr:08X}) /* End address for flash */"
        content = CodeGenerator.replace_auto_generated(
            content, "AUTO GENERATED FLASH_END_ADDRESS", end_line
        )
        
        output_path = os.path.join(self.project_path, "User/bsp/flash.h")
        CodeGenerator.save_with_preserve(output_path, content)
        return True

    def _generate_source_file(self):
        """生成flash.c"""
        periph_folder = "flash"
        template_base_dir = CodeGenerator.get_assets_dir("User_code/bsp")
        template_path = os.path.join(template_base_dir, periph_folder, "flash.c")
        
        if not os.path.exists(template_path):
            return False
        
        template_content = CodeGenerator.load_template(template_path)
        if not template_content:
            return False
        
        # 生成最大Sector数定义
        max_sector = len(self.flash_config['sectors']) - 1
        max_sector_line = f"#define BSP_FLASH_MAX_SECTOR {max_sector}"
        content = CodeGenerator.replace_auto_generated(
            template_content, "AUTO GENERATED FLASH_MAX_SECTOR", max_sector_line
        )
        
        # 生成擦除检查代码
        erase_check = f"  if (sector > 0 && sector <= {max_sector}) {{"
        content = CodeGenerator.replace_auto_generated(
            content, "AUTO GENERATED FLASH_ERASE_CHECK", erase_check
        )
        
        output_path = os.path.join(self.project_path, "User/bsp/flash.c")
        CodeGenerator.save_with_preserve(output_path, content)
        return True

    def _save_config(self):
        """保存配置"""
        config_path = os.path.join(self.project_path, "User/bsp/bsp_config.yaml")
        config_data = CodeGenerator.load_config(config_path)
        config_data['flash'] = {
            'enabled': True,
            'mcu_name': self.mcu_name,
            'dual_bank': self.flash_config['dual_bank'],
            'sectors': len(self.flash_config['sectors'])
        }
        CodeGenerator.save_config(config_data, config_path)

    def _load_config(self):
        """加载配置"""
        config_path = os.path.join(self.project_path, "User/bsp/bsp_config.yaml")
        config_data = CodeGenerator.load_config(config_path)
        conf = config_data.get('flash', {})
        if conf.get('enabled', False):
            self.generate_checkbox.setChecked(True)


# 更新get_bsp_page函数以包含PWM和Flash
def get_bsp_page(peripheral_name, project_path):
    """根据外设名返回对应的页面类，没有特殊类则返回默认BspSimplePeripheral"""
    name_lower = peripheral_name.lower()
    special_classes = {
        "i2c": bsp_i2c,
        "can": bsp_can,
        "fdcan": bsp_fdcan,
        "spi": bsp_spi,
        "uart": bsp_uart,
        "gpio": bsp_gpio,
        "pwm": bsp_pwm,
        "flash": bsp_flash,  # 添加Flash自动配置
        # 以后可以继续添加特殊外设
    }
    if name_lower in special_classes:
        return special_classes[name_lower](project_path)
    else:
        template_names = {
            'header': f'{name_lower}.h',
            'source': f'{name_lower}.c'
        }
        return BspSimplePeripheral(project_path, peripheral_name, template_names)


class bsp(QWidget):
    def __init__(self, project_path):
        super().__init__()
        self.project_path = project_path

    @staticmethod
    def generate_bsp(project_path, pages):
        """生成所有BSP代码"""
        # 在方法开始时导入CodeGenerator以确保可用
        from app.tools.code_generator import CodeGenerator
        
        # 自动添加 bsp.h
        src_bsp_h = os.path.join(CodeGenerator.get_assets_dir("User_code/bsp"), "bsp.h")
        dst_bsp_h = os.path.join(project_path, "User/bsp/bsp.h")
        os.makedirs(os.path.dirname(dst_bsp_h), exist_ok=True)
        if os.path.exists(src_bsp_h):
            with open(src_bsp_h, 'r', encoding='utf-8') as f:
                content = f.read()
            CodeGenerator.save_with_preserve(dst_bsp_h, content)
        
        total = 0
        success_count = 0
        fail_count = 0
        skipped_count = 0
        fail_list = []
        skipped_list = []
        
        for page in pages:
            # 只处理BSP页面：有 is_need_generate 方法但没有 component_name 属性的页面
            if hasattr(page, 'is_need_generate') and not hasattr(page, 'component_name'):
                # 先检查是否有文件存在但未勾选的情况
                if not page.is_need_generate():
                    try:
                        result = page._generate_bsp_code_internal()
                        if result == "skipped":
                            total += 1
                            skipped_count += 1
                            skipped_list.append(page.__class__.__name__)
                    except Exception:
                        pass  # 忽略未勾选页面的错误
                else:
                    # 勾选的页面，正常处理
                    total += 1
                    try:
                        result = page._generate_bsp_code_internal()
                        if result == "skipped":
                            skipped_count += 1
                            skipped_list.append(page.__class__.__name__)
                        elif result:
                            success_count += 1
                        else:
                            fail_count += 1
                            fail_list.append(page.__class__.__name__)
                    except Exception as e:
                        fail_count += 1
                        fail_list.append(f"{page.__class__.__name__} (异常: {e})")
        
        msg = f"总共处理 {total} 项，成功生成 {success_count} 项，跳过 {skipped_count} 项，失败 {fail_count} 项。"
        if skipped_list:
            msg += f"\n跳过项（文件已存在且未勾选）：\n" + "\n".join(skipped_list)
        if fail_list:
            msg += "\n失败项：\n" + "\n".join(fail_list)
        
        return msg