// rasm-core/src/disasm/format.rs
//
//! Превращает AST в код на ассемблере.
use crate::isa::{GPRs, Instruction, MemoryAddress, MnemonicKind, Operand, Registers, Scale};

/// Форматирует одну инструкцию в строку ассемблера.
pub fn format_instruction(instr: &Instruction) -> String {
    let mnemonic = format_mnemonic(instr.mnemonic);
    let ops: Vec<String> = instr.operands.iter().map(format_operand).collect();
    if ops.is_empty() {
        format!("    {}", mnemonic)
    } else {
        format!("    {} {}", mnemonic, ops.join(", "))
    }
}

fn format_mnemonic(m: MnemonicKind) -> &'static str {
    use MnemonicKind::*;
    match m {
        Mov => "mov",
        Push => "push",
        Pop => "pop",
        Add => "add",
        Sub => "sub",
        And => "and",
        Or => "or",
        Xor => "xor",
        Cmp => "cmp",
        Test => "test",
        Inc => "inc",
        Dec => "dec",
        Neg => "neg",
        Not => "not",
        Mul => "mul",
        Div => "div",
        Idiv => "idiv",
        Imul => "imul",
        Lea => "lea",
        Shl => "shl",
        Shr => "shr",
        Sar => "sar",
        Cqo => "cqo",
        Nop => "nop",
        Ret => "ret",
        Syscall => "syscall",
        Jmp => "jmp",
        Je => "je",
        Jne => "jne",
        Jg => "jg",
        Jge => "jge",
        Jl => "jl",
        Jle => "jle",
        Ja => "ja",
        Jae => "jae",
        Jb => "jb",
        Jbe => "jbe",
        Jo => "jo",
        Jno => "jno",
        Js => "js",
        Jns => "jns",
        Jp => "jp",
        Jnp => "jnp",
        Call => "call",
        Db => ".byte",
    }
}

fn format_operand(op: &Operand) -> String {
    match op {
        Operand::Register(reg) => format_register(reg),
        Operand::Immediate(val) => format_immediate(*val),
        Operand::Memory(mem) => format_memory(mem),
        Operand::Label(id) => format!("lbl_{}", id.0),
    }
}

fn format_register(reg: &Registers) -> String {
    match reg {
        Registers::GPR(gpr) => format_gpr(gpr),
        Registers::XMM(xmm) => {
            use crate::isa::XMMRs::*;
            match xmm {
                Xmm0 => "xmm0",
                Xmm1 => "xmm1",
                Xmm2 => "xmm2",
                Xmm3 => "xmm3",
                Xmm4 => "xmm4",
                Xmm5 => "xmm5",
                Xmm6 => "xmm6",
                Xmm7 => "xmm7",
                Xmm8 => "xmm8",
                Xmm9 => "xmm9",
                Xmm10 => "xmm10",
                Xmm11 => "xmm11",
                Xmm12 => "xmm12",
                Xmm13 => "xmm13",
                Xmm14 => "xmm14",
                Xmm15 => "xmm15",
            }
            .to_string()
        }
        Registers::SR(sr) => {
            use crate::isa::SegmentRegisters::*;
            match sr {
                Fs => "fs",
                Gs => "gs",
                Cs => "cs",
                Ss => "ss",
                Ds => "ds",
                Es => "es",
            }
            .to_string()
        }
        Registers::Rflags => "rflags".to_string(),
        Registers::Rip => "rip".to_string(),
    }
}

fn format_gpr(gpr: &GPRs) -> String {
    use GPRs::*;
    match gpr {
        Rax => "rax",
        Rcx => "rcx",
        Rdx => "rdx",
        Rbx => "rbx",
        Rsp => "rsp",
        Rbp => "rbp",
        Rsi => "rsi",
        Rdi => "rdi",
        R8 => "r8",
        R9 => "r9",
        R10 => "r10",
        R11 => "r11",
        R12 => "r12",
        R13 => "r13",
        R14 => "r14",
        R15 => "r15",
    }
    .to_string()
}

fn format_immediate(val: i64) -> String {
    if val >= 0 {
        format!("0x{:X}", val)
    } else {
        format!("-0x{:X}", -val)
    }
}

fn format_memory(mem: &MemoryAddress) -> String {
    let mut parts: Vec<String> = Vec::new();

    if let Some(base) = &mem.base {
        parts.push(format_register(base));
    }

    if let Some(idx) = &mem.index {
        let idx_str = format_gpr(idx);
        match mem.scale {
            Some(Scale::One) | None => parts.push(idx_str),
            Some(s) => {
                let scale_val = match s {
                    Scale::One => 1,
                    Scale::Two => 2,
                    Scale::Four => 4,
                    Scale::Eight => 8,
                };
                parts.push(format!("{}*{}", idx_str, scale_val));
            }
        }
    }

    if mem.displacement != 0 || (parts.is_empty() && mem.label.is_none()) {
        let disp_abs = format!("0x{:X}", mem.displacement.unsigned_abs());
        if parts.is_empty() {
            if mem.displacement >= 0 {
                parts.push(disp_abs);
            } else {
                parts.push(format!("-{}", disp_abs));
            }
        } else if mem.displacement >= 0 {
            parts.push(format!("+ {}", disp_abs));
        } else {
            parts.push(format!("- {}", disp_abs));
        }
    }

    if let Some(label_id) = &mem.label {
        parts.push(format!("lbl_{}", label_id.0));
    }

    if parts.is_empty() {
        "0x0".to_string()
    } else {
        let inner = parts.join(" ");
        format!("[{}]", inner)
    }
}
