use crate::{
    debug, ComputeInstructions, ControlFlowInstructions, DebugFn, Debuggable, EcallHandler,
    ExternFn, JitContext, JitFunction, JitMemory, MemoryInstructions, RiscOperand, RiscRegister,
    RiscvTranspiler, SystemInstructions, TraceCollector,
};
use std::io;

pub struct DebugBackend<B: RiscvTranspiler> {
    backend: B,
}

impl<B: RiscvTranspiler> DebugBackend<B> {
    pub const fn new(backend: B) -> Self {
        Self { backend }
    }
}

impl<B: RiscvTranspiler + Debuggable> RiscvTranspiler for DebugBackend<B> {
    fn new(
        program_size: usize,
        memory_size: usize,
        max_trace_size: u64,
        pc_start: u64,
        pc_base: u64,
        clk_bump: u64,
    ) -> Result<Self, std::io::Error> {
        let backend =
            B::new(program_size, memory_size, max_trace_size, pc_start, pc_base, clk_bump)?;

        Ok(Self::new(backend))
    }

    fn register_ecall_handler(&mut self, handler: EcallHandler) {
        self.backend.register_ecall_handler(handler);
    }

    fn start_instr(&mut self) {
        extern "C" fn print_bar(_: *mut JitContext) {
            eprintln!("--------------------------------");
        }

        extern "C" fn collect_registers(ctx: *mut JitContext) {
            let ctx = unsafe { &mut *ctx };
            if let Some(sender) = &ctx.debug_sender {
                sender
                    .send(Some(debug::State {
                        pc: ctx.pc,
                        clk: ctx.clk,
                        global_clk: ctx.global_clk,
                        registers: ctx.registers,
                    }))
                    .expect("Failed to send debug state");
            }
        }

        self.backend.start_instr();
        self.backend.call_extern_fn(collect_registers);
        self.backend.call_extern_fn(print_bar);
        self.backend.print_ctx();
    }

    fn end_instr(&mut self) {
        self.backend.end_instr();
    }

    fn inspect_register(&mut self, reg: RiscRegister, handler: DebugFn) {
        self.backend.inspect_register(reg, handler);
    }

    fn inspect_immediate(&mut self, imm: u64, handler: DebugFn) {
        self.backend.inspect_immediate(imm, handler);
    }

    fn call_extern_fn(&mut self, handler: ExternFn) {
        self.backend.call_extern_fn(handler);
    }

    fn finalize<M: JitMemory>(self) -> io::Result<JitFunction<M>> {
        self.backend.finalize()
    }
}

impl<B: RiscvTranspiler> MemoryInstructions for DebugBackend<B> {
    fn lb(&mut self, rd: RiscRegister, rs1: RiscRegister, imm: u64) {
        extern "C" fn lb(ctx: *mut JitContext) {
            let ctx = unsafe { &mut *ctx };
            eprintln!("-- opcode = lb: pc={}", ctx.pc);
        }

        self.backend.call_extern_fn(lb);
        self.backend.lb(rd, rs1, imm);
    }

    fn lh(&mut self, rd: RiscRegister, rs1: RiscRegister, imm: u64) {
        extern "C" fn lh(ctx: *mut JitContext) {
            let ctx = unsafe { &mut *ctx };
            eprintln!("-- opcode = lh: pc={}", ctx.pc);
        }

        self.backend.call_extern_fn(lh);
        self.backend.lh(rd, rs1, imm);
    }

    fn lw(&mut self, rd: RiscRegister, rs1: RiscRegister, imm: u64) {
        extern "C" fn lw(ctx: *mut JitContext) {
            let ctx = unsafe { &mut *ctx };
            eprintln!("-- opcode = lw: pc={}", ctx.pc);
        }

        extern "C" fn lw_imm_value(imm: u64) {
            eprintln!("-- lw_imm_value: value={imm}");
        }

        extern "C" fn lw_rs1_value(rs1: u64) {
            eprintln!("-- lw_rs1_value={rs1}");
        }

        extern "C" fn lw_rs1(rs1: u64) {
            eprintln!("-- lw_rs1={rs1}");
        }

        extern "C" fn lw_rd(rd: u64) {
            eprintln!("-- lw_rd={rd}");
        }

        self.inspect_immediate(imm, lw_imm_value);
        self.inspect_immediate(rd as u8 as u64, lw_rd);
        self.inspect_immediate(rs1 as u8 as u64, lw_rs1);
        self.inspect_register(rs1, lw_rs1_value);

        self.backend.call_extern_fn(lw);
        self.backend.lw(rd, rs1, imm);
    }

