/***************************************************************************
 *   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 Skrooge plugin for import and export operation.
 *
 * @author Stephane MANKOWSKI / Guillaume DE BURE
 */
#include "skgimportexportplugin.h"

#include <KActionCollection>
#include <KAction>
#include <KActionMenu>
#include <KFileDialog>
#include <KAboutData>
#include <KEncodingFileDialog>
#include <KMessageBox>
#include <KPasswordDialog>
#include <KGenericFactory>
#include <KStandardDirs>
#include <KMessageWidget>

#include <QTextCodec>
#include <QDomDocument>

#include "skgtraces.h"
#include "skgerror.h"
#include "skgbankincludes.h"
#include "skgmainpanel.h"
#include "skgoperationobject.h"
#include "skgimportexport_settings.h"

/**
 * This plugin factory.
 */
K_PLUGIN_FACTORY(SKGImportExportPluginFactory, registerPlugin<SKGImportExportPlugin>();)
/**
 * This plugin export.
 */
K_EXPORT_PLUGIN(SKGImportExportPluginFactory("skrooge_importexport", "skrooge_importexport"))

SKGImportExportPlugin::SKGImportExportPlugin(QWidget* iWidget, QObject* iParent, const QVariantList& /*iArg*/) :
    SKGInterfacePlugin(iParent),
    m_importAction(NULL), m_importBackendAction(NULL), m_importCsvUnitAction(NULL), m_exportFileAction(NULL),
    m_processingFoundTransfert(NULL), m_processingBank(NULL), m_processingAnonymize(NULL), m_validateImportedOperationsAction(NULL),
    m_openNotValidatedAction(NULL), m_mergeImportedOperationAction(NULL), m_currentBankDocument(NULL),
    m_install(false)
{
    Q_UNUSED(iWidget);
    SKGTRACEINFUNC(10);
}

SKGImportExportPlugin::~SKGImportExportPlugin()
{
    SKGTRACEINFUNC(10);
    m_currentBankDocument = NULL;

    m_importAction = NULL;
    m_importBackendAction = NULL;
    m_importCsvUnitAction = NULL;
    m_exportFileAction = NULL;
    m_processingFoundTransfert = NULL;
    m_processingAnonymize = NULL;
    m_processingBank = NULL;
    m_validateImportedOperationsAction = NULL;
    m_openNotValidatedAction = NULL;
    m_mergeImportedOperationAction = NULL;
}

