/***************************************************************************
 *   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 "skgfile_settings.h"

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

#include <kactioncollection.h>
#include <kstandardaction.h>
#include <krecentfilesaction.h>
#include <kfiledialog.h>
#include <ksplashscreen.h>
#include <kcmdlineargs.h>
#include <kaboutdata.h>
#include <knewpassworddialog.h>
#include <kpassworddialog.h>
#include <kwallet.h>
#include <kgenericfactory.h>

#include <QtGui/QWidget>
#include <QFile>
#include <kmessagewidget.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_recentFiles(NULL)
{
    SKGTRACEIN(10, "SKGFilePlugin::SKGFilePlugin");

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

SKGFilePlugin::~SKGFilePlugin()
{
    SKGTRACEIN(10, "SKGFilePlugin::~SKGFilePlugin");
    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)
{
    SKGTRACEIN(10, "SKGFilePlugin::setupActions");

    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) {
            SKGTRACEIN(10, "SKGFilePlugin::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;
}

void SKGFilePlugin::processArguments(const QStringList& iArgument)
{
    SKGTRACEIN(10, "SKGFilePlugin::processArguments");
    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);
        }
    }
}

QWidget* SKGFilePlugin::getPreferenceWidget()
{
    SKGTRACEIN(10, "SKGFilePlugin::getPreferenceWidget");
    QWidget* widget = new QWidget();
    ui.setupUi(widget);

    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 widget;
}

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()
{
    SKGTRACEIN(10, "SKGFilePlugin::refresh");

    //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;
    SKGTRACEINRC(10, "SKGFilePlugin::onNew", err);
    if (SKGMainPanel::getMainPanel() && m_currentDocument && SKGMainPanel::getMainPanel()->queryFileClose()) {
        QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));

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

        err = m_currentDocument->initialize();

        KLocale* locale = KGlobal::locale();
        if (!err) err = m_currentDocument->setLanguage(locale->language());
        QApplication::restoreOverrideCursor();

        //status bar
        if (!err) 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;
    SKGTRACEINRC(10, "SKGFilePlugin::onSave", 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
            if (!err) 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;
    SKGTRACEINRC(10, "SKGFilePlugin::onSaveAs", 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
        if (!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;
    SKGTRACEINRC(10, "SKGFilePlugin::onOpen", 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) {
                        SKGTRACEINRC(10, "SKGFilePlugin::onOpen-get kwallet passport", err);
                        //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);
                        if (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* locale = KGlobal::locale();
            if (!err) err = m_currentDocument->setLanguage(locale->language());

            //status bar
            if (!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();
                err.addError(ERR_FAIL, i18nc("Error message: Could not open a file",  "Failed to open '%1'.", fileName));
            }

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

void SKGFilePlugin::onChangePassword()
{
    SKGError err;
    SKGTRACEINRC(10, "SKGFilePlugin::onChangePassword", 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
            if (!err)  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)
{
    SKGTRACEIN(10, "SKGFilePlugin::advice");
    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"
