//! Text sink — renders one .asm instruction line with optional
//! branch-target / data-ref annotations.
//!
//! The full `write_asm` orchestration (section headers, function prologue
//! info, xref comment blocks, hex-dump of data sections) stays in
//! [`crate::formatter`]; this sink only owns the per-instruction line.

use std::collections::HashMap;
use std::io::{self, Write};

use xenia_xex::pe::PeSection;

use crate::disasm::RichDisasmItem;
use crate::xref::{XrefKind, section_for_addr};

/// Render one instruction line:
///   `  82000000:  60000000    nop`
///   `  82000004:  4800FFFC    bl 0x82000000   ; -> entry_point`
///   `  82000010:  812A0000    lwz r9, 0(r10)  ; [R] 0x828A0000 (.rdata) = dat_…`
pub fn write_instr_line<W: Write + ?Sized>(
    out: &mut W,
    item: &RichDisasmItem<'_>,
    labels: &HashMap<u32, String>,
    sections: &[PeSection],
    image_base: u32,
    data_annotation: Option<(u32, XrefKind)>,
) -> io::Result<()> {
    let disasm_text = item.item.text.display();

    // Branch-target → label annotation. Uses the structured `branch_target`
    // field (cleaner than the legacy "find 0x in disasm string" regex).
    let mut annotated = match item.item.text.branch_target {
        Some(target) => match labels.get(&target) {
            Some(lbl) => format!("{disasm_text:<40} ; -> {lbl}"),
            None => disasm_text.to_string(),
        },
        None => disasm_text.to_string(),
    };

    if let Some((data_addr, kind)) = data_annotation {
        let tag = match kind {
            XrefKind::DataRead  => "[R]",
            XrefKind::DataWrite => "[W]",
            _                   => "[&]",
        };
        let sec = section_for_addr(data_addr, sections, image_base).unwrap_or("?");
        let data_lbl = labels.get(&data_addr)
            .map(|s| format!(" = {s}"))
            .unwrap_or_default();
        if !annotated.contains("; ->") {
            annotated = format!("{annotated:<40} ; {tag} 0x{data_addr:08X} ({sec}){data_lbl}");
        } else {
            annotated = format!("{annotated}  {tag} 0x{data_addr:08X} ({sec}){data_lbl}");
        }
    }

    writeln!(out, "  {:08X}:  {:08X}    {}", item.item.addr, item.item.raw, annotated)
}