bool SKGImportExportPlugin::setupActions(SKGDocument* iDocument, const QStringList& iArgument)
{
    SKGTRACEINFUNC(10);
    Q_UNUSED(iArgument);
    m_currentBankDocument = qobject_cast<SKGDocumentBank*>(iDocument);
    if (m_currentBankDocument == NULL) {
        return false;
    }

    // Tell the host application to load my GUI component
    setComponentData(KGlobal::mainComponent());
    setXMLFile("../skrooge_importexport/skrooge_importexport.rc");

    // Imports
    KActionMenu* imports = new  KActionMenu(KIcon("document-import"), i18nc("Verb, action to import items from another format", "Import"), this);
    registerGlobalAction("import", imports);

    // Import
    QStringList overlay;
    overlay.push_back("skrooge");

    m_importAction = new KAction(KIcon("document-import", NULL, overlay), i18nc("Verb, action to import items from another format", "Import..."), this);
    m_importAction->setShortcut(Qt::CTRL + Qt::META + Qt::Key_I);
    connect(m_importAction, SIGNAL(triggered(bool)), SLOT(import()));
    imports->addAction(m_importAction);
    registerGlobalAction("import_operation", m_importAction);

    // Import backends
    QStringList overlay2;
    overlay.push_back("download");

    m_importBackendAction = new KAction(KIcon("document-import", NULL, overlay2), i18nc("Verb, action to import items from another format", "Import with backends"), this);
    m_importBackendAction->setShortcut(Qt::CTRL + Qt::META + Qt::Key_W);
    connect(m_importBackendAction, SIGNAL(triggered(bool)), SLOT(importbackends()));
    imports->addAction(m_importBackendAction);
    registerGlobalAction("import_backends", m_importBackendAction);

    // Import CSV Unit
    QStringList overlaycsv;
    overlaycsv.push_back("text-csv");
    m_importCsvUnitAction = new KAction(KIcon("document-import", NULL, overlaycsv), i18nc("Verb, action to import", "Import currency values..."), this);
    connect(m_importCsvUnitAction, SIGNAL(triggered(bool)), SLOT(import()));
    imports->addAction(m_importCsvUnitAction);
    registerGlobalAction("import_csv_unit", m_importCsvUnitAction);

    // Exports
    KActionMenu* exports = new  KActionMenu(KIcon("document-export"), i18nc("Verb, action to export items in another format", "Export"), this);
    registerGlobalAction("export", exports);

    // Export
    m_exportFileAction = new KAction(KIcon("document-export"), i18nc("Verb, action to export items to another format", "Export..."), this);
    connect(m_exportFileAction, SIGNAL(triggered(bool)), SLOT(exportFile()));
    exports->addAction(m_exportFileAction);
    m_exportFileAction->setShortcut(Qt::CTRL + Qt::META + Qt::Key_E);
    registerGlobalAction("export_operation", m_exportFileAction);

    // Processing
    KActionMenu* processing = new  KActionMenu(KIcon("tools-wizard"), i18nc("Noun, apply some kind of transformation on an item", "Processing"), this);
    registerGlobalAction("processing", processing);

    // Processing found and group
    QStringList overlaytransfers;
    overlaytransfers.push_back("view-financial-transfer");

    m_processingFoundTransfert = new KAction(KIcon("tools-wizard", NULL, overlaytransfers), i18nc("Verb, action to find and group transfers", "Find and group transfers"), this);
    connect(m_processingFoundTransfert, SIGNAL(triggered(bool)), SLOT(findTransfers()));
    processing->addAction(m_processingFoundTransfert);
    m_processingFoundTransfert->setShortcut(Qt::CTRL + Qt::META + Qt::Key_G);
    registerGlobalAction("process_foundtransfer", m_processingFoundTransfert);

    m_processingAnonymize = new KAction(KIcon("tools-wizard", NULL, overlaytransfers), i18nc("Verb, action to anonymize a document", "Anonymize"), this);
    connect(m_processingAnonymize, SIGNAL(triggered(bool)), SLOT(anonymize()));
    processing->addAction(m_processingAnonymize);
    registerGlobalAction("process_anonymize", m_processingAnonymize);

    // Processing banks
    m_processingBank = new KAction(KIcon("tools-wizard"), i18nc("Verb, action to clean an import", "Clean bank's imports"), this);
    connect(m_processingBank, SIGNAL(triggered(bool)), SLOT(cleanBanks()));
    processing->addAction(m_processingBank);
    registerGlobalAction("process_banks", m_processingBank);

    // Processing banks
    QStringList overlayValidate;
    overlayValidate.push_back("dialog-ok-apply");
    m_validateImportedOperationsAction = new KAction(KIcon("document-import", NULL, overlayValidate), i18nc("Verb, action to validate imported operations", "Validate imported operations"), this);
    connect(m_validateImportedOperationsAction, SIGNAL(triggered(bool)), SLOT(validateImportedOperations()));
    m_validateImportedOperationsAction->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_V);
    registerGlobalAction("validate_imported_operation", m_validateImportedOperationsAction);


    KAction* act = new KAction(KIcon("document-import", NULL, overlayValidate), i18nc("Verb, action to merge", "Validate operations that do not require further action"), this);
    connect(act, SIGNAL(triggered(bool)), SLOT(validateAllOperations()));
    registerGlobalAction("process_validate", act);

    QStringList overlayopen;
    overlayopen.push_back("skg_open");
    m_openNotValidatedAction = new KAction(KIcon("document-import", NULL, overlayopen), i18nc("Verb, action to open", "Open imported operations not yet validated..."), this);
    m_openNotValidatedAction->setData(QString("skg://skrooge_operation_plugin/?title=" % SKGServices::encodeForUrl(i18nc("Noun, a list of items", "Operations imported and not yet validated")) %
                                      "&title_icon=" % SKGServices::encodeForUrl(icon()) %
                                      "&operationWhereClause=" % SKGServices::encodeForUrl("t_imported='P'")));
    connect(m_openNotValidatedAction, SIGNAL(triggered(bool)), SKGMainPanel::getMainPanel(), SLOT(openPage()));
    m_openNotValidatedAction->setShortcut(Qt::META + Qt::Key_V);
    registerGlobalAction("view_open_not_validated", m_openNotValidatedAction);

    m_mergeImportedOperationAction = new KAction(KIcon("merge"), i18nc("Verb, action to merge", "Merge imported operations"), this);
    connect(m_mergeImportedOperationAction, SIGNAL(triggered(bool)), SLOT(mergeImportedOperation()));
    m_mergeImportedOperationAction->setShortcut(Qt::CTRL + Qt::ALT + Qt::Key_M);
    registerGlobalAction("merge_imported_operation", m_mergeImportedOperationAction);

    // Get last argument
    connect(this, SIGNAL(importFileName(QString)), this, SLOT(import(QString)), Qt::QueuedConnection);

    // Open/import input file
    int nbArg = iArgument.count();
    if (nbArg) {
        QString filename = iArgument.at(nbArg - 1);
        QString extension = QFileInfo(filename).suffix().toUpper();
        QString extensionDocument = m_currentBankDocument->getFileExtension().toUpper();
        if (QFile(filename).exists() && extension != extensionDocument) {
            processArguments(iArgument);
        }
    }

    // Krunner operations
    QString dirName = QDir::homePath() % "/.skrooge/";
    QStringList fileList = QDir(dirName).entryList(QStringList() << "add_operation_*.txt", QDir::Files);
    if (fileList.count()) {
        m_currentBankDocument->sendMessage(i18nc("Information message", "You have some krunner's operations to import"));
    }

    return true;
}

