/* DING: Desktop Icons New Generation for GNOME Shell
 *
 * Copyright (C) 2021 Sergio Costas (rastersoft@gmail.com)
 *
 * 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, version 3 of the License.
 *
 * 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/>.
 */
'use strict';
const DBusUtils = imports.dbusUtils;
const GLib = imports.gi.GLib;
const Gdk = imports.gi.Gdk;
const Gtk = imports.gi.Gtk;
const Gio = imports.gi.Gio;

const TemplatesScriptsManager = imports.templatesScriptsManager;
const DesktopIconsUtil = imports.desktopIconsUtil;
const Prefs = imports.preferences;
const ShowErrorPopup = imports.showErrorPopup;
const SignalManager = imports.signalManager;

const Gettext = imports.gettext.domain('ding');

const _ = Gettext.gettext;

var FileItemMenu = class {
    constructor(desktopManager) {
        this._currentFileItem = null;
        this._menu = null;
        this._menuSignals = new SignalManager.SignalManager();
        this._desktopManager = desktopManager;
        DBusUtils.GnomeArchiveManager.connect('changed-status', () => {
            // wait a second to ensure that everything has settled
            GLib.timeout_add(GLib.PRIORITY_DEFAULT, 1000, () => {
                this._getExtractionSupportedTypes();
                return false;
            });
        });
        this._askedSupportedTypes = false;
        this._scriptsMonitor = new TemplatesScriptsManager.TemplatesScriptsManager(
            DesktopIconsUtil.getScriptsDir(),
            TemplatesScriptsManager.TemplatesScriptsManagerFlags.ONLY_EXECUTABLE,
            this._onScriptClicked.bind(this));
    }

    _getExtractionSupportedTypes() {
        this._decompressibleTypes = [];
        try {
            if (DBusUtils.GnomeArchiveManager.isAvailable) {
                DBusUtils.GnomeArchiveManager.proxy.GetSupportedTypesRemote('extract',
                    (result, error) => {
                        if (error) {
                            console.error(error, "Can't get the extractable types; ensure that File-Roller is installed.\n");
                            return;
                        }
                        for (let key of result.values()) {
                            for (let type of key.values()) {
                                this._decompressibleTypes.push(Object.values(type)[0]);
                            }
                        }
                    }
                );
            }
            this._askedSupportedTypes = true;
        } catch (e) {
            console.error(e, 'Error while getting supported types.');
        }
    }

    _onScriptClicked(menuItemPath) {
        let pathList = 'NAUTILUS_SCRIPT_SELECTED_FILE_PATHS=';
        let uriList = 'NAUTILUS_SCRIPT_SELECTED_URIS=';
        let currentUri = `NAUTILUS_SCRIPT_CURRENT_URI=${DesktopIconsUtil.getDesktopDir().get_uri()}`;
        let params = [menuItemPath];
        for (let item of this._desktopManager.getCurrentSelection(false)) {
            if (!item.isSpecial) {
                pathList += `${item.file.get_path()}\n`;
                uriList += `${item.file.get_uri()}\n`;
                params.push(item.file.get_path());
            }
        }

        let environ = DesktopIconsUtil.getFilteredEnviron();
        environ.push(pathList);
        environ.push(uriList);
        environ.push(currentUri);
        DesktopIconsUtil.trySpawn(null, params, environ);
    }

    refreshedIcons() {
        if (!this._menu) {
            return;
        }
        this._currentFileItem = this._desktopManager.getFileItemFromURI(this._currentFileItem.uri);
        if (!this._currentFileItem) {
            this._menuSignals.disconnectAllSignals();
            this._menu.destroy();
            this._menu = null;
        }
    }

    _addSeparator() {
        this._menu.add(new Gtk.SeparatorMenuItem());
    }

    _addElementToMenu(label, action = null) {
        let element = new Gtk.MenuItem({label});
        this._menu.add(element);
        if (action) {
            this._menuSignals.connectSignal(element, 'activate', action);
        }
        return element;
    }

    showMenu(fileItem, event, atWidget = false) {
        if (!this._askedSupportedTypes) {
            this._getExtractionSupportedTypes();
        }
        this._currentFileItem = fileItem;

        let selectedItemsNum = this._desktopManager.getNumberOfSelectedItems();

        if (this._menu) {
            this._menuSignals.disconnectAllSignals();
            this._menu.destroy();
        }

        this._menu = new Gtk.Menu();
        const menuStyleContext = this._menu.get_style_context();
        menuStyleContext.add_class('desktopmenu');
        menuStyleContext.add_class('fileitemmenu');

        if (!fileItem.isStackMarker) {
            this._addElementToMenu(
                selectedItemsNum > 1 ? _('Open All...') : _('Open'),
                this._doMultiOpen.bind(this)
            );
        }

        this._menuSignals.connectSignal(this._menu, 'selection-done', () => {
            this._menuSignals.disconnectAllSignals();
            this._menu.destroy();
            this._menu = null;
        }, {after: true});

        let keepStacked = Prefs.desktopSettings.get_boolean('keep-stacked');
        if (keepStacked && !fileItem.stackUnique) {
            if (!fileItem.isSpecial && !fileItem.isDirectory && !fileItem.isValidDesktopFile) {
                let unstackList = Prefs.getUnstackList();
                let typeInList = unstackList.includes(fileItem.attributeContentType);
                this._addElementToMenu(
                    typeInList ? _('Stack This Type') : _('Unstack This Type'),
                    () => {
                        this._desktopManager.onToggleStackUnstackThisTypeClicked(this._currentFileItem.attributeContentType, typeInList, unstackList);
                    }
                );
            }
        }

        // fileExtra == NONE

        if (fileItem.isAllSelectable &&  !fileItem.isStackMarker) {
            let submenu = this._scriptsMonitor.createMenu();
            if (submenu !== null) {
                this._addElementToMenu(_('Scripts')).set_submenu(submenu);
                this._addSeparator();
            }

            this._addElementToMenu(
                selectedItemsNum > 1 ? _('Open All With Other Application...') : _('Open With Other Application'),
                this._doOpenWith.bind(this)
            ).set_sensitive(selectedItemsNum > 0);

            if (DBusUtils.discreteGpuAvailable && fileItem.trustedDesktopFile && (selectedItemsNum == 1)) {
                this._addElementToMenu(
                    _('Launch using Dedicated Graphics Card'),
                    () => {
                        this._currentFileItem.doDiscreteGpu();
                    }
                );
            }

            this._addSeparator();

            if (fileItem.attributeCanExecute && !fileItem.isDirectory && !fileItem.isValidDesktopFile && fileItem.execLine && Gio.content_type_can_be_executable(fileItem.attributeContentType)) {
                let execLine = fileItem.execLine;
                this._addElementToMenu(_('Run as a program'), () => {
                    DesktopIconsUtil.spawnCommandLine(`"${execLine}"`);
                });
                this._addSeparator();
            }

            let allowCutCopyTrash = this._desktopManager.checkIfSpecialFilesAreSelected();
            this._addElementToMenu(
                _('Cut'),
                () => {
                    this._desktopManager.doCut();
                }
            ).set_sensitive(!allowCutCopyTrash);

            this._addElementToMenu(
                _('Copy'),
                () => {
                    this._desktopManager.doCopy();
                }
            ).set_sensitive(!allowCutCopyTrash);

            if (fileItem.canRename && (selectedItemsNum == 1)) {
                this._addElementToMenu(
                    _('Rename…'),
                    () => {
                        this._desktopManager.doRename(this._currentFileItem, false);
                    }
                );
            }

            this._addSeparator();

            this._addElementToMenu(
                _('Move to Trash'),
                () => {
                    this._desktopManager.doTrash();
                }
            ).set_sensitive(!allowCutCopyTrash);

            if (Prefs.nautilusSettings.get_boolean('show-delete-permanently')) {
                this._addElementToMenu(
                    _('Delete permanently'),
                    () => {
                        this._desktopManager.doDeletePermanently();
                    }
                ).set_sensitive(!allowCutCopyTrash);
            }

            if (fileItem.isValidDesktopFile && !this._desktopManager.writableByOthers && !fileItem.writableByOthers && (selectedItemsNum == 1)) {
                this._addSeparator();
                this._addElementToMenu(
                    fileItem.trustedDesktopFile ? _("Don't Allow Launching") : _('Allow Launching'),
                    () => {
                        this._currentFileItem.onAllowDisallowLaunchingClicked();
                    }
                );
            }
        }

        // fileExtra == TRASH

        if (fileItem.isTrash) {
            this._addSeparator();
            this._addElementToMenu(
                _('Empty Trash'),
                () => {
                    this._desktopManager.doEmptyTrash();
                }
            );
        }

        // fileExtra == EXTERNAL_DRIVE

        if (fileItem.isDrive) {
            this._addSeparator();
            if (fileItem.canEject) {
                this._addElementToMenu(
                    _('Eject'),
                    () => {
                        this._currentFileItem.eject();
                    }
                );
            }
            if (fileItem.canUnmount) {
                this._addElementToMenu(
                    _('Unmount'),
                    () => {
                        this._currentFileItem.unmount();
                    }
                );
            }
        }

        if (fileItem.isAllSelectable && !this._desktopManager.checkIfSpecialFilesAreSelected() && (selectedItemsNum >= 1)) {
            this._addSeparator();
            let addedExtractHere = false;
            if (this._getExtractableAutoAr()) {
                addedExtractHere = true;
                this._addElementToMenu(
                    _('Extract Here'),
                    () => this._desktopManager.getCurrentSelection(false).forEach(f =>
                        this._desktopManager.autoAr.extractFile(f.fileName)));
            }
            if (selectedItemsNum == 1 && this._getExtractable()) {
                if (!addedExtractHere) {
                    this._addElementToMenu(
                        _('Extract Here'),
                        () => {
                            this._extractFileFromSelection(true);
                        }
                    );
                }
                this._addElementToMenu(
                    _('Extract To...'),
                    () => {
                        this._extractFileFromSelection(false);
                    }
                );
            }

            if (!fileItem.isDirectory) {
                this._addElementToMenu(
                    _('Send to...'),
                    this._mailFilesFromSelection.bind(this)
                );
            }

            if (this._desktopManager.getCurrentSelection().every(f => f.isDirectory)) {
                this._addElementToMenu(
                    Gettext.ngettext(
                        'Compress {0} folder', 'Compress {0} folders', selectedItemsNum).replace(
                        '{0}', selectedItemsNum),
                    () => this._doCompressFilesFromSelection()
                );
            } else {
                this._addElementToMenu(
                    Gettext.ngettext(
                        'Compress {0} file', 'Compress {0} files', selectedItemsNum).replace(
                        '{0}', selectedItemsNum),
                    () => this._doCompressFilesFromSelection()
                );
            }


            this._addElementToMenu(
                Gettext.ngettext('New Folder with {0} item', 'New Folder with {0} items', selectedItemsNum).replace('{0}', selectedItemsNum),
                () => {
                    this._doNewFolderFromSelection(this._currentFileItem);
                }
            );

            this._addSeparator();
        }

        if (!fileItem.isStackMarker) {
            this._addElementToMenu(
                selectedItemsNum > 1 ? _('Common Properties') : _('Properties'),
                this._onPropertiesClicked.bind(this)
            );

            this._addSeparator();

            this._addElementToMenu(
                selectedItemsNum > 1 ? _('Show All in Files') : _('Show in Files'),
                this._onShowInFilesClicked.bind(this)
            );
        }

        if (fileItem.isDirectory && (fileItem.path != null) && (selectedItemsNum == 1)) {
            this._addElementToMenu(
                _('Open in Terminal'),
                () => {
                    DesktopIconsUtil.launchTerminal(this._currentFileItem.path, null);
                }
            );
        }

        this._menu.show_all();
        if (atWidget) {
            this._menu.popup_at_widget(fileItem.container, Gdk.Gravity.CENTER, Gdk.Gravity.NORTH_WEST, event);
        } else {
            this._menu.popup_at_pointer(event);
        }
    }

    _onPropertiesClicked() {
        let propertiesFileList = this._desktopManager.getCurrentSelection(true);
        const timestamp = Gtk.get_current_event_time();
        DBusUtils.RemoteFileOperations.ShowItemPropertiesRemote(propertiesFileList, timestamp);
    }

    _onShowInFilesClicked() {
        let showInFilesList = this._desktopManager.getCurrentSelection(true);
        if (this._desktopManager.useNemo) {
            try {
                for (let element of showInFilesList) {
                    DesktopIconsUtil.trySpawn(GLib.get_home_dir(), ['nemo', element], DesktopIconsUtil.getFilteredEnviron());
                }
                return;
            } catch (err) {
                console.error(err, 'Error trying to launch Nemo.');
            }
        }
        const timestamp = Gtk.get_current_event_time();
        DBusUtils.RemoteFileOperations.ShowItemsRemote(showInFilesList, timestamp);
    }

    _doMultiOpen() {
        for (let fileItem of this._desktopManager.getCurrentSelection(false)) {
            fileItem.unsetSelected();
            fileItem.doOpen();
        }
    }

    _doOpenWith() {
        let fileItems = this._desktopManager.getCurrentSelection(false);
        if (fileItems) {
            const context = Gdk.Display.get_default().get_app_launch_context();
            context.set_timestamp(Gtk.get_current_event_time());
            let mimetype = Gio.content_type_guess(fileItems[0].fileName, null)[0];
            if (fileItems[0].isDirectory) {
                mimetype = 'inode/directory';
            }
            let chooser = Gtk.AppChooserDialog.new_for_content_type(null,
                Gtk.DialogFlags.MODAL + Gtk.DialogFlags.USE_HEADER_BAR,
                mimetype);
            chooser.set_type_hint(Gdk.WindowTypeHint.NORMAL);
            const windowGroup = new Gtk.WindowGroup();
            windowGroup.add_window(chooser);
            let signals = new SignalManager.SignalManager();
            chooser.show_all();
            signals.connectSignal(chooser, 'close', () => {
                chooser.response(Gtk.ResponseType.CANCEL);
            });
            signals.connectSignal(chooser, 'response', (actor, retval) => {
                if (retval == Gtk.ResponseType.OK) {
                    let appInfo = chooser.get_app_info();
                    if (appInfo) {
                        let fileList = [];
                        for (let item of fileItems) {
                            fileList.push(item.file);
                        }
                        appInfo.launch(fileList, context);
                    }
                }
                chooser.hide();
                signals.disconnectAllSignals();
                chooser = null;
                signals = null;
            });
        }
    }

    _extractFileFromSelection(extractHere) {
        let extractFileItemURI;
        let extractFolderName;
        let position;
        const header = _('No Extraction Folder');
        const text = _('Unable to extract File, extraction Folder Does not Exist');

        for (let fileItem of this._desktopManager.getCurrentSelection(false)) {
            extractFileItemURI = fileItem.file.get_uri();
            extractFolderName = fileItem.fileName;
            position = fileItem.getCoordinates().slice(0, 2);
            fileItem.unsetSelected();
        }

        if (extractHere) {
            extractFolderName = DesktopIconsUtil.getFileExtensionOffset(extractFolderName).basename;
            const targetURI = this._desktopManager.doNewFolder(position, extractFolderName, {rename: false});
            if (targetURI) {
                DBusUtils.RemoteFileOperations.ExtractRemote(extractFileItemURI, targetURI, true);
            } else {
                this._desktopManager.DBusManager.doNotify(header, text);
            }
            return;
        }

        const dialog = new Gtk.FileChooserDialog({
            title: _('Select Extract Destination'),
            modal: true,
            type_hint: Gdk.WindowTypeHint.NORMAL,
        });
        const windowGroup = new Gtk.WindowGroup();
        windowGroup.add_window(dialog);
        dialog.set_action(Gtk.FileChooserAction.SELECT_FOLDER);
        dialog.set_create_folders(true);
        dialog.set_current_folder_uri(DesktopIconsUtil.getDesktopDir().get_uri());
        dialog.add_button(_('Cancel'), Gtk.ResponseType.CANCEL);
        dialog.add_button(_('Select'), Gtk.ResponseType.ACCEPT);
        dialog.show_all();
        dialog.connect('close', () => {
            dialog.response(Gtk.ResponseType.CANCEL);
        });
        dialog.connect('response', (actor, response) => {
            if (response === Gtk.ResponseType.ACCEPT) {
                const folder = dialog.get_uri();
                if (folder) {
                    DBusUtils.RemoteFileOperations.ExtractRemote(extractFileItemURI, folder, true);
                } else {
                    this._desktopManager.DBusManager.doNotify(header, text);
                }
            }
            dialog.destroy();
        });
    }

    _getExtractableAutoAr() {
        let fileList = this._desktopManager.getCurrentSelection(false);
        if (DBusUtils.GnomeArchiveManager.isAvailable && (fileList.length == 1)) {
            return false;
        }
        for (let item of fileList) {
            if (!this._desktopManager.autoAr.fileIsCompressed(item.fileName)) {
                return false;
            }
        }
        return true;
    }

    _getExtractable() {
        for (let item of this._desktopManager.getCurrentSelection(false)) {
            return this._decompressibleTypes.includes(item.attributeContentType);
        }
        return false;
    }

    _mailFilesFromSelection() {
        if (this._desktopManager.checkIfDirectoryIsSelected()) {
            let WindowError = new ShowErrorPopup.ShowErrorPopup(_('Can not email a Directory'),
                _('Selection includes a Directory, compress the directory to a file first.'),
                false);
            WindowError.run();
            return;
        }
        let xdgEmailCommand = [];
        xdgEmailCommand.push('xdg-email');
        for (let fileItem of this._desktopManager.getCurrentSelection(false)) {
            fileItem.unsetSelected();
            xdgEmailCommand.push('--attach');
            xdgEmailCommand.push(fileItem.file.get_path());
        }
        DesktopIconsUtil.trySpawn(null, xdgEmailCommand);
    }

    _doCompressFilesFromSelection() {
        let desktopFolder = DesktopIconsUtil.getDesktopDir();
        if (desktopFolder) {
            if (DBusUtils.GnomeArchiveManager.isAvailable) {
                const toCompress = this._desktopManager.getCurrentSelection(true);
                DBusUtils.RemoteFileOperations.CompressRemote(toCompress, desktopFolder.get_uri(), true);
            } else {
                const toCompress = this._desktopManager.getCurrentSelection(false);
                this._desktopManager.autoAr.compressFileItems(toCompress, desktopFolder.get_path());
            }
        }
        this._desktopManager.unselectAll();
    }

    _doNewFolderFromSelection(clickedItem) {
        if (!clickedItem) {
            return;
        }
        let position = clickedItem.savedCoordinates;
        let newFolderFileItems = this._desktopManager.getCurrentSelection(true);
        this._desktopManager.unselectAll();
        clickedItem.removeFromGrid(true);
        let newFolder = this._desktopManager.doNewFolder(position);
        if (newFolder) {
            DBusUtils.RemoteFileOperations.MoveURIsRemote(newFolderFileItems, newFolder);
        }
    }
};
