/* places.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 PlacesMenuButton */

const { Clutter, Gio, GObject, St } = imports.gi;

const ExtensionUtils = imports.misc.extensionUtils;

const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;

const _ = ExtensionUtils.gettext;
const Me = ExtensionUtils.getCurrentExtension();

const PlacesManager = Me.imports.placesManager;
const { ModalUserDialog } = Me.imports.userDialog;

const BOOKMARKS_MAINMENU = 0;

const KEY_BOOKMARKS_POSITION = 'bookmarks-position';
const KEY_SHOW_PLACES = 'show-places';

const PANELMENU_BUTTON_ICON = 1;
const PANELMENU_BUTTON_TEXT = 2;

const PopupPlaceMenuItem = GObject.registerClass(
class PopupPlaceMenuItem extends PopupMenu.PopupBaseMenuItem {
    _init(info) {
        super._init();
        this._icon = new St.Icon({
            style_class: 'popup-menu-icon',
            x_align: Clutter.ActorAlign.END,
        });
        this.add_child(this._icon);
        this.label = new St.Label({
            text: info.name,
            x_expand: true,
            y_expand: true,
            y_align: Clutter.ActorAlign.CENTER,
        });
        this.add_child(this.label);
        this.label_actor = this.label;
        this._setIcon(info.icon);
        if (info.can_eject) this._addButton('media-eject-symbolic', _('Eject'), {
            content: 'ejectVolume',
            value: info.name,
            signal: 'eject',
            callback: () => {
                info.eject();
            },
        });
        if (info.can_empty) this._addButton('edit-clear-all-symbolic', _('Clear list'), {
            content: 'clearRecentDocuments',
            value: null,
            signal: 'empty',
            callback: () => {
                info.empty();
            },
        });
        if (info.can_remove) this._addButton('edit-clear-symbolic', _('Remove'), {
            content: 'removeRecentDocument',
            value: info.name,
            signal: 'remove',
            callback: () => {
                info.remove();
            },
        });
        this._info = info;
    }

    _addButton(iconName, accessibleName, params) {
        let button = new St.Button({
            style_class: 'icon-button',
            icon_name: iconName,
            accessible_name: accessibleName,
            can_focus: true,
            reactive: true,
            track_hover: true,
        });
        button.connect('clicked', () => {
            let dialog = new ModalUserDialog(params.content, params.value);
            dialog.connect(params.signal, params.callback);
            dialog.open();
            Main.overview.hide();
        });
        this.add_child(button);
    }

    _setIcon(icon) {
        if (icon instanceof GObject.Object && GObject.type_is_a(icon, Gio.Icon)) {
            this._icon.gicon = icon;
        } else {
            this._icon.icon_name = icon;
        }
    }

    activate(event) {
        this._info.launch(event.get_time());
        Main.overview.hide();
        super.activate(event);
    }
});

class PopupPlacesMenuSection extends PopupMenu.PopupMenuSection {
    constructor(...params) {
        super();
        this._init(...params);
    }

    _init(manager) {
        this._placesManager = new PlacesManager[manager]();
        this._placesManager.connectObject('updated', () => this._reload(), this);
        this._reload();
    }

    _addPlaces(menu, kind) {
        let places = this._placesManager.get(kind);
        places.forEach(info => {
            menu.addMenuItem(new PopupPlaceMenuItem(info));
        });
    }

    _reload() {
        this.visible = this._placesManager.size > 0;
    }

    destroy() {
        this._placesManager.disconnectObject(this);
        this._placesManager.destroy();
        super.destroy();
    }
}

class PopupPlacesBookmarksMenuSection extends PopupPlacesMenuSection {
    _init(settings) {
        this._settings = settings;
        this._settings.connectObject('changed::%s'.format(KEY_BOOKMARKS_POSITION), () => this._reload(), this);
        super._init('PlacesBookmarksManager');
    }

