//! Produce text!!!

use glyphon::{
    Attrs, Buffer, Color, FontSystem, Metrics, Shaping, SwashCache, TextArea, TextBounds,
};
use sys_locale::get_locale;

/// Contains the font system, swash cache, and is generally permanent
pub struct TextSystem {
    pub fonts: FontSystem,
    pub swash: SwashCache,
}

impl TextSystem {
    pub fn new() -> color_eyre::Result<Self> {
        // Until we can assume otherwise, hardcode this
        let locale = get_locale().unwrap_or_else(|| "en-US".to_string());
        let mut db = glyphon::fontdb::Database::new();
        #[cfg(not(target_arch = "wasm32"))]
        {
            db.load_system_fonts();
        }
        let fonts = FontSystem::new_with_locale_and_db(locale, db);

        Ok(Self {
            fonts,
            swash: SwashCache::new(),
        })
    }
}

#[derive(serde::Deserialize, Debug, Clone, Copy)]
struct TextColor(
    #[serde(deserialize_with = "palette::serde::deserialize_with_optional_alpha")] palette::Srgba,
);

impl TextSystem {
    /// Loading requested data here
    pub fn prepare(&mut self, _data: &TextSystemData) {}

    /// Convert a 2d point as the world describes it, to a 2d point that
    /// the world glyphon can understand
    fn convert_world_to_glyphon_world(&self, point: glam::Vec2) -> glam::Vec2 {
        // Converts it to a representation of [0, 1] from the world domain of [-WHE, +WHE]
        // let normalized = self.glyphon_to_world.transform_point3(point.extend(0.0));
        // Maps the normalized coords to the glyphon scale of 2 * WHE
        // normalized.xy() * 2.0 * WORLD_HALF_EXTENTS.as_vec2()
        point
    }

    /// Produce world space text.
    ///
    /// Moving the camera *should* move this text.
    pub fn produce_world(
        &mut self,
        _data: &TextSystemData,
    ) -> impl IntoIterator<Item = (Buffer, BufferMapper)> {
        let metrics = Metrics::new(32.0, 20.0);
        let mut buffer = Buffer::new(&mut self.fonts, metrics);
        {
            let mut buffer = buffer.borrow_with(&mut self.fonts);
            buffer.set_size(250.0, 25.0);
            let attrs = Attrs::new().family(glyphon::Family::Name("Poiret One"));
            buffer.set_text("Hello, game! 🦀\n", attrs, Shaping::Advanced);
            buffer.shape_until_scroll(true);
        }
        let loc = glam::Vec2::new(32.0, 32.0);
        let mut buffer2 = Buffer::new(&mut self.fonts, metrics);
        {
            let mut buffer2 = buffer2.borrow_with(&mut self.fonts);
            buffer2.set_size(250.0, 25.0);
            let attrs = Attrs::new()
                .family(glyphon::Family::Name("Poiret One"))
                .color(glyphon::Color::rgb(255, 255, 255));
            buffer2.set_text("Y tho? 🦀\n", attrs, Shaping::Advanced);
            buffer2.shape_until_scroll(true);
        }
        let loc2 = glam::Vec2::new(32.0, 64.0);
        vec![
            (
                buffer,
                BufferMapper::new(self.convert_world_to_glyphon_world(loc), 1.0),
            ),
            (
                buffer2,
                BufferMapper::new(self.convert_world_to_glyphon_world(loc2), 1.0),
            ),
        ]
    }

    /// Produce camera space text.
    ///
    /// Moving the game camera should *not* move this text.
    pub fn produce_camera(
        &mut self,
        _data: &TextSystemData,
    ) -> impl IntoIterator<Item = (Buffer, BufferMapper)> {
        let metrics = Metrics::new(32.0, 20.0);
        let mut buffer = Buffer::new(&mut self.fonts, metrics);
        {
            let mut buffer = buffer.borrow_with(&mut self.fonts);
            buffer.set_size(200.0, 25.0);
            let srgb_red: palette::Srgb<u8> = palette::named::WHITE.into_format();
            let (r, g, b) = srgb_red.into_components();
            let color = glyphon::Color::rgb(r, g, b);
            let attrs = Attrs::new()
                .family(glyphon::Family::Name("Poiret One"))
                .color(color);
            buffer.set_text("Hello, Rust! 🦀\n", attrs, Shaping::Advanced);
            buffer.shape_until_scroll(true);
        }
        vec![(buffer, BufferMapper::new((32.0, 32.0).into(), 1.0))]
    }
}

/// Resposible for configuring the TextArea of a given buffer
pub struct BufferMapper {
    position: glam::Vec2,
    scale: f32,
}

impl BufferMapper {
    fn new(position: glam::Vec2, scale: f32) -> Self {
        Self { position, scale }
    }

    pub(crate) fn produce<'a>(&self, buffer: &'a Buffer) -> TextArea<'a> {
        TextArea {
            buffer,
            left: self.position.x,
            top: self.position.y,
            scale: self.scale,
            bounds: TextBounds {
                ..Default::default()
            },
            default_color: Color::rgba(255, 0, 0, 200),
        }
    }
}

/// Contains ephemural data about what the game wants to display with text
pub struct TextSystemData {}

impl TextSystemData {
    /// Extract the data necessary to produce text
    pub fn extract() -> Self {
        Self {}
    }
}