//! Тесты на `emit.rs`.
use rasm_core::codegen::emit::Emitter;

fn new_emit() -> Emitter {
    Emitter::new()
}

#[test]
fn emit_new_is_empty() {
    let e = new_emit();
    assert!(e.buf.is_empty());
    assert_eq!(e.offset(), 0);
}

#[test]
fn emit_with_capacity() {
    let e = Emitter::with_capacity(64);
    assert!(e.buf.is_empty());
    assert!(e.buf.capacity() >= 64);
}

#[test]
fn emit_default() {
    let e: Emitter = Default::default();
    assert!(e.buf.is_empty());
}

#[test]
fn emit_u8() {
    let mut e = new_emit();
    e.emit_u8(0x48);
    assert_eq!(e.buf, &[0x48]);
    assert_eq!(e.offset(), 1);
}

#[test]
fn emit_u8_multiple() {
    let mut e = new_emit();
    e.emit_u8(0x48);
    e.emit_u8(0xB8);
    e.emit_u8(0x01);
    assert_eq!(e.buf, &[0x48, 0xB8, 0x01]);
}

#[test]
fn emit_i8_positive() {
    let mut e = new_emit();
    e.emit_i8(42);
    assert_eq!(e.buf, &[42]);
}

#[test]
fn emit_i8_negative() {
    let mut e = new_emit();
    e.emit_i8(-1);
    assert_eq!(e.buf, &[0xFF]);
}

#[test]
fn emit_i8_zero() {
    let mut e = new_emit();
    e.emit_i8(0);
    assert_eq!(e.buf, &[0]);
}

#[test]
fn emit_u16_zero() {
    let mut e = new_emit();
    e.emit_u16(0);
    assert_eq!(e.buf, &[0, 0]);
}

#[test]
fn emit_u16_max() {
    let mut e = new_emit();
    e.emit_u16(0xFFFF);
    assert_eq!(e.buf, &[0xFF, 0xFF]);
}

#[test]
fn emit_u16_le() {
    let mut e = new_emit();
    e.emit_u16(0x1234);
    assert_eq!(e.buf, &[0x34, 0x12]);
}

#[test]
fn emit_u32_zero() {
    let mut e = new_emit();
    e.emit_u32(0);
    assert_eq!(e.buf, &[0, 0, 0, 0]);
}

#[test]
fn emit_u32_le() {
    let mut e = new_emit();
    e.emit_u32(0xDEADBEEF);
    assert_eq!(e.buf, &[0xEF, 0xBE, 0xAD, 0xDE]);
}

#[test]
fn emit_u32_after_u8() {
    let mut e = new_emit();
    e.emit_u8(0x90);
    e.emit_u32(0x12345678);
    assert_eq!(e.buf.len(), 5);
    assert_eq!(e.buf[0], 0x90);
}

#[test]
fn emit_i32_zero() {
    let mut e = new_emit();
    e.emit_i32(0);
    assert_eq!(e.buf, &[0; 4]);
}

#[test]
fn emit_i32_positive() {
    let mut e = new_emit();
    e.emit_i32(42);
    assert_eq!(e.buf, &[42, 0, 0, 0]);
}

#[test]
fn emit_i32_negative() {
    let mut e = new_emit();
    e.emit_i32(-1);
    assert_eq!(e.buf, &[0xFF, 0xFF, 0xFF, 0xFF]);
}

#[test]
fn emit_u64_zero() {
    let mut e = new_emit();
    e.emit_u64(0);
    assert_eq!(e.buf, &[0; 8]);
}

#[test]
fn emit_u64_le() {
    let mut e = new_emit();
    e.emit_u64(0x0102030405060708);
    assert_eq!(e.buf, &[0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01]);
}

#[test]
fn emit_u64_large() {
    let mut e = new_emit();
    e.emit_u64(u64::MAX);
    assert_eq!(e.buf, &[0xFF; 8]);
}

#[test]
fn emit_i64_zero() {
    let mut e = new_emit();
    e.emit_i64(0);
    assert_eq!(e.buf, &[0; 8]);
}

#[test]
fn emit_i64_positive() {
    let mut e = new_emit();
    e.emit_i64(0x12345678);
    let expected = 0x12345678i64.to_le_bytes();
    assert_eq!(e.buf, &expected);
}

#[test]
fn emit_i64_negative() {
    let mut e = new_emit();
    e.emit_i64(-1);
    assert_eq!(e.buf, &[0xFF; 8]);
}

#[test]
fn emit_bytes_empty() {
    let mut e = new_emit();
    e.emit_bytes(&[]);
    assert!(e.buf.is_empty());
}

#[test]
fn emit_bytes_single() {
    let mut e = new_emit();
    e.emit_bytes(&[0xCC]);
    assert_eq!(e.buf, &[0xCC]);
}

