/***************************************************************************
 *   Copyright (C) 2008 by S. MANKOWSKI / G. DE BURE support@mankowski.fr  *
 *                                                                         *
 *   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/>  *
 ***************************************************************************/
/** @file
 * This file is a plugin for file operation.
 *
 * @author Stephane MANKOWSKI / Guillaume DE BURE
 */
#include "skgfileplugin.h"

#include <KActionCollection>
#include <KStandardAction>
#include <KRecentFilesAction>
#include <KFileDialog>
#include <KSplashScreen>
#include <KCmdLineArgs>
#include <KAboutData>
#include <knewpassworddialog.h>
#include <KPasswordDialog>
#include <kwallet.h>
#include <KGenericFactory>

#include <QtGui/QWidget>
#include <QFile>
#include <KMessageWidget>

#include "skgfile_settings.h"
#include "skgtraces.h"
#include "skgerror.h"
#include "skgmainpanel.h"
#include "skgtransactionmng.h"

/**
 * This plugin factory.
 */
K_PLUGIN_FACTORY(SKGFilePluginFactory, registerPlugin<SKGFilePlugin>();)
/**
 * This plugin export.
 */
K_EXPORT_PLUGIN(SKGFilePluginFactory("skg_file", "skg_file"))

SKGFilePlugin::SKGFilePlugin(QWidget* iWidget, QObject* iParent, const QVariantList& /*iArg*/) :
    SKGInterfacePlugin(iParent),
    m_saveAction(NULL), m_saveAsAction(NULL), m_passwordAction(NULL), m_recentFiles(NULL), m_currentDocument(NULL)
{
    Q_UNUSED(iWidget);
    SKGTRACEINFUNC(10);

    // Set save on close mode
    SKGMainPanel::getMainPanel()->setSaveOnClose(skgfile_settings::saveonclose());
}

SKGFilePlugin::~SKGFilePlugin()
{
    SKGTRACEINFUNC(10);
    if (m_recentFiles) {
        m_recentFiles->saveEntries(KConfigGroup(KGlobal::config(), "RecentFiles"));
    }

    m_currentDocument = NULL;
    m_recentFiles = NULL;
    m_saveAction = NULL;
    m_saveAsAction = NULL;
    m_passwordAction = NULL;
}

bool SKGFilePlugin::setupActions(SKGDocument* iDocument, const QStringList& iArgument)
{
    SKGTRACEINFUNC(10);

    m_currentDocument = iDocument;
    if (m_currentDocument == NULL) {
        return false;
    }

    setComponentData(KGlobal::mainComponent());
    setXMLFile("../skg_file/skg_file.rc");

    // Menu
    KAction* opennew = KStandardAction::openNew(this, SLOT(onNew()), actionCollection());
    registerGlobalAction("file_new", opennew);

    KAction* open = KStandardAction::open(this, SLOT(onOpen()), actionCollection());
    registerGlobalAction("file_open", open);

    m_saveAction = KStandardAction::save(this, SLOT(onSave()), actionCollection());
    registerGlobalAction("file_save", m_saveAction);

    m_saveAsAction = KStandardAction::saveAs(this, SLOT(onSaveAs()), actionCollection());
    registerGlobalAction("file_save_as", m_saveAsAction);

    m_passwordAction = new KAction(KIcon("document-encrypt"),  i18nc("Action allowing the user to change his document password", "Change password..."), this);
    connect(m_passwordAction, SIGNAL(triggered(bool)), this, SLOT(onChangePassword()));
    m_passwordAction->setShortcut(Qt::CTRL + Qt::Key_K);
    registerGlobalAction("file_change_password", m_passwordAction);

    // Recent file
    m_recentFiles = KStandardAction::openRecent(this, SLOT(onOpen(KUrl)), actionCollection());
    if (m_recentFiles) {
        m_recentFiles->loadEntries(KConfigGroup(KGlobal::config(), "RecentFiles"));
    }

    // Get last argument
    connect(this, SIGNAL(loadFile(KUrl)), this, SLOT(onOpen(KUrl)), Qt::QueuedConnection);

    int nbArg = iArgument.count();
    int openMode = 1;  // 0=no open, 1=open last opened if settings set, 2=new document
    if (nbArg) {
        openMode = 2;
        QString filename = iArgument.at(nbArg - 1);
        QString extension = QFileInfo(filename).suffix().toUpper();
        QString extensionDocument = m_currentDocument->getFileExtension().toUpper();
        if (QFile(filename).exists() && extension == extensionDocument) {
            processArguments(iArgument);
            openMode = 0;
        }
    }

    if (openMode) {
        // Read Setting
        bool openlastfile = skgfile_settings::openlastfile();
        if (openMode == 1 && openlastfile) {
            QString lastOpenedFile = skgfile_settings::lastfilepath();
            if (!lastOpenedFile.isEmpty() && QFile(lastOpenedFile).exists()) {
                if (SKGMainPanel::getMainPanel()) {
                    KSplashScreen* splashScreen = SKGMainPanel::getMainPanel()->splashScreen();
                    if (splashScreen) {
                        splashScreen->showMessage(i18nc("Splash screen message",  "Opening file %1...", lastOpenedFile), Qt::AlignLeft, QColor(221, 130, 8));    // krazy:exclude=qmethods
                    }
                }
                Q_EMIT loadFile(lastOpenedFile);
            } else {
                openMode = 2;
            }
        }

        if (openMode == 2) {
            onNew();
        }
    }

    // To be sure that the document has the right parameters
    savePreferences();

    return true;
}