    fn lbu(&mut self, rd: RiscRegister, rs1: RiscRegister, imm: u64) {
        extern "C" fn lbu(ctx: *mut JitContext) {
            let ctx = unsafe { &mut *ctx };
            eprintln!("-- opcode = lbu: pc={}", ctx.pc);
        }

        self.backend.call_extern_fn(lbu);
        self.backend.lbu(rd, rs1, imm);
    }

    fn lhu(&mut self, rd: RiscRegister, rs1: RiscRegister, imm: u64) {
        extern "C" fn lhu(ctx: *mut JitContext) {
            let ctx = unsafe { &mut *ctx };
            eprintln!("-- opcode = lhu: pc={}", ctx.pc);
        }

        self.backend.call_extern_fn(lhu);
        self.backend.lhu(rd, rs1, imm);
    }

    fn ld(&mut self, rd: RiscRegister, rs1: RiscRegister, imm: u64) {
        extern "C" fn ld(ctx: *mut JitContext) {
            let ctx = unsafe { &mut *ctx };
            eprintln!("-- opcode = ld: pc={}", ctx.pc);
        }

        extern "C" fn ld_imm_value(imm: u64) {
            eprintln!("-- ld_imm_value: value={imm}");
        }

        extern "C" fn ld_rs1_value(rs1: u64) {
            eprintln!("-- ld_rs1_value={rs1}");
        }

        self.backend.call_extern_fn(ld);
        self.inspect_immediate(imm, ld_imm_value);
        self.inspect_register(rs1, ld_rs1_value);
        self.backend.ld(rd, rs1, imm);
    }

    fn lwu(&mut self, rd: RiscRegister, rs1: RiscRegister, imm: u64) {
        extern "C" fn lwu(ctx: *mut JitContext) {
            let ctx = unsafe { &mut *ctx };
            eprintln!("-- opcode = lwu: pc={}", ctx.pc);
        }

        self.backend.call_extern_fn(lwu);
        self.backend.lwu(rd, rs1, imm);
    }

    fn sb(&mut self, rs1: RiscRegister, rs2: RiscRegister, imm: u64) {
        extern "C" fn sb(ctx: *mut JitContext) {
            let ctx = unsafe { &mut *ctx };
            eprintln!("-- opcode = sb: pc={}", ctx.pc);
        }

        self.backend.call_extern_fn(sb);
        self.backend.sb(rs1, rs2, imm);
    }

    fn sh(&mut self, rs1: RiscRegister, rs2: RiscRegister, imm: u64) {
        extern "C" fn sh(ctx: *mut JitContext) {
            let ctx = unsafe { &mut *ctx };
            eprintln!("-- opcode = sh: pc={}", ctx.pc);
        }

        self.backend.call_extern_fn(sh);
        self.backend.sh(rs1, rs2, imm);
    }

    fn sw(&mut self, rs1: RiscRegister, rs2: RiscRegister, imm: u64) {
        extern "C" fn sw(ctx: *mut JitContext) {
            let ctx = unsafe { &mut *ctx };
            eprintln!("-- opcode = sw: pc={}", ctx.pc);
        }

        extern "C" fn sw_imm_value(imm: u64) {
            eprintln!("-- sw_imm_value: value={imm}");
        }

        extern "C" fn sw_rs1_value(rs1: u64) {
            eprintln!("-- sw_rs1_value={rs1}");
        }

        extern "C" fn sw_rs2_value(rs2: u64) {
            eprintln!("-- sw_rs2_value={rs2}");
        }

        extern "C" fn sw_rs2(rs2: u64) {
            eprintln!("-- sw_rs2={rs2}");
        }

        extern "C" fn sw_rs1(rs1: u64) {
            eprintln!("-- sw_rs1={rs1}");
        }

        self.inspect_immediate(rs1 as u8 as u64, sw_rs1);
        self.inspect_immediate(rs2 as u8 as u64, sw_rs2);
        self.inspect_immediate(imm, sw_imm_value);

        self.inspect_register(rs1, sw_rs1_value);
        self.inspect_register(rs2, sw_rs2_value);

        self.backend.call_extern_fn(sw);
        self.backend.sw(rs1, rs2, imm);
    }

