/*****************************************************************************
 * Copyright (C) 2003 Shie Erlich <erlich@users.sourceforge.net>             *
 * Copyright (C) 2003 Rafi Yanai <yanai@users.sourceforge.net>               *
 *                                                                           *
 * 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 package 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 package; if not, write to the Free Software               *
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA *
 *****************************************************************************/

#include "krpopupmenu.h"

// QtGui
#include <QPixmap>

#include <KCoreAddons/KProcess>
#include <KI18n/KLocalizedString>
#include <KIOWidgets/KRun>
#include <KIconThemes/KIconLoader>
#include <KWidgetsAddons/KMessageBox>
#include <KService/KMimeTypeTrader>
#include <KService/KToolInvocation>
#include <KXmlGui/KActionCollection>
#include <KIOCore/KFileItemListProperties>
#include <KIOWidgets/KAbstractFileItemActionPlugin>
#include <KCoreAddons/KPluginMetaData>

#include "listpanel.h"
#include "krview.h"
#include "krviewitem.h"
#include "panelfunc.h"
#include "listpanelactions.h"
#include "../krservices.h"
#include "../defaults.h"
#include "../MountMan/kmountman.h"
#include "../krusader.h"
#include "../krslots.h"
#include "../krusaderview.h"
#include "../panelmanager.h"
#include "../krtrashhandler.h"
#include "../UserAction/useractionpopupmenu.h"
#include "../VFS/krarchandler.h"

void KrPopupMenu::run(const QPoint &pos, KrPanel *panel)
{
    KrPopupMenu menu(panel);
    QAction * res = menu.exec(pos);
    int result = -1;
    if (res && res->data().canConvert<int>())
        result = res->data().toInt();
    menu.performAction(result);
}

/**
 * Copied from dolphin/src/dolphincontextmenu.cpp and modified to add only compress and extract submenus.
 */
bool KrPopupMenu::addCompressAndExtractPluginActions()
{

    KFileItemListProperties props(_items);

    QVector<KPluginMetaData> jsonPlugins = KPluginLoader::findPlugins("kf5/kfileitemaction",
                                                                      [=](const KPluginMetaData& metaData) {
        return metaData.pluginId() == "compressfileitemaction" || metaData.pluginId() == "extractfileitemaction";
    });

    foreach (const KPluginMetaData &jsonMetadata, jsonPlugins) {
        KAbstractFileItemActionPlugin* abstractPlugin = KPluginLoader(jsonMetadata.fileName())
                                                            .factory()->create<KAbstractFileItemActionPlugin>();
        if (abstractPlugin) {
            abstractPlugin->setParent(this);
            addActions(abstractPlugin->actions(props, this));
        }
    }

    return !jsonPlugins.isEmpty();
}

