/***************************************************************************
 *   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 for BACKEND import / export.
 *
 * @author Stephane MANKOWSKI / Guillaume DE BURE
 */
#include "skgimportpluginbackend.h"

#include <QFile>
#include <QFileInfo>
#include <QDir>
#include <QProcess>
#include <QtConcurrentMap>
#include <QApplication>

#include <KLocale>
#include <KAboutData>
#include <KGenericFactory>
#include <KStandardDirs>

#include "skgtraces.h"
#include "skgservices.h"
#include "skgbankincludes.h"
#include "skgimportexportmanager.h"

/**
 * This plugin factory.
 */
K_PLUGIN_FACTORY(SKGImportPluginBackendFactory, registerPlugin<SKGImportPluginBackend>();)
/**
 * This plugin export.
 */
K_EXPORT_PLUGIN(SKGImportPluginBackendFactory("skrooge_import_backend", "skrooge_import_backend"))

SKGImportPluginBackend::SKGImportPluginBackend(QObject* iImporter, const QVariantList& iArg)
    : SKGImportPlugin(iImporter)
{
    SKGTRACEINFUNC(10);
    Q_UNUSED(iArg);

    foreach(const QString & file, KStandardDirs().findAllResources("data", "skrooge/backends/*.backend")) {
        m_listBackends.push_back(QFileInfo(file).baseName().toUpper());
    }
}

SKGImportPluginBackend::~SKGImportPluginBackend()
{}

bool SKGImportPluginBackend::isImportPossible()
{
    SKGTRACEINFUNC(10);
    return (!m_importer ? true : m_listBackends.contains(m_importer->getFileNameExtension()));
}

struct download {
    download(int iNbToDownload, const QString& iCmd, const QString& iPwd)
        : m_nbToDownload(iNbToDownload), m_cmd(iCmd), m_pwd(iPwd)
    {
    }

    typedef QString result_type;

    QString operator()(const QString& iAccountId)
    {
        QString file = QDir::tempPath() % "/" % iAccountId % ".csv";
        int retry = 0;
        do {
            // Build cmd
            QString cmd = m_cmd;
            cmd = cmd.replace("%2", SKGServices::intToString(m_nbToDownload)).replace("%1", iAccountId).replace("%3", m_pwd);

            // Execute
            QProcess p;
            p.setStandardOutputFile(file);
            p.start(cmd);
            if (p.waitForFinished(1000 * 60 * 2) && p.exitCode() == 0) {
                return iAccountId;
            }
            ++retry;
        } while (retry < 6);
        return "";
    }

    int m_nbToDownload;
    QString m_cmd;
    QString m_pwd;
};