    fn sd(&mut self, rs1: RiscRegister, rs2: RiscRegister, imm: u64) {
        extern "C" fn sd(ctx: *mut JitContext) {
            let ctx = unsafe { &mut *ctx };
            eprintln!("-- opcode = sd: pc={}", ctx.pc);
        }

        extern "C" fn sd_imm_value(imm: u64) {
            eprintln!("-- sd_imm_value: value={imm}");
        }

        extern "C" fn sd_rs1_value(rs1: u64) {
            eprintln!("-- sd_rs1_value={rs1}");
        }

        extern "C" fn sd_rs2_value(rs2: u64) {
            eprintln!("-- sd_rs2_value={rs2}");
        }

        self.backend.call_extern_fn(sd);
        self.inspect_immediate(imm, sd_imm_value);
        self.inspect_register(rs1, sd_rs1_value);
        self.inspect_register(rs2, sd_rs2_value);
        self.backend.sd(rs1, rs2, imm);
    }
}

impl<B: RiscvTranspiler> ControlFlowInstructions for DebugBackend<B> {
    fn beq(&mut self, rs1: RiscRegister, rs2: RiscRegister, imm: u64) {
        extern "C" fn beq(ctx: *mut JitContext) {
            let ctx = unsafe { &mut *ctx };
            eprintln!("-- opcode = beq: pc={}", ctx.pc);
        }

        self.backend.call_extern_fn(beq);
        self.backend.beq(rs1, rs2, imm);
    }

    fn bne(&mut self, rs1: RiscRegister, rs2: RiscRegister, imm: u64) {
        extern "C" fn bne(ctx: *mut JitContext) {
            let ctx = unsafe { &mut *ctx };
            eprintln!("-- opcode = bne: pc={}", ctx.pc);
        }

        self.backend.call_extern_fn(bne);
        self.backend.bne(rs1, rs2, imm);
    }

    fn blt(&mut self, rs1: RiscRegister, rs2: RiscRegister, imm: u64) {
        extern "C" fn blt(ctx: *mut JitContext) {
            let ctx = unsafe { &mut *ctx };
            eprintln!("-- opcode = blt: pc={}", ctx.pc);
        }

        self.backend.call_extern_fn(blt);
        self.backend.blt(rs1, rs2, imm);
    }

    fn bge(&mut self, rs1: RiscRegister, rs2: RiscRegister, imm: u64) {
        extern "C" fn bge(ctx: *mut JitContext) {
            let ctx = unsafe { &mut *ctx };
            eprintln!("-- opcode = bge: pc={}", ctx.pc);
        }

        self.backend.call_extern_fn(bge);
        self.backend.bge(rs1, rs2, imm);
    }

    fn bltu(&mut self, rs1: RiscRegister, rs2: RiscRegister, imm: u64) {
        extern "C" fn bltu(ctx: *mut JitContext) {
            let ctx = unsafe { &mut *ctx };
            eprintln!("-- opcode = bltu: pc={}", ctx.pc);
        }

        self.backend.call_extern_fn(bltu);
        self.backend.bltu(rs1, rs2, imm);
    }

    fn bgeu(&mut self, rs1: RiscRegister, rs2: RiscRegister, imm: u64) {
        extern "C" fn bgeu(ctx: *mut JitContext) {
            let ctx = unsafe { &mut *ctx };
            eprintln!("-- opcode = bgeu: pc={}", ctx.pc);
        }

        self.backend.call_extern_fn(bgeu);
        self.backend.bgeu(rs1, rs2, imm);
    }

