//! Common window model types.

use crate::scene::{Color, UiSize};

#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct WindowId(u64);

impl WindowId {
    pub const fn from_raw(raw: u64) -> Self {
        Self(raw)
    }

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

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum DecorationMode {
    Auto,
    Server,
    Client,
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum WindowLifecycle {
    LogicalClosed,
    Opening,
    AwaitingInitialConfigure,
    OpenHidden,
    OpenVisible,
    Closing,
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub struct WindowConfigured {
    /// Current logical content size and platform presentation scale.
    ///
    /// Backends should emit a new configuration when either the logical size or
    /// scale changes. This lets hosts relayout for size changes while renderers
    /// can update drawable/text scale when a window moves between displays.
    pub actual_inner_size: UiSize,
    pub scale_factor: f32,
    pub visible: bool,
    pub maximized: bool,
    pub fullscreen: bool,
}

#[derive(Clone, Debug, PartialEq)]
pub struct WindowSpec {
    pub key: Option<String>,
    pub title: String,
    pub app_id: Option<String>,
    pub open: bool,
    pub visible: bool,
    pub requested_inner_size: Option<UiSize>,
    pub min_inner_size: Option<UiSize>,
    pub max_inner_size: Option<UiSize>,
    pub decorations: DecorationMode,
    pub maximized: bool,
    pub fullscreen: bool,
    pub resizable: bool,
    pub base_color: Color,
}

impl WindowSpec {
    pub fn new(title: impl Into<String>) -> Self {
        Self {
            key: None,
            title: title.into(),
            app_id: None,
            open: true,
            visible: true,
            requested_inner_size: None,
            min_inner_size: None,
            max_inner_size: None,
            decorations: DecorationMode::Auto,
            maximized: false,
            fullscreen: false,
            resizable: true,
            base_color: Color::rgb(0x08, 0x0C, 0x14),
        }
    }

    pub fn base_color(mut self, color: Color) -> Self {
        self.base_color = color;
        self
    }

    pub fn key(mut self, key: impl Into<String>) -> Self {
        self.key = Some(key.into());
        self
    }

    pub fn app_id(mut self, app_id: impl Into<String>) -> Self {
        self.app_id = Some(app_id.into());
        self
    }

    pub fn open(mut self, open: bool) -> Self {
        self.open = open;
        self
    }

    pub fn visible(mut self, visible: bool) -> Self {
        self.visible = visible;
        self
    }

    pub fn requested_inner_size(mut self, size: UiSize) -> Self {
        self.requested_inner_size = Some(size);
        self
    }

    pub fn min_inner_size(mut self, size: UiSize) -> Self {
        self.min_inner_size = Some(size);
        self
    }

    pub fn max_inner_size(mut self, size: UiSize) -> Self {
        self.max_inner_size = Some(size);
        self
    }

    pub fn decorations(mut self, decorations: DecorationMode) -> Self {
        self.decorations = decorations;
        self
    }

    pub fn maximized(mut self, maximized: bool) -> Self {
        self.maximized = maximized;
        self
    }

    pub fn fullscreen(mut self, fullscreen: bool) -> Self {
        self.fullscreen = fullscreen;
        self
    }

    pub fn resizable(mut self, resizable: bool) -> Self {
        self.resizable = resizable;
        self
    }
}

#[derive(Clone, Debug, Default, PartialEq)]
pub struct WindowUpdate {
    pub title: Option<String>,
    pub open: Option<bool>,
    pub visible: Option<bool>,
    pub requested_inner_size: Option<Option<UiSize>>,
    pub min_inner_size: Option<Option<UiSize>>,
    pub max_inner_size: Option<Option<UiSize>>,
    pub decorations: Option<DecorationMode>,
    pub maximized: Option<bool>,
    pub fullscreen: Option<bool>,
    pub resizable: Option<bool>,
}

impl WindowUpdate {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn title(mut self, title: impl Into<String>) -> Self {
        self.title = Some(title.into());
        self
    }

    pub fn open(mut self, open: bool) -> Self {
        self.open = Some(open);
        self
    }

    pub fn visible(mut self, visible: bool) -> Self {
        self.visible = Some(visible);
        self
    }

    pub fn requested_inner_size(mut self, size: Option<UiSize>) -> Self {
        self.requested_inner_size = Some(size);
        self
    }

    pub fn min_inner_size(mut self, size: Option<UiSize>) -> Self {
        self.min_inner_size = Some(size);
        self
    }

    pub fn max_inner_size(mut self, size: Option<UiSize>) -> Self {
        self.max_inner_size = Some(size);
        self
    }

    pub fn decorations(mut self, decorations: DecorationMode) -> Self {
        self.decorations = Some(decorations);
        self
    }

    pub fn maximized(mut self, maximized: bool) -> Self {
        self.maximized = Some(maximized);
        self
    }

    pub fn fullscreen(mut self, fullscreen: bool) -> Self {
        self.fullscreen = Some(fullscreen);
        self
    }

    pub fn resizable(mut self, resizable: bool) -> Self {
        self.resizable = Some(resizable);
        self
    }

    pub fn apply_to(&self, spec: &mut WindowSpec) {
        if let Some(title) = &self.title {
            spec.title = title.clone();
        }
        if let Some(open) = self.open {
            spec.open = open;
        }
        if let Some(visible) = self.visible {
            spec.visible = visible;
        }
        if let Some(requested_inner_size) = self.requested_inner_size {
            spec.requested_inner_size = requested_inner_size;
        }
        if let Some(min_inner_size) = self.min_inner_size {
            spec.min_inner_size = min_inner_size;
        }
        if let Some(max_inner_size) = self.max_inner_size {
            spec.max_inner_size = max_inner_size;
        }
        if let Some(decorations) = self.decorations {
            spec.decorations = decorations;
        }
        if let Some(maximized) = self.maximized {
            spec.maximized = maximized;
        }
        if let Some(fullscreen) = self.fullscreen {
            spec.fullscreen = fullscreen;
        }
        if let Some(resizable) = self.resizable {
            spec.resizable = resizable;
        }
    }
}