    _reload() {
        let item;
        this.removeAll();
        this._addPlaces(this, 'special');
        if (this._settings.get_enum(KEY_BOOKMARKS_POSITION) === BOOKMARKS_MAINMENU) {
            this._addPlaces(this, 'default');
        } else {
            item = new PopupMenu.PopupSubMenuMenuItem(_('Bookmarks'), true);
            item.icon.icon_name = 'user-bookmarks';
            this._addPlaces(item.menu, 'default');
            if (item.menu.numMenuItems > 0) this.addMenuItem(item);
        }
        item = new PopupMenu.PopupSubMenuMenuItem(_('Bookmarks (custom)'), true);
        item.icon.icon_name = 'user-bookmarks';
        this._addPlaces(item.menu, 'custom');
        if (item.menu.numMenuItems > 0) this.addMenuItem(item);
        super._reload();
    }

    destroy() {
        this._settings.disconnectObject(this);
        super.destroy();
    }
}

class PopupPlacesRecentMenuSection extends PopupPlacesMenuSection {
    _init() {
        super._init('PlacesRecentManager');
    }

    _reload() {
        this.removeAll();
        if (this._placesManager.size > 0) {
            this._addPlaces(this, 'recent');
            let item = new PopupMenu.PopupSubMenuMenuItem(_('Last used documents'), true);
            item.icon.icon_name = 'document-open-recent';
            this._addPlaces(item.menu, 'recent-item');
            this.addMenuItem(item);
        }
        super._reload();
    }
}

class PopupPlacesVolumesMenuSection extends PopupPlacesMenuSection {
    _init() {
        super._init('PlacesVolumesManager');
    }

    _reload() {
        let item;
        this.removeAll();
        this._addPlaces(this, 'special');
        item = new PopupMenu.PopupSubMenuMenuItem(_('Network Drives'), true);
        item.icon.icon_name = 'network-server';
        this._addPlaces(item.menu, 'network');
        if (item.menu.numMenuItems > 0) this.addMenuItem(item);
        item = new PopupMenu.PopupSubMenuMenuItem(_('Removable Devices'), true);
        item.icon.icon_name = 'media-removable';
        this._addPlaces(item.menu, 'device');
        if (item.menu.numMenuItems > 0) this.addMenuItem(item);
        super._reload();
    }
}

var PlacesMenuButton = GObject.registerClass(
class PlacesMenuButton extends PanelMenu.Button {
    _init(settings) {
        super._init(0.5, _('Places'), false);
        this.menu.actor.add_style_class_name('panelmanager-menu');
        this._hbox = new St.BoxLayout({ style_class: 'panel-status-indicators-box' });
        this._icon = new St.Icon({
            style_class: 'system-status-icon',
            icon_name: 'folder-symbolic',
        });
        this._hbox.add_child(this._icon);
        this._label = new St.Label({
            text: _('Places'),
            y_expand: true,
            y_align: Clutter.ActorAlign.CENTER,
        });
        this._hbox.add_child(this._label);
        this.add_child(this._hbox);
        this._itemBookmarks = new PopupPlacesBookmarksMenuSection(settings);
        this.menu.addMenuItem(this._itemBookmarks);
        this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
        this._itemVolumes = new PopupPlacesVolumesMenuSection();
        this.menu.addMenuItem(this._itemVolumes);
        this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
        this._itemRecent = new PopupPlacesRecentMenuSection();
        this.menu.addMenuItem(this._itemRecent);
        this._settings = settings;
        this._settings.connectObject('changed::%s'.format(KEY_SHOW_PLACES), () => this._onChangedShowPlaces(), this);
        this._onChangedShowPlaces();
    }

    _onChangedShowPlaces() {
        let showPlaces = this._settings.get_enum(KEY_SHOW_PLACES);
        this._icon.visible = showPlaces !== PANELMENU_BUTTON_TEXT;
        this._label.visible = showPlaces !== PANELMENU_BUTTON_ICON;
    }

    _onDestroy() {
        this._settings.disconnectObject(this);
        super._onDestroy();
    }
});