bool SKGFilePlugin::processArguments(const QStringList& iArgument)
{
    SKGTRACEINFUNC(10);
    bool output = false;
    int nbArg = iArgument.count();
    if (nbArg && m_currentDocument) {
        QString filename = iArgument.at(nbArg - 1);
        QString extension = QFileInfo(filename).suffix().toUpper();
        QString extensionDocument = m_currentDocument->getFileExtension().toUpper();
        if (QFile(filename).exists() && extension == extensionDocument) {
            if (SKGMainPanel::getMainPanel()) {
                KSplashScreen* splashScreen = SKGMainPanel::getMainPanel()->splashScreen();
                if (splashScreen) {
                    splashScreen->showMessage(i18nc("Splash screen message", "Opening file %1...", filename), Qt::AlignLeft, QColor(221, 130, 8));    // krazy:exclude=qmethods
                }
            }
            Q_EMIT loadFile(filename);
            output = true;
        }
    }
    return output;
}

QWidget* SKGFilePlugin::getPreferenceWidget()
{
    SKGTRACEINFUNC(10);
    QWidget* w = new QWidget();
    ui.setupUi(w);

    ui.kcfg_prefix->addItem("");
    ui.kcfg_prefix->addItem(".");

    ui.kcfg_suffix->addItem(".old");
    ui.kcfg_suffix->addItem(".back");
    ui.kcfg_suffix->addItem(".<DATE>.back");
    ui.kcfg_suffix->addItem(".<DATE>.old");
    ui.kcfg_suffix->addItem("~");

    ui.kcfg_selectedWallet->addItems(KWallet::Wallet::walletList());

    return w;
}

KConfigSkeleton* SKGFilePlugin::getPreferenceSkeleton()
{
    return skgfile_settings::self();
}

SKGError SKGFilePlugin::savePreferences() const
{
    SKGError err;
    if (m_currentDocument) {
        // Read Setting
        QString prefix;
        QString suffix;
        if (skgfile_settings::backup_enabled()) {
            prefix = skgfile_settings::prefix();
            suffix = skgfile_settings::suffix();
        }

        // Save setting in document
        m_currentDocument->setBackupParameters(prefix, suffix);

        // Set save on close mode
        SKGMainPanel::getMainPanel()->setSaveOnClose(skgfile_settings::saveonclose());
    }
    return err;
}

void SKGFilePlugin::refresh()
{
    SKGTRACEINFUNC(10);

    // Refresh action status
    if (m_currentDocument) {
        bool test = (m_currentDocument->getDatabase() != NULL);
        if (m_saveAction) {
            m_saveAction->setEnabled(m_currentDocument->isFileModified());
        }
        if (m_saveAsAction) {
            m_saveAsAction->setEnabled(test);
        }
        if (m_passwordAction) {
            m_passwordAction->setEnabled(test);
        }
    }
}

QString SKGFilePlugin::title() const
{
    return i18nc("Noun, a file as in a text file", "File");
}

QString SKGFilePlugin::icon() const
{
    return "document-save";
}

QString SKGFilePlugin::toolTip() const
{
    return i18nc("File Management, as in Save File, Save As...", "File management");
}

QStringList SKGFilePlugin::tips() const
{
    QStringList output;
    output.push_back(i18nc("Description of a tip", "<p>... the last opened file can be open automatically when the application is launched.</p>"));
    output.push_back(i18nc("Description of a tip", "<p>... you can secure your document with a password.</p>"));
    return output;
}

