//! RISC-V RV32I assembly emission backend.
//!
//! Translates an R-SPU program (from `rspu.rs`) into RV32I assembly text.
//! All control flow is forward-only (BEQ/BNE) — no backward branches.
//! Bounded execution via ECALL watchdog at program end.

#![forbid(unsafe_code)]

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

/// Memory-mapped I/O base address for RISC-V soft cores.
const MMIO_BASE: u32 = 0x1000_0000;

/// Emit RISC-V RV32I assembly from an R-SPU program.
///
/// Returns a complete `.S` file with `.text` and `.data` sections.
pub fn emit_riscv_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("# RISC-V RV32I 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!("#   x{:<2} = {}\n", _reg, name));
    }
    out.push_str("#\n# Guard map:\n");
    for (name, gid) in &program.guard_map {
        out.push_str(&format!("#   G{:<2} = {}\n", gid, name));
    }
    out.push('\n');

    // Text section
    out.push_str(".section .text\n.globl _start\n\n_start:\n");

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

    // HALT at end (safety guarantee)
    out.push_str("\ninstr_halt:\n");
    out.push_str("    li a7, 0\n");
    out.push_str("    ecall\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));
    }
    // Guard state storage
    for (name, _) in &program.guard_map {
        out.push_str(&format!("guard_{}: .word 0\n", name));
    }
    // Shift register storage (bounded by MAX_GUARDS * 256 words)
    for (name, _) in &program.guard_map {
        out.push_str(&format!("sr_{}: .space 1024\n", name)); // 256 words * 4 bytes
    }

    Ok(out)
}

