// Copyright (C) 2026 alto555
// SPDX-License-Identifier: GPL-2.0-only

//! Bitmap font atlases, baked from DejaVu Sans Mono at several sizes.
//!
//! Eight-bit coverage per pixel, not one bit.  Thresholding the rasteriser's
//! coverage to on/off is what made the text look crude: at these sizes a stem
//! lands between pixels more often than on one, so hard edges give uneven
//! weights.  Glyphs are blended over whatever is beneath them.
//!
//! Several sizes are baked because the layout has to be legible on a laptop
//! and on a 4K panel, and scaling a bitmap font is not the same as rendering
//! it at the size you want.
//!
//! Generated, not hand-authored - see docs/regen.sh --font.

use std::sync::atomic::{AtomicUsize, Ordering};

pub struct Font {
    /// Nominal pixel size it was baked at.
    pub px: usize,
    pub cw: usize,
    pub ch: usize,
    off: usize,
}

pub const FIRST: u8 = 32;
pub const LAST: u8 = 126;

static ATLAS: &[u8] = include_bytes!("font_atlas.bin");
pub static FONTS: [Font; 4] = [
    Font { px: 13, cw: 8, ch: 17, off: 0 },
    Font { px: 16, cw: 10, ch: 19, off: 12920 },
    Font { px: 20, cw: 12, ch: 24, off: 30970 },
    Font { px: 26, cw: 16, ch: 32, off: 58330 },
];

static SELECTED: AtomicUsize = AtomicUsize::new(0);

/// Pick the baked size nearest to `px`.  Returns the size actually chosen.
pub fn set_size(px: usize) -> usize {
    let mut best = 0usize;
    for (i, f) in FONTS.iter().enumerate() {
        let d = (f.px as i64 - px as i64).abs();
        if d < (FONTS[best].px as i64 - px as i64).abs() {
            best = i;
        }
    }
    SELECTED.store(best, Ordering::Relaxed);
    FONTS[best].px
}

/// Sizes available, for a usage message.
pub fn sizes() -> Vec<usize> {
    FONTS.iter().map(|f| f.px).collect()
}

#[inline]
pub fn current() -> &'static Font {
    &FONTS[SELECTED.load(Ordering::Relaxed)]
}

impl Font {
    /// Coverage bitmap of one character, row-major, `cw * ch` bytes.
    #[inline]
    pub fn glyph(&self, c: u8) -> &'static [u8] {
        let i = (c.max(FIRST).min(LAST) - FIRST) as usize;
        let n = self.cw * self.ch;
        &ATLAS[self.off + i * n..self.off + (i + 1) * n]
    }
}

/// Advance width of the selected font.
#[inline]
pub fn cw() -> i64 {
    current().cw as i64
}

/// Line height of the selected font.
#[inline]
pub fn ch() -> i64 {
    current().ch as i64
}