int SKGFilePlugin::getOrder() const
{
    // Must be one of the first
    return 1;
}

void SKGFilePlugin::onNew()
{
    SKGError err;
    SKGTRACEINFUNCRC(10, err);
    if (SKGMainPanel::getMainPanel() && m_currentDocument && SKGMainPanel::getMainPanel()->queryFileClose()) {
        QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));

        SKGMainPanel::getMainPanel()->closeAllPages(true);

        err = m_currentDocument->initialize();

        KLocale* loc = KGlobal::locale();
        IFOKDO(err, m_currentDocument->setLanguage(loc->language()))
        QApplication::restoreOverrideCursor();

        // status bar
        IFOKDO(err, SKGError(0, i18nc("Successful message after creating a document", "Document successfully created.")))
        else {
            err.addError(ERR_FAIL, i18nc("Error message: Could not create a document",  "Document creation failed."));
        }

        // Display error
        SKGMainPanel::displayErrorMessage(err);
    }
}

void SKGFilePlugin::onSave()
{
    SKGError err;
    SKGTRACEINFUNCRC(10, err);
    if (m_currentDocument && SKGMainPanel::getMainPanel()) {
        if (m_currentDocument->getCurrentFileName().isEmpty()) {
            onSaveAs();
        } else {
            QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
            err = m_currentDocument->save();
            QApplication::restoreOverrideCursor();

            // Refresh
            SKGMainPanel::getMainPanel()->refresh();

            // status bar
            IFOKDO(err, SKGError(0, i18nc("Successfully saved a file", "File successfully saved.")))
            else {
                err.addError(ERR_FAIL, i18nc("Error message: Could not save a file",  "Cannot save file"));
            }

            // Display error
            SKGMainPanel::displayErrorMessage(err);
        }
    }
}

void SKGFilePlugin::onSaveAs()
{
    SKGError err;
    SKGTRACEINFUNCRC(10, err);
    if (m_currentDocument && SKGMainPanel::getMainPanel()) {
        QString fileName = SKGMainPanel::getSaveFileName("kfiledialog:///" % m_currentDocument->objectName(),
                           "*." % m_currentDocument->getFileExtension() % '|' % i18nc("Associated with the file extension : for example, .csv --> CSV document", "%1 document", KCmdLineArgs::aboutData()->programName()) ,
                           SKGMainPanel::getMainPanel());
        if (fileName.isEmpty()) {
            return;
        }

        QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
        err = m_currentDocument->saveAs(fileName, true);
        QApplication::restoreOverrideCursor();

        // Refresh
        SKGMainPanel::getMainPanel()->refresh();

        // status bar
        IFOK(err) {
            err = SKGError(0, i18nc("Successfully saved a file", "File '%1' saved.", fileName));
            // Add in recentFiles
            if (m_recentFiles) {
                m_recentFiles->addUrl(fileName);
                m_recentFiles->saveEntries(KConfigGroup(KGlobal::config(), "RecentFiles"));
            }
            // Set as last open file in kcfg
            KSharedConfigPtr config = KSharedConfig::openConfig();
            KConfigGroup pref = config->group("File");
            pref.writePathEntry("lastfilepath", fileName);

        } else {
            err.addError(ERR_FAIL, i18nc("Error message: Could not save a file",  "Failed to save '%1'.", fileName));
        }

        // Display error
        SKGMainPanel::displayErrorMessage(err);
    }
}

void SKGFilePlugin::onReOpen()
{
    QAction* act = qobject_cast< QAction* >(sender());
    if (act) {
        QString filename = act->data().toString();
        QFile(SKGDocument::getTemporaryFile(filename)).remove();
        onOpen(filename);
    }
}

