// rasm-core/src/codegen/emit.rs
//
//! # Низкоуровневые хелперы эмиттера байт
//!
//! Этот модуль отвечает за одну простую задачу: записывать байты в выходной буфер.
//! Все методы кодирования инструкций в `encode.rs` опираются именно на эти примитивы.
//!
//! ## Формат байт-кода x86_64 (краткая шпаргалка)
//!
//! Инструкция x86_64 строится из следующих необязательных полей слева направо:
//!
//! ```text
//! [REX prefix] [Opcode] [ModRM] [SIB] [Displacement] [Immediate]
//! ```
//!
//! - **REX prefix** (`0x40`..`0x4F`): нужен для 64-битных операций и регистров R8–R15.
//!   Биты: `0100 W R X B`, где W=64бит, R=reg, X=index, B=base/rm.
//! - **Opcode**: 1–3 байта кода операции.
//! - **ModRM**: описывает режим адресации и задействованные регистры.
//!   Биты: `mod(2) reg(3) rm(3)`.
//! - **SIB**: масштабирование индексного регистра. Биты: `scale(2) index(3) base(3)`.
//! - **Displacement**: знаковое смещение (1, 2 или 4 байта, little-endian).
//! - **Immediate**: непосредственное значение (1, 2, 4 или 8 байт, little-endian).

/// Буфер эмиттера — тонкая обёртка над `Vec<u8>` с удобными методами записи.
///
/// Все многобайтовые значения записываются в формате **little-endian**,
/// как того требует архитектура x86_64.
#[derive(Debug, Default)]
pub struct Emitter {
    pub buf: Vec<u8>,
}

impl Emitter {
    pub fn new() -> Self {
        Self {
            buf: Vec::with_capacity(256),
        }
    }

    /// Создаёт эмиттер с заранее выделенной ёмкостью (в байтах).
    pub fn with_capacity(cap: usize) -> Self {
        Self {
            buf: Vec::with_capacity(cap),
        }
    }

    /// Текущее смещение (количество байт, записанных в буфер).
    /// Используется для вычисления адресов меток и релокаций.
    #[inline]
    pub fn offset(&self) -> usize {
        self.buf.len()
    }

    /// Записывает один байт.
    #[inline]
    pub fn emit_u8(&mut self, byte: u8) {
        self.buf.push(byte);
    }

    /// Записывает знаковый байт (удобно для smещений типа `i8`).
    #[inline]
    pub fn emit_i8(&mut self, byte: i8) {
        self.buf.push(byte as u8);
    }

    /// Записывает 2 байта в little-endian.
    #[inline]
    pub fn emit_u16(&mut self, val: u16) {
        self.buf.extend_from_slice(&val.to_le_bytes());
    }

    /// Записывает 4 байта в little-endian.
    /// Используется для 32-битных immediate и displacement.
    #[inline]
    pub fn emit_u32(&mut self, val: u32) {
        self.buf.extend_from_slice(&val.to_le_bytes());
    }

    /// Записывает знаковый 32-битный int в little-endian.
    /// Используется для displacement в адресации памяти.
    #[inline]
    pub fn emit_i32(&mut self, val: i32) {
        self.buf.extend_from_slice(&val.to_le_bytes());
    }

    /// Записывает 8 байт в little-endian.
    /// Используется для 64-битных immediate (MOV reg, imm64).
    #[inline]
    pub fn emit_u64(&mut self, val: u64) {
        self.buf.extend_from_slice(&val.to_le_bytes());
    }

    /// Записывает знаковый 64-битный int в little-endian.
    #[inline]
    pub fn emit_i64(&mut self, val: i64) {
        self.buf.extend_from_slice(&val.to_le_bytes());
    }

    /// Записывает слайс байт как есть (например, многобайтовый опкод).
    #[inline]
    pub fn emit_bytes(&mut self, bytes: &[u8]) {
        self.buf.extend_from_slice(bytes);
    }

    // =========================================================================
    // REX prefix
    // =========================================================================

    /// Формирует и записывает REX-префикс.
    ///
    /// Структура: `0100 W R X B`
    /// - `w`: 1 = 64-битная операция
    /// - `r`: расширение поля `reg` в ModRM (регистры R8–R15)
    /// - `x`: расширение поля `index` в SIB
    /// - `b`: расширение поля `rm`/`base` в ModRM или SIB
    #[inline]
    pub fn emit_rex(&mut self, w: bool, r: bool, x: bool, b: bool) {
        let rex = 0x40 | ((w as u8) << 3) | ((r as u8) << 2) | ((x as u8) << 1) | (b as u8);
        self.buf.push(rex);
    }

    /// Записывает REX.W (самый частый случай: 64-битная операция над классическими регистрами).
    #[inline]
    pub fn emit_rex_w(&mut self) {
        self.buf.push(0x48); // REX.W = 0100_1000
    }

    // =========================================================================
    // ModRM и SIB
    // =========================================================================

    /// Формирует байт ModRM.
    ///
    /// - `mod_`: режим адресации (0b00=memory, 0b01=mem+disp8, 0b10=mem+disp32, 0b11=register)
    /// - `reg`: номер регистра-источника/назначения (3 бита, биты 3–5)
    /// - `rm`: номер регистра или код режима памяти (3 бита, биты 0–2)
    #[inline]
    pub fn emit_modrm(&mut self, mod_: u8, reg: u8, rm: u8) {
        self.buf.push((mod_ << 6) | ((reg & 0x7) << 3) | (rm & 0x7));
    }

    /// Формирует байт SIB (Scale-Index-Base).
    ///
    /// - `scale`: 0=×1, 1=×2, 2=×4, 3=×8
    /// - `index`: номер индексного регистра (3 бита)
    /// - `base`: номер базового регистра (3 бита)
    #[inline]
    pub fn emit_sib(&mut self, scale: u8, index: u8, base: u8) {
        self.buf
            .push((scale << 6) | ((index & 0x7) << 3) | (base & 0x7));
    }

    // =========================================================================
    // Патчинг (для релокаций)
    // =========================================================================

    /// Записывает i32 по произвольному смещению в буфере.
    /// Используется для патчинга placeholder-адресов меток после кодирования всего кода.
    pub fn patch_i32(&mut self, offset: usize, val: i32) {
        let bytes = val.to_le_bytes();
        self.buf[offset..offset + 4].copy_from_slice(&bytes);
    }

    /// Записывает u32 по произвольному смещению в буфере.
    pub fn patch_u32(&mut self, offset: usize, val: u32) {
        let bytes = val.to_le_bytes();
        self.buf[offset..offset + 4].copy_from_slice(&bytes);
    }
}
