/***************************************************************************
 *   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
 * A skrooge plugin to manage scheduled operations
 *
 * @author Stephane MANKOWSKI
 */
#include "skgscheduledplugin.h"
#include "skgscheduledpluginwidget.h"
#include "skgscheduledboardwidget.h"
#include "skgscheduled_settings.h"
#include "skgrecurrentoperationobject.h"
#include "skgtraces.h"
#include "skgmainpanel.h"
#include "skgtransactionmng.h"
#include "skgdocumentbank.h"
#include <skgsuboperationobject.h>

#include <kactioncollection.h>
#include <kstandardaction.h>
#include <kaboutdata.h>

/**
 * This plugin factory.
 */
K_PLUGIN_FACTORY(SKGScheduledPluginFactory, registerPlugin<SKGScheduledPlugin>();)
/**
 * This plugin export.
 */
K_EXPORT_PLUGIN(SKGScheduledPluginFactory("skrooge_scheduled", "skrooge_scheduled"))

SKGScheduledPlugin::SKGScheduledPlugin(QObject* iParent, const QVariantList& /*iArg*/) : SKGInterfacePlugin(iParent)
{
    SKGTRACEIN(10, "SKGScheduledPlugin::SKGScheduledPlugin");
}

SKGScheduledPlugin::~SKGScheduledPlugin()
{
    SKGTRACEIN(10, "SKGScheduledPlugin::~SKGScheduledPlugin");
    m_currentBankDocument = NULL;
    m_scheduleOperationAction = NULL;
}

bool SKGScheduledPlugin::setupActions(SKGDocument* iDocument, const QStringList& iArgument)
{
    SKGTRACEIN(10, "SKGScheduledPlugin::setupActions");
    Q_UNUSED(iArgument);
    m_currentBankDocument = qobject_cast<SKGDocumentBank*>(iDocument);
    if (m_currentBankDocument == NULL) return false;

    setComponentData(KGlobal::mainComponent());
    setXMLFile("../skrooge_scheduled/skrooge_scheduled.rc");

    //Create yours actions here
    m_scheduleOperationAction = new KAction(KIcon("skrooge_schedule"), i18nc("Verb, create a scheduled operation", "Schedule"), this);
    connect(m_scheduleOperationAction, SIGNAL(triggered(bool)), this, SLOT(actionScheduleOperation()));
    actionCollection()->addAction(QLatin1String("schedule_operation"), m_scheduleOperationAction);
    m_scheduleOperationAction->setShortcut(Qt::CTRL + Qt::Key_I);

    if (SKGMainPanel::getMainPanel()) SKGMainPanel::getMainPanel()->registerGlobalAction("schedule_operation", m_scheduleOperationAction);
    return true;
}

void SKGScheduledPlugin::refresh()
{
    SKGTRACEIN(10, "SKGScheduledPlugin::refresh");
    if (SKGMainPanel::getMainPanel()) {
        SKGObjectBase::SKGListSKGObjectBase selection = SKGMainPanel::getMainPanel()->getSelectedObjects();

        if (selection.count() > 0) {
            bool onOperation = (selection.at(0).getRealTable() == "operation" &&  selection.at(0).getTable() != "v_operation_consolidated");
            if (m_scheduleOperationAction) m_scheduleOperationAction->setEnabled(onOperation);
        } else {
            if (m_scheduleOperationAction) m_scheduleOperationAction->setEnabled(false);
        }

        //Automatic insert
        if (m_currentBankDocument && m_currentBankDocument->getDatabase() != NULL) {
            QString doc_id = m_currentBankDocument->getUniqueIdentifier();
            if (m_docUniqueIdentifier != doc_id && m_currentBankDocument->getParameter("SKG_DB_BANK_VERSION") >= "0.5") {
                m_docUniqueIdentifier = doc_id;

                SKGError err;
                //Read Setting
                bool check_on_open = skgscheduled_settings::check_on_open();

                if (check_on_open) {
                    SKGBEGINTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Insert recurrent operations"), err);
                    int nbi = 0;
                    err = SKGRecurrentOperationObject::process(m_currentBankDocument, nbi);
                }
                //Display error
                SKGMainPanel::displayErrorMessage(err);
            }
        }
    }
}

int SKGScheduledPlugin::getNbDashboardWidgets()
{
    return 1;
}

QString SKGScheduledPlugin::getDashboardWidgetTitle(int iIndex)
{
    if (iIndex == 0) return i18nc("Noun, the title of a section", "Scheduled operations");
    return "";
}

SKGWidget* SKGScheduledPlugin::getDashboardWidget(int iIndex)
{
    if (iIndex == 0) return new SKGScheduledBoardWidget(m_currentBankDocument);
    return NULL;
}

