//! Architecture-facing interfaces.

pub mod arm;

use crate::{flow::FlowInfo, ir::Block};

/// Flat generic register metadata.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct RegisterDesc<R> {
    /// Architecture-defined register identity.
    pub reg: R,

    /// Human-readable register name.
    pub name: &'static str,

    /// Register width in bits.
    pub bits: u16,
}

/// Pure ISA protocol: decoding, flow analysis, and disassembly.
pub trait Arch {
    /// Architecture execution mode.
    type Mode: Copy + Eq;

    /// Decoded instruction type.
    type Inst;

    /// Architecture-specific register identity.
    type Reg: Copy + Eq;

    /// Decode-time error.
    type DecodeError;

    /// Stable architecture name.
    fn name(&self) -> &'static str;

    /// Pointer width in bytes for the given mode.
    fn address_size(&self, mode: Self::Mode) -> u8;

    /// Maximum instruction length in bytes.
    fn max_instruction_len(&self) -> u8;

    /// Architectural register set.
    fn registers(&self) -> &'static [RegisterDesc<Self::Reg>];

    /// Stack pointer register, if the architecture has one.
    fn stack_pointer(&self) -> Option<Self::Reg>;

    /// Decode one instruction from `bytes`.
    fn decode(
        &self,
        bytes: &[u8],
        mode: Self::Mode,
    ) -> Result<(usize, Self::Inst), Self::DecodeError>;

    /// Return instruction-level control-flow facts for one decoded instruction.
    fn flow_info(&self, inst: &Self::Inst, pc: u64, mode: Self::Mode) -> FlowInfo;

    /// Render one decoded instruction as a disassembly string.
    fn disasm(&self, inst: &Self::Inst, pc: u64, mode: Self::Mode) -> String;
}

/// Translation-session knowledge available during lifting.
pub trait LiftEnv {
    /// Returns the IR block associated with a statically known target address.
    fn block_for_target(&self, addr: u64) -> Option<Block>;

    /// Returns the fallthrough block for the current instruction, if one exists.
    fn fallthrough_block(&self) -> Option<Block>;
}

/// Extension trait for architectures that can lift into Slonik IR.
pub trait LiftArch: Arch {
    /// Lift-time error.
    type LiftError;

    /// Per-lift mutable context.
    type LiftCtx<'ir, 'fb>
    where
        Self: 'ir,
        'ir: 'fb;

    /// Lift one decoded instruction into Slonik IR.
    fn lift(
        &self,
        cx: &mut Self::LiftCtx<'_, '_>,
        inst: &Self::Inst,
    ) -> Result<(), Self::LiftError>;
}
