/***************************************************************************
 *   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 OFX import / export.
 *
 * @author Stephane MANKOWSKI / Guillaume DE BURE
 */
#include "skgimportpluginofx.h"
#include "skgtraces.h"
#include "skgservices.h"
#include "skgbankincludes.h"
#include "skgimportexportmanager.h"

#include <qfileinfo.h>

#include <klocale.h>
#include <kgenericfactory.h>

SKGError SKGImportPluginOfx::m_ofxError;
QList<OfxStatementData> SKGImportPluginOfx::m_ofxInitialBalance;


/**
 * This plugin factory.
 */
K_PLUGIN_FACTORY(SKGImportPluginOfxFactory, registerPlugin<SKGImportPluginOfx>();)
/**
 * This plugin export.
 */
K_EXPORT_PLUGIN(SKGImportPluginOfxFactory("skrooge_import_ofx", "skrooge_import_ofx"))

SKGImportPluginOfx::SKGImportPluginOfx(QObject* iImporter, const QVariantList& iArg)
    : SKGImportPlugin(iImporter)
{
    SKGTRACEIN(10, "SKGImportPluginOfx::SKGImportPluginOfx");
    Q_UNUSED(iArg);
}

SKGImportPluginOfx::~SKGImportPluginOfx()
{
}

bool SKGImportPluginOfx::isImportPossible()
{
    SKGTRACEIN(10, "SKGImportPluginOfx::isImportPossible");
    return (!m_importer ? true : m_importer->getFileNameExtension() == "OFX" || m_importer->getFileNameExtension() == "QFX");
}

SKGError SKGImportPluginOfx::importFile()
{
    if (!m_importer) return SKGError(ERR_ABORT, i18nc("Error message", "Invalid parameters"));

    SKGError err;
    SKGTRACEINRC(2, "SKGImportPluginOfx::importFile", err);

    if (!QFile(m_importer->getLocalFileName()).exists()) {
        err.setReturnCode(ERR_INVALIDARG);
        err.setMessage(i18nc("Error message",  "Open file '%1' failed", m_importer->getFileName().prettyUrl()));
    }

    if (!err) err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import %1 file", "OFX"));
    if (!err) {
        SKGImportPluginOfx::m_ofxError = SKGError();
        SKGImportPluginOfx::m_ofxInitialBalance.clear();

        try {
            LibofxContextPtr ctx = libofx_get_new_context();

            ofx_set_account_cb(ctx, SKGImportPluginOfx::ofxAccountCallback, m_importer);
            ofx_set_statement_cb(ctx, SKGImportPluginOfx::ofxStatementCallback, m_importer);
            ofx_set_transaction_cb(ctx, SKGImportPluginOfx::ofxTransactionCallback, m_importer);
            //ofx_set_security_cb(ctx, ofxSecurityCallback, this);
            int rc = libofx_proc_file(ctx, m_importer->getLocalFileName().toUtf8(), AUTODETECT);
            if (rc)  err = SKGError(ERR_FAIL, i18nc("Error message",  "Import OFX file '%1' failed", m_importer->getFileName().prettyUrl()));
            if (!err) err = SKGImportPluginOfx::m_ofxError;
            {
                //This is an option ==> no error management
                SKGError err;
                int nb = SKGImportPluginOfx::m_ofxInitialBalance.count();
                for (int i = 0; !err && i < nb; ++i) {
                    OfxStatementData data = SKGImportPluginOfx::m_ofxInitialBalance.at(i);

                    //Get account
                    SKGAccountObject act;
                    if (!err) err = getAccount(data.account_ptr, m_importer->getDocument(), act);

                    //Date of balance
                    QDate date = (data.ledger_balance_date_valid == true ? QDateTime::fromTime_t (data.ledger_balance_date).date() : QDate::currentDate());

                    //Get unit
                    SKGUnitObject unit;
                    if (!err) err = m_importer->getDefaultUnit(unit);

                    //Current amount
                    double currentAmount = act.getAmount(date);

                    //Update account
                    if (!err) err = act.setInitialBalance(data.ledger_balance - currentAmount, unit);
                    if (!err) err = act.save();
                    if (!err) err = m_importer->getDocument()->sendMessage(i18nc("An information message", "The initial balance of '%1' has been set", act.getName()));
                }
            }
        } catch (...) {
            err = SKGError(ERR_HANDLE, i18nc("Error message", "OFX import failed"));
        }

        SKGImportPluginOfx::m_ofxInitialBalance.clear();

        if (!err) err = m_importer->getDocument()->endTransaction(true);
        else  m_importer->getDocument()->endTransaction(false);
    }
    return err;
}