bool SKGImportExportPlugin::processArguments(const QStringList& iArgument)
{
    SKGTRACEINFUNC(10);
    bool output = false;
    int nbArg = iArgument.count();
    if (nbArg) {
        QString filename = iArgument.at(nbArg - 1);
        if (QFile(filename).exists()) {
            Q_EMIT importFileName(filename);
            output = true;
        }
    }
    return output;
}

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

    ui.kHeaderPositionFrm->hide();
    ui.kColumnsPositionsFrm->hide();
    ui.kCsvMappingFrm->hide();

    // Build list of known backends
    QStringList backends;
    foreach(const QString & file, KStandardDirs().findAllResources("data", "skrooge/backends/*.backend")) {
        backends.push_back(QFileInfo(file).baseName().toLower());
    }
    ui.kbackendText->setText(i18nc("Information", "You must enter the list of backends to use separated by a ';'.\nExample: backendA;backendB.\n\nHere is the list of known backends: %1.", backends.join(";")));

    // Add date formats
    QStringList dateFormats;
    dateFormats << i18nc("Format date", "Automatic detection")
                << "YYYYMMDD"
                << "MMDDYYYY"
                << "DDMMYYYY"
                << "MM-DD-YY"
                << "DD-MM-YY"
                << "MM-DD-YYYY"
                << "DD-MM-YYYY"
                << "YYYY-MM-DD"
                << "DDMMMYYYY"
                << "DD-MMM-YY"
                << "DD-MMM-YYYY";
    ui.kcfg_qif_date_format->addItems(dateFormats);
    ui.kcfg_csv_date_format->addItems(dateFormats);

    return w;
}

KConfigSkeleton* SKGImportExportPlugin::getPreferenceSkeleton()
{
    return skgimportexport_settings::self();
}

QString SKGImportExportPlugin::title() const
{
    return i18nc("Noun", "Import / Export");
}

QString SKGImportExportPlugin::icon() const
{
    return "utilities-file-archiver";
}

QString SKGImportExportPlugin::toolTip() const
{
    return i18nc("Noun", "Import / Export management");
}

QStringList SKGImportExportPlugin::tips() const
{
    QStringList output;
    output.push_back(i18nc("Description of a tips", "<p>... skrooge is able to detect automatically transfers after an import.</p>"));
    output.push_back(i18nc("Description of a tips", "<p>... you can automatically import operation with backend.</p>"));
    output.push_back(i18nc("Description of a tips", "<p>... you can import many files in one shot.</p>"));
    output.push_back(i18nc("Description of a tips", "<p>... unit amounts can be imported through a CSV file.</p>"));
    output.push_back(i18nc("Description of a tips", "<p>... you can customize your CSV import with regular expressions defined in setting panel.</p>"));
    output.push_back(i18nc("Description of a tips", "<p>... you can export the full content of your document into a XML file.</p>"));
    output.push_back(i18nc("Description of a tips", "<p>... you can apply automatic rules after an import to set the right categories.</p>"));
    output.push_back(i18nc("Description of a tips", "<p>... you can convert file by using the batch tool '%1'.</p>", "skroogeconvert"));
    output.push_back(i18nc("Description of a tips", "<p>... skrooge uses the name of the imported file to find the target account.</p>"));
    return output;
}

int SKGImportExportPlugin::getOrder() const
{
    return 70;
}

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

    if (m_currentBankDocument && SKGMainPanel::getMainPanel()) {
        bool test = (m_currentBankDocument->getDatabase() != NULL);
        if (m_openNotValidatedAction) {
            m_openNotValidatedAction->setEnabled(test);
        }
        if (m_importAction) {
            m_importAction->setEnabled(test);
        }
        if (m_exportFileAction) {
            m_exportFileAction->setEnabled(test);
        }
        if (m_importCsvUnitAction) {
            m_importCsvUnitAction->setEnabled(test);
        }
        if (m_processingFoundTransfert) {
            m_processingFoundTransfert->setEnabled(test);
        }
        if (m_processingAnonymize) {
            m_processingAnonymize->setEnabled(test);
        }
        if (m_processingBank) {
            m_processingBank->setEnabled(test);
        }

        SKGObjectBase::SKGListSKGObjectBase selection = SKGMainPanel::getMainPanel()->getSelectedObjects();
        if (selection.count() > 0 && test) {
            bool onOperation = (selection.at(0).getRealTable() == "operation");
            if (m_validateImportedOperationsAction) {
                m_validateImportedOperationsAction->setEnabled(onOperation);
            }
            if (m_mergeImportedOperationAction) {
                m_mergeImportedOperationAction->setEnabled(onOperation);
            }
        } else {
            if (m_validateImportedOperationsAction) {
                m_validateImportedOperationsAction->setEnabled(false);
            }
            if (m_mergeImportedOperationAction) {
                m_mergeImportedOperationAction->setEnabled(false);
            }
        }

        // Automatic download
        if (test) {
            QString doc_id = m_currentBankDocument->getUniqueIdentifier();
            if (m_docUniqueIdentifier != doc_id) {
                m_docUniqueIdentifier = doc_id;

                SKGError err;

                if (skgimportexport_settings::download_on_open()) {
                    // Check frequency
                    QString lastAutomaticDownload = m_currentBankDocument->getParameter("SKG_LAST_BACKEND_AUTOMATIC_DOWNLOAD");
                    if (!lastAutomaticDownload.isEmpty()) {
                        // The automatic import is not done if at least one manual import has not been done
                        QDate lastAutomaticDownloadDate = QDate::fromString(lastAutomaticDownload, "yyyy-MM-dd");
                        if ((lastAutomaticDownloadDate.daysTo(QDate::currentDate()) >= 1 && skgimportexport_settings::download_frequency() == 0) ||
                            (lastAutomaticDownloadDate.daysTo(QDate::currentDate()) >= 7 && skgimportexport_settings::download_frequency() == 1) ||
                            (lastAutomaticDownloadDate.daysTo(QDate::currentDate()) >= 30 && skgimportexport_settings::download_frequency() == 2))

                        {
                            // Import
                            importbackends();
                        }
                    }
                }
            }
        }
    }
}

