use std::fmt::{self, Display};

use yggdrasil_abi::arch::SavedFrame;

use crate::debugger::SymbolResolver;

use super::{Error, InstructionFormatter, Target};

pub struct TargetImpl;
pub struct Unsupported;

pub struct FlagFormat(u64);

impl Display for FlagFormat {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        const FLAGS: &[(u64, &str)] = &[(27, "Q"), (28, "V"), (29, "C"), (30, "Z"), (31, "N")];
        write!(f, " {: <#8x} [ ", self.0)?;
        for (bit, flag) in FLAGS {
            if self.0 & (1 << bit) != 0 {
                write!(f, "{} ", flag)?;
            }
        }
        write!(f, "]")
    }
}

impl InstructionFormatter<()> for Unsupported {
    fn format_instruction(&mut self, _insn: &(), _out: &mut String) {}
}

impl Target for TargetImpl {
    type Instruction = ();
    type InstructionFormatter = Unsupported;
    type Register = usize;

    fn disassemble(
        _window: &[u8],
        _ip: usize,
        _limit: usize,
    ) -> Result<Vec<(usize, Self::Instruction)>, Error> {
        Ok(vec![])
    }
    fn new_instruction_formatter(_resolver: SymbolResolver) -> Self::InstructionFormatter {
        Unsupported
    }

    fn flags_register_as_display(frame: &SavedFrame) -> impl Display {
        FlagFormat(frame.spsr_el1)
    }
    fn register_list(frame: &SavedFrame, out: &mut Vec<(String, Self::Register)>) {
        for i in 0..10 {
            out.push((format!("x{}", i), frame.gp_regs[i]));
        }
        out.push(("pc".into(), frame.elr_el1 as _));
        out.push(("lr".into(), frame.gp_regs[30]));
    }
    fn real_ip(frame: &SavedFrame) -> usize {
        frame.elr_el1 as _
    }
}
