//! ARM Thumb-2 assembly emission backend.
//!
//! Translates an R-SPU program (from `rspu.rs`) into ARM Thumb-2 assembly text.
//! Uses IT blocks for conditional execution — no backward branches.
//! Bounded execution via BKPT watchdog at program end.

#![forbid(unsafe_code)]

use super::rspu_isa::*;
use crate::error::MirrError;

/// Memory-mapped I/O base address for ARM Cortex-M.
const MMIO_BASE: u32 = 0x4000_0000;

/// Emit ARM Thumb-2 assembly from an R-SPU program.
///
/// Returns a complete `.S` file with `.text` and `.data` sections.
pub fn emit_arm_asm(program: &RspuProgram) -> Result<String, MirrError> {
    let mut out = String::with_capacity(program.instructions.len() * 80);
    let mut label_counter: u32 = 0;

    // Header
    out.push_str("@ ARM Thumb-2 Assembly — generated by MIRR compiler\n");
    out.push_str(&format!("@ Registers used: {}\n", program.registers_used));
    out.push_str(&format!("@ Guards used:    {}\n", program.guards_used));
    out.push_str(&format!("@ Instructions:   {}\n", program.instructions.len()));
    out.push_str("@\n@ Register map:\n");

    for (name, _reg) in &program.register_map {
        out.push_str(&format!("@   R{} = {}\n", _reg, name));
    }
    out.push_str("@\n@ Guard map:\n");
    for (name, gid) in &program.guard_map {
        out.push_str(&format!("@   G{} = {}\n", gid, name));
    }
    out.push('\n');

    // Directives
    out.push_str(".syntax unified\n");
    out.push_str(".thumb\n");
    out.push_str(".section .text\n");
    out.push_str(".globl _start\n");
    out.push_str(".type _start, %function\n\n");
    out.push_str("_start:\n");

    // Emit instructions
    for (i, instr) in program.instructions.iter().enumerate() {
        out.push_str(&format!("\ninstr_{}:\n", i));
        emit_arm_instruction(&mut out, instr, &mut label_counter);
    }

    // HALT at end (safety guarantee)
    out.push_str("\ninstr_halt:\n");
    out.push_str("    bkpt #0\n");

    // Data section
    out.push_str("\n.section .data\n");
    for (name, _reg) in &program.register_map {
        out.push_str(&format!("signal_{}: .word 0\n", name));
    }
    for (name, _) in &program.guard_map {
        out.push_str(&format!("guard_{}: .word 0\n", name));
    }
    for (name, _) in &program.guard_map {
        out.push_str(&format!("sr_{}: .space 1024\n", name));
    }

    Ok(out)
}