SKGTabPage* SKGScheduledPlugin::getWidget()
{
    SKGTRACEIN(10, "SKGScheduledPlugin::getWidget");
    return new SKGScheduledPluginWidget(m_currentBankDocument);
}

QWidget* SKGScheduledPlugin::getPreferenceWidget()
{
    SKGTRACEIN(10, "SKGScheduledPlugin::getPreferenceWidget");
    QWidget* widget = new QWidget();
    ui.setupUi(widget);
    return widget;
}

KConfigSkeleton* SKGScheduledPlugin::getPreferenceSkeleton()
{
    return skgscheduled_settings::self();
}

QString SKGScheduledPlugin::title() const
{
    return i18nc("Noun", "Scheduled operations");
}

QString SKGScheduledPlugin::icon() const
{
    return "chronometer";
}

QString SKGScheduledPlugin::toolTip() const
{
    return i18nc("Noun", "Operations scheduled management");
}

int SKGScheduledPlugin::getOrder() const
{
    return 20;
}

QStringList SKGScheduledPlugin::tips() const
{
    QStringList output;
    output.push_back(i18nc("Description of a tips", "<p>... you can schedule operations or templates.</p>"));
    return output;
}

bool SKGScheduledPlugin::isInPagesChooser() const
{
    return true;
}

bool SKGScheduledPlugin::isEnabled() const
{
    return true;
}

SKGError SKGScheduledPlugin::savePreferences() const
{
    SKGError err;
    if (m_currentBankDocument) {
        //Read Setting
        if (skgscheduled_settings::create_template()) {
            SKGObjectBase::SKGListSKGObjectBase recurrents;
            err = m_currentBankDocument->getObjects("v_recurrentoperation", "(select count(1) from operation where operation.id=rd_operation_id and t_template='N')=1", recurrents);
            int nb = recurrents.count();
            if (nb) {
                SKGBEGINPROGRESSTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Conversion schedule"), err, nb);
                for (int i = 0; !err && i < nb; ++i) {
                    //Migration of existing schedule in template mode
                    SKGRecurrentOperationObject recOp = recurrents.at(i);
                    SKGOperationObject operationObj;

                    if (!err) recOp.getParentOperation(operationObj);

                    SKGOperationObject operationObjOrig = operationObj;
                    if (!err) err = operationObjOrig.duplicate(operationObj , operationObjOrig.getDate(), true);

                    if (!err) err = recOp.setParentOperation(operationObj);
                    if (!err) err = recOp.save();
                    if (!err) err = recOp.load();

                    if (!err) err = operationObjOrig.setAttribute("r_recurrentoperation_id", SKGServices::intToString(recOp.getID()));
                    if (!err) err = operationObjOrig.save();

                    if (!err) err = m_currentBankDocument->stepForward(i + 1);
                }
                if (!err) m_currentBankDocument->sendMessage(i18nc("An information message",  "All scheduled operations have been converted in template"), true);
            }
        }
    }
    return err;
}

SKGError SKGScheduledPlugin::scheduleOperation(const SKGOperationObject& iOperation, SKGRecurrentOperationObject& oRecurrent) const
{
    SKGError err;
    SKGOperationObject operationObjDuplicate = iOperation;
    bool isTemplate = operationObjDuplicate.isTemplate();

    SKGOperationObject operationObjOrig;
    if (!isTemplate && skgscheduled_settings::create_template()) {
        //The selected operation is not a template and settings is set to create one
        operationObjOrig = operationObjDuplicate;
        if (!err) err = operationObjOrig.duplicate(operationObjDuplicate, operationObjOrig.getDate(), true);
        if (!err) m_currentBankDocument->sendMessage(i18nc("An information message",  "A template has been created"), true);
    }

    SKGRecurrentOperationObject recOp;
    err = operationObjDuplicate.addRecurrentOperation(recOp);
    if (!err) err = recOp.warnEnabled(skgscheduled_settings::remind_me());
    if (!err) err = recOp.setWarnDays(skgscheduled_settings::remind_me_days());
    if (!err) err = recOp.autoWriteEnabled(skgscheduled_settings::auto_write());
    if (!err) err = recOp.setAutoWriteDays(skgscheduled_settings::auto_write_days());
    if (!err) err = recOp.timeLimit(skgscheduled_settings::nb_times());
    if (!err) err = recOp.setTimeLimit(skgscheduled_settings::nb_times_val());
    if (!err) err = recOp.setPeriodIncrement(skgscheduled_settings::once_every());
    if (!err) err = recOp.setPeriodUnit((SKGRecurrentOperationObject::PeriodUnit) SKGServices::stringToInt(skgscheduled_settings::once_every_unit()));
    if (!err && !isTemplate) err = recOp.setDate(recOp.getNextDate());
    if (!err) err = recOp.save();
    if (!isTemplate && skgscheduled_settings::create_template()) {
        if (!err) err = recOp.load();
        if (!err) err = operationObjOrig.setAttribute("r_recurrentoperation_id", SKGServices::intToString(recOp.getID()));
        if (!err) err = operationObjOrig.save();
    }

    oRecurrent = recOp;

    return err;
}