SKGError SKGImportExportPlugin::importbackends()
{
    SKGError err;
    SKGTRACEINFUNCRC(10, err);
    if (m_currentBankDocument) {
        // Check if already in a transaction
        if (!m_currentBankDocument->checkExistingTransaction()) {
            // Repeat later
            QTimer::singleShot(300, this, SLOT(importbackends()));
            return err;
        }

        // Get backends list to used
        QStringList backends = SKGServices::splitCSVLine(skgimportexport_settings::backends());
        int nbBackends = backends.count();

        // Import
        SKGBEGINPROGRESSTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Import with backends"), err, nbBackends);
        for (int i = 0; !err && i < nbBackends; ++i) {
            // Is password needed?
            QString pwd;
            QHash< QString, QString > properties;
            err = SKGServices::readPropertyFile(KStandardDirs().findResource("data", "skrooge/backends/" % backends[i] % ".backend"), properties);
            if (!err && (properties["getaccounts"].contains("%3") || properties["getoperations"].contains("%3"))) {
                QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor));
                QPointer<KPasswordDialog> dlg = new KPasswordDialog(SKGMainPanel::getMainPanel());
                dlg->setPrompt(i18nc("Question", "The backend '%1' needs a password.\nPlease enter the password.", backends[i]));
                int rc = dlg->exec();
                pwd = dlg->password();
                delete dlg;
                QApplication::restoreOverrideCursor();

                if (rc != QDialog::Accepted) {
                    continue;
                }
            }

            QString codec = m_currentBankDocument->getParameter("SKG_LAST_CODEC_USED_FOR_IMPORT");
            if (codec.isEmpty()) {
                codec = QTextCodec::codecForLocale()->name();
            }
            IFOKDO(err, m_currentBankDocument->setParameter("SKG_LAST_CODEC_USED_FOR_IMPORT", codec))

            SKGImportExportManager imp1(m_currentBankDocument, KUrl("." % backends[i]));
            QMap<QString, QString> parameters = imp1.getImportParameters();
            parameters["password"] = pwd;
            imp1.setImportParameters(parameters);
            imp1.setAutomaticValidation(skgimportexport_settings::automatic_validation());
            imp1.setAutomaticApplyRules(skgimportexport_settings::apply_rules());
            imp1.setSinceLastImportDate(skgimportexport_settings::since_last_import());
            imp1.setCodec(codec);
            IFOKDO(err, imp1.importFile())
            IFOKDO(err, m_currentBankDocument->stepForward(i + 1))
        }

        // Memorize the last download date
        IFOKDO(err, m_currentBankDocument->setParameter("SKG_LAST_BACKEND_AUTOMATIC_DOWNLOAD", QDate::currentDate().toString("yyyy-MM-dd")))
    }

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

    // Open last modified operations if setting activated
    IFOK(err) openLastModifiedIfSetting();

    return err;
}

void SKGImportExportPlugin::import(const QString& iFile)
{
    import(QList<KUrl>() << KUrl::fromLocalFile(iFile));
}

