use kahlo::prelude::*;

use crate::{
    component::Component,
    input::MouseButton,
    layout::{LayoutNode, LayoutNodeAccess, LayoutNodeContainer, SizePolicy},
    ui::UIHandle,
};

use super::{Label, Widget};

enum ButtonState {
    Idle,
    Hovered,
    Clicked,
}

pub struct Button<C: Component> {
    layout: LayoutNode,
    label: Label<C>,
    state: ButtonState,
    hook: Option<Box<dyn Fn() -> Option<C::Msg>>>,
}

impl<C: Component> Button<C> {
    pub fn new(uih: &UIHandle) -> Self {
        let mut layout = uih.new_layout_node();
        layout
            .set_width_policy(SizePolicy::expands(1))
            .set_height_policy(SizePolicy {
                minimum: uih.theme().ui_font_size as usize,
                desired: (uih.theme().ui_font_size * 1.5) as usize,
                slack_weight: 0,
            });
        Self {
            layout,
            label: Label::new(uih),
            state: ButtonState::Idle,
            hook: None,
        }
    }

    pub fn set_label(&mut self, label: &str) {
        self.label.set_text(label);
    }

    pub fn set_hook(&mut self, to: Box<dyn Fn() -> Option<C::Msg>>) {
        self.hook = Some(to);
    }
}

impl<C: Component> LayoutNodeContainer for Button<C> {
    fn layout_node(&self) -> &LayoutNode {
        &self.layout
    }
    fn layout_child(&self, ndx: usize) -> Option<LayoutNodeAccess<'_>> {
        if ndx == 0 {
            Some(self.label.layout_node())
        } else {
            None
        }
    }
    fn layout_child_count(&self) -> usize {
        1
    }
}

impl<C: Component> Widget<C> for Button<C> {
    fn poll(
        &mut self,
        uih: &mut UIHandle,
        input_state: Option<&crate::input::InputState>,
    ) -> Vec<<C as Component>::Msg> {
        let mut result: Vec<<C as Component>::Msg> = vec![];
        if let (Some(istate), Some(area)) = (input_state, self.layout.render_area()) {
            if area.contains_inclusive(istate.mouse.pos) {
                println!("cursor on button");
                if istate.mouse.buttons.active(MouseButton::Left) {
                    self.state = ButtonState::Clicked;
                } else if istate.mouse.released().active(MouseButton::Left) {
                    self.state = ButtonState::Idle;
                    result.extend(self.hook.as_ref().map(|v| v()).flatten().into_iter());
                } else {
                    self.state = ButtonState::Hovered;
                }
            } else {
                self.state = ButtonState::Idle;
            }
        }
        result.extend(self.label.poll(uih, input_state).into_iter());
        result
    }

    fn layout_node(&self) -> LayoutNodeAccess {
        LayoutNodeAccess::new(self)
    }
    fn layout_node_mut(&mut self) -> &mut LayoutNode {
        &mut self.layout
    }

    fn render(&self, theme: &crate::theme::Theme, target: &mut kahlo::RgbaBitmap) {
        let colour = match self.state {
            ButtonState::Idle => theme.background,
            ButtonState::Hovered => theme.panel,
            ButtonState::Clicked => theme.foreground,
        };
        target.fill_region(self.layout.render_area().unwrap(), colour);
        self.label.render(theme, target);
    }
}