SKGError SKGImportPluginOfx::getAccount(OfxAccountData* iAccountData, SKGDocumentBank* iDoc, SKGAccountObject& oAccount)
{
    SKGError err;
    QString accountNumber = iAccountData->account_id;
    QString bankNumber = iAccountData->bank_id;
    //Correction BUG 234771 vvvvv
    accountNumber = accountNumber.trimmed();
    bankNumber = bankNumber.trimmed();
    //Correction BUG 234771 ^^^^^
    if (accountNumber.startsWith(bankNumber % ' ')) {
        accountNumber = accountNumber.right(accountNumber.length() - bankNumber.length() - 1);
        QStringList splitNumbers = accountNumber.split(' ');
        if (splitNumbers.count() == 2) accountNumber = splitNumbers.at(1);
    }

    //Check if account is already existing
    err = iDoc->getObject("v_account", "t_number='" % accountNumber % '\'', oAccount);

    return err;
}

int SKGImportPluginOfx::ofxStatementCallback(struct OfxStatementData data, void* pv)
{
    if (SKGImportPluginOfx::m_ofxError) return 0;
    SKGTRACEINRC(5, "SKGImportPluginOfx::ofxStatementCallback", SKGImportPluginOfx::m_ofxError);

    SKGImportExportManager* impotExporter = static_cast<SKGImportExportManager*>(pv);
    if (!impotExporter) return 0;
    SKGDocumentBank* doc = impotExporter->getDocument();
    if (!doc) return 0;

    ////Get data
    OfxAccountData* accountData = data.account_ptr;
    if (accountData && data.ledger_balance_valid == true) {
        //Get account
        SKGAccountObject act;
        SKGImportPluginOfx::m_ofxError = getAccount(accountData, doc, act);
        if (!SKGImportPluginOfx::m_ofxError) {
            if (act.getNbOperation() > 1) {
                SKGImportPluginOfx::m_ofxError = doc->sendMessage(i18nc("An information message", "The initial balance of '%1' has not been set because some operations are already existing", act.getName()), SKGDocument::Warning);
            } else {
                m_ofxInitialBalance.push_back(data);
            }
        }
    }

    return SKGImportPluginOfx::m_ofxError.getReturnCode();
}

