import numpy as np
import copy
from typing import List
from FourChZreg_define import *


class AssemblyTemplateManager:
    """汇编指令模板管理器"""

    def __init__(self, mk_instance, mk_instr, **kwargs):
        self.mk = mk_instance
        self.mk_instr = mk_instr
        self.config_file = kwargs.get('config_file')
        self.templates = {
            'general_send_wait': self._generic_awg_control_template,
            'ramp_fixed': self._ramp_mcu_fixed_template,
            'ramp_step': self._ramp_mcu_template
        }

    def create_instructions(self, **kwargs):
        template_type = kwargs.get('instr_type', str)
        params = copy.deepcopy(kwargs)
        return self.templates[template_type](**params)

    def _codeword_encode(self, **kwargs):
        sendc = kwargs.pop('sendc', 0)
        wave_hold = kwargs.pop('wave_hold', 0)
        ff_amp_index = kwargs.pop('ff_amp_index', 0)
        fm_amp_index = kwargs.pop('fm_amp_index', 0)
        bias_index = kwargs.pop('bias_index', 0)
        fcw_index = kwargs.pop('fcw_index', 0)
        pcw_index = kwargs.pop('pcw_index', 0)
        code_clr = kwargs.pop('code_clr', 0)
        env_index = kwargs.pop('env_index', 0)

        codeword = 0
        codeword |= sendc << 31
        codeword |= wave_hold << 30
        codeword |= ff_amp_index << 28
        codeword |= fm_amp_index << 26
        codeword |= bias_index << 24
        codeword |= fcw_index << 22
        codeword |= pcw_index << 19
        codeword |= code_clr << 18
        codeword |= env_index << 12

        return codeword

    def _codeword_gen(self, **kwargs):
        codeword_configs = kwargs.pop('codeword_configs', [])
        codeword_list = []
        for config in codeword_configs:
            codeword = self._codeword_encode(**config)
            codeword_list.append(codeword)
        return codeword_list

    def write_register(self, address, value):
        self.mk.rw_once('w', address, value, self.config_file)

    def _generic_awg_control_template(self, **kwargs):
        """
        通用 AWG汇编控制模版 (支持扫参)
        """
        channel = kwargs.get('channel', 0)
        codeword_list = self._codeword_gen(**kwargs)
        send_interval_list = kwargs.get('send_interval', [100])
        cycle_num = kwargs.get('cycle_num', 1)
        sweep_config = kwargs.get('sweep_config', {})
        sweep_num = sweep_config.get('sweep_num', 1)
        sweep_offsets = sweep_config.get('offsets', [])
        sweep_steps = sweep_config.get('steps', [])
        sweep_reg_num = len(sweep_offsets)

        dtcm_payload = [sweep_num, cycle_num, len(codeword_list), sweep_reg_num]

        for cw, wait_clk in zip(codeword_list, send_interval_list):
            dtcm_payload.append(cw)
            dtcm_payload.append(wait_clk)

        if sweep_reg_num > 0:
            dtcm_payload.extend(sweep_offsets)
            dtcm_payload.extend([s & 0xFFFFFFFF for s in sweep_steps])

        dtcm_base = addr_base['DTCM0_BASE'] + channel * 0x600000
        target_addr = dtcm_base + FourChZreg_define['mcu_reg']['DTFR'] + 4
        self.write_register(target_addr, dtcm_payload)

        return f"""
        start:
            lui  x1 , 0x100
            lui  x2 , 0x200

            addi x3 , x0 , 4
            addi x4 , x0 , 22
            addi x5 , x1 , 0x40
            addi x6 , x2 , 0x40

        load_nco_params_loop:
            addi x4 , x4 , -1
            lw   x31, 0x00(x5)
            sw   x31, 0x00(x6)
            add  x5 , x5 , x3
            add  x6 , x6 , x3
            bne  x4 , x0 , load_nco_params_loop

            lw   x31, 0xb4(x1)
            sw   x31, 0xb4(x2)

            lw   x10, 0xdc(x1)
            lw   x11, 0xe0(x1)
            lw   x12, 0xe4(x1)
            lw   x17, 0xe8(x1)

            addi x13, x1 , 0xec
            slli x14, x12, 3
            add  x18, x13, x14
            slli x14, x17, 2
            add  x20, x18, x14

        run_sweep_iteration:
            addi x28, x11, 0
        middle_ensemble_loop:
            addi x28, x28, -1
            addi x26, x12, 0
            addi x25, x13, 0

        inner_wave_send_loop:
            addi x26, x26, -1
            lw   x31, 0x00(x25)
            lw   x30, 0x04(x25)
            addi x25, x25, 8

            send x0 , x31, 0

            bne  x26, x0 , inner_wait_branch
            beq  x0 , x0 , outer_wait_branch

        inner_wait_branch:
            wait x0 , x30, -24
            bne  x26, x0 , inner_wave_send_loop

        outer_wait_branch:
            wait x0 , x30, -36
            bne  x28, x0 , middle_ensemble_loop

            beq  x10, x0 , mcu_exit
            addi x10, x10, -1
            wait x0 , x0 , 100

            beq  x17, x0 , run_sweep_iteration
            addi x4 , x17, 0
            addi x21, x18, 0
            addi x23, x20, 0

        update_param_loop:
            addi x4 , x4 , -1
            lw   x29, 0(x21)
            lw   x24, 0(x23)

            add  x5 , x1 , x29
            lw   x31, 0(x5)
            add  x31, x31, x24
            sw   x31, 0(x5)

            add  x6 , x2 , x29
            sw   x31, 0(x6)

            addi x21, x21, 4
            addi x23, x23, 4
            bne  x4 , x0 , update_param_loop

            beq  x0 , x0 , run_sweep_iteration

        mcu_exit:
            exit x0 , x0 , 0
        """

    def _ramp_mcu_template(self, **kwargs):
        channel = kwargs.pop('channel')
        ramp_mcu_registers = []
        ramp_mcu_registers.append(0 << 16)
        ramp_mcu_registers += [1 << 31]
        param_num = kwargs.pop('param_num')
        ensemble_num = kwargs.pop('ensemble_num')
        ramp_mcu_registers.append(param_num)
        ramp_mcu_registers.append(ensemble_num)
        height_list = kwargs.pop('height', 0)
        length_list = kwargs.pop('step_time', 0)
        for height, length in zip(height_list, length_list):
            ramp_mcu_registers += [height << 16]
            ramp_mcu_registers += [length]
            wait = 65536 / height * length
            ramp_mcu_registers += [wait]

        dtcm_base = addr_base['DTCM0_BASE'] + channel * 0x600000
        target_addr = dtcm_base + FourChZreg_define['mcu_reg']['DTFR'] + 4
        self.write_register(target_addr, ramp_mcu_registers)

        return f"""
           start:
               lui  x1 , 0x100
               lui  x2 , 0x200
               lw   x31, 0xdc(x1)
               sw   x31, 0xb8(x2)
               lw   x28, 0xe0(x1)
               addi x6 , x0, 12
               lw   x7 , 0xe8(x1)
           ensemble_loop:
               addi x7 , x7, -1
               addi x8 , x1, 0
               lw   x5 , 0xe4(x1)
           ramp_loop:
               addi x5,  x5, -1
               lw   x31, 0xec(x8)
               lw   x30, 0xf0(x8)
               lw   x29, 0xf4(x8)
               add  x8 , x8 , x6
               sw   x30, 0xc0(x2)
               sw   x31, 0xbc(x2)
               sw   x28, 0xc4(x2)
               wait x0 , x29, -30
               bne  x5 , x0 , ramp_loop
               bne  x7 , x0 , ensemble_loop
               sw   x0, 0xc4(x2)
               exit x0, x0, 0                      
           """

    def _ramp_mcu_fixed_template(self, **kwargs):
        channel = kwargs.pop('channel')
        ramp_mcu_registers = []
        ramp_mcu_registers += [1 << 31]
        ensemble_num = kwargs.pop('ensemble_num')
        config_param_num = kwargs.pop('config_param_num')
        ramp_mcu_registers.append(ensemble_num)
        ramp_mcu_registers.append(config_param_num)
        fixed_value_list = kwargs.pop('fixed_value')
        wait_list = kwargs.pop('wait_clk')
        for fixed_value, wait_clk in zip(fixed_value_list, wait_list):
            ramp_mcu_registers += [fixed_value << 16 | 1 << 15]
            ramp_mcu_registers += [wait_clk]
        dtcm_base = addr_base['DTCM0_BASE'] + channel * 0x600000
        target_addr = dtcm_base + FourChZreg_define['mcu_reg']['DTFR'] + 4
        self.write_register(target_addr, ramp_mcu_registers)

        return f"""
           start:
               lui  x1 , 0x100
               lui  x2 , 0x200
               lw   x31, 0xdc(x1)
               sw   x31, 0xc4(x2)
               addi x3 , x0, 8
               lw   x4 , 0xe0(x1)
           ensemble_loop:
               addi x4 , x4, -1
               addi x6 , x1, 0
               lw   x5 , 0xe4(x1)
           ramp_loop:
               addi x5,  x5, -1
               lw   x31, 0xe8(x6)
               lw   x30, 0xec(x6)
               sw   x31, 0xb8(x2)
               add  x6 , x6 , x3
               bne  x5 , x0 , ramp_loop_wait
               jal  x0 , ensemble_loop_wait
           ramp_loop_wait:
               wait x0 , x30, -24
               jal  x0 , ramp_loop
           ensemble_loop_wait:
               wait x0 , x30, -36
               bne  x4 , x0 , ensemble_loop
           exit:
               wait x0 , x0, 15
               sw   x0, 0xb8(x2)
               sw   x0, 0xc4(x2)
               exit x0, x0, 0                      
           """

    def _write_machine_codes_to_chip(self, machine_codes: str, **kwargs):
        # 修正：同时把通道号传给 make_inst 的 channel_id 字段
        channel = kwargs.pop('channel')
        self.mk_instr.write(
            machine_codes,
            self.config_file,
            channel_id=channel,
            show=kwargs.pop('instr_show', False)
        )
        if 'inner_sync' in kwargs:
            inner_sync = kwargs.pop('inner_sync')
            if inner_sync:
                self.write_register(addr_base['SYST_BASE'] + FourChZreg_define['sys_reg']['SYNCR'], 15 << 28 | 1 << 17)
                self.mk.rw_once('r', addr_base['SYST_BASE'] + FourChZreg_define['pll_reg']['INTPLL_CLKRXPD'], [0] * 20,
                                self.config_file)
                self.mk.rw_once('r', addr_base['DBGM_BASE'], [0] * 2048, self.config_file)


def instruction_config(mk_instance, mk_instr, **kwargs):
    # 直接将 **kwargs 传入，__init__ 会自动匹配字典里的 'channel_id'，彻底避免重复传参报错
    asm_templates = AssemblyTemplateManager(mk_instance, mk_instr, **kwargs)
    machine_codes = asm_templates.create_instructions(**kwargs)
    asm_templates._write_machine_codes_to_chip(machine_codes, **kwargs)