import { Gdk, Gio, GLib, Gtk } from './src/dependencies/prefs/gi.js';
import { ExtensionPreferences } from './src/dependencies/prefs.js';

import LayoutPrefs from './src/prefs/layoutsPrefs.js';
import { Shortcuts } from './src/common.js';
import './src/prefs/shortcutListener.js';

export default class Prefs extends ExtensionPreferences {
    fillPreferencesWindow(window) {
        // Load css file
        const provider = new Gtk.CssProvider();
        const path = GLib.build_filenamev([this.path, 'stylesheet.css']);
        provider.load_from_path(path);
        Gtk.StyleContext.add_provider_for_display(
            Gdk.Display.get_default(),
            provider,
            Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
        );

        window.set_can_navigate_back(true);

        const settings = this.getSettings();
        const builder = new Gtk.Builder();
        builder.set_translation_domain(this.uuid);
        builder.add_from_file(`${this.path}/src/ui/prefs.ui`);

        // Add general preference page
        window.add(builder.get_object('general'));

        // Add keybindings preference page
        window.add(builder.get_object('keybindings'));

        // Add layouts preference page on condition of advanced setting
        const layoutsPage = builder.get_object('layouts');
        settings.connect('changed::enable-advanced-experimental-features', () => {
            settings.get_boolean('enable-advanced-experimental-features')
                ? window.add(layoutsPage)
                : window.remove(layoutsPage);
        });

        if (settings.get_boolean('enable-advanced-experimental-features'))
            window.add(layoutsPage);

        // Bind settings to GUI
        this._bindSwitches(settings, builder);
        this._bindSpinbuttons(settings, builder);
        this._bindComboRows(settings, builder);
        this._bindRadioButtons(settings, builder);
        this._bindKeybindings(settings, builder);
        this._bindColorButtons(settings, builder);

        // LayoutPrefs manages everything related to layouts on the
        // prefs side (including the keyboard shortcuts)
        new LayoutPrefs(settings, builder, this.path);

        // Set visibility for deprecated settings
        this._setDeprecatedSettings(settings, builder);

        // Add a button into the headerbar with info
        this._addHeaderBarInfoButton(window, settings, builder);
    }

    /*
    * Bind GUI switches to settings.
    */
    _bindSwitches(settings, builder) {
        const switches = [
            'enable-tiling-popup',
            'tiling-popup-all-workspace',
            'enable-raise-tile-group',
            'tilegroups-in-app-switcher',
            'maximize-with-gap',
            'show-layout-panel-indicator',
            'enable-advanced-experimental-features',
            'disable-tile-groups',
            'low-performance-move-mode',
            'monitor-switch-grace-period',
            'adapt-edge-tiling-to-favorite-layout',
            'enable-tile-animations',
            'enable-untile-animations',
            'enable-hold-maximize-inverse-landscape',
            'enable-hold-maximize-inverse-portrait'
        ];

        switches.forEach(key => {
            const widget = builder.get_object(key.replaceAll('-', '_'));
            settings.bind(key, widget, 'active', Gio.SettingsBindFlags.DEFAULT);
        });
    }

    /*
    * Bind GUI spinbuttons to settings.
    */
    _bindSpinbuttons(settings, builder) {
        const spinButtons = [
            'window-gap',
            'single-screen-gap',
            'screen-top-gap',
            'screen-left-gap',
            'screen-right-gap',
            'screen-bottom-gap',
            'focus-hint-outline-size',
            'focus-hint-outline-border-radius',
            'toggle-maximize-tophalf-timer',
            'vertical-preview-area',
            'horizontal-preview-area'
        ];

        spinButtons.forEach(key => {
            const widget = builder.get_object(key.replaceAll('-', '_'));
            settings.bind(key, widget, 'value', Gio.SettingsBindFlags.DEFAULT);
        });
    }

    /*
    * Bind GUI AdwComboRows to settings.
    */
    _bindComboRows(settings, builder) {
        const comboRows = [
            'focus-hint-outline-style',
            'move-adaptive-tiling-mod',
            'move-favorite-layout-mod',
            'ignore-ta-mod'
        ];

        comboRows.forEach(key => {
            const widget = builder.get_object(key.replaceAll('-', '_'));
            settings.bind(key, widget, 'selected', Gio.SettingsBindFlags.DEFAULT);
            widget.set_selected(settings.get_int(key));
        });
    }

    /*
    * Bind GUI color buttons to settings.
    */
    _bindColorButtons(settings, builder) {
        const switches = [
            'focus-hint-color'
        ];

        switches.forEach(key => {
            const widget = builder.get_object(`${key.replaceAll('-', '_')}_button`);
            widget.connect('color-set', () => {
                settings.set_string(key, widget.get_rgba().to_string());
            });

            // initialize color
            const rgba = new Gdk.RGBA();
            rgba.parse(settings.get_string(key));
            widget.set_rgba(rgba);
        });
    }

