// Generated by RustGObjectGenerator
// This file is licensed under the same terms as the project it belongs to

use crate::backends::supported_terminals::TerminalRepository;
use crate::i18n::gettext;
use crate::root_store::RootStore;
use adw::prelude::*;
use adw::subclass::prelude::*;
use glib::Properties;
use glib::clone;
use glib::subclass::Signal;
use gtk::{StringObject, glib};
use std::cell::{Cell, RefCell};
use std::sync::OnceLock;

mod imp {
    use gtk::StringObject;

    use super::*;

    #[derive(Properties, Default)]
    #[properties(wrapper_type = super::TerminalComboRow)]
    pub struct TerminalComboRow {
        #[property(get, set)]
        root_store: RefCell<RootStore>,
        pub selected_item_signal_handler: RefCell<Option<glib::SignalHandlerId>>,
        pub is_rebuilding: Cell<bool>,
    }

    #[glib::derived_properties]
    impl ObjectImpl for TerminalComboRow {
        fn constructed(&self) {
            self.parent_constructed();
            let obj = self.obj();

            obj.set_title(&gettext("Preferred Terminal"));
            obj.set_use_subtitle(true);

            let signal_handler = obj.connect_selected_item_notify(clone!(
                #[weak]
                obj,
                move |combo| {
                    if obj.imp().is_rebuilding.get() {
                        return;
                    }

                    let Some(selected) = combo.selected_item().and_downcast::<StringObject>()
                    else {
                        return;
                    };

                    let root_store = obj.root_store();
                    let terminal_repository = root_store.terminal_repository();
                    let selected_terminal_setting: String =
                        root_store.settings().string("selected-terminal");
                    let loading_terminals = terminal_repository.json_terminals_query().is_loading()
                        || terminal_repository.flatpak_terminals_query().is_loading();

                    if !selected_terminal_setting.is_empty() && loading_terminals {
                        return;
                    }

                    if let Some(terminal) = obj
                        .root_store()
                        .terminal_repository()
                        .terminal_by_name(&selected.string())
                    {
                        obj.root_store().set_selected_terminal_name(&terminal.name);
                    }
                }
            ));

            self.selected_item_signal_handler
                .replace(Some(signal_handler));

            obj.rebuild_terminals_list();

            obj.root_store().terminal_repository().connect_closure(
                "terminals-changed",
                false,
                glib::closure_local!(
                    #[weak]
                    obj,
                    move |_: TerminalRepository| {
                        obj.rebuild_terminals_list();
                    }
                ),
            );
        }

        fn signals() -> &'static [Signal] {
            static SIGNALS: OnceLock<Vec<Signal>> = OnceLock::new();
            SIGNALS.get_or_init(|| {
                vec![
                    // No signals defined
                ]
            })
        }
    }

    #[glib::object_subclass]
    impl ObjectSubclass for TerminalComboRow {
        const NAME: &'static str = "TerminalComboRow";
        type Type = super::TerminalComboRow;
        type ParentType = adw::ComboRow;
    }

    impl WidgetImpl for TerminalComboRow {
        fn size_allocate(&self, width: i32, height: i32, baseline: i32) {
            self.parent_size_allocate(width, height, baseline);
        }

        fn snapshot(&self, snapshot: &gtk::Snapshot) {
            self.parent_snapshot(snapshot);
        }
    }

    // Generate Impl blocks for each parent class
    impl ComboRowImpl for TerminalComboRow {
        // Default implementations that forward to parent
    }

    impl ActionRowImpl for TerminalComboRow {
        // Default implementations that forward to parent
    }

    impl PreferencesRowImpl for TerminalComboRow {
        // Default implementations that forward to parent
    }

    impl ListBoxRowImpl for TerminalComboRow {
        // Default implementations that forward to parent
    }

    impl TerminalComboRow {
        // No template callbacks defined
    }
}

glib::wrapper! {
    pub struct TerminalComboRow(ObjectSubclass<imp::TerminalComboRow>)
        @extends adw::ComboRow, adw::ActionRow, adw::PreferencesRow, gtk::ListBoxRow, gtk::Widget,
        @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget, gtk::Actionable;
}

impl TerminalComboRow {
    pub fn new() -> Self {
        glib::Object::new()
    }

    pub fn new_with_params(root_store: RootStore) -> Self {
        glib::Object::builder()
            .property("root-store", root_store)
            .build()
    }

    pub fn set_selected_by_name(&self, name: &str) {
        let Some(terminals_strings) = self
            .model()
            .and_then(|model| model.downcast::<gtk::StringList>().ok())
        else {
            return;
        };
        for i in 0..terminals_strings.n_items() {
            let Some(item) = terminals_strings.item(i).and_downcast::<StringObject>() else {
                continue;
            };
            if item.string() == name {
                self.set_selected(i);
                return;
            }
        }
    }
    pub fn rebuild_terminals_list(&self) {
        let terminals = self
            .root_store()
            .clone()
            .terminal_repository()
            .all_terminals();
        let terminals = terminals
            .iter()
            .map(|x| x.name.as_ref())
            .collect::<Vec<_>>();

        let terminal_list = gtk::StringList::new(&terminals);

        let signal_handler = self.imp().selected_item_signal_handler.borrow();
        let Some(signal_handler) = signal_handler.as_ref() else {
            debug_assert!(
                false,
                "selected_item_signal_handler should be initialized in constructed"
            );
            return;
        };

        self.imp().is_rebuilding.set(true);
        self.block_signal(signal_handler);
        {
            self.set_model(Some(&terminal_list));
            if let Some(selected_terminal) = self.root_store().selected_terminal() {
                self.set_selected_by_name(&selected_terminal.name);
            }
        }
        self.unblock_signal(signal_handler);

        glib::idle_add_local_once(clone!(
            #[weak(rename_to = this)]
            self,
            move || {
                this.imp().is_rebuilding.set(false);
            }
        ));
    }
}

impl Default for TerminalComboRow {
    fn default() -> Self {
        Self::new()
    }
}
