//! CP437-Bitmap-Fonts mit proportionaler (variabler) Laufweite.
//!
//! Alle Atlanten sind nach demselben Schema aufgebaut: 32 Spalten × 8 Zeilen,
//! row-major mit Codepoint = Index (`A` an 65, `a` an 97 → col = c%32,
//! row = c/32). Die Glyphen liegen weiß auf schwarz *ohne* Alpha; wir leiten
//! die Maske beim Laden aus der Luminanz ab (`ui::key_luminance`, geteilt mit
//! Cursor- und Ornament-Sprites).
//!
//! Proportionale Breite: statt jeder Glyph-Zelle ihre volle Breite zu geben,
//! **messen** wir beim Laden aus der Alpha-Maske die Tinten-Spannweite jedes
//! Glyphs (linkeste/rechteste nicht-leere Spalte). Gezeichnet wird tight
//! gecroppt, der Cursor rückt nur um Ink-Breite + Tracking vor; leere Glyphen
//! (Space) bekommen eine feste Breite. So entsteht kein Sonderfall im Shader —
//! nur die UV- und Advance-Rechnung wird pro Glyph variabel.
//!
//! Ein [`Font`] wird zur Laufzeit aus dem geladenen Atlas gebaut und kennt
//! seinen GPU-Textur-Index, damit `ui::Ui::text` ohne Zusatzargumente damit
//! zeichnen kann.

use crate::engine::tga::Image;

const COLS: u32 = 32;

/// Ein geladener Glyph-Atlas: Maße, gemessene Glyph-Breiten und der zugehörige
/// Sprite-Textur-Index.
pub(crate) struct Font {
    pub(crate) glyph_h: f32,
    glyph_w:   f32,
    atlas_w:   f32,
    atlas_h:   f32,
    tex:       usize,
    /// Ink-Startspalte je Codepoint (innerhalb der Zelle, 0..glyph_w).
    left:      [u8; 256],
    /// Ink-Breite je Codepoint (0 = leer/Space).
    width:     [u8; 256],
    tracking:  f32,
    space_adv: f32,
}

/// Die im Projekt geladenen Fonts, gebaut in render::run und an `ui::layout`
/// gereicht.
pub(crate) struct Fonts {
    pub(crate) ega: Font,
    /// Kleinerer Font, für späteres HUD-Kleinzeug bereitgehalten.
    #[allow(dead_code)]
    pub(crate) cga: Font,
}

impl Font {
    /// EGA 8×14 (`assets/textures/fonts/oldschool-ega-8x14.tga`, 256×112).
    pub(crate) fn ega(tex: usize, atlas: &Image) -> Font {
        Font::measure(8.0, 14.0, 256.0, 112.0, tex, atlas)
    }
    /// CGA 8×8 (`assets/textures/fonts/oldschool-cga-8x8.tga`, 256×64).
    pub(crate) fn cga(tex: usize, atlas: &Image) -> Font {
        Font::measure(8.0, 8.0, 256.0, 64.0, tex, atlas)
    }

    /// Glyph-Breiten aus der Alpha-Maske des Atlas messen.
    fn measure(glyph_w: f32, glyph_h: f32, atlas_w: f32, atlas_h: f32, tex: usize, atlas: &Image) -> Font {
        let (gw, gh, aw) = (glyph_w as usize, glyph_h as usize, atlas_w as usize);
        let mut left = [0u8; 256];
        let mut width = [0u8; 256];
        for code in 0..256usize {
            let col = (code as u32 % COLS) as usize;
            let row = (code as u32 / COLS) as usize;
            let (l, w) = ink_bounds(&atlas.rgba, aw, col * gw, row * gh, gw, gh);
            left[code] = l;
            width[code] = w;
        }
        Font {
            glyph_h, glyph_w, atlas_w, atlas_h, tex, left, width,
            // 1px Abstand zwischen Glyphen; Space = halbe Zellbreite.
            tracking: 1.0,
            space_adv: (glyph_w * 0.5).round(),
        }
    }

    pub(crate) fn tex(&self) -> usize { self.tex }