void SKGScheduledPlugin::actionScheduleOperation()
{
    SKGError err;
    SKGTRACEINRC(10, "SKGScheduledPlugin::actionScheduleOperation", err);

    //Get Selection
    if (SKGMainPanel::getMainPanel()) {
        SKGObjectBase::SKGListSKGObjectBase selection = SKGMainPanel::getMainPanel()->getSelectedObjects();
        int nb = selection.count();
        if (nb && m_currentBankDocument) {
            QStringList list;
            SKGBEGINPROGRESSTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Operation schedule"), err, nb);
            for (int i = 0; !err && i < nb; ++i) {
                SKGOperationObject operationObj = selection.at(i);
                SKGRecurrentOperationObject rop;
                err = scheduleOperation(operationObj, rop);
                if (!err) err = m_currentBankDocument->stepForward(i + 1);
                list.push_back(operationObj.getUniqueID());
            }

            if (!err) {
                //Call operation plugin
                QDomDocument doc("SKGML");
                QDomElement root = doc.createElement("parameters");
                doc.appendChild(root);

                root.setAttribute("selection", SKGServices::stringsToCsv(list));

                SKGMainPanel::getMainPanel()->openPage(SKGMainPanel::getMainPanel()->getPluginByName("Skrooge scheduled plugin"), -1, doc.toString());
            }
        }

        //status bar
        if (!err)  err = SKGError(0, i18nc("Successful message after an user action", "Operation scheduled."));
        else err.addError(ERR_FAIL, i18nc("Error message",  "Operation schedule failed"));

        //Display error
        SKGMainPanel::displayErrorMessage(err);
    }
}

SKGAdviceList SKGScheduledPlugin::advices() const
{
    SKGTRACEIN(10, "SKGScheduledPlugin::advices");
    SKGAdviceList output;

    //Recurrent operation with the last inserted operation having a different amount
    SKGStringListList result;
    m_currentBankDocument->executeSelectSqliteOrder("SELECT r.id, r.rd_operation_id, r.f_CURRENTAMOUNT, r2.f_CURRENTAMOUNT FROM v_recurrentoperation_display r INNER JOIN (SELECT MAX(d_date), f_CURRENTAMOUNT, r_recurrentoperation_id FROM v_operation_display GROUP BY r_recurrentoperation_id) r2 WHERE r2.r_recurrentoperation_id=r.id AND r.f_CURRENTAMOUNT<>r2.f_CURRENTAMOUNT", result);
    int nb = result.count();
    for (int i = 1; i < nb; ++i) { //Ignore header
        //Get parameters
        QStringList line = result.at(i);
        int idRecu = SKGServices::stringToInt(line.at(0));
        QString idOper = line.at(1);
        QString amountRecu = line.at(2);
        QString amountLastOperation = line.at(3);

        SKGRecurrentOperationObject recu(m_currentBankDocument, idRecu);
        QString name = recu.getDisplayName();

        SKGAdvice ad;
        ad.setUUID("skgscheduledplugin_notuptodate|" % idOper % ';' % amountLastOperation);
        ad.setPriority(4);
        ad.setShortMessage(i18nc("Advice on making the best (short)", "Scheduled operation '%1' not uptodate", name));
        ad.setLongMessage(i18nc("Advice on making the best (long)", "The scheduled operation '%1' does not have the amount of the last inserted operation (%2)", name, amountLastOperation));
        QStringList autoCorrections;
        autoCorrections.push_back(i18nc("Advice on making the best (action)", "Update the operation amount (%1)", amountLastOperation));
        ad.setAutoCorrections(autoCorrections);
        output.push_back(ad);
    }

    //Possible recurrent operations
    m_currentBankDocument->executeSelectSqliteOrder("SELECT op1.id, op1.t_displayname FROM v_operation_displayname op1 WHERE op1.d_date>date('now','-2 month') AND op1.t_TRANSFER='N' AND op1.r_recurrentoperation_id=0 AND op1.d_date<>'0000-00-00' AND EXISTS (SELECT 1 FROM v_operation_tmp1 op2 WHERE op1.rd_account_id=op2.rd_account_id AND op1.r_payee_id=op2.r_payee_id AND op1.rc_unit_id=op2.rc_unit_id AND op1.f_QUANTITY=op2.f_QUANTITY AND op1.d_date=date(op2.d_date, '+1 month')) AND NOT EXISTS (SELECT 1 FROM recurrentoperation ro, v_operation_tmp1 op2  WHERE ro.rd_operation_id=op2.id AND ro.i_period_increment=1 AND ro.t_period_unit='M' AND op1.rd_account_id=op2.rd_account_id AND op1.r_payee_id=op2.r_payee_id AND op1.f_QUANTITY=op2.f_QUANTITY AND op1.rc_unit_id=op2.rc_unit_id)", result);
    nb = result.count();
    for (int i = 1; i < nb; ++i) { //Ignore header
        //Get parameters
        QStringList line = result.at(i);
        QString id = line.at(0);
        QString name = line.at(1);

        SKGAdvice ad;
        ad.setUUID("skgscheduledplugin_possibleschedule|" % id);
        ad.setPriority(4);
        ad.setShortMessage(i18nc("Advice on making the best (short)", "Possible schedule '%1'", name));
        ad.setLongMessage(i18nc("Advice on making the best (long)", "The operation '%1' seems to be regularly created and could be scheduled", name));
        QStringList autoCorrections;
        autoCorrections.push_back(i18nc("Advice on making the best (action)", "Monthly schedule the operation '%1'", name));
        ad.setAutoCorrections(autoCorrections);
        output.push_back(ad);
    }
    return output;
}