/// Emit a single ARM instruction.
fn emit_arm_instruction(out: &mut String, instr: &RspuInstruction, label_counter: &mut u32) {
    match instr {
        RspuInstruction::TagBranch { tag_value, target_pc } => {
            out.push_str(&format!("    @ TAG_BRANCH {}, {}\n", tag_value, target_pc));
        }
        // Register tier
        RspuInstruction::LoadInput { dst, port } => {
            let addr = MMIO_BASE + (*port as u32) * 4;
            out.push_str(&format!("    ldr r{}, =0x{:08X}\n", dst, addr));
            out.push_str(&format!("    ldr r{}, [r{}]\n", dst, dst));
        }
        RspuInstruction::StoreOutput { src, port } => {
            let addr = MMIO_BASE + (*port as u32) * 4;
            out.push_str(&format!("    ldr r12, =0x{:08X}\n", addr));
            out.push_str(&format!("    str r{}, [r12]\n", src));
        }
        RspuInstruction::Mov { dst, src } => {
            out.push_str(&format!("    mov r{}, r{}\n", dst, src));
        }
        RspuInstruction::LoadImm { dst, value, .. } => {
            if *value <= 255 {
                out.push_str(&format!("    mov r{}, #{}\n", dst, value));
            } else {
                out.push_str(&format!("    ldr r{}, ={}\n", dst, value));
            }
        }

        // ALU tier
        RspuInstruction::Alu { op, dst, a, b } => {
            emit_arm_alu(out, *op, *dst, *a, *b);
        }
        RspuInstruction::AluImm { op, dst, a, imm } => {
            emit_arm_alu_imm(out, *op, *dst, *a, *imm);
        }
        RspuInstruction::AluUnary { op, dst, src } => match op {
            AluUnaryOp::Not => out.push_str(&format!("    mvn r{}, r{}\n", dst, src)),
            AluUnaryOp::Negate => out.push_str(&format!("    neg r{}, r{}\n", dst, src)),
        },

        // Temporal tier — shift register emulation
        RspuInstruction::SrInit { guard, length, cond } => {
            // Store condition value to guard state.
            out.push_str(&format!("    ldr r12, =guard_g{}\n", guard));
            out.push_str(&format!("    str r{}, [r12]\n", cond));
            // Zero-fill shift register chain for `length` stages.
            out.push_str(&format!("    ldr r12, =sr_g{}\n", guard));
            out.push_str("    mov r11, #0\n");
            let stages = (*length).min(256);
            for s in 0..stages {
                out.push_str(&format!("    str r11, [r12, #{}]\n", s * 4));
            }
        }
        RspuInstruction::SrTick { guard } => {
            // Shift register chain: stage[i+1] = stage[i], stage[0] = guard state.
            // Shift all stages down by one position (bounded by 256 stages).
            *label_counter += 1;
            let shift_label = *label_counter;
            out.push_str(&format!("    ldr r12, =sr_g{}\n", guard));
            out.push_str(&format!("    ldr r11, =guard_g{}\n", guard));
            out.push_str("    ldr r10, [r11]\n"); // new value from guard state
                                                  // Shift stages 1..N ← 0..N-1 (reverse order to avoid overwrite)
            out.push_str("    mov r9, #256\n"); // max stages
            out.push_str(&format!("shift_loop_arm_{}:\n", shift_label));
            out.push_str("    sub r9, r9, #1\n");
            out.push_str("    cmp r9, #0\n");
            out.push_str(&format!("    beq shift_done_arm_{}\n", shift_label));
            out.push_str("    lsl r8, r9, #2\n"); // offset = i * 4
            out.push_str("    sub r7, r8, #4\n"); // prev offset = (i-1) * 4
            out.push_str("    ldr r6, [r12, r7]\n");
            out.push_str("    str r6, [r12, r8]\n");
            out.push_str(&format!("    b shift_loop_arm_{}\n", shift_label));
            out.push_str(&format!("shift_done_arm_{}:\n", shift_label));
            // Store new value at stage 0
            out.push_str("    str r10, [r12]\n");
        }
        RspuInstruction::SrQuery { dst, guard } => {
            out.push_str(&format!("    ldr r12, =guard_g{}\n", guard));
            out.push_str(&format!("    ldr r{}, [r12]\n", dst));
            out.push_str(&format!("    and r{}, r{}, #1\n", dst, dst));
        }

        // Temporal tier — counter
        RspuInstruction::CtrInit { guard, target, cond: _ } => {
            out.push_str(&format!("    ldr r12, =guard_g{}\n", guard));
            out.push_str(&format!("    ldr r11, ={}\n", target));
            out.push_str("    str r11, [r12]\n");
        }
        RspuInstruction::CtrTick { guard } => {
            out.push_str(&format!("    ldr r12, =guard_g{}\n", guard));
            out.push_str("    ldr r11, [r12]\n");
            out.push_str("    sub r11, r11, #1\n");
            out.push_str("    str r11, [r12]\n");
        }
        RspuInstruction::CtrQuery { dst, guard } => {
            out.push_str(&format!("    ldr r12, =guard_g{}\n", guard));
            out.push_str(&format!("    ldr r{}, [r12]\n", dst));
            out.push_str(&format!("    cmp r{}, #0\n", dst));
            out.push_str(&format!("    mov r{}, #0\n", dst));
            out.push_str("    it lt\n");
            out.push_str(&format!("    movlt r{}, #1\n", dst));
        }

        // Guard combination
        RspuInstruction::GuardAnd { dst, a, b } => {
            out.push_str(&format!("    ldr r12, =guard_g{}\n", a));
            out.push_str("    ldr r11, [r12]\n");
            out.push_str(&format!("    ldr r12, =guard_g{}\n", b));
            out.push_str("    ldr r10, [r12]\n");
            out.push_str("    and r11, r11, r10\n");
            out.push_str(&format!("    ldr r12, =guard_g{}\n", dst));
            out.push_str("    str r11, [r12]\n");
        }
        RspuInstruction::GuardOr { dst, a, b } => {
            out.push_str(&format!("    ldr r12, =guard_g{}\n", a));
            out.push_str("    ldr r11, [r12]\n");
            out.push_str(&format!("    ldr r12, =guard_g{}\n", b));
            out.push_str("    ldr r10, [r12]\n");
            out.push_str("    orr r11, r11, r10\n");
            out.push_str(&format!("    ldr r12, =guard_g{}\n", dst));
            out.push_str("    str r11, [r12]\n");
        }

        // Reflex tier
        RspuInstruction::ReflexIf { guard, dst, src } => {
            out.push_str(&format!("    ldr r12, =guard_g{}\n", guard));
            out.push_str("    ldr r11, [r12]\n");
            out.push_str("    cmp r11, #0\n");
            out.push_str("    it ne\n");
            out.push_str(&format!("    movne r{}, r{}\n", dst, src));
        }
        RspuInstruction::Prev { dst, signal, delay: _ } => {
            // Prev reads a signal's value from N cycles ago.
            // Full implementation requires per-signal shift registers.
            // Current: pass-through (safe over-approximation — uses current value).
            out.push_str(&format!("    mov r{}, r{}\n", dst, signal));
        }

        // Safety tier
        RspuInstruction::EmergencyStop => {
            out.push_str("    bkpt #0xFF\n");
        }

        // LTL assertion tier
        RspuInstruction::AssertAlways { cond, property_id } => {
            *label_counter += 1;
            let label = *label_counter;
            out.push_str(&format!("    cmp r{}, #0\n", cond));
            out.push_str(&format!("    bne assert_pass_{}\n", label));
            out.push_str(&format!("    bkpt #{}\n", 200 + property_id));
            out.push_str(&format!("assert_pass_{}:\n", label));
        }
        RspuInstruction::AssertNever { cond, property_id } => {
            *label_counter += 1;
            let label = *label_counter;
            out.push_str(&format!("    cmp r{}, #0\n", cond));
            out.push_str(&format!("    beq assert_pass_{}\n", label));
            out.push_str(&format!("    bkpt #{}\n", 300 + property_id));
            out.push_str(&format!("assert_pass_{}:\n", label));
        }

        // Exception tier
        RspuInstruction::Trap { code } => {
            out.push_str(&format!("    bkpt #{}\n", code));
        }
        RspuInstruction::TrapIf { cond, code } => {
            *label_counter += 1;
            let label = *label_counter;
            out.push_str(&format!("    cmp r{}, #0\n", cond));
            out.push_str(&format!("    beq skip_trap_{}\n", label));
            out.push_str(&format!("    bkpt #{}\n", code));
            out.push_str(&format!("skip_trap_{}:\n", label));
        }
        RspuInstruction::Halt => {
            out.push_str("    bkpt #0\n");
        }

        // Control tier
        RspuInstruction::ModeSwitch { mode } => {
            out.push_str(&format!("    @ MODE_SWITCH {}\n", mode));
        }
        RspuInstruction::Nop => {
            out.push_str("    nop\n");
        }
        RspuInstruction::Fence => {
            out.push_str("    dmb\n");
        }

        // Tagged tier
        RspuInstruction::TagLoad { dst, tag } => {
            out.push_str(&format!("    mov r{}, #{}\n", dst, tag));
        }
        RspuInstruction::TagCheck { src, expected } => {
            *label_counter += 1;
            let label = *label_counter;
            out.push_str(&format!("    cmp r{}, #{}\n", src, expected));
            out.push_str(&format!("    beq tag_ok_{}\n", label));
            out.push_str("    bkpt #708\n");
            out.push_str(&format!("tag_ok_{}:\n", label));
        }
        RspuInstruction::TagRead { dst, src } => {
            out.push_str(&format!("    mov r{}, r{}\n", dst, src));
        }

        // Deadline
        RspuInstruction::DeadlineSet { cycles } => {
            out.push_str(&format!("    @ DEADLINE_SET {}\n", cycles));
        }

        // Totality engine tier
        RspuInstruction::Verify { cert_offset } => {
            out.push_str(&format!("    @ VERIFY offset={}\n", cert_offset));
        }
        RspuInstruction::Certify { dst } => {
            out.push_str(&format!("    mov r{}, #1\n", dst));
        }
        RspuInstruction::TotalCheck { expected_properties } => {
            out.push_str(&format!("    @ TOTAL_CHECK {}\n", expected_properties));
        }

        // Symbolic tier
        RspuInstruction::Match { dst, src, table_offset } => {
            out.push_str(&format!("    @ MATCH r{}, r{}, table=#{}\n", dst, src, table_offset));
            out.push_str(&format!("    mov r{}, r{}\n", dst, src));
        }
        RspuInstruction::IntervalLo { dst, src } => {
            out.push_str(&format!("    mov r{}, r{}\n", dst, src));
        }
        RspuInstruction::IntervalHi { dst, src } => {
            out.push_str(&format!("    mov r{}, r{}\n", dst, src));
        }
        RspuInstruction::IntervalCheck { src, bounds } => {
            out.push_str(&format!("    @ INTERVAL_CHECK r{}, r{}\n", src, bounds));
        }
    }
}