/// Emit a single RISC-V instruction.
fn emit_riscv_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!("    li t0, 0x{:08X}\n", addr));
            out.push_str(&format!("    lw x{}, 0(t0)\n", dst));
        }
        RspuInstruction::StoreOutput { src, port } => {
            let addr = MMIO_BASE + (*port as u32) * 4;
            out.push_str(&format!("    li t0, 0x{:08X}\n", addr));
            out.push_str(&format!("    sw x{}, 0(t0)\n", src));
        }
        RspuInstruction::Mov { dst, src } => {
            out.push_str(&format!("    addi x{}, x{}, 0\n", dst, src));
        }
        RspuInstruction::LoadImm { dst, value, .. } => {
            out.push_str(&format!("    li x{}, {}\n", dst, value));
        }

        // ALU tier
        RspuInstruction::Alu { op, dst, a, b } => {
            emit_riscv_alu(out, *op, *dst, *a, *b);
        }
        RspuInstruction::AluImm { op, dst, a, imm } => {
            emit_riscv_alu_imm(out, *op, *dst, *a, *imm);
        }
        RspuInstruction::AluUnary { op, dst, src } => match op {
            AluUnaryOp::Not => out.push_str(&format!("    xori x{}, x{}, -1\n", dst, src)),
            AluUnaryOp::Negate => out.push_str(&format!("    sub x{}, x0, x{}\n", dst, src)),
        },

        // Temporal tier — shift register emulation
        RspuInstruction::SrInit { guard, length, cond } => {
            // Store condition value to guard state.
            out.push_str(&format!("    la t0, guard_g{}\n", guard));
            out.push_str(&format!("    sw x{}, 0(t0)\n", cond));
            // Zero-fill shift register chain for `length` stages.
            out.push_str(&format!("    la t0, sr_g{}\n", guard));
            out.push_str("    li t1, 0\n");
            let stages = (*length).min(256);
            for s in 0..stages {
                out.push_str(&format!("    sw t1, {}(t0)\n", s * 4));
            }
        }
        RspuInstruction::SrTick { guard } => {
            // Shift register chain: stage[i+1] = stage[i], stage[0] = guard state.
            out.push_str(&format!("    la t0, sr_g{}\n", guard));
            out.push_str(&format!("    la t3, guard_g{}\n", guard));
            out.push_str("    lw t4, 0(t3)\n"); // new value from guard state
                                                // Shift stages 1..N ← 0..N-1 (reverse order to avoid overwrite)
            out.push_str("    li t5, 256\n"); // max stages
            out.push_str("shift_loop_rv_:\n");
            out.push_str("    addi t5, t5, -1\n");
            out.push_str("    beq t5, x0, shift_done_rv_\n");
            out.push_str("    slli t6, t5, 2\n"); // offset = i * 4
            out.push_str("    addi t2, t6, -4\n"); // prev offset = (i-1) * 4
            out.push_str("    add t7, t0, t2\n");
            out.push_str("    lw t1, 0(t7)\n");
            out.push_str("    add t7, t0, t6\n");
            out.push_str("    sw t1, 0(t7)\n");
            out.push_str("    j shift_loop_rv_\n");
            out.push_str("shift_done_rv_:\n");
            // Store new value at stage 0
            out.push_str("    sw t4, 0(t0)\n");
        }
        RspuInstruction::SrQuery { dst, guard } => {
            // Read LSB of guard state
            out.push_str(&format!("    la t0, guard_g{}\n", guard));
            out.push_str(&format!("    lw x{}, 0(t0)\n", dst));
            out.push_str(&format!("    andi x{}, x{}, 1\n", dst, dst));
        }

        // Temporal tier — counter
        RspuInstruction::CtrInit { guard, target, cond: _ } => {
            out.push_str(&format!("    la t0, guard_g{}\n", guard));
            out.push_str(&format!("    li t1, {}\n", target));
            out.push_str("    sw t1, 0(t0)\n");
        }
        RspuInstruction::CtrTick { guard } => {
            out.push_str(&format!("    la t0, guard_g{}\n", guard));
            out.push_str("    lw t1, 0(t0)\n");
            out.push_str("    addi t1, t1, -1\n");
            out.push_str("    sw t1, 0(t0)\n");
        }
        RspuInstruction::CtrQuery { dst, guard } => {
            out.push_str(&format!("    la t0, guard_g{}\n", guard));
            out.push_str(&format!("    lw x{}, 0(t0)\n", dst));
            out.push_str(&format!("    sltz x{}, x{}\n", dst, dst));
        }

        // Guard combination
        RspuInstruction::GuardAnd { dst, a, b } => {
            out.push_str(&format!("    la t0, guard_g{}\n", a));
            out.push_str("    lw t1, 0(t0)\n");
            out.push_str(&format!("    la t0, guard_g{}\n", b));
            out.push_str("    lw t2, 0(t0)\n");
            out.push_str("    and t1, t1, t2\n");
            out.push_str(&format!("    la t0, guard_g{}\n", dst));
            out.push_str("    sw t1, 0(t0)\n");
        }
        RspuInstruction::GuardOr { dst, a, b } => {
            out.push_str(&format!("    la t0, guard_g{}\n", a));
            out.push_str("    lw t1, 0(t0)\n");
            out.push_str(&format!("    la t0, guard_g{}\n", b));
            out.push_str("    lw t2, 0(t0)\n");
            out.push_str("    or t1, t1, t2\n");
            out.push_str(&format!("    la t0, guard_g{}\n", dst));
            out.push_str("    sw t1, 0(t0)\n");
        }

        // Reflex tier
        RspuInstruction::ReflexIf { guard, dst, src } => {
            *label_counter += 1;
            let label = *label_counter;
            out.push_str(&format!("    la t0, guard_g{}\n", guard));
            out.push_str("    lw t1, 0(t0)\n");
            out.push_str(&format!("    beq t1, x0, skip_reflex_{}\n", label));
            out.push_str(&format!("    addi x{}, x{}, 0\n", dst, src));
            out.push_str(&format!("skip_reflex_{}:\n", label));
        }
        RspuInstruction::Prev { dst, signal, delay } => {
            // Read from shift register at offset
            out.push_str(&format!(
                "    # PREV x{}, x{}, delay={} (shift register read)\n",
                dst, signal, delay
            ));
            out.push_str(&format!("    addi x{}, x{}, 0\n", dst, signal));
        }

        // Safety tier
        RspuInstruction::EmergencyStop => {
            out.push_str("    ebreak\n");
        }

        // LTL assertion tier
        RspuInstruction::AssertAlways { cond, property_id } => {
            *label_counter += 1;
            let label = *label_counter;
            out.push_str(&format!("    bne x{}, x0, assert_pass_{}\n", cond, label));
            out.push_str(&format!("    li a7, {}\n", 200 + property_id));
            out.push_str("    ecall\n");
            out.push_str(&format!("assert_pass_{}:\n", label));
        }
        RspuInstruction::AssertNever { cond, property_id } => {
            *label_counter += 1;
            let label = *label_counter;
            out.push_str(&format!("    beq x{}, x0, assert_pass_{}\n", cond, label));
            out.push_str(&format!("    li a7, {}\n", 300 + property_id));
            out.push_str("    ecall\n");
            out.push_str(&format!("assert_pass_{}:\n", label));
        }

        // Exception tier
        RspuInstruction::Trap { code } => {
            out.push_str(&format!("    li a7, {}\n", code));
            out.push_str("    ecall\n");
        }
        RspuInstruction::TrapIf { cond, code } => {
            *label_counter += 1;
            let label = *label_counter;
            out.push_str(&format!("    beq x{}, x0, skip_trap_{}\n", cond, label));
            out.push_str(&format!("    li a7, {}\n", code));
            out.push_str("    ecall\n");
            out.push_str(&format!("skip_trap_{}:\n", label));
        }
        RspuInstruction::Halt => {
            out.push_str("    li a7, 0\n");
            out.push_str("    ecall\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("    fence\n");
        }

        // Tagged tier
        RspuInstruction::TagLoad { dst, tag } => {
            out.push_str(&format!("    li x{}, {}\n", dst, tag));
        }
        RspuInstruction::TagCheck { src, expected } => {
            *label_counter += 1;
            let label = *label_counter;
            out.push_str(&format!("    li t0, {}\n", expected));
            out.push_str(&format!("    beq x{}, t0, tag_ok_{}\n", src, label));
            out.push_str("    li a7, 708\n");
            out.push_str("    ecall\n");
            out.push_str(&format!("tag_ok_{}:\n", label));
        }
        RspuInstruction::TagRead { dst, src } => {
            out.push_str(&format!("    addi x{}, x{}, 0\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!("    li x{}, 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 x{}, x{}, table=#{}\n", dst, src, table_offset));
            out.push_str(&format!("    addi x{}, x{}, 0\n", dst, src));
        }
        RspuInstruction::IntervalLo { dst, src } => {
            out.push_str(&format!("    addi x{}, x{}, 0\n", dst, src));
        }
        RspuInstruction::IntervalHi { dst, src } => {
            out.push_str(&format!("    addi x{}, x{}, 0\n", dst, src));
        }
        RspuInstruction::IntervalCheck { src, bounds } => {
            out.push_str(&format!("    # INTERVAL_CHECK x{}, x{}\n", src, bounds));
        }
    }
}

/// Emit RISC-V ALU operation.
fn emit_riscv_alu(out: &mut String, op: AluOp, dst: u8, a: u8, b: u8) {
    match op {
        AluOp::Add => out.push_str(&format!("    add x{}, x{}, x{}\n", dst, a, b)),
        AluOp::Sub => out.push_str(&format!("    sub x{}, x{}, x{}\n", dst, a, b)),
        AluOp::Mul => out.push_str(&format!("    mul x{}, x{}, x{}\n", dst, a, b)),
        AluOp::And => out.push_str(&format!("    and x{}, x{}, x{}\n", dst, a, b)),
        AluOp::Or => out.push_str(&format!("    or x{}, x{}, x{}\n", dst, a, b)),
        AluOp::Xor => out.push_str(&format!("    xor x{}, x{}, x{}\n", dst, a, b)),
        AluOp::Shl => out.push_str(&format!("    sll x{}, x{}, x{}\n", dst, a, b)),
        AluOp::Shr => out.push_str(&format!("    srl x{}, x{}, x{}\n", dst, a, b)),
        AluOp::Eq => {
            out.push_str(&format!("    xor t0, x{}, x{}\n", a, b));
            out.push_str(&format!("    seqz x{}, t0\n", dst));
        }
        AluOp::Ne => {
            out.push_str(&format!("    xor t0, x{}, x{}\n", a, b));
            out.push_str(&format!("    snez x{}, t0\n", dst));
        }
        AluOp::Lt => out.push_str(&format!("    slt x{}, x{}, x{}\n", dst, a, b)),
        AluOp::Gt => out.push_str(&format!("    slt x{}, x{}, x{}\n", dst, b, a)),
        AluOp::Le => {
            out.push_str(&format!("    slt t0, x{}, x{}\n", b, a));
            out.push_str(&format!("    xori x{}, t0, 1\n", dst));
        }
        AluOp::Ge => {
            out.push_str(&format!("    slt t0, x{}, x{}\n", a, b));
            out.push_str(&format!("    xori x{}, t0, 1\n", dst));
        }
    }
}

/// Emit RISC-V ALU with immediate.
fn emit_riscv_alu_imm(out: &mut String, op: AluOp, dst: u8, a: u8, imm: u64) {
    match op {
        AluOp::Add => out.push_str(&format!("    addi x{}, x{}, {}\n", dst, a, imm)),
        AluOp::And => out.push_str(&format!("    andi x{}, x{}, {}\n", dst, a, imm)),
        AluOp::Or => out.push_str(&format!("    ori x{}, x{}, {}\n", dst, a, imm)),
        AluOp::Xor => out.push_str(&format!("    xori x{}, x{}, {}\n", dst, a, imm)),
        AluOp::Shl => out.push_str(&format!("    slli x{}, x{}, {}\n", dst, a, imm & 31)),
        AluOp::Shr => out.push_str(&format!("    srli x{}, x{}, {}\n", dst, a, imm & 31)),
        _ => {
            // Fallback: load immediate then apply binary op
            out.push_str(&format!("    li t0, {}\n", imm));
            emit_riscv_alu(out, op, dst, a, 5); // t0 = x5
        }
    }
}