    /// UV-Rechteck + Zeichenbreite (Pixel) eines Glyphs, tight gecroppt.
    /// `None` für leere Glyphen (Space) — dort wird nichts gezeichnet.
    pub(crate) fn glyph_quad(&self, code: u8) -> Option<([f32; 4], f32)> {
        let w = self.width[code as usize];
        if w == 0 { return None; }
        let col = (code as u32 % COLS) as f32;
        let row = (code as u32 / COLS) as f32;
        let x0 = col * self.glyph_w + self.left[code as usize] as f32;
        let y0 = row * self.glyph_h;
        let uv = [
            x0 / self.atlas_w,                  y0 / self.atlas_h,
            (x0 + w as f32) / self.atlas_w,    (y0 + self.glyph_h) / self.atlas_h,
        ];
        Some((uv, w as f32))
    }

    /// Wie weit der Cursor nach diesem Glyph vorrückt.
    pub(crate) fn advance(&self, code: u8) -> f32 {
        match self.width[code as usize] {
            0 => self.space_adv,
            w => w as f32 + self.tracking,
        }
    }

    /// Breite einer Zeichenkette in Pixeln (Summe der Advances).
    pub(crate) fn text_width(&self, s: &str) -> f32 {
        s.chars().map(|ch| self.advance(cp437(ch))).sum()
    }
}

/// Linkeste Ink-Spalte und Ink-Breite einer Glyph-Zelle in der Alpha-Maske.
/// `(0, 0)` wenn die Zelle leer ist.
fn ink_bounds(rgba: &[u8], atlas_w: usize, x0: usize, y0: usize, gw: usize, gh: usize) -> (u8, u8) {
    let (mut lo, mut hi) = (gw, 0usize);
    let mut any = false;
    for y in 0..gh {
        for x in 0..gw {
            let a = rgba[((y0 + y) * atlas_w + (x0 + x)) * 4 + 3];
            if a > 0 {
                any = true;
                if x < lo { lo = x; }
                if x > hi { hi = x; }
            }
        }
    }
    if any { (lo as u8, (hi - lo + 1) as u8) } else { (0, 0) }
}

/// Ein Unicode-Zeichen auf seinen CP437-Codepoint abbilden. ASCII ist
/// identisch; für die im Projekt nötigen deutschen Sonderzeichen gibt es
/// feste Plätze, alles andere fällt auf '?' zurück.
pub(crate) fn cp437(ch: char) -> u8 {
    match ch {
        c if (c as u32) < 128 => c as u8,
        'Ç' => 128, 'ü' => 129, 'é' => 130, 'ä' => 132, 'à' => 133,
        'å' => 134, 'ç' => 135, 'ë' => 137, 'è' => 138, 'ï' => 139,
        'î' => 140, 'ì' => 141, 'Ä' => 142, 'Å' => 143, 'É' => 144,
        'ö' => 148, 'ò' => 149, 'û' => 150, 'ù' => 151, 'Ö' => 153,
        'Ü' => 154, 'á' => 160, 'í' => 161, 'ó' => 162, 'ú' => 163,
        'ñ' => 164, 'Ñ' => 165, 'ß' => 225, '°' => 248,
        _ => b'?',
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn ink_bounds_finds_span_and_blank() {
        // 4px-breite "Zelle" (atlas_w=4, 1 Zeile): Ink in Spalten 1..=2.
        // rgba: x0 leer, x1 voll, x2 voll, x3 leer (nur Alpha relevant).
        let rgba = vec![
            0,0,0,0,   255,255,255,255,   255,255,255,255,   0,0,0,0,
        ];
        assert_eq!(ink_bounds(&rgba, 4, 0, 0, 4, 1), (1, 2));
        // Komplett leere Zelle.
        let blank = vec![0u8; 4 * 4];
        assert_eq!(ink_bounds(&blank, 4, 0, 0, 4, 1), (0, 0));
    }

    #[test]
    fn cp437_maps_ascii_and_umlauts() {
        assert_eq!(cp437('A'), 65);
        assert_eq!(cp437('a'), 97);
        assert_eq!(cp437('ä'), 132);
        assert_eq!(cp437('ß'), 225);
        assert_eq!(cp437('世'), b'?'); // außerhalb CP437 → Fallback
    }
}
