use gtk::{glib, prelude::*, subclass::prelude::*};
use rand::{thread_rng, Rng};

use crate::project::Project;

#[derive(Debug, Default, Eq, PartialEq, Clone, Copy, glib::Enum)]
#[enum_type(name = "PaneStyle")]
pub enum PaneStyle {
    #[default]
    Light,
    Dark,
}

mod imp {
    use std::cell::Cell;

    use super::*;

    #[derive(Debug, Default, gtk::CompositeTemplate, glib::Properties)]
    #[properties(wrapper_type = super::SymbolicPane)]
    #[template(resource = "/org/gnome/design/SymbolicPreview/symbolic_pane.ui")]
    pub struct SymbolicPane {
        #[template_child]
        pub(super) size_32_image: TemplateChild<gtk::Image>,
        #[template_child]
        pub(super) size_64_image: TemplateChild<gtk::Image>,
        #[template_child]
        pub(super) size_16_image: TemplateChild<gtk::Image>,
        #[template_child]
        pub(super) grid: TemplateChild<gtk::Grid>,
        #[template_child]
        pub(super) states: TemplateChild<gtk::Box>,
        #[template_child]
        pub(super) linked: TemplateChild<gtk::Box>,
        #[property(get, set, construct_only, builder(PaneStyle::default()))]
        pub(super) style: Cell<PaneStyle>,
    }

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

        fn class_init(klass: &mut Self::Class) {
            klass.bind_template();
            klass.set_css_name("symbolic-pane");
        }

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

    #[glib::derived_properties]
    impl ObjectImpl for SymbolicPane {
        fn constructed(&self) {
            self.parent_constructed();
            let obj = self.obj();
            if obj.style() == PaneStyle::Dark {
                obj.add_css_class("dark-pane");
            } else {
                obj.add_css_class("light-pane");
            }
        }
    }
    impl WidgetImpl for SymbolicPane {}
    impl BoxImpl for SymbolicPane {}
}

glib::wrapper! {
    pub struct SymbolicPane(ObjectSubclass<imp::SymbolicPane>)
        @extends gtk::Widget, gtk::Box;
}

impl SymbolicPane {
    pub fn new(style: PaneStyle) -> Self {
        glib::Object::builder().property("style", style).build()
    }

    pub fn load_from_project(&self, project: &Project) {
        let imp = self.imp();

        // The 64, 32, 16 sizes preview

        imp.size_16_image.set_from_icon_name(Some(&project.name()));
        imp.size_32_image.set_from_icon_name(Some(&project.name()));
        imp.size_64_image.set_from_icon_name(Some(&project.name()));

        // Replace the icon for the four states buttons
        let mut child = imp.states.first_child().unwrap();
        loop {
            let image = child
                .clone()
                .downcast::<gtk::Button>()
                .unwrap()
                .first_child()
                .and_downcast::<gtk::Image>()
                .unwrap();
            image.set_from_icon_name(Some(&project.name()));

            if let Some(next_child) = child.next_sibling() {
                child = next_child;
            } else {
                break;
            }
        }
    }

    pub fn load_samples(&self, samples: Vec<String>) {
        let imp = self.imp();
        let icon_name = imp.size_16_image.icon_name().unwrap();
        let mut icons = samples;

        let mut rng = thread_rng();
        let linked_idx: usize = rng.gen_range(0..3);
        let mut i = 0;

        let mut child = imp.linked.first_child().unwrap();
        loop {
            let image = child
                .clone()
                .downcast::<gtk::Button>()
                .unwrap()
                .first_child()
                .and_downcast::<gtk::Image>()
                .unwrap();

            if i == linked_idx {
                image.set_from_icon_name(Some(&icon_name));
            } else if let Some(icon_name) = icons.pop() {
                image.set_from_icon_name(Some(&icon_name));
            }
            i += 1;
            if let Some(next_child) = child.next_sibling() {
                child = next_child;
            } else {
                break;
            }
        }

        let cols = 6;
        let rows = 3;
        let (grid_i, grid_j) = (rng.gen_range(0..rows), rng.gen_range(0..cols));
        for i in 0..rows {
            for j in 0..cols {
                if let Some(child) = imp.grid.child_at(j, i) {
                    let child = child.downcast::<gtk::Image>().unwrap();
                    if i == grid_i && j == grid_j {
                        child.set_from_icon_name(Some(&icon_name));
                    } else if let Some(icon_name) = icons.pop() {
                        child.set_from_icon_name(Some(&icon_name));
                    }
                }
            }
        }
    }
}
