/* prefs.js
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

/* exported init fillPreferencesWindow */

const { Adw, Gio, GLib, GObject, Gtk } = imports.gi;

const ExtensionUtils = imports.misc.extensionUtils;

const _ = ExtensionUtils.gettext;

function checkAppId(appId) {
    if (appId.indexOf('.desktop') < 0) return false;
    if (GLib.file_test(appId, GLib.FileTest.EXISTS) ||
        GLib.file_test('/usr/share/applications/'+appId, GLib.FileTest.EXISTS) ||
        GLib.file_test(GLib.get_home_dir()+'/.local/share/applications/'+appId, GLib.FileTest.EXISTS) ||
        Glib.find_program_in_path(appId)) return true;
    return false;
}

function getPreferences(group) {
    let prefGroup = null;
    switch (group) {
    case 'activities':
        prefGroup = {
            title: _('Activities'),
            prefs: [
                {
                    type: 'combo',
                    key: 'show-activities',
                    title: _('Activities button'),
                    subtitle: _('Select the desired display mode of Activities button in the top bar.'),
                    values: {
                        'default': _('Default'),
                        'icon': _('GNOME icon'),
                        'none': _('Not show'),
                    },
                },
            ],
        };
        break;
    case 'applications':
        prefGroup = {
            title: _('Applications'),
            prefs: [
                {
                    type: 'combo',
                    key: 'show-applications',
                    title: _('Applications button'),
                    subtitle: _('Select the display mode for the Applications button in the top bar.'),
                    values: {
                        'all' : _('Icon and Text'),
                        'icon': _('Icon'),
                        'text': _('Text'),
                    },
                },
                {
                    type: 'label',
                    title: _('Change which applications are shown'),
                    subtitle: _('To specify which applications will be displayed the GNOME menu editor (alacarte) is required.'),
                },
            ],
        };
        if (GLib.file_test('/usr/bin/alacarte', GLib.FileTest.EXISTS)) prefGroup.prefs.splice(1, 1, {
            type : 'button',
            title: _('Change which applications are shown'),
            subtitle: _('Use the GNOME menu editor to specify which applications will be displayed.'),
            app: 'alacarte',
            appName: _('Main Menu'),
        });
        break;
    case 'places':
        prefGroup = {
            title: _('Places'),
            prefs: [
                {
                    type: 'combo',
                    key: 'show-places',
                    title: _('Places button'),
                    subtitle: _('Select the display mode for the Places button in the top bar.'),
                    values: {
                        'all': _('Icon and Text'),
                        'icon': _('Icon'),
                        'text': _('Text'),
                    },
                },
                {
                    type: 'combo',
                    key: 'bookmarks-position',
                    title: _('Position of Bookmarks'),
                    subtitle: _('Select the position for the bookmarks in the places menu. Custom bookmarks are displayed in a submenu.'),
                    values: {
                         'mainmenu': _('Main Menu'),
                         'submenu': _('Submenu'),
                    },
                },
            ],
        };
        break;
    case 'favorites':
        prefGroup = {
            title: _('Favorites'),
            prefs: [
                {
                    type: 'combo',
                    key: 'show-favorites',
                    title: _('Favorites button'),
                    subtitle: _('Select the display mode for the Favorites button in the top bar.'),
                    values: {
                        'all': _('Icon and Text'),
                        'icon': _('Icon'),
                        'text': _('Text'),
                    },
                },
                {
                    type: 'combo',
                    key: 'favorites-scope',
                    title: _('Menu of the Favorites button'),
                    subtitle: _('Change which favorites are shown in the Favorites button menu.'),
                    values: {
                        'own': _('Only own Favorites'),
                        'all': _('All Favorites'),
                    },
                },
                {
                    type: 'favorites',
                    key: 'favorite-apps',
                    title: _('Own Favorites'),
                },
            ],
        };
        break;
    case 'datemenu':
        prefGroup = {
            title: _('Date and Time'),
            prefs: [
                {
                    type: 'switcher',
                    key: 'show-events-icon',
                    title: _('Show day events icon'),
                    subtitle: _('If true, display day events icon, in addition to time.'),
                },
            ],
        };
        break;
    case 'systemmenu':
        prefGroup = {
            title: _('System menu'),
            prefs: [
                {
                    type: 'combo',
                    key: 'show-systemmenu',
                    title: _('System menu button'),
                    subtitle: _('Select the display mode for the System menu button in the top bar.'),
                    values: {
                        'default': _('Default'),
                        'icon': _('Shutdown icon'),
                    },
                },
                {
                    type: 'switcher',
                    key: 'show-gnome-terminal',
                    title: _('Show GNOME Terminal'),
                    subtitle: _('Specifies whether or not to show GNOME Terminal in the system menu.'),
                },
                {
                    type: 'switcher',
                    key: 'show-hibernate',
                    title: _('Show Hibernate'),
                    subtitle: _('If true, display Hibernate item in the Shutdown menu.'),
                },
                {
                    type: 'switcher',
                    key: 'show-hybrid-sleep',
                    title: _('Show Hybrid Sleep'),
                    subtitle: _('If true, display Hybrid Sleep item in the Shutdown menu.'),
                },
                {
                    type: 'switcher',
                    key: 'show-suspend-then-hibernate',
                    title: _('Show Suspend Then Hibernate'),
                    subtitle: _('If true, display Suspend Then Hibernate item in the Shutdown menu.'),
                },
                {
                    type: 'switcher',
                    key: 'show-basic-settings',
                    title: _('Show Basic Settings'),
                    subtitle: _('Determines whether or not Basic Settings on System menu is displayed.'),
                },
            ],
        };
        break;
    case 'dash':
        prefGroup = {
            title: _('Dash'),
            prefs: [
                {
                    type: 'switcher',
                    key: 'show-dash',
                    title: _('Dash'),
                    subtitle: _('Determines whether the Dash is displayed or not.'),
                },
                {
                    type: 'switcher',
                    key: 'show-dash-applications',
                    title: _('Show Applications icon on Dash'),
                    subtitle: _('Determines whether the Show Applications icon in the Dash is displayed or not.'),
                },
            ],
        };
        break;
    default: // Nothing to do
    }
    return prefGroup;
}