KrPopupMenu::KrPopupMenu(KrPanel *thePanel, QWidget *parent) : QMenu(parent), panel(thePanel), empty(false),
        multipleSelections(false), actions(0), _item(0)
{
    KrViewItemList items;
    panel->view->getSelectedKrViewItems(&items);

    for (KrViewItemList::Iterator it = items.begin(); it != items.end(); ++it) {
        vfile *file = panel->func->files()->vfs_search(((*it)->name()));
        QUrl url = file->vfile_getUrl();
        _items.append(KFileItem(url, file->vfile_getMime(), file->vfile_getMode()));
    }

    if (items.empty()) {
        addCreateNewMenu();
        addSeparator();
        addEmptyMenuEntries();
        return;
    } else if (items.size() > 1) multipleSelections = true;

    QList<QString> protocols;
    for (int i = 0; i < items.size(); ++i) {
        protocols.append(panel->func->getVFile(items[ i ]) ->vfile_getUrl().scheme());
    }
    bool inTrash = protocols.contains("trash");
    bool trashOnly = (protocols.count() == 1) && (protocols[ 0 ] == "trash");

    KrViewItem *item = items.first();
    vfile *vf = panel->func->getVFile(item);
    _item = &_items.first();

    // ------------ the OPEN/BROWSE option - open preferred service
    QAction * openAct = addAction(i18n("Open/Run"));
    openAct->setData(QVariant(OPEN_ID));
    if (!multipleSelections) {   // meaningful only if one file is selected
        openAct->setIcon(item->icon());
        openAct->setText(vf->vfile_isExecutable() && !vf->vfile_isDir() ? i18n("Run") : i18n("Open"));
        // open in a new tab (if folder)
        if (vf->vfile_isDir()) {
            QAction * openTab = addAction(i18n("Open in New Tab"));
            openTab->setData(QVariant(OPEN_TAB_ID));
            openTab->setIcon(krLoader->loadIcon("tab-new", KIconLoader::Panel));
            openTab->setText(i18n("Open in New Tab"));
        }
        QUrl arcPath = panel->func->browsableArchivePath(vf->vfile_getName());
        if (!arcPath.isEmpty()) {
            bool theArchiveMustBeBrowsedAsADirectory = KConfigGroup(krConfig, "Archives").readEntry("ArchivesAsDirectories", _ArchivesAsDirectories) &&
                                                       KRarcHandler::arcSupported(vf->vfile_getMime());
            if (!theArchiveMustBeBrowsedAsADirectory) {
                // Add an option to browse the archive
                QAction *browseAct = addAction(i18n("Browse"));
                browseAct->setData(QVariant(BROWSE_ID));
                browseAct->setIcon(krLoader->loadIcon("", KIconLoader::Panel));
                browseAct->setText(i18n("Browse Archive"));
            }
        }
        addSeparator();
    }

    // ------------- Preview - normal vfs only ?
    if (panel->func->files()->vfs_getType() == vfs::VFS_NORMAL) {
        // create the preview popup
        QStringList names;
        panel->gui->getSelectedNames(&names);
        preview.setUrls(panel->func->files() ->vfs_getFiles(names));
        QAction *pAct = addMenu(&preview);
        pAct->setData(QVariant(PREVIEW_ID));
        pAct->setText(i18n("Preview"));
    }

    // -------------- Open with: try to find-out which apps can open the file
    // this too, is meaningful only if one file is selected or if all the files
    // have the same mimetype !
    QString mime = panel->func->getVFile(item)->vfile_getMime();
    // check if all the list have the same mimetype
    for (int i = 1; i < items.size(); ++i) {
        if (panel->func->getVFile(items[ i ]) ->vfile_getMime() != mime) {
            mime.clear();
            break;
        }
    }
    if (!mime.isEmpty()) {
        offers = KMimeTypeTrader::self()->query(mime);
        for (int i = 0; i < offers.count(); ++i) {
            QExplicitlySharedDataPointer<KService> service = offers[i];
            if (service->isValid() && service->isApplication()) {
                openWith.addAction(krLoader->loadIcon(service->icon(), KIconLoader::Small), service->name())->setData(QVariant(SERVICE_LIST_ID + i));
            }
        }
        openWith.addSeparator();
        if (vf->vfile_isDir())
            openWith.addAction(krLoader->loadIcon("utilities-terminal", KIconLoader::Small), i18n("Terminal"))->setData(QVariant(OPEN_TERM_ID));
        openWith.addAction(i18n("Other..."))->setData(QVariant(CHOOSE_ID));
        QAction *owAct = addMenu(&openWith);
        owAct->setData(QVariant(OPEN_WITH_ID));
        owAct->setText(i18n("Open With"));
        addSeparator();
    }

    // --------------- user actions
    QAction *uAct = new UserActionPopupMenu(panel->func->files()->vfs_getFile(item->name()));
    addAction(uAct);
    uAct->setText(i18n("User Actions"));

    // add compress and extract plugins (if available)
    bool success = addCompressAndExtractPluginActions();

    /*
     * When KF 5.25 is adopted by all distros, we can remove these 2 lines (and corresponding code)
     * because since KF 5.25 compress and extract submenus are standalone plugins.
     */
    if (!success) {
        fileItemActions.setItemListProperties(KFileItemListProperties(_items));
        fileItemActions.addServiceActionsTo(this);
    }

    addSeparator();

    // ------------- 'create new' submenu
    addCreateNewMenu();
    addSeparator();

    // ---------- COPY
    addAction(i18n("Copy..."))->setData(QVariant(COPY_ID));
    if (panel->func->files() ->vfs_isWritable()) {
        // ------- MOVE
        addAction(i18n("Move..."))->setData(QVariant(MOVE_ID));
        // ------- RENAME - only one file
        if (!multipleSelections && !inTrash)
            addAction(i18n("Rename"))->setData(QVariant(RENAME_ID));

        // -------- MOVE TO TRASH
        KConfigGroup saver(krConfig, "General");
        bool trash = saver.readEntry("Move To Trash", _MoveToTrash);
        if (trash && !inTrash)
            addAction(i18n("Move to Trash"))->setData(QVariant(TRASH_ID));
        // -------- DELETE
        addAction(i18n("Delete"))->setData(QVariant(DELETE_ID));
        // -------- SHRED - only one file
        /*      if ( panel->func->files() ->vfs_getType() == vfs::VFS_NORMAL &&
                    !vf->vfile_isDir() && !multipleSelections )
                 addAction( i18n( "Shred" ) )->setData( QVariant( SHRED_ID ) );*/
    }

    // ---------- link handling
    // create new shortcut or redirect links - only on local directories:
    if (panel->func->files() ->vfs_getType() == vfs::VFS_NORMAL) {
        addSeparator();
        linkPopup.addAction(i18n("New Symlink..."))->setData(QVariant(NEW_SYMLINK_ID));
        linkPopup.addAction(i18n("New Hardlink..."))->setData(QVariant(NEW_LINK_ID));
        if (panel->func->getVFile(item)->vfile_isSymLink())
            linkPopup.addAction(i18n("Redirect Link..."))->setData(QVariant(REDIRECT_LINK_ID));
        QAction *linkAct = addMenu(&linkPopup);
        linkAct->setData(QVariant(LINK_HANDLING_ID));
        linkAct->setText(i18n("Link Handling"));
    }
    addSeparator();

    // ---------- calculate space
    if (panel->func->files() ->vfs_getType() == vfs::VFS_NORMAL && (vf->vfile_isDir() || multipleSelections))
        addAction(panel->gui->actions()->actCalculate);

    // ---------- mount/umount/eject
    if (panel->func->files() ->vfs_getType() == vfs::VFS_NORMAL && vf->vfile_isDir() && !multipleSelections) {
        if (krMtMan.getStatus(panel->func->files() ->vfs_getFile(item->name()).path()) == KMountMan::MOUNTED)
            addAction(i18n("Unmount"))->setData(QVariant(UNMOUNT_ID));
        else if (krMtMan.getStatus(panel->func->files() ->vfs_getFile(item->name()).path()) == KMountMan::NOT_MOUNTED)
            addAction(i18n("Mount"))->setData(QVariant(MOUNT_ID));
        if (krMtMan.ejectable(panel->func->files() ->vfs_getFile(item->name()).path()))
            addAction(i18n("Eject"))->setData(QVariant(EJECT_ID));
    }

    // --------- send by mail
    if (KrServices::supportedTools().contains("MAIL") && !vf->vfile_isDir()) {
        addAction(i18n("Send by Email"))->setData(QVariant(SEND_BY_EMAIL_ID));
    }

    // --------- empty trash
    if (trashOnly) {
        addAction(i18n("Restore"))->setData(QVariant(RESTORE_TRASHED_FILE_ID));
        addAction(i18n("Empty Trash"))->setData(QVariant(EMPTY_TRASH_ID));
    }

#ifdef SYNCHRONIZER_ENABLED
    // --------- synchronize
    if (panel->view->numSelected()) {
        addAction(i18n("Synchronize Selected Files..."))->setData(QVariant(SYNC_SELECTED_ID));
    }
#endif

    // --------- copy/paste
    addSeparator();
    addAction(i18n("Copy to Clipboard"))->setData(QVariant(COPY_CLIP_ID));
    if (panel->func->files() ->vfs_isWritable()) {
        addAction(i18n("Cut to Clipboard"))->setData(QVariant(MOVE_CLIP_ID));
        addAction(i18n("Paste from Clipboard"))->setData(QVariant(PASTE_CLIP_ID));
    }
    addSeparator();

    // --------- properties
    addAction(panel->gui->actions()->actProperties);
}