    fn jal(&mut self, rd: RiscRegister, imm: u64) {
        extern "C" fn jal(ctx: *mut JitContext) {
            let ctx = unsafe { &mut *ctx };
            eprintln!("-- opcode = jal: pc={}", ctx.pc);
        }

        self.backend.call_extern_fn(jal);
        self.backend.jal(rd, imm);
    }

    fn jalr(&mut self, rd: RiscRegister, rs1: RiscRegister, imm: u64) {
        extern "C" fn jalr(ctx: *mut JitContext) {
            let ctx = unsafe { &mut *ctx };
            eprintln!("-- opcode = jalr: pc={}", ctx.pc);
        }

        self.backend.call_extern_fn(jalr);

        extern "C" fn jalr_reg_value(register: u64) {
            eprintln!("-- jalr_reg_value: value={register}");
        }

        extern "C" fn jalr_imm_value(imm: u64) {
            eprintln!("-- jalr_imm_value: value={imm}");
        }

        extern "C" fn jalr_rs1(rs1: u64) {
            eprintln!("-- jalr_rs1={rs1}");
        }

        extern "C" fn jalr_rd(rd: u64) {
            eprintln!("-- jalr_rd={rd}");
        }

        self.inspect_immediate(rd as u8 as u64, jalr_rd);
        self.backend.inspect_register(rs1, jalr_reg_value);
        self.backend.inspect_immediate(rs1 as u8 as u64, jalr_rs1);
        self.backend.inspect_immediate(imm, jalr_imm_value);

        self.backend.jalr(rd, rs1, imm);
    }
}

impl<B: RiscvTranspiler> SystemInstructions for DebugBackend<B> {
    fn ecall(&mut self) {
        extern "C" fn ecall(ctx: *mut JitContext) {
            let ctx = unsafe { &mut *ctx };
            eprintln!("ecall at pc: {}", ctx.pc);
        }

        self.backend.call_extern_fn(ecall);
        self.backend.ecall();
    }

    fn unimp(&mut self) {
        extern "C" fn unimp(ctx: *mut JitContext) {
            let ctx = unsafe { &mut *ctx };
            eprintln!("Unimplemented instruction at pc: {}", ctx.pc);
            // Trap via SIGILL so the parent maps this to a typed `Unimplemented`
            // cause instead of a generic abort.
            unsafe { libc::raise(libc::SIGILL) };
        }

        self.backend.call_extern_fn(unimp);
    }
}

impl<B: RiscvTranspiler> ComputeInstructions for DebugBackend<B> {
    fn add(&mut self, rd: RiscRegister, rs1: RiscOperand, rs2: RiscOperand) {
        extern "C" fn add(ctx: *mut JitContext) {
            let ctx = unsafe { &mut *ctx };
            eprintln!("-- opcode = add: pc={}", ctx.pc);
        }

        self.backend.call_extern_fn(add);
        self.backend.add(rd, rs1, rs2);
    }

    fn sub(&mut self, rd: RiscRegister, rs1: RiscOperand, rs2: RiscOperand) {
        extern "C" fn sub(ctx: *mut JitContext) {
            let ctx = unsafe { &mut *ctx };
            eprintln!("-- opcode = sub: pc={}", ctx.pc);
        }

        self.backend.call_extern_fn(sub);
        self.backend.sub(rd, rs1, rs2);
    }

    fn xor(&mut self, rd: RiscRegister, rs1: RiscOperand, rs2: RiscOperand) {
        extern "C" fn xor(ctx: *mut JitContext) {
            let ctx = unsafe { &mut *ctx };
            eprintln!("-- opcode = xor: pc={}", ctx.pc);
        }

        self.backend.call_extern_fn(xor);
        self.backend.xor(rd, rs1, rs2);
    }

    fn or(&mut self, rd: RiscRegister, rs1: RiscOperand, rs2: RiscOperand) {
        extern "C" fn or(ctx: *mut JitContext) {
            let ctx = unsafe { &mut *ctx };
            eprintln!("-- opcode = or: pc={}", ctx.pc);
        }

        self.backend.call_extern_fn(or);
        self.backend.or(rd, rs1, rs2);
    }