const PanelManagerPreferences = GObject.registerClass(
class PanelManagerPreferences extends Adw.PreferencesPage {
    _init(settings) {
        super._init();
        this._settings = settings;
        this._createPrefGroup(getPreferences('activities'));
        this._createPrefGroup(getPreferences('applications'));
        this._createPrefGroup(getPreferences('places'));
        this._createPrefGroup(getPreferences('favorites'));
        this._createPrefGroup(getPreferences('datemenu'));
        this._createPrefGroup(getPreferences('systemmenu'));
        this._createPrefGroup(getPreferences('dash'));
    }

    _createPrefGroup(group) {
        if (group === null) return;
        let prefGroup = new Adw.PreferencesGroup({ title: group.title });
        group.prefs.forEach(pref => {
            let prefWidget = this._createPrefWidget(pref);
            if (prefWidget !== null) prefGroup.add(prefWidget);
        });
        this.add(prefGroup);
    }

    _createPrefWidget(pref) {
        let prefAction = null;
        let prefWidget = null;
        let settings = pref.schema ? new Gio.Settings({ schema_id: pref.schema }) : this._settings;
        switch (pref.type) {
        case 'button':
            prefAction = new Gtk.Button({
                label: pref.appName,
                valign: Gtk.Align.CENTER,
                width_request: 240,
            });
            prefAction.connect('clicked', () => {
                let [success, argv] = GLib.shell_parse_argv(pref.app);
                GLib.spawn_async(null, argv, null, GLib.SpawnFlags.SEARCH_PATH, null);
            });
            break;
        case 'combo':
            let model = new Gtk.StringList();
            for (let value in pref.values) {
                model.append(pref.values[value]);
            }
            prefAction = new Gtk.DropDown({
                model: model,
                valign: Gtk.Align.CENTER,
                width_request: 240,
            });
            prefAction.connect('notify::selected-item', () => {
                settings.set_enum(pref.key, prefAction.get_selected());
            });
            prefAction.set_selected(settings.get_enum(pref.key));
            settings.connect('changed::%s'.format(pref.key), () => {
                prefAction.set_selected(settings.get_enum(pref.key));
            });
            break;
        case 'favorites':
            let child = new Gtk.Box({
                orientation: Gtk.Orientation.VERTICAL,
                margin_top: 12,
                margin_bottom: 12,
                margin_start: 12,
                margin_end: 12,
                spacing: 8,
            });
            prefWidget = new Adw.PreferencesRow({ child: child }); 
            let frame = new Gtk.Frame();
            child.append(frame);
            let scrolledWindow = new Gtk.ScrolledWindow();
            scrolledWindow.set_min_content_height(200);
            scrolledWindow.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
            frame.set_child(scrolledWindow);
            let listStore = new Gtk.ListStore();
            listStore.set_column_types([ GObject.TYPE_STRING, GObject.TYPE_STRING ]);
            let treeView = new Gtk.TreeView({ model: listStore });
            scrolledWindow.set_child(treeView);
            treeView.get_selection().set_mode(Gtk.SelectionMode.SINGLE);
            let columnFavorites = new Gtk.TreeViewColumn({
                title: pref.title,
                sort_column_id: 1,
            });
            let textRenderer = new Gtk.CellRendererText();
            columnFavorites.pack_start(textRenderer, true);
            columnFavorites.add_attribute(textRenderer, 'text', 1);
            treeView.insert_column(columnFavorites, 0);
            let toolbar = new Gtk.CenterBox({ orientation: Gtk.Orientation.HORIZONTAL });
            child.append(toolbar);
            let addButton = new Gtk.Button({
                label: _('Select application'),
                hexpand: true,
            });
            addButton.connect('clicked', () => {
                this._favoriteSearch(settings, pref.key);
            });
            toolbar.set_start_widget(addButton);
            let inputButton = new Gtk.Button({
                label: _('Desktop file ID'),
                hexpand: true,
                margin_start: 8,
                margin_end: 8,
            });
            inputButton.connect('clicked', () => {
                this._favoriteInput(settings, pref.key);
            });
            toolbar.set_center_widget(inputButton);
            let delButton = new Gtk.Button({
                label: _('Remove Favorite'),
                hexpand: true,
            });
            delButton.connect('clicked', () => {
                let [ any, model, iter ] = treeView.get_selection().get_selected();
                strArray = settings.get_strv(pref.key);
                if (any && strArray.length > 0) {
                    let item = listStore.get_value(iter, 0);
                    let index = strArray.map(i => {
                        return i;
                    }).indexOf(item);
                    if (index < 0) return;
                    strArray.splice(index, 1);
                    settings.set_strv(pref.key, strArray);
                }
            });
            toolbar.set_end_widget(delButton);
            let strArray = [];
            let treeViewSelection = treeView.get_selection();
            let updateListStore = () => {
                strArray = settings.get_strv(pref.key);
                strArray.sort((a, b) => {
                    try { a = Gio.DesktopAppInfo.new(a).get_name(); } catch (e) {}
                    try { b = Gio.DesktopAppInfo.new(b).get_name(); } catch (e) {}
                    return a.toLowerCase() > b.toLowerCase();
                });
                listStore.clear();
                strArray.forEach(app => {
                    if (Gio.DesktopAppInfo.new(app)) {
                        let item = Gio.DesktopAppInfo.new(app).get_name();
                        listStore.set(listStore.append(), [ 0, 1 ], [ app, item ]);
                    }
                });
                treeViewSelection.select_path(Gtk.TreePath.new_from_string(String(0)));
            }
            settings.connect('changed::'+pref.key, updateListStore);
            updateListStore();
            break;
        case 'label':
            prefWidget = new Adw.ActionRow({
                title: pref.title,
                subtitle: pref?.subtitle ?? null,
            });
            break;
        case 'switcher':
            prefAction = new Gtk.Switch({
                active: settings.get_boolean(pref.key),
                valign: Gtk.Align.CENTER,
            });
            settings.bind(pref.key, prefAction, 'active', Gio.SettingsBindFlags.DEFAULT);
            break;
        default: // Nothing to do
        }
        if (prefAction !== null) {
            prefWidget = new Adw.ActionRow({
                title: pref.title,
                subtitle: pref?.subtitle ?? null,
            });
            prefWidget.add_suffix(prefAction);
            prefWidget.set_activatable_widget(prefAction);
        }
        return prefWidget;
    }

    _favoriteDialog() {
        let dialog = new Gtk.Dialog({
            title: _('Add Favorite'),
            modal: true,
            resizable: false,
            transient_for: this.get_root(),
            use_header_bar: true,
        });
        dialog.add_button(_('Cancel'), Gtk.ResponseType.CANCEL);
        return dialog;
    }

    _favoriteInput(schema, key) {
        let dialog = this._favoriteDialog();
        let addButton = dialog.add_button(_('Add'), Gtk.ResponseType.OK);
        addButton.sensitive = 0;
        dialog.set_default_response(Gtk.ResponseType.OK);
        let grid = new Gtk.Grid({
            column_spacing: 15,
            margin_top: 15,
            margin_bottom: 15,
            margin_start: 15,
            margin_end: 15,
        });
        dialog.get_content_area().append(grid);
        grid.attach(new Gtk.Label({
            label: _('Desktop file ID:'),
            xalign: 0,
        }), 0, 0, 1, 1);
        let entry = new Gtk.Entry({
            activates_default: true,
            hexpand: true,
        });
        entry.connect('changed', () => {
            addButton.sensitive = 0;
            let appId = entry.get_text();
            if (checkAppId(appId)) {
                let strArray = schema.get_strv(key);
                if (strArray.indexOf(appId) >= 0) return;
                addButton.sensitive = 1;
            }
        });
        grid.attach(entry, 1, 0, 1, 1);
        dialog.connect('response', (dialog, response_id) => {
            if (response_id === Gtk.ResponseType.OK) {
                let appId = entry.get_text();
                let strArray = schema.get_strv(key);
                strArray.push(appId);
                schema.set_strv(key, strArray);
            }
            dialog.destroy();
        });
        dialog.show();
    }

    _favoriteSearch(schema, key) {
        let dialog = this._favoriteDialog();
        let addButton = dialog.add_button(_('Add'), Gtk.ResponseType.OK);
        addButton.sensitive = 0;
        dialog.set_default_response(Gtk.ResponseType.OK);
        let grid = new Gtk.Grid({
            margin_top: 15,
            margin_bottom: 15,
            margin_start: 15,
            margin_end: 15,
        });
        dialog.get_content_area().append(grid);
        let appChooser = new Gtk.AppChooserWidget();
        appChooser.set_default_text('');
        appChooser.set_show_all(true);
        appChooser.connect('application-selected', () => {
            addButton.sensitive = 0;
            let appInfo = appChooser.get_app_info();
            if (!appInfo) return;
            let strArray = schema.get_strv(key);
            if (strArray.indexOf(appInfo.get_id()) >= 0) return;
            addButton.sensitive = 1;
        });
        appChooser.set_size_request(400, 240);
        grid.attach(appChooser, 0, 0, 1, 1);
        dialog.connect('response', (dialog, response_id) => {
            if (response_id === Gtk.ResponseType.OK) {
                let appInfo = appChooser.get_app_info();
                let strArray = schema.get_strv(key);
                strArray.push(appInfo.get_id());
                schema.set_strv(key, strArray);
            }
            dialog.destroy();
        });
        dialog.show();
    }
});

function init() {
    ExtensionUtils.initTranslations();
}

function fillPreferencesWindow(window) {
    window.add(new PanelManagerPreferences(ExtensionUtils.getSettings()));
}
