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

mod imp {
    use super::*;

    use std::cell::Cell;

    use glib::Properties;

    #[derive(Properties)]
    #[properties(wrapper_type = super::ColorWidget)]
    pub struct ColorWidget {
        #[property(get, set = Self::set_color, explicit_notify)]
        pub color: Cell<gdk::RGBA>,
        #[property(get, set = Self::set_gradient, explicit_notify)]
        pub gradient: Cell<gdk::RGBA>,
    }

    impl Default for ColorWidget {
        fn default() -> Self {
            let color = Cell::new(gdk::RGBA::BLACK);
            let gradient = Cell::new(gdk::RGBA::BLACK);

            Self { color, gradient }
        }
    }

    impl ColorWidget {
        pub fn set_color(&self, color: gdk::RGBA) {
            if color != self.color.replace(color) {
                self.obj().notify_color();
                self.obj().queue_draw();
            }
        }

        pub fn set_gradient(&self, color: gdk::RGBA) {
            if color != self.gradient.replace(color) {
                self.obj().notify_gradient();
                self.obj().queue_draw();
            }
        }
    }

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

        fn class_init(klass: &mut Self::Class) {
            klass.set_layout_manager_type::<gtk::BinLayout>();
        }
    }

    #[glib::derived_properties]
    impl ObjectImpl for ColorWidget {}
    impl WidgetImpl for ColorWidget {
        fn snapshot(&self, snapshot: &gtk::Snapshot) {
            let widget = self.obj();
            let width = widget.width() as f32;
            let height = widget.height() as f32;

            let rect = graphene::Rect::new(0.0, 0.0, width, height);

            let bound_rect = gsk::RoundedRect::from_rect(rect, 6.0);

            snapshot.push_rounded_clip(&bound_rect);

            let color_start = gsk::ColorStop::new(0.0, self.color.get());
            let color_end = gsk::ColorStop::new(1.0, self.gradient.get());

            let start = graphene::Point::new(0.0, 0.0);
            let end = graphene::Point::new(0.0, height);

            snapshot.append_linear_gradient(&rect, &start, &end, &[color_start, color_end]);
            snapshot.pop();
        }
    }
}

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

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

impl ColorWidget {
    pub fn new(&self) -> Self {
        Self::default()
    }
}