int SKGImportPluginOfx::ofxAccountCallback(struct OfxAccountData data, void* pv)
{
    if (SKGImportPluginOfx::m_ofxError) return 0;
    SKGTRACEINRC(5, "SKGImportPluginOfx::ofxAccountCallback", SKGImportPluginOfx::m_ofxError);

    SKGImportExportManager* impotExporter = static_cast<SKGImportExportManager*>(pv);
    if (!impotExporter) return 0;
    SKGDocumentBank* doc = impotExporter->getDocument();
    if (!doc) return 0;

    SKGObjectBase tmp;
    if (data.account_id_valid == true) {
        QString agencyNumber = QString::fromUtf8(data.branch_id);
        QString accountNumber = QString::fromUtf8(data.account_id);
        QString bankNumber = QString::fromUtf8(data.bank_id);
        //Correction BUG 234771 vvvvv
        accountNumber = accountNumber.trimmed();
        bankNumber = bankNumber.trimmed();
        //Correction BUG 234771 ^^^^^
        if (accountNumber.startsWith(bankNumber % ' ')) {
            accountNumber = accountNumber.right(accountNumber.length() - bankNumber.length() - 1);
            QStringList splitNumbers = accountNumber.split(' ');
            if (splitNumbers.count() == 2) {
                agencyNumber = splitNumbers.at(0);
                accountNumber = splitNumbers.at(1);
            }
        }

        //Check if account is already existing
        SKGAccountObject account;
        SKGImportPluginOfx::m_ofxError = getAccount(&data, doc, account);
        if (!SKGImportPluginOfx::m_ofxError) {
            //Already existing
            account = tmp;
            SKGImportPluginOfx::m_ofxError = impotExporter->setDefaultAccount(&account);
        } else {
            //Not existing
            QString bankId = (data.bank_id_valid == true ? QString::fromUtf8(data.bank_id) : (data.broker_id_valid == true ? QString::fromUtf8(data.broker_id) : i18nc("Adjective, an unknown item", "Unknown")));
            if (!bankId.isEmpty()) {
                //Check if bank is already existing
                SKGBankObject bank;
                SKGImportPluginOfx::m_ofxError = doc->getObject("v_bank", "t_bank_number='" % bankId % '\'', tmp);
                if (!SKGImportPluginOfx::m_ofxError) {
                    //Already existing
                    bank = tmp;
                } else {
                    //Create new bank
                    bank = SKGBankObject(doc);
                    SKGImportPluginOfx::m_ofxError = bank.setName(bankId);
                    if (!SKGImportPluginOfx::m_ofxError) SKGImportPluginOfx::m_ofxError = bank.setNumber(QString::fromUtf8(data.bank_id));
                    if (!SKGImportPluginOfx::m_ofxError) SKGImportPluginOfx::m_ofxError = bank.save();
                }

                //Create new account
                QString name = QString::fromUtf8(data.account_name);
                if (name.isEmpty()) name = QString::fromUtf8(data.account_id);
                else name = name.remove("Bank account ");

                if (!SKGImportPluginOfx::m_ofxError) SKGImportPluginOfx::m_ofxError = bank.addAccount(account);
                if (!SKGImportPluginOfx::m_ofxError) SKGImportPluginOfx::m_ofxError = account.setName(name);
                if (!SKGImportPluginOfx::m_ofxError) SKGImportPluginOfx::m_ofxError = account.setNumber(accountNumber);
                if (!SKGImportPluginOfx::m_ofxError) SKGImportPluginOfx::m_ofxError = account.setAgencyNumber(agencyNumber);
                if (!SKGImportPluginOfx::m_ofxError) SKGImportPluginOfx::m_ofxError = account.setComment(QString::fromUtf8(data.account_name));
                SKGAccountObject::AccountType type = SKGAccountObject::CURRENT;
                if (data.account_type_valid == true) {
                    switch (data.account_type) {
                    case OfxAccountData::OFX_CHECKING:
                    case OfxAccountData::OFX_SAVINGS:
                    case OfxAccountData::OFX_CREDITLINE:
                    case OfxAccountData::OFX_CMA:
                        type = SKGAccountObject::CURRENT;
                        break;
                    case OfxAccountData::OFX_MONEYMRKT:
                    case OfxAccountData::OFX_INVESTMENT:
                        type = SKGAccountObject::INVESTMENT;
                        break;
                    case OfxAccountData::OFX_CREDITCARD:
                        type = SKGAccountObject::CREDITCARD;
                        break;
                    default:
                        break;
                    }
                }

                if (!SKGImportPluginOfx::m_ofxError) SKGImportPluginOfx::m_ofxError = account.setType(type);
                if (!SKGImportPluginOfx::m_ofxError) SKGImportPluginOfx::m_ofxError = account.save();

                if (!SKGImportPluginOfx::m_ofxError) SKGImportPluginOfx::m_ofxError = impotExporter->setDefaultAccount(&account);

                SKGTRACEL(10) << "Account [" << name << "] created" << endl;
            }
        }
    }
    if (data.currency_valid == true) {
        //Check if unit is already existing
        SKGUnitObject unit(doc);
        SKGImportPluginOfx::m_ofxError = doc->getObject("v_unit", "t_name='" % QString::fromUtf8(data.currency) % "' OR t_name like '%(" % QString::fromUtf8(data.currency) % ")%'", tmp);
        if (!SKGImportPluginOfx::m_ofxError) {
            //Already existing
            unit = tmp;
            SKGImportPluginOfx::m_ofxError = impotExporter->setDefaultUnit(&unit);
        } else {
            //Create new account
            SKGImportPluginOfx::m_ofxError = unit.setName(QString::fromUtf8(data.currency));
            if (!SKGImportPluginOfx::m_ofxError) SKGImportPluginOfx::m_ofxError = unit.setSymbol(QString::fromUtf8(data.currency));
            if (!SKGImportPluginOfx::m_ofxError) SKGImportPluginOfx::m_ofxError = unit.save();

            if (!SKGImportPluginOfx::m_ofxError) SKGImportPluginOfx::m_ofxError = impotExporter->setDefaultUnit(&unit);
        }
    }
    return SKGImportPluginOfx::m_ofxError.getReturnCode();
}

