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

#include <klocalizedstring.h>
#include <KCompressionDevice>
#include <kpluginfactory.h>

#include <QDomDocument>
#include <qfileinfo.h>

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

/**
 * This plugin factory.
 */
K_PLUGIN_FACTORY(SKGImportPluginXhbFactory, registerPlugin<SKGImportPluginXhb>();)

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

SKGImportPluginXhb::~SKGImportPluginXhb()
{
}

bool SKGImportPluginXhb::isImportPossible()
{
    SKGTRACEINFUNC(10);
    return (!m_importer ? true : m_importer->getFileNameExtension() == "XHB");
}

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

    // Initialisation
    // Open file
    KCompressionDevice file(m_importer->getLocalFileName(), KCompressionDevice::GZip);
    if (!file.open(QIODevice::ReadOnly)) {
        err.setReturnCode(ERR_INVALIDARG).setMessage(i18nc("Error message",  "Open file '%1' failed", m_importer->getFileName().toDisplayString()));
    } else {
        QDomDocument doc;

        // Set the file without uncompression
        QString errorMsg;
        int errorLine = 0;
        int errorCol = 0;
        bool contentOK = doc.setContent(file.readAll(), &errorMsg, &errorLine, &errorCol);
        file.close();

        // Get root
        QDomElement docElem = doc.documentElement();

        if (!contentOK) {
            err.setReturnCode(ERR_ABORT).setMessage(i18nc("Error message",  "%1-%2: '%3'", errorLine, errorCol, errorMsg));
        }
        if (!contentOK) {
            err.addError(ERR_INVALIDARG, i18nc("Error message",  "Invalid XML content in file '%1'", m_importer->getFileName().toDisplayString()));
        } else {
            err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import %1 file", "XHB"), 4);

            QMap<QString, SKGAccountObject> mapIdAccount;
            QMap<QString, SKGCategoryObject> mapIdCategory;
            QMap<QString, SKGPayeeObject> mapIdPayee;

            SKGUnitObject unit;
            IFOKDO(err, m_importer->getDefaultUnit(unit))

            // Step 1-Create accounts
            IFOK(err) {
                QDomNodeList accountList = docElem.elementsByTagName("account");
                int nb = accountList.count();
                err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import accounts"), nb);
                for (int i = 0; !err && i < nb; ++i) {
                    // Get account object
                    QDomElement account = accountList.at(i).toElement();

                    // Create the bank
                    SKGBankObject bank(m_importer->getDocument());
                    QString bname = getAttribute(account,  "bankname");
                    if (bname.isEmpty()) {
                        bname = "HOMEBANK";
                    }
                    IFOKDO(err, bank.setName(bname))
                    IFOKDO(err, bank.save())

                    // Creation of the account
                    SKGAccountObject accountObj;
                    IFOKDO(err, bank.addAccount(accountObj))
                    IFOKDO(err, accountObj.setName(getAttribute(account,  "name")))
                    IFOKDO(err, accountObj.setNumber(getAttribute(account,  "number")))
                    IFOKDO(err, accountObj.setMinLimitAmount(SKGServices::stringToDouble(getAttribute(account,  "minimum"))))
                    IFOKDO(err, accountObj.minLimitAmountEnabled(accountObj.getMinLimitAmount() != 0.0))
                    QString type = getAttribute(account,  "type");
                    IFOKDO(err, accountObj.setType(type == "2" ? SKGAccountObject::WALLET : (type == "3" ? SKGAccountObject::ASSETS : (type == "4" ? SKGAccountObject::CREDITCARD : (type == "5" ? SKGAccountObject::LOAN : SKGAccountObject::CURRENT)))))
                    IFOK(err) {
                        QString flags = getAttribute(account,  "flags");
                        err = accountObj.setClosed(flags == "2" ||  flags == "3");
                    }
                    IFOKDO(err, accountObj.save())

                    // Change parent bank in case of ASSETS
                    if (accountObj.getType() == SKGAccountObject::WALLET) {
                        // Get blank bank
                        SKGBankObject blankBank(m_importer->getDocument());
                        IFOKDO(err, blankBank.setName(""))
                        if (blankBank.exist()) {
                            err = blankBank.load();
                        } else {
                            err = blankBank.save();
                        }
                        IFOKDO(err, accountObj.setBank(blankBank))
                        IFOKDO(err, accountObj.save())
                    }

                    // Set initial balance
                    IFOKDO(err, accountObj.setInitialBalance(SKGServices::stringToDouble(getAttribute(account,  "initial")), unit))

                    mapIdAccount[getAttribute(account,  "key")] = accountObj;

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

                SKGENDTRANSACTION(m_importer->getDocument(),  err);
            }
            IFOKDO(err, m_importer->getDocument()->stepForward(1))

            // Step 2-Get payees
            IFOK(err) {
                QDomNodeList partyList = docElem.elementsByTagName("pay");
                int nb = partyList.count();
                err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import payees"), nb);
                for (int i = 0; !err && i < nb; ++i) {
                    // Get payee object
                    QDomElement party = partyList.at(i).toElement();
                    SKGPayeeObject payeeObject;
                    err = SKGPayeeObject::createPayee(m_importer->getDocument(), getAttribute(party,  "name"), payeeObject);
                    mapIdPayee[getAttribute(party,  "key")] = payeeObject;

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

                SKGENDTRANSACTION(m_importer->getDocument(),  err);
            }
            IFOKDO(err, m_importer->getDocument()->stepForward(2))

            // Step 3-Create categories
            IFOK(err) {
                QDomNodeList categoryList = docElem.elementsByTagName("cat");
                int nb = categoryList.count();
                err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import categories"), 2 * nb);
                for (int j = 0; !err && j < 2; ++j) {
                    for (int i = 0; !err && i < nb; ++i) {
                        // Get account object
                        QDomElement category = categoryList.at(i).toElement();

                        // Creation of the category
                        bool catCreated = false;
                        SKGCategoryObject catObj(m_importer->getDocument());
                        QString parent2 = getAttribute(category,  "parent");
                        if (parent2.isEmpty()) {
                            parent2 = '0';
                        }
                        if (parent2 == "0" && j == 0) {
                            IFOKDO(err, catObj.setName(getAttribute(category,  "name")))
                            IFOKDO(err, catObj.save())

                            mapIdCategory[getAttribute(category,  "key")] = catObj;
                            catCreated = true;

                        } else if (parent2 != "0" && j == 1) {
                            SKGCategoryObject catParentObj = mapIdCategory[parent2];
                            IFOKDO(err, catObj.setName(getAttribute(category,  "name")))
                            IFOKDO(err, catObj.setParentCategory(catParentObj))
                            IFOKDO(err, catObj.save())

                            mapIdCategory[getAttribute(category,  "key")] = catObj;
                            catCreated = true;
                        }

                        // Creation of the attached budget
                        if (!err && catCreated) {
                            QString b0 = getAttribute(category,  "b0");
                            QString b1 = getAttribute(category,  "b1");
                            if (!b0.isEmpty() || !b1.isEmpty()) {
                                for (int m = 1; !err && m <= 12; ++m) {
                                    SKGBudgetObject budget(m_importer->getDocument());

                                    int year = QDate::currentDate().year();
                                    IFOKDO(err, budget.setCategory(catObj))
                                    IFOKDO(err, budget.setYear(year))
                                    IFOKDO(err, budget.setMonth(m))
                                    IFOKDO(err, budget.setBudgetedAmount(SKGServices::stringToDouble(getAttribute(category,  'b' % SKGServices::intToString(b0.isEmpty() ? m : 0)))))
                                    IFOKDO(err, budget.save())
                                }
                            }
                        }

                        IFOKDO(err, m_importer->getDocument()->stepForward(nb * j + i + 1))
                    }
                }

                SKGENDTRANSACTION(m_importer->getDocument(),  err);
            }
            IFOKDO(err, m_importer->getDocument()->stepForward(3))

            // Step 4-Create transaction
            IFOK(err) {
                err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import operations"), 2);
                for (int j = 1; !err && j <= 2; ++j) {
                    QDomNodeList transactionList = docElem.elementsByTagName(j == 1 ? "ope" : "fav");
                    int nb = transactionList.count();
                    err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import operations"), nb);
                    for (int i = 0; !err && i < nb; ++i) {
                        // Get account object
                        QDomElement transaction = transactionList.at(i).toElement();

                        // Get attributes
                        QString idAccount = getAttribute(transaction,  "account");
                        QString dst_account = getAttribute(transaction,  "dst_account");
                        if (idAccount != "0") {
                            SKGAccountObject account = mapIdAccount[idAccount];
                            QDate date = QDate(1, 1, 1).addDays(1 + SKGServices::stringToInt(getAttribute(transaction,  j == 1 ? "date" : "nextdate")));
                            QString mode = getAttribute(transaction,  "paymode");
                            if (mode == "1") {
                                mode = i18nc("Noun: type of payement", "Credit card");
                            } else if (mode == "2") {
                                mode = i18nc("Noun: type of payement", "Check");
                            } else if (mode == "3") {
                                mode = i18nc("Noun: type of payement", "Cash");
                            } else if (mode == "4" || mode == "5") {
                                mode = i18nc("Noun: type of payement", "Transfer");
                            } else {
                                mode = i18nc("Noun: type of payement", "Other");
                            }
                            QString comment = QString(getAttribute(transaction,  "wording") % ' ' % getAttribute(transaction,  "info")).trimmed();
                            int flags = SKGServices::stringToInt(getAttribute(transaction,  "flags"));
                            double amount = SKGServices::stringToDouble(getAttribute(transaction,  "amount"));
                            SKGOperationObject::OperationStatus status = (flags == 1 ||  flags == 3 ? SKGOperationObject::CHECKED : SKGOperationObject::NONE);
                            SKGPayeeObject payee = mapIdPayee[ getAttribute(transaction,  "payee")];
                            SKGCategoryObject category = mapIdCategory[getAttribute(transaction,  "category")];

                            // Creation of the operation
                            SKGOperationObject opObj;
                            IFOKDO(err, account.addOperation(opObj, true))
                            IFOKDO(err, opObj.setDate(date))
                            IFOKDO(err, opObj.setUnit(unit))
                            IFOKDO(err, opObj.setPayee(payee))
                            IFOKDO(err, opObj.setMode(mode))

                            IFOKDO(err, opObj.setComment(comment))
                            IFOKDO(err, opObj.setTemplate(j == 2))
                            IFOKDO(err, opObj.setImported(true))
                            IFOKDO(err, opObj.setImportID("HXB-" % SKGServices::intToString(i)))
                            IFOKDO(err, opObj.setStatus(status))
                            IFOKDO(err, opObj.save())

                            SKGSubOperationObject subObj;
                            IFOKDO(err, opObj.addSubOperation(subObj))
                            IFOKDO(err, subObj.setCategory(category))
                            IFOKDO(err, subObj.setComment(comment))
                            IFOKDO(err, subObj.setQuantity(amount))
                            IFOKDO(err, subObj.save())

                            // Transfert
                            /*if(dst_account != idAccount && dst_account != "0") {
                                SKGAccountObject account = mapIdAccount[dst_account];
                                SKGOperationObject op2Obj;
                                if(!err) err = account.addOperation(op2Obj, true);
                                if(!err) err = op2Obj.setDate(date);
                                if(!err) err = op2Obj.setUnit(unit);
                                if(!err) err = op2Obj.setPayee(payee);
                                if(!err) err = op2Obj.setMode(mode);

                                if(!err) err = op2Obj.setComment(comment);
                                if(!err) err = op2Obj.setTemplate(j == 2);
                                if(!err) err = op2Obj.setImported(true);
                                if(!err) err = op2Obj.setImportID("HXB-" % SKGServices::intToString(i) % "_TR");
                                if(!err) err = op2Obj.setStatus(status);
                                if(!err) err = op2Obj.save();

                                SKGSubOperationObject subObj;
                                if(!err) err = op2Obj.addSubOperation(subObj);
                                if(!err) err = subObj.setCategory(category);
                                if(!err) err = subObj.setComment(comment);
                                if(!err) err = subObj.setQuantity(-amount);
                                if(!err) err = subObj.save();

                                if(!err) err = op2Obj.setGroupOperation(opObj);
                                if(!err) err = op2Obj.save();
                            }*/

                            // Scheduled operations
                            if (j == 2) {
                                // Creation of scheduled operation
                                SKGRecurrentOperationObject recu;
                                IFOKDO(err, opObj.addRecurrentOperation(recu))
                                int limit = SKGServices::stringToInt(getAttribute(transaction,  "limit"));
                                IFOKDO(err, recu.timeLimit(flags & (1 << 7)))
                                IFOKDO(err, recu.setTimeLimit(limit))
                                int punit = SKGServices::stringToInt(getAttribute(transaction,  "unit"));
                                int pstep = SKGServices::stringToInt(getAttribute(transaction,  "every"));
                                SKGRecurrentOperationObject::PeriodUnit p = SKGRecurrentOperationObject::MONTH;
                                if (punit == 0) {
                                    p = SKGRecurrentOperationObject::DAY;
                                } else if (punit == 1) {
                                    p = SKGRecurrentOperationObject::WEEK;
                                } else if (punit == 3) {
                                    p = SKGRecurrentOperationObject::YEAR;
                                }
                                IFOKDO(err, recu.setPeriodIncrement(pstep))
                                IFOKDO(err, recu.setPeriodUnit(p))
                                IFOKDO(err, recu.warnEnabled(flags & (1 << 5)))
                                IFOKDO(err, recu.autoWriteEnabled(flags & (1 << 2)))
                                IFOKDO(err, recu.save())
                                // #define OF_VALID        (1<<0)
                                // #define OF_INCOME       (1<<1)
                                // #define OF_AUTO         (1<<2)
                                // #define OF_ADDED        (1<<3)
                                // #define OF_CHANGED      (1<<4)
                                // #define OF_REMIND       (1<<5)
                                // #define OF_CHEQ2        (1<<6)
                                // #define OF_LIMIT        (1<<7)
                            }
                        }

                        if (!err && i % 500 == 0) {
                            err = m_importer->getDocument()->executeSqliteOrder("ANALYZE");
                        }
                        IFOKDO(err, m_importer->getDocument()->stepForward(i + 1))
                    }

                    SKGENDTRANSACTION(m_importer->getDocument(),  err);
                    IFOKDO(err, m_importer->getDocument()->stepForward(j))
                }

                SKGENDTRANSACTION(m_importer->getDocument(),  err);
            }
            IFOKDO(err, m_importer->getDocument()->stepForward(4))

            SKGENDTRANSACTION(m_importer->getDocument(),  err);

            IFOKDO(err, m_importer->getDocument()->executeSqliteOrder("ANALYZE"))
        }
    }

    return err;
}


QString SKGImportPluginXhb::getAttribute(const QDomElement& iElement, const QString& iAttribute)
{
    QString val = iElement.attribute(iAttribute);
    if (val == "(null)") {
        val = "";
    }
    return val;
}

QString SKGImportPluginXhb::getMimeTypeFilter() const
{
    return "*.xhb|" % i18nc("A file format", "Homebank document");
}

#include <skgimportpluginxhb.moc>
