/***************************************************************************
 *   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 "skgtraces.h"
#include "skgservices.h"
#include "skgbankincludes.h"
#include "skgimportexportmanager.h"

#include <QFile>
#include <qfileinfo.h>
#include <QDir>
#include <qprocess.h>
#include <qtconcurrentmap.h>
#include <QApplication>

#include <klocale.h>
#include <kaboutdata.h>
#include <kgenericfactory.h>
#include <kstandarddirs.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)
{
    SKGTRACEIN(10, "SKGImportPluginBackend::SKGImportPluginBackend");
    Q_UNUSED(iArg);

    foreach(const QString & file, KStandardDirs().findAllResources("data", KGlobal::mainComponent().aboutData()->appName() % "/backends/*.backend")) {
        m_listBackends.push_back(QFileInfo(file).baseName().toUpper());
    }
}

SKGImportPluginBackend::~SKGImportPluginBackend()
{}

bool SKGImportPluginBackend::isImportPossible()
{
    SKGTRACEIN(10, "SKGImportPluginBackend::isImportPossible");
    return (!m_importer ? true : m_listBackends.contains(QFileInfo(m_importer->getFileName()).suffix().toUpper()));
}

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()) 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;
    SKGTRACEINRC(2, "SKGImportPluginBackend::importFile", err);

    SKGBEGINPROGRESSTRANSACTION(*m_importer->getDocument(), i18nc("Noun, name of the user action", "Import with %1", "Backend"), err, 3);
    QHash< QString, QString > properties;
    QString bankendName = QFileInfo(m_importer->getFileName()).suffix().toLower();
    err = SKGServices::readPropertyFile(KStandardDirs().findResource("data", KGlobal::mainComponent().aboutData()->appName() % "/backends/" % bankendName % ".backend"), properties);
    if (!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()) {
            err.setReturnCode(ERR_FAIL);
            err.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);
                err.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);
                        err.setMessage(line);
                        err.addError(ERR_FAIL, i18nc("Error message",  "Impossible to find the account id with the regular expression '%1'", properties["getaccountid"]));
                        break;
                    }
                }

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

        //Download operations
        if (!err) err = m_importer->getDocument()->stepForward(1, i18nc("Progress message", "Download operations"));
        if (!err) {
            if (backendAccounts.isEmpty()) {
                err.setReturnCode(ERR_FAIL);
                err.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, 1);
                QStringList listDownloadedId;
                {
                    SKGTRACEINRC(10, "SKGImportExportPlugin::importbackend-download", err);
                    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", "Warning: Some accounts have not been downloaded"));
                    }
                }

                //import
                if (!err) 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);

                    //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> parameters = imp1.getImportParameters();
                        parameters["automatic_search_header"] = 'N';
                        parameters["header_position"] = '1';
                        parameters["automatic_search_columns"] = 'N';
                        parameters["columns_positions"] = properties["csvcolumns"];
                        parameters["mode_csv_unit"] = 'N';
                        parameters["balance"] = backendAccountsBalance[listDownloadedId.at(i)];
                        imp1.setImportParameters(parameters);
                        if (!err) err = imp1.importFile();

                        QFile::remove(file);
                        if (!err) err = m_importer->getDocument()->stepForward(i + 1);
                    }

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

    return err;
}

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

#include "skgimportpluginbackend.moc"