int SKGImportPluginOfx::ofxTransactionCallback(struct OfxTransactionData data, void* pv)
{
    if (SKGImportPluginOfx::m_ofxError) return 0;
    SKGTRACEINRC(5, "SKGImportPluginOfx::ofxTransactionCallback", SKGImportPluginOfx::m_ofxError);

    SKGImportExportManager* impotExporter = static_cast<SKGImportExportManager*>(pv);
    if (!impotExporter) return 0;
    SKGDocumentBank* doc = impotExporter->getDocument();
    if (!doc) return 0;

    //Get account
    SKGAccountObject account;
    SKGImportPluginOfx::m_ofxError = getAccount(data.account_ptr, doc, account);
    if (!SKGImportPluginOfx::m_ofxError) {
        //Get operation date
        QDate date = QDateTime::fromTime_t (data.date_posted_valid == true ? data.date_posted : data.date_initiated).date();

        //Get unit
        SKGUnitObject unit;
        SKGImportPluginOfx::m_ofxError = impotExporter->getDefaultUnit(unit);

        //Create id
        QString ID;
        if (data.fi_id_valid == true) {
            if (QString::fromUtf8(data.fi_id).count('0') != QString::fromUtf8(data.fi_id).count()) ID = QString("ID-") % QString::fromUtf8(data.fi_id);
        } else if (data.reference_number_valid == true) {
            if (QString::fromUtf8(data.reference_number).count('0') != QString::fromUtf8(data.reference_number).count()) ID = QString("REF-") % data.reference_number;
        }

        //Create operation
        SKGOperationObject ope;
        if (!SKGImportPluginOfx::m_ofxError) SKGImportPluginOfx::m_ofxError = account.addOperation(ope);
        if (!SKGImportPluginOfx::m_ofxError) SKGImportPluginOfx::m_ofxError = ope.setDate(date);
        if (!SKGImportPluginOfx::m_ofxError) SKGImportPluginOfx::m_ofxError = ope.setUnit(unit);
        if (!SKGImportPluginOfx::m_ofxError) SKGImportPluginOfx::m_ofxError = ope.setAttribute("t_imported", "T");
        if (!SKGImportPluginOfx::m_ofxError) SKGImportPluginOfx::m_ofxError = ope.setImportID(ID);
        if (!SKGImportPluginOfx::m_ofxError) {
            QString payeeName;
            if (data.payee_id_valid == true) payeeName = QString::fromUtf8(data.payee_id);
            else if (data.name_valid == true) payeeName = QString::fromUtf8(data.name);
            if (!payeeName.isEmpty()) {
                SKGPayeeObject payeeObj;
                SKGImportPluginOfx::m_ofxError = SKGPayeeObject::createPayee(doc, payeeName, payeeObj);
                if (!SKGImportPluginOfx::m_ofxError) SKGImportPluginOfx::m_ofxError = ope.setPayee(payeeObj);
            }
        }
        if (!SKGImportPluginOfx::m_ofxError && data.memo_valid == true) SKGImportPluginOfx::m_ofxError = ope.setComment(QString::fromUtf8(data.memo));
        if (!SKGImportPluginOfx::m_ofxError && data.check_number_valid == true) SKGImportPluginOfx::m_ofxError = ope.setNumber(SKGServices::stringToInt(data.check_number));
        if (!SKGImportPluginOfx::m_ofxError && data.invtransactiontype_valid == true) SKGImportPluginOfx::m_ofxError = ope.setMode(i18nc("Noun, the title of an item", "Title"));
        if (!SKGImportPluginOfx::m_ofxError && data.transactiontype_valid == true) {
            QString mode;
            switch (data.transactiontype) {
            case OFX_CREDIT: {
                mode = i18nc("Noun, type of OFX transaction", "Credit");
                break;
            }
            case OFX_DEBIT: {
                mode = i18nc("Noun, type of OFX transaction", "Debit");
                break;
            }
            case OFX_INT: {
                mode = i18nc("Noun, type of OFX transaction", "Interest");
                break;
            }
            case OFX_DIV: {
                mode = i18nc("Noun, type of OFX transaction", "Dividend");
                break;
            }
            case OFX_FEE: {
                mode = i18nc("Noun, type of OFX transaction", "FI fee");
                break;
            }
            case OFX_SRVCHG: {
                mode = i18nc("Noun, type of OFX transaction", "Service charge");
                break;
            }
            case OFX_DEP: {
                mode = i18nc("Noun, type of OFX transaction", "Deposit");
                break;
            }
            case OFX_ATM: {
                mode = i18nc("Noun, type of OFX transaction", "ATM");
                break;
            }
            case OFX_POS: {
                mode = i18nc("Noun, type of OFX transaction", "Point of sale");
                break;
            }
            case OFX_XFER: {
                mode = i18nc("An operation mode", "Transfer");
                break;
            }
            case OFX_CHECK: {
                mode = i18nc("An operation mode", "Check");
                break;
            }
            case OFX_PAYMENT: {
                mode = i18nc("Noun, type of OFX transaction", "Electronic payment");
                break;
            }
            case OFX_CASH: {
                mode = i18nc("An operation mode", "Withdrawal");
                break;
            }
            case OFX_DIRECTDEP: {
                mode = i18nc("Noun, type of OFX transaction", "Deposit");
                break;
            }
            case OFX_DIRECTDEBIT: {
                mode = i18nc("An operation mode", "Direct debit");
                break;
            }
            case OFX_REPEATPMT: {
                mode = i18nc("Noun, type of OFX transaction", "Repeating payment");
                break;
            }
            case OFX_OTHER: {
                mode = "";
                break;
            }
            }

            SKGImportPluginOfx::m_ofxError = ope.setMode(mode);
        }
        if (!SKGImportPluginOfx::m_ofxError) SKGImportPluginOfx::m_ofxError = ope.save();

        //Create sub operation
        SKGSubOperationObject subop;
        if (!SKGImportPluginOfx::m_ofxError) SKGImportPluginOfx::m_ofxError = ope.addSubOperation(subop);
        if (!SKGImportPluginOfx::m_ofxError && data.amount_valid == true) SKGImportPluginOfx::m_ofxError = subop.setQuantity(data.amount + (data.commission_valid == true ? data.commission : 0));
        if (!SKGImportPluginOfx::m_ofxError && data.memo_valid == true) SKGImportPluginOfx::m_ofxError = subop.setComment(QString::fromUtf8(data.memo));
        if (!SKGImportPluginOfx::m_ofxError) SKGImportPluginOfx::m_ofxError = subop.save();

        //Commission
        if (!SKGImportPluginOfx::m_ofxError && data.commission_valid == true && data.amount_valid == true && data.commission > 0) {
            //Create splitter operation
            SKGSubOperationObject subop2;
            if (!SKGImportPluginOfx::m_ofxError) SKGImportPluginOfx::m_ofxError = ope.addSubOperation(subop2);
            if (!SKGImportPluginOfx::m_ofxError) SKGImportPluginOfx::m_ofxError = subop2.setComment(i18nc("Noun, a quantity of money taken by a financial institution to perform an operation", "Commission"));
            if (!SKGImportPluginOfx::m_ofxError) SKGImportPluginOfx::m_ofxError = subop2.setQuantity(-data.commission);
            if (!SKGImportPluginOfx::m_ofxError) SKGImportPluginOfx::m_ofxError = subop2.save();
        }

    }
    return SKGImportPluginOfx::m_ofxError.getReturnCode();
}

QString SKGImportPluginOfx::getMimeTypeFilter() const
{
    return "*.ofx *.qfx|" % i18nc("A file format", "OFX file");
}

#include "skgimportpluginofx.moc"
