use adw::{prelude::*, subclass::prelude::*};
use gettextrs::gettext;
use gtk::{gio, glib};

use super::{config, window::Window};

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

    use super::*;

    #[derive(Default, Debug)]
    pub struct Application {
        pub(super) counter: Cell<u8>,
    }

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

    impl ObjectImpl for Application {
        fn constructed(&self) {
            self.parent_constructed();

            self.obj().add_main_option(
                "new-window",
                glib::Char::from(b'w'),
                glib::OptionFlags::NONE,
                glib::OptionArg::None,
                &gettext("Open a new window"),
                None,
            );
        }
    }

    impl ApplicationImpl for Application {
        fn handle_local_options(&self, options: &glib::VariantDict) -> glib::ExitCode {
            let app = self.obj();

            if options.contains("new-window") {
                if let Err(err) = app.register(None::<&gio::Cancellable>) {
                    tracing::error!("Failed to register the application: {err}");
                }

                if app.is_remote() {
                    app.activate_action("new-window", None);
                    return glib::ExitCode::SUCCESS;
                }
            }

            self.parent_handle_local_options(options)
        }

        fn startup(&self) {
            self.parent_startup();
            self.obj().setup_actions();
        }

        fn activate(&self) {
            self.parent_activate();
            let app = self.obj();
            if let Some(window) = app.active_window() {
                window.present();
                return;
            }

            // No window available -> we have to create one
            let window = app.create_window();
            let group = gtk::WindowGroup::new();
            group.add_window(&window);
            window.present();
            tracing::info!("Created application window.");
        }
    }

    impl GtkApplicationImpl for Application {}

    impl AdwApplicationImpl for Application {}
}

glib::wrapper! {
    pub struct Application(ObjectSubclass<imp::Application>)
        @extends gio::Application, gtk::Application, adw::Application,
        @implements gio::ActionMap, gio::ActionGroup;
}

impl Application {
    pub fn run() -> glib::ExitCode {
        tracing::info!("Contrast ({})", config::APP_ID);
        tracing::info!("Version: {} ({})", config::VERSION, config::PROFILE);
        tracing::info!("Datadir: {}", config::PKGDATADIR);

        // Create new GObject and downcast it into Application
        glib::Object::builder::<Self>()
            .property("application-id", config::APP_ID)
            .property("resource-base-path", "/org/gnome/design/Contrast")
            .build()
            .run()
    }

    fn create_window(&self) -> Window {
        let counter = self.imp().counter.get() + 1;
        self.imp().counter.set(counter);
        Window::new(self, counter)
    }

    fn setup_actions(&self) {
        // New Window
        let new_action = gio::ActionEntry::builder("new-window")
            .activate(|app: &Self, _, _| {
                let win = app.create_window();
                win.present();
            })
            .build();

        // Quit
        let quit_action = gio::ActionEntry::builder("quit")
            .activate(|app: &Self, _, _| app.quit())
            .build();

        // About
        let about_action = gio::ActionEntry::builder("about")
            .activate(|app: &Self, _, _| {
                let window = app.active_window().unwrap();
                adw::AboutDialog::builder()
                    .application_name(gettext("Contrast"))
                    .version(config::VERSION)
                    .comments(gettext("Check contrast between two colors"))
                    .website("https://gitlab.gnome.org/World/design/contrast")
                    .developers(vec![
                        "Bilal Elmoussaoui",
                        "Zander Brown",
                        "Christopher Davis",
                    ])
                    .artists(vec!["Jakub Steiner", "Tobias Bernard"])
                    .translator_credits(gettext("translator-credits"))
                    .application_icon(config::APP_ID)
                    .license_type(gtk::License::Gpl30)
                    .build()
                    .present(Some(&window));
            })
            .build();
        self.add_action_entries([new_action, quit_action, about_action]);
        // Accels
        self.set_accels_for_action("app.quit", &["<primary>q"]);
        self.set_accels_for_action("app.new-window", &["<primary>n"]);
        self.set_accels_for_action("window.close", &["<primary>w"]);
        self.set_accels_for_action("win.reverse-colors", &["<primary>r"]);
    }
}
