use std::collections::HashMap;

use bone_text::{FontFace, GlyphId, load_font};
use swash::{
    FontRef,
    scale::{Render, ScaleContext, Source, image::Image},
    zeno::Format,
};
use thiserror::Error;

use crate::layout::{LayoutOffset, LayoutPx, LayoutSize};
use crate::theme::FontSize;

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct SdfBaseSize(u32);

impl SdfBaseSize {
    pub const DEFAULT: Self = Self(64);

    #[must_use]
    pub const fn new(value: u32) -> Option<Self> {
        if value >= 8 && value <= 256 {
            Some(Self(value))
        } else {
            None
        }
    }

    #[must_use]
    pub const fn px(self) -> u32 {
        self.0
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct SdfSpread(u32);

impl SdfSpread {
    pub const DEFAULT: Self = Self(8);

    #[must_use]
    pub const fn new(value: u32) -> Option<Self> {
        if value >= 1 && value <= 32 {
            Some(Self(value))
        } else {
            None
        }
    }

    #[must_use]
    pub const fn px(self) -> u32 {
        self.0
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct GlyphAtlasKey {
    pub face: FontFace,
    pub glyph: GlyphId,
}

impl GlyphAtlasKey {
    #[must_use]
    pub const fn new(face: FontFace, glyph: GlyphId) -> Self {
        Self { face, glyph }
    }
}

#[derive(Copy, Clone, Debug, PartialEq)]
pub struct AtlasEntry {
    pub uv_min: [f32; 2],
    pub uv_max: [f32; 2],
    pub atlas_origin: [u32; 2],
    pub atlas_size: [u32; 2],
    pub bearing: LayoutOffset,
    pub glyph_size: LayoutSize,
    pub spread: LayoutPx,
    pub base_size: FontSize,
}

impl AtlasEntry {
    #[must_use]
    pub fn display_extent(self, target: FontSize) -> LayoutSize {
        let scale = self.scale_to(target);
        LayoutSize::new(
            LayoutPx::saturating_nonneg(self.glyph_size.width.value() * scale),
            LayoutPx::saturating_nonneg(self.glyph_size.height.value() * scale),
        )
    }

    #[must_use]
    pub fn display_bearing(self, target: FontSize) -> LayoutOffset {
        let scale = self.scale_to(target);
        LayoutOffset::new(
            LayoutPx::saturating(self.bearing.dx.value() * scale),
            LayoutPx::saturating(self.bearing.dy.value() * scale),
        )
    }

    fn scale_to(self, target: FontSize) -> f32 {
        target.as_px_f32() / self.base_size.as_px_f32()
    }
}

#[derive(Copy, Clone, Debug, PartialEq)]
pub struct SdfAtlasParams {
    pub base_size: SdfBaseSize,
    pub spread: SdfSpread,
    pub atlas_extent: u32,
}

impl SdfAtlasParams {
    pub const STANDARD: Self = Self {
        base_size: SdfBaseSize::DEFAULT,
        spread: SdfSpread::DEFAULT,
        atlas_extent: 1024,
    };
}

#[derive(Debug, Error, PartialEq, Eq)]
pub enum SdfAtlasError {
    #[error("glyph id {0} exceeds the swash u16 range")]
    GlyphOutOfRange(u32),
    #[error("tile {tile_width}x{tile_height} exceeds atlas extent {atlas_extent}")]
    TileExceedsAtlas {
        tile_width: u32,
        tile_height: u32,
        atlas_extent: u32,
    },
    #[error("atlas full: no shelf space for {tile_width}x{tile_height} tile")]
    Full { tile_width: u32, tile_height: u32 },
}

const SDF_OUTSIDE_FAR: u8 = 255;

pub struct SdfAtlas {
    params: SdfAtlasParams,
    pixels: Vec<u8>,
    sans: FontRef<'static>,
    mono: FontRef<'static>,
    scale_ctx: ScaleContext,
    entries: HashMap<GlyphAtlasKey, AtlasEntry>,
    packer: ShelfPacker,
    version: u64,
}

impl SdfAtlas {
    #[must_use]
    pub fn new(params: SdfAtlasParams) -> Self {
        let extent = params.atlas_extent as usize;
        Self {
            params,
            pixels: vec![SDF_OUTSIDE_FAR; extent * extent],
            sans: load_font(FontFace::Sans),
            mono: load_font(FontFace::Mono),
            scale_ctx: ScaleContext::new(),
            entries: HashMap::new(),
            packer: ShelfPacker::new(params.atlas_extent),
            version: 0,
        }
    }

    #[must_use]
    pub fn params(&self) -> SdfAtlasParams {
        self.params
    }

    #[must_use]
    pub fn extent(&self) -> u32 {
        self.params.atlas_extent
    }

    #[must_use]
    pub fn pixels(&self) -> &[u8] {
        &self.pixels
    }

    #[must_use]
    pub fn version(&self) -> u64 {
        self.version
    }

    #[must_use]
    pub fn entry(&self, key: GlyphAtlasKey) -> Option<AtlasEntry> {
        self.entries.get(&key).copied()
    }

    #[must_use]
    pub fn entries_len(&self) -> usize {
        self.entries.len()
    }

    pub fn ensure(&mut self, key: GlyphAtlasKey) -> Result<AtlasEntry, SdfAtlasError> {
        if let Some(entry) = self.entries.get(&key) {
            return Ok(*entry);
        }
        let glyph_id_u16 = u16::try_from(key.glyph.raw())
            .map_err(|_| SdfAtlasError::GlyphOutOfRange(key.glyph.raw()))?;
        let font = match key.face {
            FontFace::Sans => self.sans,
            FontFace::Mono => self.mono,
        };
        let tile = rasterise_glyph_sdf(&font, glyph_id_u16, &mut self.scale_ctx, self.params)?;
        let placed = self
            .packer
            .place(tile.width, tile.height)
            .ok_or(SdfAtlasError::Full {
                tile_width: tile.width,
                tile_height: tile.height,
            })?;
        blit_tile(&mut self.pixels, self.params.atlas_extent, placed, &tile);
        let entry = atlas_entry(self.params, placed, &tile);
        self.entries.insert(key, entry);
        self.version = self.version.wrapping_add(1);
        Ok(entry)
    }
}

impl core::fmt::Debug for SdfAtlas {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        f.debug_struct("SdfAtlas")
            .field("entries", &self.entries.len())
            .field("extent", &self.params.atlas_extent)
            .field("base_size", &self.params.base_size.px())
            .field("spread", &self.params.spread.px())
            .field("version", &self.version)
            .finish_non_exhaustive()
    }
}

struct GlyphTile {
    pixels: Vec<u8>,
    width: u32,
    height: u32,
    inner_width: u32,
    inner_height: u32,
    bearing_left: f32,
    bearing_top: f32,
}

#[allow(clippy::cast_precision_loss)]
fn rasterise_glyph_sdf(
    font: &FontRef<'_>,
    glyph: u16,
    scale_ctx: &mut ScaleContext,
    params: SdfAtlasParams,
) -> Result<GlyphTile, SdfAtlasError> {
    let base = params.base_size.px();
    let spread = params.spread.px();
    let mut scaler = scale_ctx
        .builder(*font)
        .size(base as f32)
        .hint(false)
        .build();
    let mut image = Image::new();
    if !Render::new(&[Source::Outline])
        .format(Format::Alpha)
        .render_into(&mut scaler, glyph, &mut image)
    {
        return Ok(empty_tile(spread));
    }
    let placement = image.placement;
    let inner_w = placement.width;
    let inner_h = placement.height;
    if inner_w == 0 || inner_h == 0 {
        return Ok(empty_tile(spread));
    }
    let tile_w = inner_w + 2 * spread;
    let tile_h = inner_h + 2 * spread;
    if tile_w > params.atlas_extent || tile_h > params.atlas_extent {
        return Err(SdfAtlasError::TileExceedsAtlas {
            tile_width: tile_w,
            tile_height: tile_h,
            atlas_extent: params.atlas_extent,
        });
    }
    let mask = build_padded_mask(&image.data, inner_w, inner_h, spread);
    let pixels = compute_sdf_bytes(&mask, tile_w, tile_h, spread);
    Ok(GlyphTile {
        pixels,
        width: tile_w,
        height: tile_h,
        inner_width: inner_w,
        inner_height: inner_h,
        bearing_left: placement.left as f32,
        bearing_top: placement.top as f32,
    })
}

fn empty_tile(spread: u32) -> GlyphTile {
    let tile_w = 2 * spread;
    let tile_h = 2 * spread;
    let pixels = vec![SDF_OUTSIDE_FAR; (tile_w as usize) * (tile_h as usize)];
    GlyphTile {
        pixels,
        width: tile_w,
        height: tile_h,
        inner_width: 0,
        inner_height: 0,
        bearing_left: 0.0,
        bearing_top: 0.0,
    }
}

fn build_padded_mask(coverage: &[u8], inner_w: u32, inner_h: u32, pad: u32) -> Vec<bool> {
    let tile_w = inner_w + 2 * pad;
    let tile_h = inner_h + 2 * pad;
    let mut mask = vec![false; (tile_w as usize) * (tile_h as usize)];
    (0..inner_h).for_each(|y| {
        (0..inner_w).for_each(|x| {
            let src = (y as usize) * (inner_w as usize) + (x as usize);
            let dst = ((y + pad) as usize) * (tile_w as usize) + (x + pad) as usize;
            mask[dst] = coverage[src] >= 128;
        });
    });
    mask
}

const FAR: i32 = i32::MAX / 4;

#[allow(clippy::cast_precision_loss)]
fn compute_sdf_bytes(mask: &[bool], width: u32, height: u32, spread: u32) -> Vec<u8> {
    let outside = distance_field_to(mask, width, height, true);
    let inside = distance_field_to(mask, width, height, false);
    let spread_f = spread as f32;
    (0..(width as usize) * (height as usize))
        .map(|i| {
            let do_ = (outside[i] as f32).sqrt();
            let di = (inside[i] as f32).sqrt();
            let signed = if mask[i] { -di } else { do_ };
            let normalised = ((signed / spread_f) * 0.5 + 0.5).clamp(0.0, 1.0);
            #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
            let byte = (normalised * 255.0 + 0.5) as u8;
            byte
        })
        .collect()
}

fn distance_field_to(mask: &[bool], width: u32, height: u32, target_inside: bool) -> Vec<i32> {
    let w = width as usize;
    let h = height as usize;
    let n = w * h;
    let mut dx: Vec<i32> = vec![FAR; n];
    let mut dy: Vec<i32> = vec![FAR; n];
    let mut sq: Vec<i32> = vec![FAR; n];
    (0..h).for_each(|y| {
        (0..w).for_each(|x| {
            if mask[y * w + x] == target_inside {
                let i = y * w + x;
                dx[i] = 0;
                dy[i] = 0;
                sq[i] = 0;
            }
        });
    });
    sweep_forward(&mut dx, &mut dy, &mut sq, w, h);
    sweep_backward(&mut dx, &mut dy, &mut sq, w, h);
    sq
}

fn compare(
    dx: &mut [i32],
    dy: &mut [i32],
    sq: &mut [i32],
    target: usize,
    other: usize,
    odx: i32,
    ody: i32,
) {
    let nx = dx[other] + odx;
    let ny = dy[other] + ody;
    let nsq = nx.saturating_mul(nx).saturating_add(ny.saturating_mul(ny));
    if nsq < sq[target] {
        dx[target] = nx;
        dy[target] = ny;
        sq[target] = nsq;
    }
}

fn sweep_forward(dx: &mut [i32], dy: &mut [i32], sq: &mut [i32], w: usize, h: usize) {
    (0..h).for_each(|y| {
        (0..w).for_each(|x| {
            let i = y * w + x;
            if x > 0 {
                compare(dx, dy, sq, i, i - 1, 1, 0);
            }
            if y > 0 {
                let up = i - w;
                compare(dx, dy, sq, i, up, 0, 1);
                if x > 0 {
                    compare(dx, dy, sq, i, up - 1, 1, 1);
                }
                if x + 1 < w {
                    compare(dx, dy, sq, i, up + 1, 1, 1);
                }
            }
        });
        (0..w).rev().for_each(|x| {
            let i = y * w + x;
            if x + 1 < w {
                compare(dx, dy, sq, i, i + 1, 1, 0);
            }
        });
    });
}

fn sweep_backward(dx: &mut [i32], dy: &mut [i32], sq: &mut [i32], w: usize, h: usize) {
    (0..h).rev().for_each(|y| {
        (0..w).rev().for_each(|x| {
            let i = y * w + x;
            if x + 1 < w {
                compare(dx, dy, sq, i, i + 1, 1, 0);
            }
            if y + 1 < h {
                let down = i + w;
                compare(dx, dy, sq, i, down, 0, 1);
                if x + 1 < w {
                    compare(dx, dy, sq, i, down + 1, 1, 1);
                }
                if x > 0 {
                    compare(dx, dy, sq, i, down - 1, 1, 1);
                }
            }
        });
        (0..w).for_each(|x| {
            let i = y * w + x;
            if x > 0 {
                compare(dx, dy, sq, i, i - 1, 1, 0);
            }
        });
    });
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
struct PlacedTile {
    x: u32,
    y: u32,
    width: u32,
    height: u32,
}

struct ShelfPacker {
    extent: u32,
    cursor_x: u32,
    shelf_y: u32,
    shelf_height: u32,
}

impl ShelfPacker {
    fn new(extent: u32) -> Self {
        Self {
            extent,
            cursor_x: 0,
            shelf_y: 0,
            shelf_height: 0,
        }
    }

    fn place(&mut self, tile_w: u32, tile_h: u32) -> Option<PlacedTile> {
        if tile_w > self.extent || tile_h > self.extent {
            return None;
        }
        if self.cursor_x.saturating_add(tile_w) > self.extent {
            self.shelf_y = self.shelf_y.checked_add(self.shelf_height)?;
            self.cursor_x = 0;
            self.shelf_height = 0;
        }
        if self.shelf_y.saturating_add(tile_h) > self.extent {
            return None;
        }
        let placed = PlacedTile {
            x: self.cursor_x,
            y: self.shelf_y,
            width: tile_w,
            height: tile_h,
        };
        self.cursor_x += tile_w;
        if tile_h > self.shelf_height {
            self.shelf_height = tile_h;
        }
        Some(placed)
    }
}

fn blit_tile(dst: &mut [u8], extent: u32, placed: PlacedTile, tile: &GlyphTile) {
    (0..placed.height).for_each(|row| {
        let src_off = (row as usize) * (placed.width as usize);
        let dst_off = ((placed.y + row) as usize) * (extent as usize) + placed.x as usize;
        let len = placed.width as usize;
        dst[dst_off..dst_off + len].copy_from_slice(&tile.pixels[src_off..src_off + len]);
    });
}

#[allow(clippy::cast_precision_loss)]
fn atlas_entry(params: SdfAtlasParams, placed: PlacedTile, tile: &GlyphTile) -> AtlasEntry {
    let extent = params.atlas_extent as f32;
    let uv_min = [placed.x as f32 / extent, placed.y as f32 / extent];
    let uv_max = [
        (placed.x + placed.width) as f32 / extent,
        (placed.y + placed.height) as f32 / extent,
    ];
    let pad = params.spread.px() as f32;
    let bearing = LayoutOffset::new(
        LayoutPx::saturating(tile.bearing_left - pad),
        LayoutPx::saturating(tile.bearing_top + pad),
    );
    let glyph_size = LayoutSize::new(
        LayoutPx::saturating_nonneg(tile.inner_width as f32 + 2.0 * pad),
        LayoutPx::saturating_nonneg(tile.inner_height as f32 + 2.0 * pad),
    );
    AtlasEntry {
        uv_min,
        uv_max,
        atlas_origin: [placed.x, placed.y],
        atlas_size: [placed.width, placed.height],
        bearing,
        glyph_size,
        spread: LayoutPx::saturating_nonneg(pad),
        base_size: FontSize::from_px(f64::from(params.base_size.px())),
    }
}

#[cfg(test)]
mod tests {
    use super::{
        AtlasEntry, GlyphAtlasKey, PlacedTile, SdfAtlas, SdfAtlasError, SdfAtlasParams,
        SdfBaseSize, SdfSpread, ShelfPacker,
    };
    use crate::theme::FontSize;
    use bone_text::{FontFace, GlyphId, load_font};

    #[derive(Copy, Clone, Debug, PartialEq)]
    struct SdfPixel(u8);

    impl SdfPixel {
        const ON_EDGE: Self = Self(128);

        const fn raw(self) -> u8 {
            self.0
        }

        const fn is_inside(self) -> bool {
            self.0 < Self::ON_EDGE.0
        }
    }

    fn glyph_for(face: FontFace, ch: char) -> GlyphId {
        let font = load_font(face);
        GlyphId::new(u32::from(font.charmap().map(u32::from(ch))))
    }

    fn small_params() -> SdfAtlasParams {
        SdfAtlasParams {
            base_size: SdfBaseSize::DEFAULT,
            spread: SdfSpread::DEFAULT,
            atlas_extent: 256,
        }
    }

    #[track_caller]
    fn ensure_ok(atlas: &mut SdfAtlas, key: GlyphAtlasKey) -> AtlasEntry {
        let Ok(entry) = atlas.ensure(key) else {
            panic!("ensure must succeed for {key:?}")
        };
        entry
    }

    #[track_caller]
    fn place_ok(packer: &mut ShelfPacker, w: u32, h: u32) -> PlacedTile {
        let Some(p) = packer.place(w, h) else {
            panic!("place {w}x{h} must succeed")
        };
        p
    }

    #[test]
    fn base_size_constructor_rejects_extremes() {
        assert!(SdfBaseSize::new(0).is_none());
        assert!(SdfBaseSize::new(7).is_none());
        assert!(SdfBaseSize::new(8).is_some());
        assert!(SdfBaseSize::new(256).is_some());
        assert!(SdfBaseSize::new(257).is_none());
    }

    #[test]
    fn spread_constructor_rejects_extremes() {
        assert!(SdfSpread::new(0).is_none());
        assert!(SdfSpread::new(1).is_some());
        assert!(SdfSpread::new(32).is_some());
        assert!(SdfSpread::new(33).is_none());
    }

    #[test]
    fn pixel_threshold_distinguishes_inside_and_outside() {
        assert!(SdfPixel::ON_EDGE.raw() == 128);
        assert!(!SdfPixel::ON_EDGE.is_inside());
        assert!(SdfPixel(0).is_inside());
        assert!(!SdfPixel(255).is_inside());
    }

    #[test]
    fn fresh_atlas_has_zero_entries_and_zero_version() {
        let atlas = SdfAtlas::new(small_params());
        assert_eq!(atlas.entries_len(), 0);
        assert_eq!(atlas.version(), 0);
        assert_eq!(atlas.extent(), 256);
        assert_eq!(atlas.pixels().len(), 256 * 256);
    }

    #[test]
    fn ensure_glyph_writes_atlas_and_bumps_version() {
        let mut atlas = SdfAtlas::new(small_params());
        let key = GlyphAtlasKey::new(FontFace::Sans, glyph_for(FontFace::Sans, 'A'));
        let entry = ensure_ok(&mut atlas, key);
        assert!(atlas.entries_len() == 1);
        assert!(atlas.version() == 1);
        assert!(entry.uv_max[0] > entry.uv_min[0]);
        assert!(entry.uv_max[1] > entry.uv_min[1]);
        assert!(entry.glyph_size.width.value() > 0.0);
        assert!(entry.glyph_size.height.value() > 0.0);
    }

    #[test]
    fn ensure_idempotent_for_same_key() {
        let mut atlas = SdfAtlas::new(small_params());
        let key = GlyphAtlasKey::new(FontFace::Sans, glyph_for(FontFace::Sans, 'B'));
        let first = ensure_ok(&mut atlas, key);
        let v_after_first = atlas.version();
        let second = ensure_ok(&mut atlas, key);
        assert_eq!(first, second);
        assert_eq!(atlas.version(), v_after_first);
        assert_eq!(atlas.entries_len(), 1);
    }

    #[test]
    fn ensure_writes_inside_pixels_for_solid_letter() {
        let mut atlas = SdfAtlas::new(small_params());
        let key = GlyphAtlasKey::new(FontFace::Sans, glyph_for(FontFace::Sans, 'I'));
        let entry = ensure_ok(&mut atlas, key);
        let extent = atlas.extent();
        let any_inside = (entry.atlas_origin[1]..entry.atlas_origin[1] + entry.atlas_size[1])
            .flat_map(|y| {
                (entry.atlas_origin[0]..entry.atlas_origin[0] + entry.atlas_size[0])
                    .map(move |x| (x, y))
            })
            .any(|(x, y)| {
                let v = atlas.pixels()[(y as usize) * (extent as usize) + x as usize];
                SdfPixel(v).is_inside()
            });
        assert!(
            any_inside,
            "expected at least one inside pixel inside I tile"
        );
    }

    #[test]
    fn donut_glyph_center_stays_outside() {
        let mut atlas = SdfAtlas::new(small_params());
        let key = GlyphAtlasKey::new(FontFace::Sans, glyph_for(FontFace::Sans, 'O'));
        let entry = ensure_ok(&mut atlas, key);
        let extent = atlas.extent();
        let cx = entry.atlas_origin[0] + entry.atlas_size[0] / 2;
        let cy = entry.atlas_origin[1] + entry.atlas_size[1] / 2;
        let value = atlas.pixels()[(cy as usize) * (extent as usize) + cx as usize];
        assert!(
            !SdfPixel(value).is_inside(),
            "center of O lies inside the donut hole, must be outside fill, raw={value}",
        );
    }

    #[test]
    fn empty_glyph_returns_no_geometry_but_inserts_entry() {
        let mut atlas = SdfAtlas::new(small_params());
        let space = GlyphAtlasKey::new(FontFace::Sans, glyph_for(FontFace::Sans, ' '));
        let entry = ensure_ok(&mut atlas, space);
        assert!(
            entry.glyph_size.width.value() > 0.0,
            "tile retains spread padding"
        );
    }

    #[test]
    fn empty_glyph_tile_is_filled_with_outside_pixels() {
        let mut atlas = SdfAtlas::new(small_params());
        let space = GlyphAtlasKey::new(FontFace::Sans, glyph_for(FontFace::Sans, ' '));
        let entry = ensure_ok(&mut atlas, space);
        let extent = atlas.extent();
        let any_inside = (entry.atlas_origin[1]..entry.atlas_origin[1] + entry.atlas_size[1])
            .flat_map(|y| {
                (entry.atlas_origin[0]..entry.atlas_origin[0] + entry.atlas_size[0])
                    .map(move |x| (x, y))
            })
            .any(|(x, y)| {
                let v = atlas.pixels()[(y as usize) * (extent as usize) + x as usize];
                SdfPixel(v).is_inside()
            });
        assert!(
            !any_inside,
            "empty glyph tile must be all outside pixels, never sampled as inside fill",
        );
    }

    #[test]
    fn out_of_range_glyph_yields_typed_error() {
        let mut atlas = SdfAtlas::new(small_params());
        let key = GlyphAtlasKey::new(FontFace::Sans, GlyphId::new(0x0001_0000));
        match atlas.ensure(key) {
            Err(SdfAtlasError::GlyphOutOfRange(g)) => assert_eq!(g, 0x0001_0000),
            other => panic!("expected GlyphOutOfRange, got {other:?}"),
        }
    }

    #[test]
    fn distinct_faces_produce_distinct_entries() {
        let mut atlas = SdfAtlas::new(small_params());
        let sans = GlyphAtlasKey::new(FontFace::Sans, glyph_for(FontFace::Sans, 'A'));
        let mono = GlyphAtlasKey::new(FontFace::Mono, glyph_for(FontFace::Mono, 'A'));
        let _ = ensure_ok(&mut atlas, sans);
        let _ = ensure_ok(&mut atlas, mono);
        assert_eq!(atlas.entries_len(), 2);
        assert_ne!(atlas.entry(sans), atlas.entry(mono));
    }

    #[test]
    fn entries_lookup_returns_inserted() {
        let mut atlas = SdfAtlas::new(small_params());
        let key = GlyphAtlasKey::new(FontFace::Sans, glyph_for(FontFace::Sans, 'C'));
        assert!(atlas.entry(key).is_none());
        let inserted = ensure_ok(&mut atlas, key);
        assert_eq!(atlas.entry(key), Some(inserted));
    }

    #[test]
    fn shelf_packer_lays_tiles_left_to_right_then_wraps() {
        let mut packer = ShelfPacker::new(64);
        let a = place_ok(&mut packer, 20, 30);
        let b = place_ok(&mut packer, 20, 30);
        let c = place_ok(&mut packer, 20, 30);
        let d = place_ok(&mut packer, 20, 30);
        assert_eq!((a.x, a.y), (0, 0));
        assert_eq!((b.x, b.y), (20, 0));
        assert_eq!((c.x, c.y), (40, 0));
        assert_eq!((d.x, d.y), (0, 30));
    }

    #[test]
    fn shelf_packer_returns_none_when_full() {
        let mut packer = ShelfPacker::new(32);
        assert!(packer.place(32, 16).is_some());
        assert!(packer.place(32, 16).is_some());
        assert!(packer.place(32, 16).is_none());
    }

    #[test]
    fn shelf_packer_rejects_oversized_tile() {
        let mut packer = ShelfPacker::new(64);
        assert!(packer.place(65, 4).is_none());
        assert!(packer.place(4, 65).is_none());
    }

    #[test]
    fn tile_exceeding_extent_yields_typed_error() {
        let mut atlas = SdfAtlas::new(SdfAtlasParams {
            base_size: SdfBaseSize::DEFAULT,
            spread: SdfSpread::DEFAULT,
            atlas_extent: 16,
        });
        let key = GlyphAtlasKey::new(FontFace::Sans, glyph_for(FontFace::Sans, 'A'));
        match atlas.ensure(key) {
            Err(SdfAtlasError::TileExceedsAtlas {
                tile_width,
                tile_height,
                atlas_extent,
            }) => {
                assert_eq!(atlas_extent, 16);
                assert!(tile_width > atlas_extent || tile_height > atlas_extent);
            }
            other => panic!("expected TileExceedsAtlas, got {other:?}"),
        }
    }

    #[test]
    fn shelf_full_yields_typed_error() {
        let mut atlas = SdfAtlas::new(SdfAtlasParams {
            base_size: SdfBaseSize::DEFAULT,
            spread: SdfSpread::DEFAULT,
            atlas_extent: 64,
        });
        let chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%";
        let outcome = chars.chars().try_fold((), |(), ch| {
            atlas
                .ensure(GlyphAtlasKey::new(
                    FontFace::Sans,
                    glyph_for(FontFace::Sans, ch),
                ))
                .map(|_| ())
        });
        assert!(
            matches!(outcome, Err(SdfAtlasError::Full { .. })),
            "expected Full once shelves run out, got {outcome:?}",
        );
    }

    #[test]
    fn display_extent_scales_with_target_size() {
        let mut atlas = SdfAtlas::new(small_params());
        let key = GlyphAtlasKey::new(FontFace::Sans, glyph_for(FontFace::Sans, 'A'));
        let entry = ensure_ok(&mut atlas, key);
        let small = entry.display_extent(FontSize::from_px(6.0));
        let big = entry.display_extent(FontSize::from_px(96.0));
        assert!(big.width.value() > small.width.value() * 15.0);
        assert!(big.height.value() > small.height.value() * 15.0);
    }

    #[test]
    fn atlas_size_supports_target_size_range() {
        let mut atlas = SdfAtlas::new(small_params());
        let key = GlyphAtlasKey::new(FontFace::Sans, glyph_for(FontFace::Sans, 'g'));
        let entry = ensure_ok(&mut atlas, key);
        let extent_at_min = entry.display_extent(FontSize::from_px(6.0));
        let extent_at_max = entry.display_extent(FontSize::from_px(96.0));
        assert!(extent_at_min.width.value() > 0.0 && extent_at_min.height.value() > 0.0);
        assert!(extent_at_max.width.value() > extent_at_min.width.value());
        assert!(extent_at_max.height.value() > extent_at_min.height.value());
    }

    #[test]
    fn display_bearing_scales_with_target_size_and_preserves_sign() {
        let mut atlas = SdfAtlas::new(small_params());
        let key = GlyphAtlasKey::new(FontFace::Sans, glyph_for(FontFace::Sans, 'A'));
        let entry = ensure_ok(&mut atlas, key);
        let bearing_small = entry.display_bearing(FontSize::from_px(8.0));
        let bearing_large = entry.display_bearing(FontSize::from_px(64.0));
        assert!((bearing_large.dx.value() - bearing_small.dx.value() * 8.0).abs() < 1e-3);
        assert!((bearing_large.dy.value() - bearing_small.dy.value() * 8.0).abs() < 1e-3);
    }

    #[test]
    fn ascender_bearing_dy_exceeds_descender_in_font_y_up_space() {
        let mut atlas = SdfAtlas::new(small_params());
        let target = FontSize::from_px(64.0);
        let ascender = ensure_ok(
            &mut atlas,
            GlyphAtlasKey::new(FontFace::Sans, glyph_for(FontFace::Sans, 'l')),
        );
        let descender = ensure_ok(
            &mut atlas,
            GlyphAtlasKey::new(FontFace::Sans, glyph_for(FontFace::Sans, 'g')),
        );
        let asc_dy = ascender.display_bearing(target).dy.value();
        let desc_dy = descender.display_bearing(target).dy.value();
        assert!(
            asc_dy > 0.0,
            "swash returns Y-up: ascender 'l' bearing.dy must be positive, got {asc_dy}",
        );
        assert!(
            asc_dy > desc_dy,
            "ascender bearing.dy {asc_dy} must exceed descender bearing.dy {desc_dy} in Y-up space",
        );
    }

    #[test]
    fn signed_distance_byte_at_edge_is_close_to_threshold() {
        let mut atlas = SdfAtlas::new(small_params());
        let key = GlyphAtlasKey::new(FontFace::Sans, glyph_for(FontFace::Sans, 'I'));
        let entry = ensure_ok(&mut atlas, key);
        let extent = atlas.extent();
        let column = entry.atlas_origin[0] + entry.atlas_size[0] / 2;
        let edge_band: Vec<u8> = (entry.atlas_origin[1]
            ..entry.atlas_origin[1] + entry.atlas_size[1])
            .map(|row| atlas.pixels()[(row as usize) * (extent as usize) + column as usize])
            .collect();
        let near_edge = edge_band
            .iter()
            .any(|v| (i32::from(*v) - i32::from(SdfPixel::ON_EDGE.raw())).abs() <= 32);
        assert!(
            near_edge,
            "expected at least one pixel near edge value 128, got {edge_band:?}"
        );
    }
}
