// SPDX-License-Identifier: GPL-3.0-or-later
use gtk::gdk;
use gtk::glib;
use gtk::subclass::prelude::*;
use gtk::{CompositeTemplate, prelude::*};

use crate::utils;

mod imp {
    use super::*;

    use glib::Properties;

    use std::cell::Cell;

    #[derive(Debug, Properties, CompositeTemplate)]
    #[template(resource = "/org/gnome/design/Emblem/ui/color_button.ui")]
    #[properties(wrapper_type = super::ColorButton)]
    pub struct ColorButton {
        #[property(get, set = Self::set_color, explicit_notify)]
        pub color: Cell<gdk::RGBA>,
    }

    impl Default for ColorButton {
        fn default() -> Self {
            let color = gdk::RGBA::BLACK;

            Self {
                color: Cell::new(color),
            }
        }
    }

    #[glib::object_subclass]
    impl ObjectSubclass for ColorButton {
        const NAME: &'static str = "ColorButton";
        type Type = super::ColorButton;
        type ParentType = gtk::Button;

        fn class_init(klass: &mut Self::Class) {
            klass.bind_template();
            klass.bind_template_callbacks();
        }

        fn instance_init(obj: &glib::subclass::InitializingObject<Self>) {
            obj.init_template();
        }
    }

    #[glib::derived_properties]
    impl ObjectImpl for ColorButton {}
    impl WidgetImpl for ColorButton {}
    impl ButtonImpl for ColorButton {}

    #[gtk::template_callbacks]
    impl ColorButton {
        #[template_callback]
        async fn on_clicked(&self) {
            if let Err(err) = self.obj().present_dialog().await {
                log::error!("Could not select color: {err}");
            }
        }

        pub fn set_color(&self, color: gdk::RGBA) {
            let widget = self.obj();
            if color != self.color.replace(color) {
                if utils::black_has_better_contrast(&color) {
                    widget.add_css_class("black");
                    widget.remove_css_class("white");
                } else {
                    widget.add_css_class("white");
                    widget.remove_css_class("black");
                }

                widget.notify_color();
            }
        }
    }
}

glib::wrapper! {
    pub struct ColorButton(ObjectSubclass<imp::ColorButton>)
        @extends gtk::Widget, gtk::Button,
        @implements gtk::ConstraintTarget, gtk::Buildable, gtk::Accessible, gtk::Actionable;
}

impl Default for ColorButton {
    fn default() -> Self {
        glib::Object::new()
    }
}

impl ColorButton {
    async fn present_dialog(&self) -> anyhow::Result<()> {
        let window = self.root().and_downcast::<crate::Window>().unwrap();
        let dialog = gtk::ColorDialog::new();

        let mut color = match dialog
            .choose_rgba_future(Some(&window), Some(&self.color()))
            .await
        {
            Err(err) if err.matches(gtk::DialogError::Dismissed) => return Ok(()),
            res => res?,
        };

        // We do not allow alpha < 1.0;
        color.set_alpha(1.0);
        self.set_color(color);

        Ok(())
    }
}
