use crate::prelude::*;
use savory::prelude::*;

#[derive(Rich, Element)]
pub struct ScreenInfoNotifier {
    design_system: DesignSystem,
    #[rich(read)]
    current_screen_info: ScreenInfo,
}

pub enum Msg {
    SizeChanged,
    DesignSystemChanged(DesignSystem),
}

impl Element for ScreenInfoNotifier {
    type Message = Msg;
    type Config = Config;

    fn init(config: Self::Config, orders: &mut impl Orders<Msg>) -> Self {
        orders
            .subscribe(|ds: DesignSystemChanged| Msg::DesignSystemChanged(ds.0))
            .stream(streams::window_event(Ev::Resize, |_| Msg::SizeChanged));

        let (width, height) = Self::get_screen_size();
        let screen_info = config.design_system.screen_info(width, height);
        orders.notify(NewScreenInfo(screen_info));

        Self {
            current_screen_info: screen_info,
            design_system: DesignSystem::default(),
        }
    }

    fn update(&mut self, msg: Msg, orders: &mut impl Orders<Msg>) {
        match msg {
            Msg::DesignSystemChanged(val) => self.design_system = val,
            Msg::SizeChanged => {
                let (width, height) = Self::get_screen_size();
                let new_screen_info = self.design_system.screen_info(width, height);
                if self.current_screen_info != new_screen_info {
                    self.current_screen_info = new_screen_info;
                    orders.notify(NewScreenInfo(new_screen_info));
                }
            }
        }
    }
}

impl ScreenInfoNotifier {
    fn get_screen_size() -> (u32, u32) {
        web_sys::window()
            .and_then(|window| {
                let get_val = |js_val: Result<wasm_bindgen::JsValue, _>| {
                    js_val
                        .ok()
                        .and_then(|v| v.as_f64())
                        .map(|v| v.round() as u32)
                        .unwrap_or(0)
                };
                let width = get_val(window.inner_width());
                let height = get_val(window.inner_height());
                Some((width, height))
            })
            .unwrap_or((0, 0))
    }
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct NewScreenInfo(pub ScreenInfo);

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct ScreenInfo {
    pub class: ScreenClass,
    pub orientation: ScreenOrientation,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum ScreenClass {
    Phone,
    Tablet,
    Desktop,
    BigDesktop,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum ScreenOrientation {
    Portrait,
    Landscape,
}
