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

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

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

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

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

SKGImportPluginXhb::~SKGImportPluginXhb()
{
}

bool SKGImportPluginXhb::isImportPossible()
{
    SKGTRACEIN(10, "SKGImportPluginXhb::isImportPossible");
    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;
    SKGTRACEINRC(2, "SKGImportPluginXhb::importFile", err);

    //Initialisation
    //Open file
    QIODevice* file = KFilterDev::deviceForFile(m_importer->getLocalFileName(), "application/x-gzip");
    if (!file->open(QIODevice::ReadOnly)) {
        err.setReturnCode(ERR_INVALIDARG);
        err.setMessage(i18nc("Error message",  "Open file '%1' failed", m_importer->getFileName().prettyUrl()));
    } 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);
            err.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().prettyUrl()));
        } 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;
            if (!err) err = m_importer->getDefaultUnit(unit);

            //Step 1-Create accounts
            if (!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";
                    if (!err) err = bank.setName(bname);
                    if (!err) err = bank.save();

                    //Creation of the account
                    SKGAccountObject accountObj;
                    if (!err) err = bank.addAccount(accountObj);
                    if (!err) err = accountObj.setName(getAttribute(account,  "name"));
                    if (!err) err = accountObj.setNumber(getAttribute(account,  "number"));
                    QString type = getAttribute(account,  "type");
                    if (!err) err = accountObj.setType(type == "2" ? SKGAccountObject::WALLET : (type == "3" ? SKGAccountObject::ASSETS : (type == "4" ? SKGAccountObject::CREDITCARD : (type == "5" ? SKGAccountObject::LOAN : SKGAccountObject::CURRENT))));
                    if (!err) {
                        QString flags = getAttribute(account,  "flags");
                        err = accountObj.setClosed(flags == "2" ||  flags == "3");
                    }
                    if (!err) err = accountObj.save();
                    if (!err) {
                        accountObj.setClosed(false); //To be sure that the modification is possible. NOT SAVED
                        err = accountObj.setInitialBalance(SKGServices::stringToDouble(getAttribute(account,  "initial")), unit);
                    }

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

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

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

            //Step 2-Get payees
            if (!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;

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

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

            //Step 3-Create categories
            if (!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 parent = getAttribute(category,  "parent");
                        if (parent == "0" && j == 0) {
                            if (!err) err = catObj.setName(getAttribute(category,  "name"));
                            if (!err) err = catObj.save();

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

                        } else if (parent != "0" && j == 1) {
                            SKGCategoryObject catParentObj = mapIdCategory[parent];
                            if (!err) err = catObj.setName(getAttribute(category,  "name"));
                            if (!err) err = catObj.setParentCategory(catParentObj);
                            if (!err) 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();
                                    if (!err) err = budget.setCategory(catObj);
                                    if (!err) err = budget.setYear(year);
                                    if (!err) err = budget.setMonth(m);
                                    if (!err) err = budget.setBudgetedAmount(SKGServices::stringToDouble(getAttribute(category,  'b' % SKGServices::intToString(b0.isEmpty() ? m : 0))));
                                    if (!err) err = budget.save();
                                }
                            }
                        }

                        if (!err) err = m_importer->getDocument()->stepForward(nb * j + i + 1);
                    }
                }

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

            //Step 4-Create transaction
            if (!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;
                            if (!err) err = account.addOperation(opObj);
                            if (!err) err = opObj.setDate(date);
                            if (!err) err = opObj.setUnit(unit);
                            if (!err) err = opObj.setPayee(payee);
                            if (!err) err = opObj.setMode(mode);

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

                            SKGSubOperationObject subObj;
                            if (!err) err = opObj.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();

                            //Transfert
                            /*if(dst_account != idAccount && dst_account != "0") {
                                SKGAccountObject account = mapIdAccount[dst_account];
                                SKGOperationObject op2Obj;
                                if(!err) err = account.addOperation(op2Obj);
                                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;
                                if (!err) err = opObj.addRecurrentOperation(recu);
                                int limit = SKGServices::stringToInt(getAttribute(transaction,  "limit"));
                                if (!err) err = recu.timeLimit(flags & (1 << 7));
                                if (!err) 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;
                                if (!err) err = recu.setPeriodIncrement(pstep);
                                if (!err) err = recu.setPeriodUnit(p);
                                if (!err) err = recu.warnEnabled(flags & (1 << 5));
                                if (!err) err = recu.autoWriteEnabled(flags & (1 << 2));
                                if (!err) 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");
                        if (!err) err = m_importer->getDocument()->stepForward(i + 1);
                    }

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

                    if (!err) err = m_importer->getDocument()->stepForward(j);
                }

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

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

            if (!err) err = m_importer->getDocument()->executeSqliteOrder("ANALYZE");
        }
    }

    delete file;
    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"
