//! End-to-end test against the prebuilt MiSans Regular SDF atlas.
//!
//! The fixture is generated by
//!   `icu bake-font /Users/.../MiSans-Regular.ttf \
//!     --charset "ABC...xyz0..9 .,:;..." --size 32 --bit-depth 4 \
//!     --spread 4 --format sdf -O tests/fixtures/`
//!
//! and committed verbatim, so the test pins the host TTF parser +
//! BSDF + mirx writer chain together with the runtime SDF reader.

use mirui::render::font::sdf::SdfFontProvider;
use mirui::render::font::{FontProvider, GlyphKind};
use mirx::{chunk_type, parse_chunk};

const ASCII_ATLAS_BYTES: &[u8] = include_bytes!("fixtures/misans_regular_ascii_32_4bit.mirx");
const CJK_ATLAS_BYTES: &[u8] = include_bytes!("fixtures/misans_regular_cjk_32_4bit.mirx");

fn open(bytes: &'static [u8]) -> SdfFontProvider {
    let parsed = parse_chunk(bytes).expect("parse mirx chunk file");
    let payload = parsed
        .chunk_payload(bytes, chunk_type::FONT)
        .expect("FONT chunk payload");
    SdfFontProvider::from_mirx_chunk(payload).expect("parse atlas header")
}

fn open_provider() -> SdfFontProvider {
    open(ASCII_ATLAS_BYTES)
}

#[test]
fn fixture_atlas_header_matches_generation_args() {
    let provider = open_provider();
    let h = provider.header();
    assert_eq!(h.source_size, 32);
    assert_eq!(h.bit_depth, 4);
    assert_eq!(h.spread, 4);
    // 32 × 32 × 4-bit ÷ 8 = 512 bytes.
    assert_eq!(h.bytes_per_glyph, 512);
    assert_eq!(h.glyph_count, 72);
}

#[test]
fn fixture_resolves_ascii_letters_and_digits() {
    let provider = open_provider();
    for ch in [
        'A', 'Z', 'a', 'z', '0', '9', ' ', '.', '!', '?', '@', '#', '&',
    ] {
        let g = provider
            .glyph(ch, 16)
            .unwrap_or_else(|| panic!("missing glyph {ch:?}"));
        match g.kind {
            GlyphKind::Sdf {
                source_size,
                bit_depth,
                ..
            } => {
                assert_eq!(source_size, 32);
                assert_eq!(bit_depth, 4);
            }
            other => panic!("expected Sdf for {ch:?}, got {other:?}"),
        }
    }
}

#[test]
fn fixture_misses_codepoint_outside_charset() {
    let provider = open_provider();
    // Chinese char wasn't in the ASCII charset.
    assert!(provider.glyph('中', 16).is_none());
}

#[test]
fn fixture_metric_table_is_codepoint_sorted() {
    let provider = open_provider();
    let metrics = provider.metrics();
    for w in metrics.windows(2) {
        assert!(
            w[0].codepoint < w[1].codepoint,
            "metrics not sorted: {} >= {}",
            w[0].codepoint,
            w[1].codepoint
        );
    }
}

#[test]
fn fixture_capital_a_atlas_has_distance_gradient() {
    // Sanity: the centre of an 'A' atlas should be far from a
    // uniform value — letters have edges, so we expect a spread of
    // quantized distances inside the 512-byte glyph slab.
    let provider = open_provider();
    let g = provider.glyph('A', 16).expect("A");
    let GlyphKind::Sdf { atlas, .. } = g.kind else {
        panic!("expected Sdf");
    };
    assert_eq!(atlas.len(), 512);
    let mut nibbles = [0u32; 16];
    for &b in atlas {
        nibbles[(b & 0x0F) as usize] += 1;
        nibbles[(b >> 4) as usize] += 1;
    }
    let nonzero_buckets = nibbles.iter().filter(|c| **c > 0).count();
    assert!(
        nonzero_buckets >= 4,
        "expected a distance gradient, got buckets {:?}",
        nibbles
    );
}

#[test]
fn cjk_fixture_resolves_common_chinese_glyphs() {
    let provider = open(CJK_ATLAS_BYTES);
    let h = provider.header();
    assert_eq!(h.source_size, 32);
    assert_eq!(h.bit_depth, 4);
    assert_eq!(h.glyph_count, 22);
    for ch in ['我', '你', '是', '不', '中', '人', 'A', '0'] {
        let g = provider
            .glyph(ch, 16)
            .unwrap_or_else(|| panic!("missing glyph {ch:?}"));
        match g.kind {
            GlyphKind::Sdf { atlas, .. } => {
                assert_eq!(atlas.len(), 512);
            }
            other => panic!("expected Sdf for {ch:?}, got {other:?}"),
        }
    }
}

#[test]
fn cjk_fixture_advance_widths_are_nonzero() {
    let provider = open(CJK_ATLAS_BYTES);
    // Real CJK glyphs always have a horizontal advance; an em-wide
    // square is the typical layout.
    for ch in ['我', '中', '人', '是'] {
        let g = provider
            .glyph(ch, 16)
            .unwrap_or_else(|| panic!("missing glyph {ch:?}"));
        assert!(g.advance > 0, "{ch:?} has zero advance");
    }
}