    /*
    * Bind radioButtons to settings.
    */
    _bindRadioButtons(settings, builder) {
        // These 'radioButtons' are basically just used as a 'fake ComboBox' with
        // explanations for the different options. So there is just *one* gsetting
        // (an int) which saves the current 'selection'.
        const radioButtons = [
            {
                key: 'dynamic-keybinding-behavior',
                rowNames: [
                    'dynamic_keybinding_disabled_row',
                    'dynamic_keybinding_window_focus_row',
                    'dynamic_keybinding_tiling_state_row',
                    'dynamic_keybinding_tiling_state_windows_row',
                    'dynamic_keybinding_favorite_layout_row'
                ]
            },
            {
                key: 'focus-hint',
                rowNames: [
                    'disabled_focus_hint_row',
                    'animated_outline_focus_hint_row',
                    'animated_upscale_focus_hint_row',
                    'static_outline_focus_hint_row'
                ]
            },
            {
                key: 'default-move-mode',
                rowNames: [
                    'edge_tiling_row',
                    'adaptive_tiling_row',
                    'favorite_layout_row',
                    'ignore_ta_row'
                ]
            }
        ];

        radioButtons.forEach(({ key, rowNames }) => {
            const currActive = settings.get_int(key);

            rowNames.forEach((name, idx) => {
                const row = builder.get_object(name.replaceAll('-', '_'));
                const checkButton = row.activatable_widget;
                checkButton.connect('toggled', () => settings.set_int(key, idx));

                // Set initial state
                if (idx === currActive)
                    checkButton.activate();
            });
        });
    }

    /*
    * Bind keybinding widgets to settings.
    */
    _bindKeybindings(settings, builder) {
        const shortcuts = Shortcuts.getAllKeys();
        shortcuts.forEach(key => {
            const shortcut = builder.get_object(key.replaceAll('-', '_'));
            shortcut.initialize(key, settings);
        });
    }

    /**
     * Sets the visibility of deprecated settings. Those setting aren't visible
     * in the GUI unless they have a user set value. That means they aren't
     * discoverable through the GUI and need to first be set with the gsetting.
     * The normal rows should have the id of: GSETTING_WITH_UNDERSCORES_row.
     * ShortcutListeners have the format of GSETTING_WITH_UNDERSCORES.
     */
    _setDeprecatedSettings(settings, builder) {
        // Keybindings
        ['toggle-tiling-popup', 'auto-tile'].forEach(s => {
            const isNonDefault = settings.get_strv(s)[0] !== settings.get_default_value(s).get_strv()[0];
            builder.get_object(s.replaceAll('-', '_')).set_visible(isNonDefault);
        });

        // Switches
        ['tilegroups-in-app-switcher'].forEach(s => {
            const isNonDefault = settings.get_boolean(s) !== settings.get_default_value(s).get_boolean();
            builder.get_object(s.replaceAll('-', '_')).set_visible(isNonDefault);
        });
    }

    _addHeaderBarInfoButton(window, settings, builder) {
        // Add headerBar button for menu
        // TODO: is this a 'reliable' method to access the headerbar?
        const page = builder.get_object('general');
        const gtkStack = page
            .get_parent()
            .get_parent()
            .get_parent();
        const adwHeaderBar = gtkStack
            .get_next_sibling()
            .get_first_child()
            .get_first_child()
            .get_first_child();

        adwHeaderBar.pack_start(builder.get_object('info_menu'));

        // Setup menu actions
        const actionGroup = new Gio.SimpleActionGroup();
        window.insert_action_group('prefs', actionGroup);

        const bugReportAction = new Gio.SimpleAction({ name: 'open-bug-report' });
        bugReportAction.connect('activate', this._openBugReport.bind(this, window));
        actionGroup.add_action(bugReportAction);

        const userGuideAction = new Gio.SimpleAction({ name: 'open-user-guide' });
        userGuideAction.connect('activate', this._openUserGuide.bind(this, window));
        actionGroup.add_action(userGuideAction);

        const changelogAction = new Gio.SimpleAction({ name: 'open-changelog' });
        changelogAction.connect('activate', this._openChangelog.bind(this, window));
        actionGroup.add_action(changelogAction);

        const licenseAction = new Gio.SimpleAction({ name: 'open-license' });
        licenseAction.connect('activate', this._openLicense.bind(this, window));
        actionGroup.add_action(licenseAction);

        const hiddenSettingsAction = new Gio.SimpleAction({ name: 'open-hidden-settings' });
        hiddenSettingsAction.connect('activate', this._openHiddenSettings.bind(this, window, builder));
        actionGroup.add_action(hiddenSettingsAction);

        // Button to return to main settings page
        const returnButton = builder.get_object('hidden_settings_return_button');
        returnButton.connect('clicked', () => window.close_subpage());
    }

    _openBugReport(window) {
        Gtk.show_uri(window, 'https://github.com/Leleat/Tiling-Assistant/issues', Gdk.CURRENT_TIME);
    }

    _openUserGuide(window) {
        Gtk.show_uri(window, 'https://github.com/Leleat/Tiling-Assistant/wiki', Gdk.CURRENT_TIME);
    }

    _openChangelog(window) {
        Gtk.show_uri(window, 'https://github.com/Leleat/Tiling-Assistant/blob/main/CHANGELOG.md', Gdk.CURRENT_TIME);
    }

    _openLicense(window) {
        Gtk.show_uri(window, 'https://github.com/Leleat/Tiling-Assistant/blob/main/LICENSE', Gdk.CURRENT_TIME);
    }

    _openHiddenSettings(window, builder) {
        const hiddenSettingsPage = builder.get_object('hidden_settings');
        window.present_subpage(hiddenSettingsPage);
    }
}
