/***************************************************************************
 *   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 <QFile>
#include <qfileinfo.h>
#include <QSqlDatabase>
#include <QSqlError>

#include <klocale.h>
#include <kgenericfactory.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 : m_importer->getFileNameExtension() == "SKG" || m_importer->getFileNameExtension() == "SQLITE");
}

SKGError SKGImportPluginSkg::importFile()
{
    if (!m_importer) return SKGError(ERR_ABORT, i18nc("Error message", "Invalid parameters"));
    SKGError err;
    SKGTRACEINRC(2, "SKGImportPluginSkg::importFile", err);
    QString lfile = m_importer->getLocalFileName();
    if (lfile.isEmpty()) {
        err.setReturnCode(ERR_INVALIDARG);
        err.setMessage(i18nc("Error message",  "Open file '%1' failed", m_importer->getFileName().prettyUrl()));
    } else {
        SKGDocumentBank docOrigin;
        err = docOrigin.load(lfile, 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 rd_unit_id, 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(SKGPayeeObject(mapOriginNew[payeeOrig.getUniqueID()]));
                        }
                        if (!err) {
                            SKGUnitObject unitOrig;
                            err = operationOrigin.getUnit(unitOrig);
                            if (!err) err = operation.setUnit(SKGUnitObject(mapOriginNew[unitOrig.getUniqueID()]));
                        }
                        if (!err) {
                            SKGOperationObject groupOrig;
                            operationOrigin.getGroupOperation(groupOrig);
                            err = operation.setGroupOperation(SKGOperationObject(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(SKGCategoryObject(mapOriginNew[catOrig.getUniqueID()]));
                            }
                            if (!err) {
                                SKGTrackerObject tracOrig;
                                subopOrigin.getTracker(tracOrig);    //Error not managed
                                if (!err) err = subop.setTracker(SKGTrackerObject(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 % 500 == 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(SKGOperationObject(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(SKGAccountObject(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 tempFile = m_importer->getDocument()->getCurrentTemporaryFile();
    if (m_importer->getDocument()->getCurrentFileName().isEmpty()) {
        //The database is only in memory
        QFile(tempFile).remove();
        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
    QString file = m_importer->getLocalFileName(false);
    QFile::remove(file);
    if (QFileInfo(file).suffix().toUpper() == "SQLITE") {
        if (SKGServices::upload(tempFile, file)) {
            //Set error message
            err.setReturnCode(ERR_FAIL);
            err.setMessage(i18nc("An error message", "Creation file '%1' failed", file));
        }
    } else {
        err = SKGServices::cryptFile(tempFile, file, "", true, m_importer->getDocument()->getDocumentHeader());
        if (!err) {
            SKGDocumentBank doc;
            err = doc.load(file);
            if (!err) err = doc.removeAllTransactions();
            if (!err) err = doc.saveAs(file, true);
        }
    }

    if (m_importer->getDocument()->getCurrentFileName().isEmpty())  QFile(tempFile).remove();

    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"