    fn and(&mut self, rd: RiscRegister, rs1: RiscOperand, rs2: RiscOperand) {
        extern "C" fn and(ctx: *mut JitContext) {
            let ctx = unsafe { &mut *ctx };
            eprintln!("-- opcode = and: pc={}", ctx.pc);
        }

        self.backend.call_extern_fn(and);
        self.backend.and(rd, rs1, rs2);
    }

    fn sll(&mut self, rd: RiscRegister, rs1: RiscOperand, rs2: RiscOperand) {
        extern "C" fn sll(ctx: *mut JitContext) {
            let ctx = unsafe { &mut *ctx };
            eprintln!("-- opcode = sll: pc={}", ctx.pc);
        }

        self.backend.call_extern_fn(sll);
        self.backend.sll(rd, rs1, rs2);
    }

    fn srl(&mut self, rd: RiscRegister, rs1: RiscOperand, rs2: RiscOperand) {
        extern "C" fn srl(ctx: *mut JitContext) {
            let ctx = unsafe { &mut *ctx };
            eprintln!("-- opcode = srl: pc={}", ctx.pc);
        }

        self.backend.call_extern_fn(srl);
        self.backend.srl(rd, rs1, rs2);
    }

    fn sra(&mut self, rd: RiscRegister, rs1: RiscOperand, rs2: RiscOperand) {
        extern "C" fn sra(ctx: *mut JitContext) {
            let ctx = unsafe { &mut *ctx };
            eprintln!("-- opcode = sra: pc={}", ctx.pc);
        }

        self.backend.call_extern_fn(sra);
        self.backend.sra(rd, rs1, rs2);
    }

    fn slt(&mut self, rd: RiscRegister, rs1: RiscOperand, rs2: RiscOperand) {
        extern "C" fn slt(ctx: *mut JitContext) {
            let ctx = unsafe { &mut *ctx };
            eprintln!("-- opcode = slt: pc={}", ctx.pc);
        }

        self.backend.call_extern_fn(slt);
        self.backend.slt(rd, rs1, rs2);
    }

    fn sltu(&mut self, rd: RiscRegister, rs1: RiscOperand, rs2: RiscOperand) {
        extern "C" fn sltu(ctx: *mut JitContext) {
            let ctx = unsafe { &mut *ctx };
            eprintln!("-- opcode = sltu: pc={}", ctx.pc);
        }

        self.backend.call_extern_fn(sltu);
        self.backend.sltu(rd, rs1, rs2);
    }

    fn mul(&mut self, rd: RiscRegister, rs1: RiscOperand, rs2: RiscOperand) {
        extern "C" fn mul(ctx: *mut JitContext) {
            let ctx = unsafe { &mut *ctx };
            eprintln!("-- opcode = mul: pc={}", ctx.pc);
        }

        self.backend.call_extern_fn(mul);
        self.backend.mul(rd, rs1, rs2);
    }

    fn mulh(&mut self, rd: RiscRegister, rs1: RiscOperand, rs2: RiscOperand) {
        extern "C" fn mulh(ctx: *mut JitContext) {
            let ctx = unsafe { &mut *ctx };
            eprintln!("-- opcode = mulh: pc={}", ctx.pc);
        }

        self.backend.call_extern_fn(mulh);
        self.backend.mulh(rd, rs1, rs2);
    }

    fn mulhu(&mut self, rd: RiscRegister, rs1: RiscOperand, rs2: RiscOperand) {
        extern "C" fn mulhu(ctx: *mut JitContext) {
            let ctx = unsafe { &mut *ctx };
            eprintln!("-- opcode = mulhu: pc={}", ctx.pc);
        }

        self.backend.call_extern_fn(mulhu);
        self.backend.mulhu(rd, rs1, rs2);
    }