SKGError SKGScheduledPlugin::executeAdviceCorrection(const QString& iAdviceIdentifier, int iSolution) const
{
    SKGError err;
    if (m_currentBankDocument && iAdviceIdentifier.startsWith(QLatin1String("skgscheduledplugin_notuptodate|"))) {
        //Get parameters
        QString parameters = iAdviceIdentifier.right(iAdviceIdentifier.length() - 31);
        int pos = parameters.indexOf(';');
        int idOper = SKGServices::stringToInt(parameters.left(pos));
        double amount = SKGServices::stringToDouble(parameters.right(parameters.length() - 1 - pos));

        //Update the operation
        {
            SKGBEGINTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Update scheduled operation"), err);
            SKGOperationObject op(m_currentBankDocument, idOper);
            SKGObjectBase::SKGListSKGObjectBase subOps;
            if (!err) err = op.getSubOperations(subOps);

            if (subOps.count() == 1) {
                //Change the quantity of the sub operation
                SKGSubOperationObject so1 = subOps.at(0);
                if (!err) err = so1.setQuantity(amount);
                if (!err) err = so1.save();
            } else if (subOps.count() >= 1) {
                //Add a split
                SKGSubOperationObject so1;
                if (!err) err = op.addSubOperation(so1);
                if (!err) err = so1.setQuantity(amount - op.getCurrentAmount());
                if (!err) err = so1.save();
            }
        }

        //status bar
        if (!err) err = SKGError(0, i18nc("Successful message after an user action", "Scheduled operation updated."));
        else err.addError(ERR_FAIL, i18nc("Error message", "Update failed"));

        //Display error
        SKGMainPanel::displayErrorMessage(err);

        return err;
    } else if (m_currentBankDocument && iAdviceIdentifier.startsWith(QLatin1String("skgscheduledplugin_possibleschedule|"))) {
        //Get parameters
        int idOper = SKGServices::stringToInt(iAdviceIdentifier.right(iAdviceIdentifier.length() - 36));

        //Update the operation
        {
            SKGBEGINTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Scheduled operation"), err);
            SKGOperationObject op(m_currentBankDocument, idOper);
            SKGRecurrentOperationObject rop;
            err = scheduleOperation(op, rop);
            if (!err) err = rop.setPeriodUnit(SKGRecurrentOperationObject::MONTH);
            if (!err) err = rop.setPeriodIncrement(1);
            if (!err) err = rop.setDate(op.getDate());
            if (!err) err = rop.setDate(rop.getNextDate());
            if (!err) err = rop.save(true, false);
        }

        //status bar
        if (!err) err = SKGError(0, i18nc("Successful message after an user action", "Operation scheduled."));
        else err.addError(ERR_FAIL, i18nc("Error message", "Schedule failed"));

        //Display error
        SKGMainPanel::displayErrorMessage(err);

        return err;
    }


    return SKGInterfacePlugin::executeAdviceCorrection(iAdviceIdentifier, iSolution);
}
#include "skgscheduledplugin.moc"
