use nalgebra_glm::{Vec2, Vec4};
use std::hash::{Hash, Hasher};

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

impl UiId {
    pub fn new(id: &str) -> Self {
        let mut hasher = std::collections::hash_map::DefaultHasher::new();
        id.hash(&mut hasher);
        Self(hasher.finish())
    }

    pub fn with_parent(parent: UiId, id: &str) -> Self {
        let mut hasher = std::collections::hash_map::DefaultHasher::new();
        parent.0.hash(&mut hasher);
        id.hash(&mut hasher);
        Self(hasher.finish())
    }
}

#[derive(Clone, Copy, Debug, Default)]
pub struct Rect {
    pub min: Vec2,
    pub max: Vec2,
}

impl Rect {
    pub fn new(x: f32, y: f32, width: f32, height: f32) -> Self {
        Self {
            min: Vec2::new(x, y),
            max: Vec2::new(x + width, y + height),
        }
    }

    pub fn from_min_max(min: Vec2, max: Vec2) -> Self {
        Self { min, max }
    }

    pub fn from_center_size(center: Vec2, size: Vec2) -> Self {
        let half = size * 0.5;
        Self {
            min: center - half,
            max: center + half,
        }
    }

    pub fn x(&self) -> f32 {
        self.min.x
    }

    pub fn y(&self) -> f32 {
        self.min.y
    }

    pub fn width(&self) -> f32 {
        self.max.x - self.min.x
    }

    pub fn height(&self) -> f32 {
        self.max.y - self.min.y
    }

    pub fn size(&self) -> Vec2 {
        Vec2::new(self.width(), self.height())
    }

    pub fn center(&self) -> Vec2 {
        (self.min + self.max) * 0.5
    }

    pub fn contains(&self, point: Vec2) -> bool {
        point.x >= self.min.x
            && point.x <= self.max.x
            && point.y >= self.min.y
            && point.y <= self.max.y
    }

    pub fn expand(&self, amount: f32) -> Self {
        Self {
            min: self.min - Vec2::new(amount, amount),
            max: self.max + Vec2::new(amount, amount),
        }
    }

    pub fn shrink(&self, amount: f32) -> Self {
        self.expand(-amount)
    }

    pub fn translate(&self, offset: Vec2) -> Self {
        Self {
            min: self.min + offset,
            max: self.max + offset,
        }
    }

    pub fn intersect(&self, other: &Rect) -> Option<Rect> {
        let min_x = self.min.x.max(other.min.x);
        let min_y = self.min.y.max(other.min.y);
        let max_x = self.max.x.min(other.max.x);
        let max_y = self.max.y.min(other.max.y);

        if min_x < max_x && min_y < max_y {
            Some(Rect {
                min: Vec2::new(min_x, min_y),
                max: Vec2::new(max_x, max_y),
            })
        } else {
            None
        }
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub enum ResizeEdge {
    #[default]
    None,
    Left,
    Right,
    Top,
    Bottom,
    TopLeft,
    TopRight,
    BottomLeft,
    BottomRight,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub enum DockPosition {
    #[default]
    None,
    Left,
    Right,
    Top,
    Bottom,
}

#[derive(Clone, Copy, Debug)]
pub struct WidgetState {
    pub hovered: bool,
    pub active: bool,
    pub focused: bool,
    pub drag_start: Option<Vec2>,
    pub panel_position: Option<Vec2>,
    pub panel_size: Option<Vec2>,
    pub collapsed: bool,
    pub resize_edge: ResizeEdge,
    pub resize_start_rect: Option<Rect>,
    pub open: bool,
    pub dock_position: DockPosition,
    pub undocked_rect: Option<Rect>,
    pub focus_order: i32,
    pub pinned: bool,
    pub resizable: bool,
    pub scroll_offset: Vec2,
    pub selected_tab: usize,
}

impl Default for WidgetState {
    fn default() -> Self {
        Self {
            hovered: false,
            active: false,
            focused: false,
            drag_start: None,
            panel_position: None,
            panel_size: None,
            collapsed: false,
            resize_edge: ResizeEdge::None,
            resize_start_rect: None,
            open: false,
            dock_position: DockPosition::None,
            undocked_rect: None,
            focus_order: 0,
            pinned: true,
            resizable: true,
            scroll_offset: Vec2::new(0.0, 0.0),
            selected_tab: 0,
        }
    }
}

#[derive(Clone, Debug)]
pub struct UiResponse {
    pub clicked: bool,
    pub double_clicked: bool,
    pub hovered: bool,
    pub active: bool,
    pub drag_started: bool,
    pub dragging: bool,
    pub drag_released: bool,
    pub changed: bool,
    pub rect: Rect,
    pub id: UiId,
}

impl UiResponse {
    pub fn none(id: UiId, rect: Rect) -> Self {
        Self {
            clicked: false,
            double_clicked: false,
            hovered: false,
            active: false,
            drag_started: false,
            dragging: false,
            drag_released: false,
            changed: false,
            rect,
            id,
        }
    }
}

#[derive(Clone, Debug)]
pub struct UiStyle {
    pub text_color: Vec4,
    pub text_color_disabled: Vec4,
    pub background_color: Vec4,
    pub background_color_hovered: Vec4,
    pub background_color_active: Vec4,
    pub border_color: Vec4,
    pub border_width: f32,
    pub corner_radius: f32,
    pub padding: f32,
    pub spacing: f32,
    pub font_size: f32,
    pub button_height: f32,
    pub slider_height: f32,
    pub checkbox_size: f32,
    pub panel_header_height: f32,
}

impl Default for UiStyle {
    fn default() -> Self {
        Self {
            text_color: Vec4::new(1.0, 1.0, 1.0, 1.0),
            text_color_disabled: Vec4::new(0.5, 0.5, 0.5, 1.0),
            background_color: Vec4::new(0.15, 0.15, 0.15, 1.0),
            background_color_hovered: Vec4::new(0.25, 0.25, 0.25, 1.0),
            background_color_active: Vec4::new(0.35, 0.35, 0.35, 1.0),
            border_color: Vec4::new(0.4, 0.4, 0.4, 1.0),
            border_width: 1.0,
            corner_radius: 4.0,
            padding: 8.0,
            spacing: 6.0,
            font_size: 18.0,
            button_height: 32.0,
            slider_height: 24.0,
            checkbox_size: 20.0,
            panel_header_height: 28.0,
        }
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Default)]
pub enum Anchor {
    #[default]
    TopLeft,
    TopCenter,
    TopRight,
    CenterLeft,
    Center,
    CenterRight,
    BottomLeft,
    BottomCenter,
    BottomRight,
}
