use cacao::appkit::FocusRingType;
use cacao::button::{BezelStyle, Button};
use cacao::color::Color;
use cacao::layout::{Layout, LayoutConstraint};
use cacao::text::{Font, Label, TextAlign};
use cacao::view::{View, ViewDelegate};

use crate::button_row::ButtonRow;
use crate::calculator::{dispatch, Msg};

pub const BUTTON_WIDTH: f64 = 57.;
pub const BUTTON_HEIGHT: f64 = 47.;

pub fn button(text: &str, msg: Msg) -> Button {
    let mut button = Button::new(text);
    button.set_bordered(false);
    button.set_bezel_style(BezelStyle::SmallSquare);
    button.set_focus_ring_type(FocusRingType::None);
    button.set_action(move || dispatch(msg.clone()));
    button.set_key_equivalent(&*text.to_lowercase());

    let font = Font::system(22.);
    button.set_font(&font);
    button.set_text_color(Color::SystemWhite);

    button
}

pub struct CalculatorView {
    pub results_wrapper: View,
    pub label: Label,
    pub row0: ButtonRow,
    pub row1: ButtonRow,
    pub row2: ButtonRow,
    pub row3: ButtonRow,
    pub dot: Button,
    pub zero: Button,
    pub equals: Button
}

impl CalculatorView {
    pub fn new() -> Self {
        let results_wrapper = View::new();

        let label = Label::new();
        let font = Font::system(40.);
        label.set_font(&font);
        label.set_text("0");
        label.set_text_color(Color::rgb(255, 255, 255));
        label.set_text_alignment(TextAlign::Right);

        Self {
            results_wrapper,
            label,

            row0: ButtonRow::new(
                [Msg::Clear, Msg::Invert, Msg::Mod, Msg::Divide],
                Color::rgb(69, 69, 69),
                Color::rgb(255, 148, 10)
            ),

            row1: ButtonRow::new(
                [Msg::Push(7), Msg::Push(8), Msg::Push(9), Msg::Multiply],
                Color::rgb(100, 100, 100),
                Color::rgb(255, 148, 10)
            ),

            row2: ButtonRow::new(
                [Msg::Push(4), Msg::Push(5), Msg::Push(6), Msg::Subtract],
                Color::rgb(100, 100, 100),
                Color::rgb(255, 148, 10)
            ),

            row3: ButtonRow::new(
                [Msg::Push(1), Msg::Push(2), Msg::Push(3), Msg::Add],
                Color::rgb(100, 100, 100),
                Color::rgb(255, 148, 10)
            ),

            zero: button("0", Msg::Push(0)),
            dot: button(".", Msg::Decimal),
            equals: button("=", Msg::Equals)
        }
    }

    pub fn render_update(&self, message: String) {
        self.label.set_text(&message);
    }
}

impl ViewDelegate for CalculatorView {
    const NAME: &'static str = "CalculatorView";

    fn did_load(&mut self, view: View) {
        view.set_background_color(Color::rgb(49, 49, 49));
        self.zero.set_background_color(Color::rgb(100, 100, 100));
        self.dot.set_background_color(Color::rgb(100, 100, 100));
        self.equals.set_background_color(Color::rgb(255, 148, 10));

        self.zero.set_key_equivalent("0");

        view.add_subview(&self.row0.view);
        view.add_subview(&self.row1.view);
        view.add_subview(&self.row2.view);
        view.add_subview(&self.row3.view);

        for button in &[&self.zero, &self.dot, &self.equals] {
            view.add_subview(button);
        }

        self.results_wrapper.add_subview(&self.label);
        view.add_subview(&self.results_wrapper);

        LayoutConstraint::activate(&[
            self.results_wrapper.top.constraint_equal_to(&view.top),
            self.results_wrapper.leading.constraint_equal_to(&view.leading),
            self.results_wrapper.trailing.constraint_equal_to(&view.trailing),
            self.results_wrapper.height.constraint_equal_to_constant(80.),
            self.label
                .leading
                .constraint_equal_to(&self.results_wrapper.leading)
                .offset(22.),
            self.label
                .trailing
                .constraint_equal_to(&self.results_wrapper.trailing)
                .offset(-16.),
            self.label
                .bottom
                .constraint_equal_to(&self.results_wrapper.bottom)
                .offset(-4.),
            // Buttons laid out from top-left
            self.row0
                .view
                .top
                .constraint_equal_to(&self.results_wrapper.bottom)
                .offset(1.),
            self.row0.view.leading.constraint_equal_to(&view.leading),
            self.row0.view.trailing.constraint_equal_to(&view.trailing),
            self.row1.view.top.constraint_equal_to(&self.row0.view.bottom).offset(1.),
            self.row1.view.leading.constraint_equal_to(&view.leading),
            self.row1.view.trailing.constraint_equal_to(&view.trailing),
            self.row2.view.top.constraint_equal_to(&self.row1.view.bottom).offset(1.),
            self.row2.view.leading.constraint_equal_to(&view.leading),
            self.row2.view.trailing.constraint_equal_to(&view.trailing),
            self.row3.view.top.constraint_equal_to(&self.row2.view.bottom).offset(1.),
            self.row3.view.leading.constraint_equal_to(&view.leading),
            self.row3.view.trailing.constraint_equal_to(&view.trailing),
            self.zero.top.constraint_equal_to(&self.row3.view.bottom).offset(1.),
            self.zero.leading.constraint_equal_to(&view.leading),
            self.zero.bottom.constraint_equal_to(&view.bottom),
            self.dot.top.constraint_equal_to(&self.row3.view.bottom).offset(1.),
            self.dot.leading.constraint_equal_to(&self.zero.trailing).offset(1.),
            self.dot.bottom.constraint_equal_to(&view.bottom),
            self.dot.width.constraint_equal_to_constant(BUTTON_WIDTH),
            self.dot.height.constraint_equal_to_constant(BUTTON_HEIGHT),
            self.equals.top.constraint_equal_to(&self.row3.view.bottom).offset(1.),
            self.equals.leading.constraint_equal_to(&self.dot.trailing).offset(1.),
            self.equals.trailing.constraint_equal_to(&view.trailing),
            self.equals.bottom.constraint_equal_to(&view.bottom),
            self.equals.width.constraint_equal_to_constant(BUTTON_WIDTH),
            self.equals.height.constraint_equal_to_constant(BUTTON_HEIGHT)
        ])
    }
}