    fn mulhsu(&mut self, rd: RiscRegister, rs1: RiscOperand, rs2: RiscOperand) {
        extern "C" fn mulhsu(ctx: *mut JitContext) {
            let ctx = unsafe { &mut *ctx };
            eprintln!("-- opcode = mulhsu: pc={}", ctx.pc);
        }

        self.backend.call_extern_fn(mulhsu);
        self.backend.mulhsu(rd, rs1, rs2);
    }

    fn div(&mut self, rd: RiscRegister, rs1: RiscOperand, rs2: RiscOperand) {
        extern "C" fn div(ctx: *mut JitContext) {
            let ctx = unsafe { &mut *ctx };
            eprintln!("-- opcode = div: pc={}", ctx.pc);
        }

        self.backend.call_extern_fn(div);
        self.backend.div(rd, rs1, rs2);
    }

    fn divu(&mut self, rd: RiscRegister, rs1: RiscOperand, rs2: RiscOperand) {
        extern "C" fn divu(ctx: *mut JitContext) {
            let ctx = unsafe { &mut *ctx };
            eprintln!("-- opcode = divu: pc={}", ctx.pc);
        }

        self.backend.call_extern_fn(divu);
        self.backend.divu(rd, rs1, rs2);
    }

    fn rem(&mut self, rd: RiscRegister, rs1: RiscOperand, rs2: RiscOperand) {
        extern "C" fn rem(ctx: *mut JitContext) {
            let ctx = unsafe { &mut *ctx };
            eprintln!("-- opcode = rem: pc={}", ctx.pc);
        }

        self.backend.call_extern_fn(rem);
        self.backend.rem(rd, rs1, rs2);
    }

    fn remu(&mut self, rd: RiscRegister, rs1: RiscOperand, rs2: RiscOperand) {
        extern "C" fn remu(ctx: *mut JitContext) {
            let ctx = unsafe { &mut *ctx };
            eprintln!("-- opcode = remu: pc={}", ctx.pc);
        }

        self.backend.call_extern_fn(remu);
        self.backend.remu(rd, rs1, rs2);
    }

    fn addw(&mut self, rd: RiscRegister, rs1: RiscOperand, rs2: RiscOperand) {
        extern "C" fn addw(ctx: *mut JitContext) {
            let ctx = unsafe { &mut *ctx };
            eprintln!("-- opcode = addw: pc={}", ctx.pc);
        }

        self.backend.call_extern_fn(addw);
        self.backend.addw(rd, rs1, rs2);
    }

    fn subw(&mut self, rd: RiscRegister, rs1: RiscOperand, rs2: RiscOperand) {
        extern "C" fn subw(ctx: *mut JitContext) {
            let ctx = unsafe { &mut *ctx };
            eprintln!("-- opcode = subw: pc={}", ctx.pc);
        }

        self.backend.call_extern_fn(subw);
        self.backend.subw(rd, rs1, rs2);
    }

    fn sllw(&mut self, rd: RiscRegister, rs1: RiscOperand, rs2: RiscOperand) {
        extern "C" fn sllw(ctx: *mut JitContext) {
            let ctx = unsafe { &mut *ctx };
            eprintln!("-- opcode = sllw: pc={}", ctx.pc);
        }

        self.backend.call_extern_fn(sllw);
        self.backend.sllw(rd, rs1, rs2);
    }

    fn srlw(&mut self, rd: RiscRegister, rs1: RiscOperand, rs2: RiscOperand) {
        extern "C" fn srlw(ctx: *mut JitContext) {
            let ctx = unsafe { &mut *ctx };
            eprintln!("-- opcode = srlw: pc={}", ctx.pc);
        }

        self.backend.call_extern_fn(srlw);
        self.backend.srlw(rd, rs1, rs2);
    }

    fn sraw(&mut self, rd: RiscRegister, rs1: RiscOperand, rs2: RiscOperand) {
        extern "C" fn sraw(ctx: *mut JitContext) {
            let ctx = unsafe { &mut *ctx };
            eprintln!("-- opcode = sraw: pc={}", ctx.pc);
        }

        self.backend.call_extern_fn(sraw);
        self.backend.sraw(rd, rs1, rs2);
    }