#[test]
fn emit_bytes_multiple() {
    let mut e = new_emit();
    e.emit_bytes(&[0x48, 0x89, 0xD8]);
    assert_eq!(e.buf, &[0x48, 0x89, 0xD8]);
}

#[test]
fn emit_bytes_chain() {
    let mut e = new_emit();
    e.emit_u8(0x90);
    e.emit_bytes(&[0x48, 0xB8]);
    e.emit_u64(0x2A);
    assert_eq!(e.buf.len(), 11);
}

#[test]
fn emit_rex_w() {
    let mut e = new_emit();
    e.emit_rex_w();
    assert_eq!(e.buf, &[0x48]);
}

#[test]
fn emit_rex_w_r_b() {
    let mut e = new_emit();
    e.emit_rex(true, true, false, true);
    // 0x40 | W=1<<3 | R=1<<2 | B=1 = 0x40 | 8 | 4 | 1 = 0x4D
    assert_eq!(e.buf, &[0x4D]);
}

#[test]
fn emit_rex_no_bits() {
    let mut e = new_emit();
    e.emit_rex(false, false, false, false);
    assert_eq!(e.buf, &[0x40]);
}

#[test]
fn emit_rex_all_bits() {
    let mut e = new_emit();
    e.emit_rex(true, true, true, true);
    // 0x40 | 8 | 4 | 2 | 1 = 0x4F
    assert_eq!(e.buf, &[0x4F]);
}

#[test]
fn emit_rex_only_x() {
    let mut e = new_emit();
    e.emit_rex(false, false, true, false);
    assert_eq!(e.buf, &[0x42]);
}

#[test]
fn emit_modrm_reg_rm() {
    let mut e = new_emit();
    e.emit_modrm(0b11, 0, 0);
    assert_eq!(e.buf, &[0xC0]);
}

#[test]
fn emit_modrm_memory() {
    let mut e = new_emit();
    e.emit_modrm(0b00, 0, 5);
    assert_eq!(e.buf, &[0x05]);
}

#[test]
fn emit_modrm_disp8() {
    let mut e = new_emit();
    e.emit_modrm(0b01, 3, 1);
    assert_eq!(e.buf, &[0x59]);
}

#[test]
fn emit_modrm_disp32() {
    let mut e = new_emit();
    e.emit_modrm(0b10, 7, 7);
    assert_eq!(e.buf, &[0xBF]);
}

#[test]
fn emit_sib_no_index() {
    let mut e = new_emit();
    e.emit_sib(0, 4, 0);
    assert_eq!(e.buf, &[0x20]);
}

#[test]
fn emit_sib_scale_1() {
    let mut e = new_emit();
    e.emit_sib(0, 3, 5);
    assert_eq!(e.buf, &[0x1D]);
}

#[test]
fn emit_sib_scale_2() {
    let mut e = new_emit();
    e.emit_sib(1, 0, 0);
    assert_eq!(e.buf, &[0x40]);
}

#[test]
fn emit_sib_scale_4() {
    let mut e = new_emit();
    e.emit_sib(2, 1, 2);
    assert_eq!(e.buf, &[0x8A]);
}

#[test]
fn emit_sib_scale_8() {
    let mut e = new_emit();
    e.emit_sib(3, 7, 7);
    assert_eq!(e.buf, &[0xFF]);
}

#[test]
fn patch_i32_at_beginning() {
    let mut e = new_emit();
    e.emit_bytes(&[0x00, 0x00, 0x00, 0x00]);
    e.patch_i32(0, 42);
    assert_eq!(e.buf, &[42, 0, 0, 0]);
}

#[test]
fn patch_i32_at_offset() {
    let mut e = new_emit();
    e.emit_bytes(&[0x90, 0x90, 0x00, 0x00, 0x00, 0x00, 0x90]);
    e.patch_i32(2, -1);
    assert_eq!(&e.buf[2..6], &[0xFF, 0xFF, 0xFF, 0xFF]);
}

#[test]
fn patch_u32_max() {
    let mut e = new_emit();
    e.emit_bytes(&[0; 4]);
    e.patch_u32(0, 0xDEADBEEF);
    assert_eq!(&e.buf, &[0xEF, 0xBE, 0xAD, 0xDE]);
}

#[test]
fn emit_offset_grows() {
    let mut e = new_emit();
    assert_eq!(e.offset(), 0);
    e.emit_u8(0x48);
    assert_eq!(e.offset(), 1);
    e.emit_u32(0x12345678);
    assert_eq!(e.offset(), 5);
    e.emit_i64(-1);
    assert_eq!(e.offset(), 13);
}

#[test]
fn emit_debug() {
    let e = new_emit();
    let d = format!("{e:?}");
    assert!(!d.is_empty());
}