KrPopupMenu::~KrPopupMenu()
{
    _items.clear();
    if (actions) delete actions;
}

void KrPopupMenu::addEmptyMenuEntries()
{
    addAction(i18n("Paste from Clipboard"))->setData(QVariant(PASTE_CLIP_ID));
}

void KrPopupMenu::addCreateNewMenu()
{
    createNewPopup.addAction(krLoader->loadIcon("folder", KIconLoader::Small), i18n("Folder..."))->setData(QVariant(MKDIR_ID));
    createNewPopup.addAction(krLoader->loadIcon("text-plain", KIconLoader::Small), i18n("Text File..."))->setData(QVariant(NEW_TEXT_FILE_ID));

    QAction *newAct = addMenu(&createNewPopup);
    newAct->setData(QVariant(CREATE_NEW_ID));
    newAct->setText(i18n("Create New"));

}

void KrPopupMenu::performAction(int id)
{
    switch (id) {
    case - 1 : // the user clicked outside of the menu
        return ;
    case OPEN_TAB_ID :
        // assuming only 1 file is selected (otherwise we won't get here)
        panel->manager()->newTab(_item->url(), panel);
        break;
    case OPEN_ID :
        foreach(KFileItem fi, _items)
            panel->func->execute(fi.name());
        break;
    case BROWSE_ID :
        panel->func->goInside(_item->url().fileName());
        break;
    case COPY_ID :
        panel->func->copyFiles();
        break;
    case MOVE_ID :
        panel->func->moveFiles();
        break;
    case RENAME_ID :
        panel->func->rename();
        break;
    case TRASH_ID :
        panel->func->deleteFiles(false);
        break;
    case DELETE_ID :
        panel->func->deleteFiles(true);
        break;
    case EJECT_ID :
        krMtMan.eject(_item->url().adjusted(QUrl::StripTrailingSlash).path());
        break;
        /*         case SHRED_ID :
                    if ( KMessageBox::warningContinueCancel( krApp,
                         i18n("<qt>Do you really want to shred <b>%1</b>? Once shred, the file is gone forever.</qt>", item->name()),
                         QString(), KStandardGuiItem::cont(), KStandardGuiItem::cancel(), "Shred" ) == KMessageBox::Continue )
                       KShred::shred( panel->func->files() ->vfs_getFile( item->name() ).adjusted(QUrl::RemoveTrailingSlash).path() );
                  break;*/
    case OPEN_KONQ_ID :
        KToolInvocation::startServiceByDesktopName("konqueror", _item->url().toDisplayString(QUrl::PreferLocalFile));
        break;
    case CHOOSE_ID : // open-with dialog
        panel->func->displayOpenWithDialog(_items.urlList());
        break;
    case MOUNT_ID :
        krMtMan.mount(_item->url().adjusted(QUrl::StripTrailingSlash).path());
        break;
    case NEW_LINK_ID :
        panel->func->krlink(false);
        break;
    case NEW_SYMLINK_ID :
        panel->func->krlink(true);
        break;
    case REDIRECT_LINK_ID :
        panel->func->redirectLink();
        break;
    case EMPTY_TRASH_ID :
        KrTrashHandler::emptyTrash();
        break;
    case RESTORE_TRASHED_FILE_ID :
        KrTrashHandler::restoreTrashedFiles(_items.urlList());
    break;
    case UNMOUNT_ID :
        krMtMan.unmount(_item->url().adjusted(QUrl::StripTrailingSlash).path());
        break;
    case COPY_CLIP_ID :
        panel->func->copyToClipboard();
        break;
    case MOVE_CLIP_ID :
        panel->func->copyToClipboard(true);
        break;
    case PASTE_CLIP_ID :
        panel->func->pasteFromClipboard();
        break;
    case SEND_BY_EMAIL_ID : {
        SLOTS->sendFileByEmail(_items.urlList());
        break;
    }
    case MKDIR_ID :
        panel->func->mkdir();
        break;
    case NEW_TEXT_FILE_ID:
        panel->func->editNew();
        break;
#ifdef SYNCHRONIZER_ENABLED
    case SYNC_SELECTED_ID : {
        QStringList selectedNames;
        foreach(KFileItem item, _items)
            selectedNames << item.name();
        if (panel->otherPanel()->view->numSelected()) {
            KrViewItemList otherItems;
            panel->otherPanel()->view->getSelectedKrViewItems(&otherItems);

            for (KrViewItemList::Iterator it2 = otherItems.begin(); it2 != otherItems.end(); ++it2) {
                QString name = (*it2) ->name();
                if (!selectedNames.contains(name))
                    selectedNames.append(name);
            }
        }
        SLOTS->slotSynchronizeDirs(selectedNames);
    }
    break;
#endif
    case OPEN_TERM_ID :
        SLOTS->runTerminal(_item->url().path());
        break;
    }

    // check if something from the open-with-offered-services was selected
    if (id >= SERVICE_LIST_ID) {
        QStringList names;
        panel->gui->getSelectedNames(&names);
        panel->func->runService(*(offers[ id - SERVICE_LIST_ID ]),
                                panel->func->files()->vfs_getFiles(names));
    }
}