SKGError SKGImportPluginBackend::importFile()
{
    if (!m_importer) {
        return SKGError(ERR_ABORT, i18nc("Error message", "Invalid parameters"));
    }
    SKGError err;
    SKGTRACEINFUNCRC(2, err);

    SKGBEGINPROGRESSTRANSACTION(*m_importer->getDocument(), i18nc("Noun, name of the user action", "Import with %1", "Backend"), err, 3);
    QHash< QString, QString > properties;
    QString bankendName = m_importer->getFileNameExtension().toLower();
    err = SKGServices::readPropertyFile(KStandardDirs().findResource("data", "skrooge/backends/" % bankendName % ".backend"), properties);
    IFOK(err) {
        // Get parameters
        QMap<QString, QString> parameters = this->getImportParameters();
        QString pwd = parameters["password"];

        // Get list of accounts
        QStringList backendAccounts;
        QMap<QString, QString> backendAccountsBalance;
        QString csvfile = QDir::tempPath() % "/skrooge_backend.csv";
        QString cmd = properties["getaccounts"].replace("%3", pwd);
        QProcess p;
        p.setStandardOutputFile(csvfile);
        p.start(cmd);
        if (!p.waitForFinished(1000 * 60 * 2) || p.exitCode() != 0) {
            err.setReturnCode(ERR_FAIL).setMessage(i18nc("Error message",  "The following command line failed:\n'%1'", cmd));
        } else {
            QFile file(csvfile);
            if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
                err.setReturnCode(ERR_INVALIDARG).setMessage(i18nc("Error message",  "Open file '%1' failed", csvfile));
            } else {
                QRegExp reggetaccounts(properties["getaccountid"]);
                QRegExp reggetaccountbalance(properties["getaccountbalance"]);

                QTextStream stream(&file);
                stream.readLine();  // To avoid header
                while (!stream.atEnd()) {
                    // Read line
                    QString line = stream.readLine().trimmed();

                    // Get account id
                    int pos = reggetaccounts.indexIn(line);
                    if (pos > -1) {
                        QString accountid = reggetaccounts.cap(1);
                        if (!backendAccounts.contains(accountid)) {
                            backendAccounts.push_back(accountid);

                            // Get account balance
                            pos = reggetaccountbalance.indexIn(line);
                            if (pos > -1) {
                                backendAccountsBalance[accountid] = reggetaccountbalance.cap(1);
                            } else {
                                backendAccountsBalance[accountid] = '0';
                            }
                        }
                    } else {
                        // This is an error
                        err.setReturnCode(ERR_FAIL).setMessage(line).addError(ERR_FAIL, i18nc("Error message",  "Impossible to find the account id with the regular expression '%1' in line '%2'", properties["getaccountid"], line));
                        break;
                    }
                }

                // close file
                file.close();
                file.remove();
            }
        }

        // Download operations
        IFOKDO(err, m_importer->getDocument()->stepForward(1, i18nc("Progress message", "Download operations")))
        IFOK(err) {
            if (backendAccounts.isEmpty()) {
                err.setReturnCode(ERR_FAIL).setMessage(i18nc("Error message",  "Your backend '%1' seems to be not well configure because no account has been found.", bankendName));
            } else {
                // Download qif file
                QDate lastDownload = SKGServices::stringToTime(m_importer->getDocument()->getParameter("SKG_LAST_" % bankendName.toUpper() % "_IMPORT_DATE")).date();
                QString lastList = m_importer->getDocument()->getParameter("SKG_LAST_" % bankendName.toUpper() % "_IMPORT_LIST");
                QString currentList = backendAccounts.join(";");

                int nbToDownload = 0;
                if (currentList != lastList) {
                    nbToDownload = 99999;
                } else {
                    nbToDownload = qMax(lastDownload.daysTo(QDate::currentDate()) * 10, 20);
                }
                QStringList listDownloadedId;
                {
                    QFuture<QString> f = QtConcurrent::mapped(backendAccounts, download(nbToDownload, properties["getoperations"], pwd));
                    f.waitForFinished();
                    listDownloadedId = f.results();
                }
                listDownloadedId.removeAll("");

                // Check
                int nb = listDownloadedId.count();
                if (100 * nb / backendAccounts.count() < 80) {
                    // Some accounts have not been downloaded
                    if (nb == 0) {
                        err = SKGError(ERR_FAIL, i18nc("Error message", "No accounts downloaded with the following command:\n%1\nCheck your backend installation.", properties["getoperations"]));
                    } else {
                        // Warning
                        m_importer->getDocument()->sendMessage(i18nc("Warning message", "Some accounts have not been downloaded"), SKGDocument::Warning);
                    }
                }

                // import
                IFOKDO(err, m_importer->getDocument()->stepForward(2, i18nc("Progress message", "Import")))
                if (!err && nb) {
                    // import
                    SKGBEGINPROGRESSTRANSACTION(*m_importer->getDocument(), "#INTERNAL#" % i18nc("Noun, name of the user action", "Import one account with %1", "Backend"), err, nb);

                    // Get all messages
                    QStringList messages;
                    QList<SKGDocument::MessageType> types;
                    IFOKDO(err, m_importer->getDocument()->getMessages(m_importer->getDocument()->getCurrentTransaction(), messages, types, true))

                    // Import all files
                    for (int i = 0; !err && i < nb; ++i) {
                        // Import
                        QString file = QDir::tempPath() % "/" % listDownloadedId.at(i) % ".csv";
                        SKGImportExportManager imp1(m_importer->getDocument(), file);
                        imp1.setAutomaticValidation(m_importer->automaticValidation());
                        imp1.setAutomaticApplyRules(m_importer->automaticApplyRules());
                        imp1.setSinceLastImportDate(m_importer->sinceLastImportDate());
                        imp1.setCodec(m_importer->getCodec());

                        QMap<QString, QString> newParameters = imp1.getImportParameters();
                        newParameters["automatic_search_header"] = 'N';
                        newParameters["header_position"] = '1';
                        newParameters["automatic_search_columns"] = 'N';
                        newParameters["columns_positions"] = properties["csvcolumns"];
                        newParameters["mode_csv_unit"] = 'N';
                        newParameters["balance"] = backendAccountsBalance[listDownloadedId.at(i)];
                        if (i != nb - 1) {
                            newParameters["donotfinalize"] = 'Y';
                        }
                        imp1.setImportParameters(newParameters);
                        IFOKDO(err, imp1.importFile())

                        QFile::remove(file);
                        IFOKDO(err, m_importer->getDocument()->stepForward(i + 1))
                    }

                    // Reset message
                    IFOKDO(err, m_importer->getDocument()->removeMessages(m_importer->getDocument()->getCurrentTransaction()))
                    int nbm = messages.count();
                    for (int j = 0; j < nbm; ++j) {
                        m_importer->getDocument()->sendMessage(messages.at(j), types.at(j));
                    }

                    IFOKDO(err, m_importer->getDocument()->setParameter("SKG_LAST_" % bankendName.toUpper() % "_IMPORT_DATE", SKGServices::dateToSqlString(QDateTime::currentDateTime())))
                    IFOKDO(err, m_importer->getDocument()->setParameter("SKG_LAST_" % bankendName.toUpper() % "_IMPORT_LIST", currentList))
                }
                IFOKDO(err, m_importer->getDocument()->stepForward(3))
            }
        }
    }

    return err;
}

QString SKGImportPluginBackend::getMimeTypeFilter() const
{
    return "";
}

#include "skgimportpluginbackend.moc"