void SKGFilePlugin::onOpen(const KUrl& iUrl)
{
    SKGError err;
    SKGTRACEINFUNCRC(10, err);
    if (SKGMainPanel::getMainPanel() && m_currentDocument && SKGMainPanel::getMainPanel()->queryFileClose()) {
        bool useKWallet = skgfile_settings::storeInKdeWallet();
        QString pwd;
        QString programName = KCmdLineArgs::aboutData()->programName();
        QString fileName = iUrl.pathOrUrl();
        if (fileName.isEmpty()) fileName = KFileDialog::getOpenUrl(KUrl("kfiledialog:///" % m_currentDocument->objectName()),
                                               "*." % m_currentDocument->getFileExtension() % '|' % i18nc("Associated with the file extension : for example, .csv --> CSV document", "%1 document", programName) ,
                                               SKGMainPanel::getMainPanel()).pathOrUrl();
        if (!fileName.isEmpty()) {
            // Check if temporary file exists
            bool restoreTmpFile = false;
            QString tmpFile = SKGDocument::getTemporaryFile(fileName);
            if (QFile(tmpFile).exists()) {
                KMessageWidget* msg = SKGMainPanel::getMainPanel()->displayMessage(i18nc("Warning message", "Your document has been restored from its temporary file. You can decide to reopen the original document."), SKGDocument::Warning);
                QAction* reopen = new QAction(i18nc("Noun", "Reopen"), msg);
                reopen->setIcon(KIcon("skg_open"));
                reopen->setData(fileName);
                msg->addAction(reopen);
                connect(reopen, SIGNAL(triggered(bool)), this, SLOT(onReOpen()));
                connect(reopen, SIGNAL(triggered(bool)), msg, SLOT(deleteLater()), Qt::QueuedConnection);

                restoreTmpFile = true;
            }

            // Open
            QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
            SKGMainPanel::getMainPanel()->closeAllPages(true);
            err = m_currentDocument->load(fileName, "", restoreTmpFile);
            QApplication::restoreOverrideCursor();

            if (err && err.getReturnCode() == ERR_ENCRYPTION) {
                m_currentDocument->close();

                // Open failed
                // Password must be asked
                QString additionalMessage;
                do {
                    // Reset error
                    err = SKGError(0, "");
                    pwd = "";

                    // Get password
                    if (useKWallet) {
                        // Use KWallet
                        QString walletName = KWallet::Wallet::walletList().value(SKGServices::stringToInt(skgfile_settings::selectedWallet()));
                        if (walletName.isEmpty()) {
                            walletName = "kdewallet";
                        }
                        KWallet::Wallet* w = KWallet::Wallet::openWallet(walletName, SKGMainPanel::getMainPanel()->winId());
                        if (w) {
                            // Change folder
                            if (!w->hasFolder(programName)) {
                                w->createFolder(programName);
                            }
                            w->setFolder(programName);

                            // Read password
                            w->readPassword(fileName, pwd);
                            if (pwd.isEmpty()) {
                                useKWallet = false;
                            }

                            delete w;
                            w = NULL;
                        }
                    }

                    if (!useKWallet) {
                        // Use password dialog
                        QPointer<KPasswordDialog> dlg = new KPasswordDialog(SKGMainPanel::getMainPanel());
                        dlg->setPrompt(additionalMessage % i18nc("Question", "This file seems to be protected.\nPlease enter the password."));
                        if (dlg->exec() == QDialog::Accepted) {
                            pwd = dlg->password();
                        }
                        delete dlg;
                    }

                    // Load file
                    if (!pwd.isEmpty()) {
                        KSplashScreen* splashScreen = SKGMainPanel::getMainPanel()->splashScreen();
                        if (splashScreen) {
                            splashScreen->hide();
                        }

                        QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
                        err = m_currentDocument->load(fileName, pwd, restoreTmpFile);
                        IFKO(err) {
                            if (err.getReturnCode() == ERR_ENCRYPTION) {
                                additionalMessage = i18nc("The user did not provide the correct password", "<b>Wrong password.</b>\n");
                                useKWallet = false;
                            } else {
                                // Load error
                                QApplication::restoreOverrideCursor();
                                break;
                            }
                        }
                        QApplication::restoreOverrideCursor();

                        if (splashScreen) {
                            splashScreen->show();
                        }
                    }
                } while (err);
            }

            KLocale* loc = KGlobal::locale();
            IFOKDO(err, m_currentDocument->setLanguage(loc->language()))

            // status bar
            IFOK(err) {
                err = SKGError(0, i18nc("Successfully opened a file", "File '%1' opened.", fileName));
                // Add in recentFiles
                if (m_recentFiles) {
                    m_recentFiles->addUrl(fileName);
                    m_recentFiles->saveEntries(KConfigGroup(KGlobal::config(), "RecentFiles"));
                }
                // Set as last open file in kcfg
                KSharedConfigPtr config = KSharedConfig::openConfig();
                KConfigGroup pref = config->group("File");
                pref.writePathEntry("lastfilepath", fileName);

                // Store password if KDE wallet if needed
                if (skgfile_settings::storeInKdeWallet() && !useKWallet) {
                    // Use KWallet
                    QString walletName = KWallet::Wallet::walletList().value(SKGServices::stringToInt(skgfile_settings::selectedWallet()));
                    if (walletName.isEmpty()) {
                        walletName = "kdewallet";
                    }
                    KWallet::Wallet* w = KWallet::Wallet::openWallet(walletName, SKGMainPanel::getMainPanel()->winId());
                    if (w) {
                        // Change folder
                        w->setFolder(programName);

                        // Write password
                        w->writePassword(fileName, pwd);

                        delete w;
                        w = NULL;
                    }
                }
            } else {
                this->onNew();
                if (err.getReturnCode() != ERR_CORRUPTION) {
                    err.addError(ERR_FAIL, i18nc("Error message: Could not open a file",  "Failed to open '%1'.", fileName));
                }
            }

            // Display error
            QAction* recovery = NULL;
#ifdef Q_OS_UNIX
            if (err.getReturnCode() == ERR_CORRUPTION) {
                recovery = new QAction(i18nc("Noun", "Try a recovery"), this);
                recovery->setIcon(KIcon("games-solve"));
                recovery->setData(SKGServices::stringsToCsv(QStringList() << fileName << pwd));
                connect(recovery, SIGNAL(triggered(bool)), this, SLOT(onRecover()));
            }
#endif
            SKGMainPanel::displayErrorMessage(err, recovery);
        }
    }
}