void SKGImportExportPlugin::import(const QList<KUrl>& iFiles)
{
    SKGError err;
    SKGTRACEINFUNCRC(10, err);
    if (m_currentBankDocument) {
        QList<KUrl> fileNames;
        QString lastCodecUsed = m_currentBankDocument->getParameter("SKG_LAST_CODEC_USED_FOR_IMPORT");
        if (lastCodecUsed.isEmpty()) {
            lastCodecUsed = QTextCodec::codecForLocale()->name();
        }
        QString codec;

        if (iFiles.isEmpty()) {
            // Panel to ask files
            KEncodingFileDialog::Result result = KEncodingFileDialog::getOpenUrlsAndEncoding(lastCodecUsed, "kfiledialog:///IMPEXP",
                                                 sender() == m_importCsvUnitAction ? QString("*.csv|" % i18nc("A file format", "CSV Files")) :
                                                 SKGImportExportManager::getImportMimeTypeFilter(),
                                                 SKGMainPanel::getMainPanel());
            foreach(const KUrl & u, result.URLs) {
                fileNames.append(KUrl(u.url()));
            }
            codec = result.encoding;
        } else {
            fileNames = iFiles;
            codec = lastCodecUsed;
        }

        int nbFiles = fileNames.count();
        if (nbFiles) {
            {
                SKGBEGINPROGRESSTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Import with codec %1", codec), err, nbFiles);

                // Read Setting
                bool automatic_validation = skgimportexport_settings::automatic_validation();
                bool automatic_rule = skgimportexport_settings::apply_rules();
                bool since_last = skgimportexport_settings::since_last_import();

                IFOKDO(err, m_currentBankDocument->setParameter("SKG_LAST_CODEC_USED_FOR_IMPORT", codec))

                for (int i = 0; !err && i < nbFiles; ++i) {
                    // Get Filename
                    KUrl fileName = fileNames.at(i);

                    // Import
                    SKGImportExportManager imp1(m_currentBankDocument, fileName);
                    imp1.setAutomaticValidation(automatic_validation);
                    imp1.setAutomaticApplyRules(automatic_rule);
                    imp1.setSinceLastImportDate(since_last);
                    imp1.setCodec(codec);

                    if (QFileInfo(fileName.path()).suffix().toUpper() == "CSV") {  // TODO(Stephane MANKOWSKI): Better
                        QMap<QString, QString> parameters = imp1.getImportParameters();

                        parameters["automatic_search_header"] = (skgimportexport_settings::automatic_search_header() ? "Y" : "N");
                        parameters["header_position"] = SKGServices::intToString(skgimportexport_settings::header_position());

                        parameters["mapping_date"] = skgimportexport_settings::mapping_date();
                        parameters["mapping_account"] = skgimportexport_settings::mapping_account();
                        parameters["mapping_number"] = skgimportexport_settings::mapping_number();
                        parameters["mapping_mode"] = skgimportexport_settings::mapping_mode();
                        parameters["mapping_payee"] = skgimportexport_settings::mapping_payee();
                        parameters["mapping_comment"] = skgimportexport_settings::mapping_comment();
                        parameters["mapping_status"] = skgimportexport_settings::mapping_status();
                        parameters["mapping_bookmarked"] = skgimportexport_settings::mapping_bookmarked();
                        parameters["mapping_category"] = skgimportexport_settings::mapping_category();
                        parameters["mapping_amount"] = skgimportexport_settings::mapping_amount();
                        parameters["mapping_quantity"] = skgimportexport_settings::mapping_quantity();
                        parameters["mapping_unit"] = skgimportexport_settings::mapping_unit();
                        parameters["mapping_idtransaction"] = skgimportexport_settings::mapping_idtransaction();
                        parameters["mapping_idgroup"] = skgimportexport_settings::mapping_idgroup();
                        parameters["mapping_sign"] = skgimportexport_settings::mapping_sign();
                        parameters["mapping_debit"] = skgimportexport_settings::mapping_debit();
                        parameters["mapping_property"] = skgimportexport_settings::mapping_property();

                        parameters["automatic_search_columns"] = (skgimportexport_settings::automatic_search_columns() ? "Y" : "N");
                        parameters["columns_positions"] = skgimportexport_settings::columns_positions();
                        parameters["mode_csv_unit"] = (sender() == m_importCsvUnitAction ? "Y" : "N");
                        parameters["date_format"] = skgimportexport_settings::csv_date_format();
                        if (!parameters["date_format"].contains("YY")) {
                            parameters["date_format"] = "";
                        }

                        imp1.setImportParameters(parameters);
                    } else if (QFileInfo(fileName.path()).suffix().toUpper() == "QIF") {  // TODO(Stephane MANKOWSKI): Better
                        QMap<QString, QString> parameters = imp1.getImportParameters();

                        parameters["date_format"] = skgimportexport_settings::qif_date_format();
                        if (!parameters["date_format"].contains("YY")) {
                            parameters["date_format"] = "";
                        }

                        imp1.setImportParameters(parameters);
                    }

                    // Optimization
                    if (i != nbFiles - 1) {
                        QMap< QString, QString > parameters = imp1.getImportParameters();
                        parameters["donotfinalize"] = 'Y';
                        imp1.setImportParameters(parameters);
                    }

                    if (m_install) {
                        QMap< QString, QString > parameters = imp1.getImportParameters();
                        parameters["install_sunriise"] = 'Y';
                        imp1.setImportParameters(parameters);
                    }

                    IFOKDO(err, imp1.importFile())
                    if (err && err.getReturnCode() == ERR_ENCRYPTION) {
                        QString pwd;
                        QString additionalMessage;
                        do {
                            // Reset error
                            err = SKGError(ERR_FAIL, i18nc("Error message", "Import of file named '%1' failed", fileName.prettyUrl()));
                            pwd = "";

                            // Use password dialog
                            QApplication::restoreOverrideCursor();
                            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;
                            QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));

                            // Load file
                            if (!pwd.isEmpty()) {
                                QMap<QString, QString> parameters = imp1.getImportParameters();;
                                parameters["password"] = pwd;
                                imp1.setImportParameters(parameters);
                                err = imp1.importFile();
                                IFKO(err) {
                                    if (err.getReturnCode() == ERR_ENCRYPTION) {
                                        additionalMessage = i18nc("The user did not provide the correct password", "<b>Wrong password.</b>\n");
                                    } else {
                                        // Import error
                                        break;
                                    }
                                }
                            } else {
                                err = SKGError(ERR_FAIL, i18nc("Error message", "Import canceled by user"));
                                break;
                            }
                        } while (err);
                    }

                    if (err && err.getReturnCode() != ERR_INSTALL) {
                        err.addError(ERR_FAIL, i18nc("Error message", "Import of file named '%1' failed", fileName.prettyUrl()));
                    }

                    IFOKDO(err, m_currentBankDocument->stepForward(i + 1))
                }
            }
        }

        // status bar
        IFOK(err) {
            err = SKGError(0, i18np("%1 file successfully imported.", "%1 files successfully imported.", nbFiles));
        }

        // Display error
        m_install = false;
        KMessageWidget* msg = SKGMainPanel::displayErrorMessage(err);
        if (err.getReturnCode() == ERR_INSTALL && msg) {
            QString application = err.property("application").toString();
            QAction* install = new QAction(i18nc("Noun", "Install %1", application), msg);
            install->setIcon(KIcon("download"));
            msg->addAction(install);
            connect(install, SIGNAL(triggered(bool)), this, SLOT(onInstall()));
            connect(install, SIGNAL(triggered(bool)), msg, SLOT(deleteLater()), Qt::QueuedConnection);
        }

        // Open last modified operations if setting activated
        IFOK(err) openLastModifiedIfSetting();
    }
}