/// Emit ARM ALU operation.
fn emit_arm_alu(out: &mut String, op: AluOp, dst: u8, a: u8, b: u8) {
    match op {
        AluOp::Add => out.push_str(&format!("    add r{}, r{}, r{}\n", dst, a, b)),
        AluOp::Sub => out.push_str(&format!("    sub r{}, r{}, r{}\n", dst, a, b)),
        AluOp::Mul => out.push_str(&format!("    mul r{}, r{}, r{}\n", dst, a, b)),
        AluOp::And => out.push_str(&format!("    and r{}, r{}, r{}\n", dst, a, b)),
        AluOp::Or => out.push_str(&format!("    orr r{}, r{}, r{}\n", dst, a, b)),
        AluOp::Xor => out.push_str(&format!("    eor r{}, r{}, r{}\n", dst, a, b)),
        AluOp::Shl => out.push_str(&format!("    lsl r{}, r{}, r{}\n", dst, a, b)),
        AluOp::Shr => out.push_str(&format!("    lsr r{}, r{}, r{}\n", dst, a, b)),
        AluOp::Eq => {
            out.push_str(&format!("    cmp r{}, r{}\n", a, b));
            out.push_str(&format!("    mov r{}, #0\n", dst));
            out.push_str("    it eq\n");
            out.push_str(&format!("    moveq r{}, #1\n", dst));
        }
        AluOp::Ne => {
            out.push_str(&format!("    cmp r{}, r{}\n", a, b));
            out.push_str(&format!("    mov r{}, #0\n", dst));
            out.push_str("    it ne\n");
            out.push_str(&format!("    movne r{}, #1\n", dst));
        }
        AluOp::Lt => {
            out.push_str(&format!("    cmp r{}, r{}\n", a, b));
            out.push_str(&format!("    mov r{}, #0\n", dst));
            out.push_str("    it lt\n");
            out.push_str(&format!("    movlt r{}, #1\n", dst));
        }
        AluOp::Gt => {
            out.push_str(&format!("    cmp r{}, r{}\n", a, b));
            out.push_str(&format!("    mov r{}, #0\n", dst));
            out.push_str("    it gt\n");
            out.push_str(&format!("    movgt r{}, #1\n", dst));
        }
        AluOp::Le => {
            out.push_str(&format!("    cmp r{}, r{}\n", a, b));
            out.push_str(&format!("    mov r{}, #0\n", dst));
            out.push_str("    it le\n");
            out.push_str(&format!("    movle r{}, #1\n", dst));
        }
        AluOp::Ge => {
            out.push_str(&format!("    cmp r{}, r{}\n", a, b));
            out.push_str(&format!("    mov r{}, #0\n", dst));
            out.push_str("    it ge\n");
            out.push_str(&format!("    movge r{}, #1\n", dst));
        }
    }
}

/// Emit ARM ALU with immediate.
fn emit_arm_alu_imm(out: &mut String, op: AluOp, dst: u8, a: u8, imm: u64) {
    match op {
        AluOp::Add => out.push_str(&format!("    add r{}, r{}, #{}\n", dst, a, imm)),
        AluOp::Sub => out.push_str(&format!("    sub r{}, r{}, #{}\n", dst, a, imm)),
        AluOp::And => out.push_str(&format!("    and r{}, r{}, #{}\n", dst, a, imm)),
        AluOp::Or => out.push_str(&format!("    orr r{}, r{}, #{}\n", dst, a, imm)),
        AluOp::Xor => out.push_str(&format!("    eor r{}, r{}, #{}\n", dst, a, imm)),
        AluOp::Shl => out.push_str(&format!("    lsl r{}, r{}, #{}\n", dst, a, imm & 31)),
        AluOp::Shr => out.push_str(&format!("    lsr r{}, r{}, #{}\n", dst, a, imm & 31)),
        _ => {
            out.push_str(&format!("    ldr r11, ={}\n", imm));
            emit_arm_alu(out, op, dst, a, 11);
        }
    }
}