    fn mulw(&mut self, rd: RiscRegister, rs1: RiscOperand, rs2: RiscOperand) {
        extern "C" fn mulw(ctx: *mut JitContext) {
            let ctx = unsafe { &mut *ctx };
            eprintln!("-- opcode = mulw: pc={}", ctx.pc);
        }

        self.backend.call_extern_fn(mulw);
        self.backend.mulw(rd, rs1, rs2);
    }

    fn divw(&mut self, rd: RiscRegister, rs1: RiscOperand, rs2: RiscOperand) {
        extern "C" fn divw(ctx: *mut JitContext) {
            let ctx = unsafe { &mut *ctx };
            eprintln!("-- opcode = divw: pc={}", ctx.pc);
        }

        self.backend.call_extern_fn(divw);
        self.backend.divw(rd, rs1, rs2);
    }

    fn divuw(&mut self, rd: RiscRegister, rs1: RiscOperand, rs2: RiscOperand) {
        extern "C" fn divuw(ctx: *mut JitContext) {
            let ctx = unsafe { &mut *ctx };
            eprintln!("-- opcode = divuw: pc={}", ctx.pc);
        }

        self.backend.call_extern_fn(divuw);
        self.backend.divuw(rd, rs1, rs2);
    }

    fn remw(&mut self, rd: RiscRegister, rs1: RiscOperand, rs2: RiscOperand) {
        extern "C" fn remw(ctx: *mut JitContext) {
            let ctx = unsafe { &mut *ctx };
            eprintln!("-- opcode = remw: pc={}", ctx.pc);
        }

        self.backend.call_extern_fn(remw);
        self.backend.remw(rd, rs1, rs2);
    }

    fn remuw(&mut self, rd: RiscRegister, rs1: RiscOperand, rs2: RiscOperand) {
        extern "C" fn remuw(ctx: *mut JitContext) {
            let ctx = unsafe { &mut *ctx };
            eprintln!("-- opcode = remuw: pc={}", ctx.pc);
        }

        self.backend.call_extern_fn(remuw);
        self.backend.remuw(rd, rs1, rs2);
    }

    fn auipc(&mut self, rd: RiscRegister, imm: u64) {
        extern "C" fn auipc(ctx: *mut JitContext) {
            let ctx = unsafe { &mut *ctx };
            eprintln!("-- opcode = auipc: pc={}", ctx.pc);
        }

        extern "C" fn auipc_rd(rd: u64) {
            eprintln!("-- auipc_rd={rd}");
        }

        extern "C" fn auipc_imm(imm: u64) {
            eprintln!("-- auipc_imm={imm}");
        }

        self.inspect_immediate(rd as u8 as u64, auipc_rd);
        self.inspect_immediate(imm, auipc_imm);

        self.backend.call_extern_fn(auipc);
        self.backend.auipc(rd, imm);
    }

    fn lui(&mut self, rd: RiscRegister, imm: u64) {
        extern "C" fn lui(ctx: *mut JitContext) {
            let ctx = unsafe { &mut *ctx };
            eprintln!("-- opcode = lui: pc={}", ctx.pc);
        }

        extern "C" fn lui_rd(rd: u64) {
            eprintln!("-- lui_rd={rd}");
        }

        extern "C" fn lui_imm(imm: u64) {
            eprintln!("-- lui_imm={imm}");
        }

        self.inspect_immediate(rd as u8 as u64, lui_rd);
        self.inspect_immediate(imm, lui_imm);

        self.backend.call_extern_fn(lui);
        self.backend.lui(rd, imm);
    }
}

impl<B: RiscvTranspiler> TraceCollector for DebugBackend<B> {
    fn trace_clk_end(&mut self) {
        self.backend.trace_clk_end();
    }

    fn trace_clk_start(&mut self) {
        self.backend.trace_clk_start();
    }

    fn trace_mem_value(&mut self, rs1: RiscRegister, imm: u64) {
        self.backend.trace_mem_value(rs1, imm);
    }

    fn trace_pc_start(&mut self) {
        self.backend.trace_pc_start();
    }

    fn trace_registers(&mut self) {
        self.backend.trace_registers();
    }
}