void SKGImportExportPlugin::onInstall()
{
    m_install = true;
    SKGMainPanel::getMainPanel()->displayMessage(i18nc("Information message", "The installation will be done during the next import"));
}

void SKGImportExportPlugin::exportFile()
{
    SKGError err;
    SKGTRACEINFUNCRC(10, err);
    if (m_currentBankDocument) {
        QString lastCodecUsed = m_currentBankDocument->getParameter("SKG_LAST_CODEC_USED_FOR_IMPORT");
        if (lastCodecUsed.isEmpty()) {
            lastCodecUsed = QTextCodec::codecForLocale()->name();
        }
        QString fileName = SKGMainPanel::getSaveFileName("kfiledialog:///IMPEXP", SKGImportExportManager::getExportMimeTypeFilter(),
                           SKGMainPanel::getMainPanel(), QString(), &lastCodecUsed);
        if (fileName.isEmpty() || !m_currentBankDocument) {
            return;
        }


        {
            SKGBEGINTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Export"), err);
            IFOK(err) {
                SKGImportExportManager imp1(m_currentBankDocument, fileName);
                imp1.setCodec(lastCodecUsed);
                err = imp1.exportFile();
            }
        }

        // status bar
        IFOKDO(err, SKGError(0, i18nc("Successful message after an user action", "File '%1' successfully exported.", fileName)))
        else {
            err.addError(ERR_FAIL, i18nc("Error message", "Export of '%1' failed", fileName));
        }

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

void SKGImportExportPlugin::anonymize()
{
    SKGError err;
    SKGTRACEINFUNCRC(10, err);
    if (m_currentBankDocument) {
        QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
        SKGImportExportManager imp1(m_currentBankDocument);
        err = imp1.anonymize();
        QApplication::restoreOverrideCursor();

        // status bar
        IFOKDO(err, SKGError(0, i18nc("An anonymized document is a document where all private data has been removed", "Document anonymized.")))

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

void SKGImportExportPlugin::findTransfers()
{
    SKGError err;
    SKGTRACEINFUNCRC(10, err);
    if (m_currentBankDocument) {
        int NbOperationsMerged = 0;
        {
            SKGBEGINTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Find and group transfers"), err);
            IFOK(err) {
                SKGImportExportManager imp1(m_currentBankDocument);
                err = imp1.findAndGroupTransfers(NbOperationsMerged);
            }
        }

        // status bar
        IFOK(err) {
            if (NbOperationsMerged) err = SKGError(0, i18np("Document successfully processed. %1 transfer created.",
                                                       "Document successfully processed. %1 transfers created.", NbOperationsMerged));
            else {
                err = m_currentBankDocument->sendMessage(i18nc("Information message", "No transfers found"));
            }
        } else {
            err.addError(ERR_FAIL, i18nc("Error message", "Processing failed."));
        }

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

        // Open last modified operations if setting activated
        if (!err && NbOperationsMerged) {
            openLastModifiedIfSetting();
        }
    }
}

void SKGImportExportPlugin::cleanBanks()
{
    SKGError err;
    SKGTRACEINFUNCRC(10, err);
    {
        SKGBEGINTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Clean bank's imports"), err);
        IFOK(err) {
            SKGImportExportManager imp1(m_currentBankDocument);
            err = imp1.cleanBankImport();
        }
    }

    // status bar
    IFOKDO(err, SKGError(0, i18nc("Successful message after an user action", "Document successfully cleaned.")))
    else {
        err.addError(ERR_FAIL, i18nc("Error message", "Clean failed."));
    }

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

    // Open last modified operations if setting activated
    IFOK(err) openLastModifiedIfSetting();
}

void SKGImportExportPlugin::validateImportedOperations()
{
    SKGError err;
    SKGTRACEINFUNCRC(10, err);

    if (SKGMainPanel::getMainPanel() && m_currentBankDocument) {
        SKGObjectBase::SKGListSKGObjectBase selection = SKGMainPanel::getMainPanel()->getSelectedObjects();
        int nb = selection.count();
        {
            SKGBEGINPROGRESSTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Validate imported operations"), err, nb);
            for (int i = 0; !err && i < nb; ++i) {
                SKGOperationObject op(selection[i]);
                if (op.getAttribute("t_imported") == "P") {
                    err = op.setImported(true);
                    IFOKDO(err, op.save())
                }
                IFOKDO(err, m_currentBankDocument->stepForward(i + 1))
            }
        }
    }

    // status bar
    IFOKDO(err, SKGError(0, i18nc("Successful message after an user action", "Imported operations validated.")))
    else {
        err.addError(ERR_FAIL, i18nc("Error message", "Validation failed"));
    }

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

void SKGImportExportPlugin::mergeImportedOperation()
{
    SKGError err;
    SKGTRACEINFUNCRC(10, err);

    if (SKGMainPanel::getMainPanel() && m_currentBankDocument) {
        SKGObjectBase::SKGListSKGObjectBase selection = SKGMainPanel::getMainPanel()->getSelectedObjects();
        int nb = selection.count();
        err = SKGError(ERR_INVALIDARG, i18nc("Error message", "Invalid selection, you must select one imported operation and one manual operation with same amounts"));
        if (nb == 2) {
            SKGOperationObject opImported(selection.at(0));
            SKGOperationObject opManual(selection.at(1));
            if (opImported.isImported() || opManual.isImported()) {
                if (opImported.isImported() && opManual.isImported()) {
                    // Both are imports, so the "imported" on is the last one
                    if (opImported.getID() < opManual.getID()) {
                        qSwap(opImported, opManual);
                    }
                } else if (!opImported.isImported()) {
                    qSwap(opImported, opManual);
                }

                // Mode force?
                bool modeForce = false;
                QAction* act = qobject_cast< QAction* >(sender());
                if (act) {
                    modeForce = (act->data().toInt() == 1);
                }

                if (!modeForce && opImported.getCurrentAmount() != opManual.getCurrentAmount()) {
                    KMessageWidget* msgWidget = SKGMainPanel::getMainPanel()->displayErrorMessage(i18nc("Question",  "Amounts are not equals. Do you want to force the merge ?"));
                    QAction* force = new QAction(i18nc("Noun", "Force the merge"), msgWidget);
                    force->setIcon(KIcon("run-build"));
                    force->setData(1);
                    msgWidget->addAction(force);
                    connect(force, SIGNAL(triggered(bool)), this, SLOT(mergeImportedOperation()));
                    connect(force, SIGNAL(triggered(bool)), msgWidget, SLOT(deleteLater()), Qt::QueuedConnection);

                    err = SKGError();
                } else {
                    SKGBEGINTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Merge imported operations"), err);
                    err = opManual.mergeAttribute(opImported);
                    IFKO(err) err.addError(ERR_FAIL, i18nc("Error message", "Merge failed"));
                }
            }
        }
    }

    // status bar
    IFOKDO(err, SKGError(0, i18nc("Successful message after an user action", "Imported operations merged.")))

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

void SKGImportExportPlugin::openLastModifiedIfSetting()
{
    // Read Setting
    bool open_after_import_or_processing = skgimportexport_settings::open_after_import_or_processing();
    if (open_after_import_or_processing) {
        // Open last operations
        QAction* act = SKGMainPanel::getMainPanel()->getGlobalAction("view_open_last_modified");
        if (act) {
            act->trigger();
        }
    }
}

void SKGImportExportPlugin::validateAllOperations()
{
    SKGError err;
    SKGTRACEINFUNCRC(10, err);
    {
        SKGBEGINTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Validate all operations"), err);
        err = m_currentBankDocument->executeSqliteOrder("UPDATE operation SET t_imported='Y' WHERE t_imported='P'");
    }

    // status bar
    IFOKDO(err, SKGError(0, i18nc("Message for successful user action", "Operations validated.")))
    else {
        err.addError(ERR_FAIL, i18nc("Error message", "Validation failed"));
    }

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

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

    // Check operations not validated
    if (!iIgnoredAdvice.contains("skgimportexportplugin_notvalidated")) {
        bool exist = false;
        m_currentBankDocument->existObjects("operation", "t_imported='P'", exist);
        if (exist) {
            SKGAdvice ad;
            ad.setUUID("skgimportexportplugin_notvalidated");
            ad.setPriority(4);
            ad.setShortMessage(i18nc("Advice on making the best (short)", "Many operations imported and not yet validated"));
            ad.setLongMessage(i18nc("Advice on making the best (long)", "After importing operations, you should review them, make corrections on, for instance, category, payee. Once done, you should mark the imported operation as validated, so that you know the operation has been fully processed."));
            QList<SKGAdvice::SKGAdviceAction> autoCorrections;
            {
                SKGAdvice::SKGAdviceAction a;
                a.Title = "skg://view_open_not_validated";
                a.IsRecommended = false;
                autoCorrections.push_back(a);
            }
            {
                SKGAdvice::SKGAdviceAction a;
                a.Title = "skg://process_validate";
                a.IsRecommended = false;
                autoCorrections.push_back(a);
            }
            ad.setAutoCorrections(autoCorrections);
            output.push_back(ad);
        }
    }

    // Krunner operations
    QString dirName = QDir::homePath() % "/.skrooge/";
    QStringList fileList = QDir(dirName).entryList(QStringList() << "add_operation_*.txt", QDir::Files);
    int nb = fileList.count();
    if (nb) {
        QStringList listAccounts;
        m_currentBankDocument->getDistinctValues("account", "t_name", "t_type IN ('C', 'D', 'W') and t_close='N'", listAccounts);
        int nbAccounts = listAccounts.count();
        for (int i = 0; i < nb; ++i) {
            QString fileName = dirName % fileList.at(i);
            QFile file(fileName);
            if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
                QTextStream stream(&file);
                stream.readLine().trimmed();  // action not used yet
                QString date = KGlobal::locale()->formatDate(SKGServices::stringToTime(stream.readLine().trimmed()).date(), KLocale::ShortDate);
                QString amount = m_currentBankDocument->formatMoney(SKGServices::stringToDouble(stream.readLine().trimmed()), m_currentBankDocument->getPrimaryUnit());
                QString payee = stream.readLine().trimmed();
                SKGAdvice ad;
                ad.setUUID("skgimportexportplugin_krunner_" % fileName);
                ad.setPriority(8);
                ad.setShortMessage(i18nc("Advice on making the best (short)", "Krunner's operation ongoing [%1 %2 %3]", date, amount, payee));
                ad.setLongMessage(i18nc("Advice on making the best (long)", "Operations created through krunner have to be fully created in skrooge."));
                QList<SKGAdvice::SKGAdviceAction> autoCorrections;
                for (int j = 0; j < nbAccounts; ++j) {
                    SKGAdvice::SKGAdviceAction a;
                    a.Title = i18nc("Advice on making the best (action)", "Import operation in %1", listAccounts.at(j));
                    a.IconName = icon();
                    a.IsRecommended = false;
                    autoCorrections.push_back(a);
                }
                {
                    SKGAdvice::SKGAdviceAction a;
                    a.Title = i18nc("Advice on making the best (action)", "Remove operation");
                    a.IconName = "edit-delete";
                    a.IsRecommended = false;
                    autoCorrections.push_back(a);
                }
                ad.setAutoCorrections(autoCorrections);
                output.push_back(ad);

                // Close file
                file.close();
            }
        }
    }
    return output;
}

SKGError SKGImportExportPlugin::executeAdviceCorrection(const QString& iAdviceIdentifier, int iSolution)
{
    if (iAdviceIdentifier.startsWith(QLatin1String("skgimportexportplugin_krunner_")) && m_currentBankDocument) {
        SKGError err;
        // Get file name
        QString fileName = iAdviceIdentifier.right(iAdviceIdentifier.length() - 30);
        QFile file(fileName);

        // Get accounts
        QStringList listAccounts;
        m_currentBankDocument->getDistinctValues("account", "t_name", "t_type IN ('C', 'D', 'W') and t_close='N'", listAccounts);
        if (iSolution < listAccounts.count()) {
            // Addition in an account

            if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
                err = SKGError(ERR_FAIL, i18nc("An erro message", "Open file '%1' failed", fileName));
            } else {
                QTextStream stream(&file);
                stream.readLine().trimmed();  // action is not used yet
                QDate date = SKGServices::stringToTime(stream.readLine().trimmed()).date();
                double amount = SKGServices::stringToDouble(stream.readLine().trimmed());
                QString payee = stream.readLine().trimmed();

                SKGBEGINTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Import krunner's operation"), err);

                // Get account
                SKGAccountObject act(m_currentBankDocument);
                err = act.setName(listAccounts.at(iSolution));
                IFOKDO(err, act.load())

                // Get unit
                SKGUnitObject unit(m_currentBankDocument);
                IFOKDO(err, unit.setName(m_currentBankDocument->getPrimaryUnit().Name))
                IFOKDO(err, unit.load())

                // Add operation
                SKGOperationObject op;
                IFOKDO(err, act.addOperation(op))
                IFOKDO(err, op.setDate(date))
                IFOKDO(err, op.setUnit(unit))

                if (!payee.isEmpty()) {
                    // Get payee
                    SKGPayeeObject pa;
                    IFOKDO(err, SKGPayeeObject::createPayee(m_currentBankDocument, payee, pa, true))
                    IFOKDO(err, op.setPayee(pa))
                }
                IFOK(err) {
                    int pos1 = fileName.indexOf("{");
                    int pos2 = fileName.indexOf("}");
                    if (pos1 != -1 && pos2 > pos1) {
                        err = op.setImportID("KR-" % fileName.mid(pos1 + 1, pos2 - pos1 - 1));
                    }
                }
                IFOKDO(err, op.save())

                // Add suboperation
                SKGSubOperationObject sop;
                IFOKDO(err, op.addSubOperation(sop))
                IFOKDO(err, sop.setQuantity(-amount))
                IFOKDO(err, sop.save())

                // Finalize the importation
                IFOK(err) {
                    bool automatic_validation = skgimportexport_settings::automatic_validation();
                    bool automatic_rule = skgimportexport_settings::apply_rules();
                    bool since_last = skgimportexport_settings::since_last_import();

                    SKGImportExportManager imp1(m_currentBankDocument);
                    imp1.setAutomaticValidation(automatic_validation);
                    imp1.setAutomaticApplyRules(automatic_rule);
                    imp1.setSinceLastImportDate(since_last);
                    err = imp1.finalizeImportation();
                }

                // Send message
                IFOKDO(err, op.getDocument()->sendMessage(i18nc("An information to the user", "The operation '%1' has been added", op.getDisplayName()), SKGDocument::Hidden));

                // Close file
                file.close();
            }

            // status bar
            IFOK(err) {
                err = SKGError(0, i18nc("Message for successful user action", "Operations imported."));
                QFile::remove(fileName);
            } else {
                err.addError(ERR_FAIL, i18nc("Error message", "Import failed"));
            }
        } else {
            err = SKGError(0, i18nc("Message for successful user action", "Operations removed."));
            QFile::remove(fileName);
        }

        // Display error
        SKGMainPanel::displayErrorMessage(err);
        return SKGError();
    }
    return SKGInterfacePlugin::executeAdviceCorrection(iAdviceIdentifier, iSolution);
}

#include "skgimportexportplugin.moc"
