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

#include <kio/netaccess.h>

#include <QFile>
#include <qfileinfo.h>
#include <QSqlDatabase>
#include <QSqlError>

#include <klocale.h>

/**
 * This plugin factory.
 */
K_PLUGIN_FACTORY(SKGImportPluginSkgFactory, registerPlugin<SKGImportPluginSkg>();)
/**
 * This plugin export.
 */
K_EXPORT_PLUGIN(SKGImportPluginSkgFactory("skrooge_import_skg", "skrooge_import_skg"))

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

    m_parameters["password"] = "";
}

SKGImportPluginSkg::~SKGImportPluginSkg()
{
}

bool SKGImportPluginSkg::isImportPossible()
{
    SKGTRACEIN(10, "SKGImportPluginSkg::isImportPossible");
    return (!m_importer ? true : QFileInfo(m_importer->getFileName()).suffix().toUpper() == "SKG" || QFileInfo(m_importer->getFileName()).suffix().toUpper() == "SQLITE");
}

SKGError SKGImportPluginSkg::importFile()
{
    if (!m_importer) return SKGError(ERR_ABORT, i18nc("Error message", "Invalid parameters"));
    SKGError err;
    SKGTRACEINRC(2, "SKGImportPluginSkg::importFile", err);
    QString iFileName = m_importer->getFileName();
    SKGTRACEL(10) << "Input filename=" << iFileName << endl;

    SKGDocumentBank docOrigin;
    err = docOrigin.load(iFileName, m_parameters["password"]);
    if (!err) {
        QMap<QString, SKGObjectBase> mapOriginNew;
        err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import %1 file", "SKG"), 10);

        //Step 1 - units
        if (!err) {
            SKGObjectBase::SKGListSKGObjectBase listUnits;
            err = docOrigin.getObjects("v_unit", "1=1 order by t_type", listUnits);
            if (!err) {
                int nb = listUnits.count();
                err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import units"), nb);
                for (int i = 0; !err && i < nb; ++i) {
                    SKGUnitObject unitOrigin = listUnits.at(i);
                    SKGUnitObject unit = unitOrigin.cloneInto(m_importer->getDocument());
                    if (unit.load().isFailed()) {
                        //This unit does not exist yet
                        if (!err) {
                            SKGUnitObject::UnitType unitType = unitOrigin.getType();
                            if (unitType == SKGUnitObject::PRIMARY || unitType == SKGUnitObject::SECONDARY) unitType = SKGUnitObject::CURRENCY;
                            err = unit.setType(unitType);
                        }
                        if (!err) {
                            SKGUnitObject parentUnitOrigin;
                            unitOrigin.getUnit(parentUnitOrigin);

                            SKGUnitObject parentUnit = mapOriginNew[parentUnitOrigin.getUniqueID()];
                            if (parentUnit != unit) err = unit.setUnit(parentUnit);
                        }
                        if (!err) err = unit.save(false);    //Save only
                    }
                    mapOriginNew[unitOrigin.getUniqueID()] = unit;

                    //Duplicate properties
                    if (!err) err = copyParameters(unitOrigin, unit);

                    //Unit values
                    SKGObjectBase::SKGListSKGObjectBase listUnitsValues;
                    if (!err) err = unitOrigin.getUnitValues(listUnitsValues);
                    int nb2 = listUnitsValues.count();
                    for (int j = 0; !err && j < nb2; ++j) {
                        SKGUnitValueObject unitValueOrigin = listUnitsValues.at(j);

                        SKGUnitValueObject  unitval;
                        err = unit.addUnitValue(unitval);
                        if (!err) err = unitval.setDate(unitValueOrigin.getDate());
                        if (!err) err = unitval.setQuantity(unitValueOrigin.getQuantity());
                        if (!err) err = unitval.save(true);    //Save or update

                        //Duplicate properties
                        if (!err) err = copyParameters(unitValueOrigin, unitval);
                    }
                    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 - bank and accounts
        if (!err) {
            SKGObjectBase::SKGListSKGObjectBase listBanks;
            err = docOrigin.getObjects("v_bank", "", listBanks);
            if (!err) {
                int nb = listBanks.count();
                err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import banks and accounts"), nb);
                for (int i = 0; !err && i < nb; ++i) {
                    SKGBankObject bankOrigin = listBanks.at(i);
                    SKGBankObject bank = bankOrigin.cloneInto(m_importer->getDocument());
                    if (bank.load().isFailed()) {
                        //This bank does not exist yet
                        if (!err) err = bank.save(false);    //Save only
                    }

                    //Duplicate properties
                    if (!err) err = copyParameters(bankOrigin, bank);

                    //Accounts
                    SKGObjectBase::SKGListSKGObjectBase listAccounts;
                    if (!err) err = bankOrigin.getAccounts(listAccounts);
                    int nb2 = listAccounts.count();
                    for (int j = 0; !err && j < nb2; ++j) {
                        SKGAccountObject accountOrigin = listAccounts.at(j);
                        SKGAccountObject account = accountOrigin.cloneInto(m_importer->getDocument());
                        if (account.load().isFailed()) {
                            //This account does not exist yet
                            if (!err) err = account.setBank(bank);
                            //Initial balance will be set on operation creation
                            if (!err) err = account.save(false);    //Save only
                        }

                        //Duplicate properties
                        if (!err) err = copyParameters(accountOrigin, account);

                        mapOriginNew[accountOrigin.getUniqueID()] = account;
                    }
                    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 - categories
        if (!err) {
            SKGObjectBase::SKGListSKGObjectBase listCategories;
            err = docOrigin.getObjects("v_category", "", listCategories);
            if (!err) {
                int nb = listCategories.count();
                err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import categories"), nb);
                for (int i = 0; !err && i < nb; ++i) {
                    SKGCategoryObject catOrigin = listCategories.at(i);

                    SKGCategoryObject cat;
                    err = SKGCategoryObject::createPathCategory(m_importer->getDocument(), catOrigin.getFullName(), cat);
                    //Duplicate properties
                    if (!err) err = copyParameters(catOrigin, cat);
                    mapOriginNew[catOrigin.getUniqueID()] = cat;
                    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(3);

        //Step 4 - trackers
        if (!err) {
            SKGObjectBase::SKGListSKGObjectBase listRefund;
            err = docOrigin.getObjects("v_refund", "", listRefund);
            if (!err) {
                int nb = listRefund.count();
                err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import trackers"), nb);
                for (int i = 0; !err && i < nb; ++i) {
                    SKGTrackerObject tracOrigin = listRefund.at(i);
                    SKGTrackerObject trac = tracOrigin.cloneInto(m_importer->getDocument());
                    err = trac.save();
                    //Duplicate properties
                    if (!err) err = copyParameters(tracOrigin, trac);
                    mapOriginNew[tracOrigin.getUniqueID()] = trac;
                    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(4);

        //Step 5 - rules
        if (!err) {
            SKGObjectBase::SKGListSKGObjectBase listRules;
            err = docOrigin.getObjects("v_rule", "", listRules);
            if (!err) {
                int nb = listRules.count();
                err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import rules"), nb);
                for (int i = 0; !err && i < nb; ++i) {
                    SKGRuleObject rulOrigin = listRules.at(i);
                    SKGRuleObject rul = rulOrigin.cloneInto(m_importer->getDocument());
                    err = rul.save(false);  //Save only
                    //Duplicate properties
                    if (!err) err = copyParameters(rulOrigin, rul);
                    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(5);

        //Step 6 - payee
        if (!err) {
            SKGObjectBase::SKGListSKGObjectBase listPayee;
            err = docOrigin.getObjects("v_payee", "", listPayee);
            if (!err) {
                int nb = listPayee.count();
                err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import payees"), nb);
                for (int i = 0; !err && i < nb; ++i) {
                    SKGPayeeObject paylOrigin = listPayee.at(i);

                    SKGPayeeObject pay;
                    err = SKGPayeeObject::createPayee(m_importer->getDocument(), paylOrigin.getName(), pay);
                    if (!err) err = pay.setAddress(paylOrigin.getAddress());
                    if (!err) err = pay.save();
                    //Duplicate properties
                    if (!err) err = copyParameters(paylOrigin, pay);
                    mapOriginNew[paylOrigin.getUniqueID()] = pay;
                    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(6);

        //Step 7 - operations and suboperation
        if (!err) {
            SKGObjectBase::SKGListSKGObjectBase listOperations;
            err = docOrigin.getObjects("v_operation", "", listOperations);
            if (!err) {
                int nb = listOperations.count();
                err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import operations"), nb);
                for (int i = 0; !err && i < nb; ++i) {
                    SKGOperationObject operationOrigin = listOperations.at(i);
                    SKGOperationObject operation = operationOrigin.cloneInto(m_importer->getDocument());
                    if (!err) err = operation.setAttribute("r_recurrentoperation_id", "");
                    if (!err) err = operation.setImported(true);
                    if (!err) {
                        QString importID = operationOrigin.getImportID();
                        if (importID.isEmpty()) importID = "SKG-" % SKGServices::intToString(operationOrigin.getID());
                        err = operation.setImportID(importID);
                    }
                    if (!err) {
                        SKGAccountObject actOrig;
                        err = operationOrigin.getParentAccount(actOrig);
                        if (!err) {
                            SKGAccountObject act = mapOriginNew[actOrig.getUniqueID()];
                            act.setClosed(false); //To be sure that the modification is possible. NOT SAVED
                            if (!err) err = operation.setParentAccount(act);
                        }
                    }
                    if (!err) {
                        SKGPayeeObject payeeOrig;
                        operationOrigin.getPayee(payeeOrig);     //Error not managed
                        if (!err) err = operation.setPayee(mapOriginNew[payeeOrig.getUniqueID()]);
                    }
                    if (!err) {
                        SKGUnitObject unitOrig;
                        err = operationOrigin.getUnit(unitOrig);
                        if (!err) err = operation.setUnit(mapOriginNew[unitOrig.getUniqueID()]);
                    }
                    if (!err) {
                        SKGOperationObject groupOrig;
                        operationOrigin.getGroupOperation(groupOrig);
                        err = operation.setGroupOperation(mapOriginNew[groupOrig.getUniqueID()]);
                    }
                    if (!err) err = operation.save(false);    //Save only

                    mapOriginNew[operationOrigin.getUniqueID()] = operation;

                    //Duplicate properties
                    if (!err) err = copyParameters(operationOrigin, operation);

                    //Sub operation
                    SKGObjectBase::SKGListSKGObjectBase listSuboperations;
                    if (!err) err = operationOrigin.getSubOperations(listSuboperations);
                    int nb2 = listSuboperations.count();
                    for (int j = 0; !err && j < nb2; ++j) {
                        SKGSubOperationObject subopOrigin = listSuboperations.at(j);

                        SKGSubOperationObject subop = subopOrigin.cloneInto(m_importer->getDocument());
                        err = subop.setParentOperation(operation);
                        if (!err) {
                            SKGCategoryObject catOrig;
                            subopOrigin.getCategory(catOrig);    //Error not managed
                            err = subop.setCategory(mapOriginNew[catOrig.getUniqueID()]);
                        }
                        if (!err) {
                            SKGTrackerObject tracOrig;
                            subopOrigin.getTracker(tracOrig);    //Error not managed
                            if (!err) err = subop.setTracker(mapOriginNew[tracOrig.getUniqueID()], true);
                        }
                        if (!err) err = subop.save(false);    //Save only

                        //Duplicate properties
                        if (!err) err = copyParameters(subopOrigin, subop);
                    }
                    if (!err) err = m_importer->getDocument()->stepForward(i + 1);

                    if (!err && i % 100 == 0) err = m_importer->getDocument()->executeSqliteOrder("ANALYZE");
                }

                if (!err) err = m_importer->getDocument()->endTransaction(true);
                else  m_importer->getDocument()->endTransaction(false);
            }
        }
        if (!err) err = m_importer->getDocument()->stepForward(7);
        /*                                      SKGObjectBase opWithThisHash;
                                            if ( SKGObjectBase::getObject ( m_importer->getDocument(), "operation", "t_imported IN ('Y','P') AND t_import_id='"+QString ( hash.toHex() ) +'\'', opWithThisHash ).isSucceeded() )*/

        //Step 8 - recurrent
        if (!err) {
            SKGObjectBase::SKGListSKGObjectBase ListRecurrentOperations;
            err = docOrigin.getObjects("v_recurrentoperation", "", ListRecurrentOperations);
            if (!err) {
                int nb = ListRecurrentOperations.count();
                err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import scheduled operations"), nb);
                for (int i = 0; !err && i < nb; ++i) {
                    SKGRecurrentOperationObject recuOrigin = ListRecurrentOperations.at(i);
                    SKGRecurrentOperationObject recu = recuOrigin.cloneInto(m_importer->getDocument());

                    SKGOperationObject opOrig;
                    err = recuOrigin.getParentOperation(opOrig);
                    if (!err) err = recu.setParentOperation(mapOriginNew[opOrig.getUniqueID()]);
                    if (!err) err = recu.save(false);    //Save only

                    //Duplicate properties
                    if (!err) err = copyParameters(recuOrigin, recu);

                    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(8);

        //Step 9 - nodes
        if (!err) {
            SKGObjectBase::SKGListSKGObjectBase listNodes;
            err = docOrigin.getObjects("v_node", "", listNodes);
            if (!err) {
                int nb = listNodes.count();
                err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import bookmarks"), nb);
                for (int i = 0; !err && i < nb; ++i) {
                    SKGNodeObject nodeOrigin = listNodes.at(i);

                    SKGNodeObject node;
                    err = SKGNodeObject::createPathNode(m_importer->getDocument(), i18n("Imported bookmarks") % OBJECTSEPARATOR % nodeOrigin.getFullName(), node);
                    if (!err) err = node.setData(nodeOrigin.getData());
                    if (!err) err = node.setOrder(nodeOrigin.getOrder());
                    if (!err) err = node.setAutoStart(nodeOrigin.isAutoStart());
                    if (!err) err = node.save(true, false);
                    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(9);

        //Step 10 - interest
        if (!err) {
            SKGObjectBase::SKGListSKGObjectBase listInterests;
            err = docOrigin.getObjects("v_interest", "", listInterests);
            if (!err) {
                int nb = listInterests.count();
                err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import interests"), nb);
                for (int i = 0; !err && i < nb; ++i) {
                    SKGInterestObject interestOrigin = listInterests.at(i);
                    SKGInterestObject interest = interestOrigin.cloneInto(m_importer->getDocument());
                    if (!err) {
                        SKGAccountObject actOrig;
                        err = interestOrigin.getAccount(actOrig);
                        if (!err) err = interest.setAccount(mapOriginNew[actOrig.getUniqueID()]);
                    }
                    if (!err) err = interest.save();

                    //Duplicate properties
                    if (!err) err = copyParameters(interestOrigin, interest);

                    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(10);

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

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



bool SKGImportPluginSkg::isExportPossible()
{
    SKGTRACEIN(10, "SKGImportPluginCsv::isExportPossible");
    return isImportPossible();
}

SKGError SKGImportPluginSkg::exportFile()
{
    if (!m_importer) return SKGError(ERR_ABORT, i18nc("Error message", "Invalid parameters"));
    SKGError err;
    SKGTRACEINRC(2, "SKGImportPluginSkg::exportFile", err);
    QString iFileName = m_importer->getFileName();
    SKGTRACEL(10) << "Input filename=" << iFileName << endl;

    QString tempFile = m_importer->getDocument()->getCurrentTemporaryFile();
    if (m_importer->getDocument()->getCurrentFileName().isEmpty()) {
        //The database is only in memory
        KIO::NetAccess::del(KUrl(tempFile), NULL);
        QSqlDatabase* fileDb = new QSqlDatabase(QSqlDatabase::addDatabase("QSQLITE", tempFile));
        fileDb->setDatabaseName(tempFile);
        if (!fileDb->open()) {
            //Set error message
            QSqlError sqlErr = fileDb->lastError();
            err = SKGError(SQLLITEERROR + sqlErr.number(), sqlErr.text());
        } else {
            m_importer->getDocument()->getDatabase()->commit();
            if (!err) err = SKGServices::copySqliteDatabase(fileDb, m_importer->getDocument()->getDatabase(), false);
            m_importer->getDocument()->getDatabase()->transaction();
        }

        fileDb->close();
        delete fileDb;
        QSqlDatabase::removeDatabase(tempFile);
    }

    //Copy file to file
    QFile::remove(iFileName);
    if (QFileInfo(m_importer->getFileName()).suffix().toUpper() == "SQLITE") {
        if (!KIO::NetAccess::file_copy(tempFile, iFileName)) {
            //Set error message
            err.setReturnCode(ERR_FAIL);
            err.setMessage(i18nc("An error message", "Creation file '%1' failed", iFileName));
        }
    } else {
        err = SKGServices::cryptFile(tempFile, iFileName, "", true, m_importer->getDocument()->getDocumentHeader());
        if (!err) {
            SKGDocumentBank doc;
            err = doc.load(iFileName);
            if (!err) err = doc.removeAllTransactions();
            if (!err) err = doc.save();
        }
    }

    if (m_importer->getDocument()->getCurrentFileName().isEmpty())  KIO::NetAccess::del(KUrl(tempFile), NULL);

    return err;
}

SKGError SKGImportPluginSkg::copyParameters(const SKGObjectBase& iFrom, const SKGObjectBase& iTo)
{
    SKGError err;

    SKGObjectBase::SKGListSKGObjectBase params;
    err = iFrom.getDocument()->getObjects("parameters", "t_uuid_parent='" % SKGServices::stringToSqlString(iFrom.getUniqueID()) % '\'', params);
    if (!err) {
        int nb = params.count();
        SKGDocument* documentTarget = (SKGDocument*) iTo.getDocument();
        for (int i = 0; !err && i < nb; ++i) {
            SKGObjectBase orig = params.at(i);
            SKGObjectBase param = orig.cloneInto(documentTarget);
            err = param.setAttribute("t_uuid_parent", iTo.getUniqueID());
            if (!err) err = param.save(true, false);
        }
    }
    return err;
}

QString SKGImportPluginSkg::getMimeTypeFilter() const
{
    return "*.skg|" % i18nc("A file format", "Skrooge document") % '\n' %
           "*.sqlite|" % i18nc("A file format", "Sqlite document");
}

#include "skgimportpluginskg.moc"