void SKGFilePlugin::onRecover()
{
    SKGError err;
    SKGTRACEINFUNCRC(10, err);
    QAction* act = qobject_cast< QAction* >(sender());
    if (act && m_currentDocument && SKGMainPanel::getMainPanel()) {
        QStringList params = SKGServices::splitCSVLine(act->data().toString());
        QString recoveredFileName;
        err = m_currentDocument->recover(params.at(0), params.at(1), recoveredFileName);

        IFOK(err) {
            // Display recovery message
            KMessageWidget* msg = SKGMainPanel::getMainPanel()->displayMessage(i18nc("Positive message", "Your document has been recovered here: %1\nTake care the recovery could be not perfect", recoveredFileName), SKGDocument::Positive);
            QAction* reopen = new QAction(i18nc("Noun", "Open the recovered file"), msg);
            reopen->setIcon(KIcon("skg_open"));
            reopen->setData(recoveredFileName);
            msg->addAction(reopen);
            connect(reopen, SIGNAL(triggered(bool)), this, SLOT(onReOpen()));
            connect(reopen, SIGNAL(triggered(bool)), msg, SLOT(deleteLater()), Qt::QueuedConnection);
        } else {
            // Display error
            SKGMainPanel::displayErrorMessage(err);
        }
    }
}

void SKGFilePlugin::onChangePassword()
{
    SKGError err;
    SKGTRACEINFUNCRC(10, err);
    if (m_currentDocument && SKGMainPanel::getMainPanel()) {
        QPointer<KNewPasswordDialog> dlg = new KNewPasswordDialog(SKGMainPanel::getMainPanel());
        dlg->setPrompt(i18n("Take care, if you lose your <b>password</b> then it will be <u><b>impossible</b></u> to open your document."));
        if (!dlg->exec()) {
            err = SKGError(0, i18nc("Successfully changed the document password", "Changing password was canceled."));
        } else {
            SKGBEGINLIGHTTRANSACTION(*m_currentDocument, i18nc("Action allowing user to change the document password", "Change password"), err);
            err = m_currentDocument->changePassword(dlg->password());

            // status
            IFOKDO(err, SKGError(0, i18nc("Successfully changed the document password", "Password changed.")))
            else {
                err.addError(ERR_FAIL, i18nc("Error message: Could not change the document password",  "Failed to change password."));
            }
        }
        delete dlg;

        // Display error
        SKGMainPanel::displayErrorMessage(err);
    }
}

SKGAdviceList SKGFilePlugin::advice(const QStringList& iIgnoredAdvice)
{
    SKGTRACEINFUNC(10);
    SKGAdviceList output;

    // Backup
    if (!iIgnoredAdvice.contains("skgfileplugin_notvalidated")) {
        SKGAdvice ad;
        ad.setUUID("skgfileplugin_notvalidated");
        ad.setPriority(2);
        ad.setShortMessage(i18nc("Advice to the user that he should backup his document", "Backup your document"));
        ad.setLongMessage(i18nc("Explain the user that he should backup his document", "Don't forget to backup your document on an other device."));
        output.push_back(ad);
    }

    return output;
}

#include "skgfileplugin.moc"
