/***************************************************************************
 *   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 search and process operations
 *
 * @author Stephane MANKOWSKI
 */
#include "skgsearchplugin.h"

#include <KActionCollection>
#include <KStandardAction>
#include <KAboutData>
#include <KGenericFactory>

#include "skgsearchpluginwidget.h"
#include "skgsearch_settings.h"
#include "skgtraces.h"
#include "skgerror.h"
#include "skgruleobject.h"
#include "skgmainpanel.h"
#include "skgtransactionmng.h"
#include "skgalarmboardwidget.h"
#include "skgdocumentbank.h"
#include "skgcategoryobject.h"

/**
 * This plugin factory.
 */
K_PLUGIN_FACTORY(SKGSearchPluginFactory, registerPlugin<SKGSearchPlugin>();)
/**
 * This plugin export.
 */
K_EXPORT_PLUGIN(SKGSearchPluginFactory("skrooge_search", "skrooge_search"))

SKGSearchPlugin::SKGSearchPlugin(QWidget* iWidget, QObject* iParent, const QVariantList& /*iArg*/) : SKGInterfacePlugin(iParent)
{
    Q_UNUSED(iWidget);
    SKGTRACEINFUNC(10);
    m_timer.setSingleShot(true);
    connect(&m_timer, SIGNAL(timeout()), this, SLOT(raiseAlarms()), Qt::QueuedConnection);
}

SKGSearchPlugin::~SKGSearchPlugin()
{
    SKGTRACEINFUNC(10);
    m_currentBankDocument = NULL;
    m_executeAll = NULL;
    m_executeImported = NULL;
    m_executeNotValidated = NULL;
    m_searchAction = NULL;
}

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

QString SKGSearchPlugin::getDashboardWidgetTitle(int iIndex)
{
    Q_UNUSED(iIndex);
    return i18nc("Noun, alarms", "Alarms");
}

SKGBoardWidget* SKGSearchPlugin::getDashboardWidget(int iIndex)
{
    Q_UNUSED(iIndex);
    return new SKGAlarmBoardWidget(m_currentBankDocument);
}

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

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

    // Create yours actions here
    // Execute on all operation
    m_executeAll = new KAction(KIcon("system-run"), i18nc("Verb, action to execute", "Execute on all operations"), this);
    connect(m_executeAll, SIGNAL(triggered(bool)), SLOT(execute()));
    registerGlobalAction("execute_all", m_executeAll);

    // Execute on imported operation
    {
        QStringList overlaycsv;
        overlaycsv.push_back("document-import");
        m_executeImported = new KAction(KIcon("system-run", NULL, overlaycsv), i18nc("Verb, action to execute", "Execute on imported operations"), this);
        connect(m_executeImported, SIGNAL(triggered(bool)), SLOT(execute()));
        registerGlobalAction("execute_imported", m_executeImported);
    }

    // Execute on not validated
    {
        QStringList overlaycsv;
        overlaycsv.push_back("dialog-ok-apply");
        m_executeNotValidated = new KAction(KIcon("system-run", NULL, overlaycsv), i18nc("Verb, action to execute", "Execute on not validated operations"), this);
        connect(m_executeNotValidated, SIGNAL(triggered(bool)), SLOT(execute()));
        registerGlobalAction("execute_not_validated", m_executeNotValidated);
    }

    // Search
    m_searchAction = actionCollection()->addAction(KStandardAction::Find, "edit_find", this, SLOT(find()));
    registerGlobalAction("edit_find", m_searchAction);

    return true;
}

void SKGSearchPlugin::refresh()
{
    SKGTRACEINFUNC(10);
    if (SKGMainPanel::getMainPanel()) {
        if (SKGMainPanel::getMainPanel()) {
            bool test = (SKGMainPanel::getMainPanel()->getSelectedObjects().count() > 0);
            if (m_executeImported) {
                m_executeImported->setEnabled(test);
            }
            if (m_executeAll) {
                m_executeAll->setEnabled(test);
            }
            if (m_executeNotValidated) {
                m_executeNotValidated->setEnabled(test);
            }
        }

        // Start alarm
        if (m_currentBankDocument && m_currentBankDocument->getDatabase() != NULL) {
            QString doc_id = m_currentBankDocument->getUniqueIdentifier();
            if (m_docUniqueIdentifier != doc_id) {
                m_docUniqueIdentifier = doc_id;

                raiseAlarms();
            }
        }
    }
}

void SKGSearchPlugin::raiseAlarms()
{
    if (m_currentBankDocument) {
        SKGObjectBase::SKGListSKGObjectBase rules;
        SKGError err = m_currentBankDocument->getObjects("v_rule", "t_action_type='A' ORDER BY i_ORDER", rules);
        int nb = rules.count();
        if (!err && nb) {
            for (int i = 0; !err && i < nb; ++i) {
                SKGRuleObject rule(rules.at(i));
                rule.execute();
            }
        }

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

        m_timer.start(skgsearch_settings::alarm_frequency() * 60 * 1000);
    }
}

void SKGSearchPlugin::execute()
{
    SKGError err;
    SKGTRACEINFUNCRC(1, err);

    // Get rules
    SKGObjectBase::SKGListSKGObjectBase rules = SKGMainPanel::getMainPanel()->getSelectedObjects();

    int nb = rules.count();
    if (m_currentBankDocument) {
        SKGBEGINPROGRESSTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Process execution"), err, nb);
        SKGRuleObject::ProcessMode mode = SKGRuleObject::ALL;
        if (sender() == m_executeImported) {
            mode = SKGRuleObject::IMPORTED;
        } else if (sender() == m_executeNotValidated) {
            mode = SKGRuleObject::IMPORTEDNOTVALIDATE;
        }
        for (int i = 0; !err && i < nb; ++i) {
            SKGRuleObject rule(rules.at(i));
            err = rule.execute(mode);
            IFOKDO(err, m_currentBankDocument->stepForward(i + 1))
        }
    }

    // status bar
    IFOKDO(err, SKGError(0, i18nc("Successful message after an user action", "Process executed")))
    else {
        err.addError(ERR_FAIL, i18nc("Error message",  "Process execution failed"));
    }

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

void SKGSearchPlugin::find()
{
    SKGError err;
    SKGTRACEINFUNCRC(10, err);
    if (SKGMainPanel::getMainPanel()) {
        SKGObjectBase::SKGListSKGObjectBase selection = SKGMainPanel::getMainPanel()->getSelectedObjects();

        QString xmlsearchcondition;
        int nb = selection.count();
        if (nb > 0) {
            QString table = selection.at(0).getRealTable();
            if (table == "account") {
                QDomDocument doc("SKGML");
                QDomElement element = doc.createElement("element");
                doc.appendChild(element);
                for (int i = 0; i < nb; ++i) {
                    QDomElement elementLine = doc.createElement("element");
                    element.appendChild(elementLine);

                    QDomElement elementElement = doc.createElement("element");
                    elementLine.appendChild(elementElement);

                    elementElement.setAttribute("attribute", "t_ACCOUNT");
                    elementElement.setAttribute("operator", "#ATT#='#V1S#'");
                    elementElement.setAttribute("value", selection.at(i).getAttribute("t_name"));
                }
                xmlsearchcondition = doc.toString();
            } else if (table == "category") {
                QDomDocument doc("SKGML");
                QDomElement element = doc.createElement("element");
                doc.appendChild(element);
                for (int i = 0; i < nb; ++i) {
                    QDomElement elementLine = doc.createElement("element");
                    element.appendChild(elementLine);

                    QDomElement elementElement = doc.createElement("element");
                    elementLine.appendChild(elementElement);

                    elementElement.setAttribute("attribute", "t_REALCATEGORY");
                    elementElement.setAttribute("operator", "#ATT#='#V1S#'");
                    SKGCategoryObject cat(selection.at(i));
                    elementElement.setAttribute("value", cat.getFullName());
                }
                xmlsearchcondition = doc.toString();
            } else if (table == "refund") {
                QDomDocument doc("SKGML");
                QDomElement element = doc.createElement("element");
                doc.appendChild(element);
                for (int i = 0; i < nb; ++i) {
                    QDomElement elementLine = doc.createElement("element");
                    element.appendChild(elementLine);

                    QDomElement elementElement = doc.createElement("element");
                    elementLine.appendChild(elementElement);

                    elementElement.setAttribute("attribute", "t_REALREFUND");
                    elementElement.setAttribute("operator", "#ATT#='#V1S#'");
                    elementElement.setAttribute("value", selection.at(i).getAttribute("t_name"));
                }
                xmlsearchcondition = doc.toString();
            } else if (table == "payee") {
                QDomDocument doc("SKGML");
                QDomElement element = doc.createElement("element");
                doc.appendChild(element);
                for (int i = 0; i < nb; ++i) {
                    QDomElement elementLine = doc.createElement("element");
                    element.appendChild(elementLine);

                    QDomElement elementElement = doc.createElement("element");
                    elementLine.appendChild(elementElement);

                    elementElement.setAttribute("attribute", "t_PAYEE");
                    elementElement.setAttribute("operator", "#ATT#='#V1S#'");
                    elementElement.setAttribute("value", selection.at(i).getAttribute("t_name"));
                }
                xmlsearchcondition = doc.toString();
            } else if (table == "operation" || table == "suboperation") {
                QStringList attributeForQuery;
                attributeForQuery << "d_date" << "i_number" << "t_mode" << "t_PAYEE" << "t_comment" << "t_REALCATEGORY" << "t_status" << "t_bookmarked" << "t_imported" << "t_ACCOUNT" << "f_REALCURRENTAMOUNT" << "t_REALREFUND";

                QDomDocument doc("SKGML");
                QDomElement element = doc.createElement("element");
                doc.appendChild(element);
                for (int i = 0; i < nb; ++i) {
                    QDomElement elementLine = doc.createElement("element");
                    element.appendChild(elementLine);

                    for (int j = 0; j < attributeForQuery.count(); ++j) {
                        QString att = attributeForQuery.at(j);
                        QString attRead = (att == "t_REALCATEGORY" ? "t_CATEGORY" : (att == "f_REALCURRENTAMOUNT" ? "f_CURRENTAMOUNT" : (att == "t_REALREFUND" ? "t_REFUND" : att)));

                        SKGObjectBase op(selection.at(i).getDocument(), "v_operation_display_all", selection.at(i).getID());
                        op.load();

                        QString val = op.getAttribute(attRead);
                        if (!val.isEmpty() && !(att == "i_number" && val == "0")) {
                            QDomElement elementElement = doc.createElement("element");
                            elementLine.appendChild(elementElement);

                            elementElement.setAttribute("attribute", att);
                            elementElement.setAttribute("operator", att.startsWith(QLatin1String("f_")) || att.startsWith(QLatin1String("i_")) ? "#ATT#=#V1#" : "#ATT#='#V1S#'");
                            elementElement.setAttribute("value", val);
                        }
                    }
                }
                xmlsearchcondition = doc.toString();
            }
        }

        // Call search plugin
        SKGMainPanel::getMainPanel()->openPage("skg://skrooge_search_plugin/?currentPage=0&xmlsearchcondition=" % SKGServices::encodeForUrl(xmlsearchcondition));
    }
}

SKGTabPage* SKGSearchPlugin::getWidget()
{
    SKGTRACEINFUNC(10);
    return new SKGSearchPluginWidget(m_currentBankDocument);
}

QWidget* SKGSearchPlugin::getPreferenceWidget()
{
    SKGTRACEINFUNC(10);
    QWidget* w = new QWidget();
    ui.setupUi(w);
    return w;
}

KConfigSkeleton* SKGSearchPlugin::getPreferenceSkeleton()
{
    return skgsearch_settings::self();
}

QString SKGSearchPlugin::title() const
{
    return i18nc("Noun", "Search and process");
}

QString SKGSearchPlugin::icon() const
{
    return "edit-find";
}

QString SKGSearchPlugin::toolTip() const
{
    return i18nc("Noun", "Search and process management");
}

int SKGSearchPlugin::getOrder() const
{
    return 35;
}

QStringList SKGSearchPlugin::tips() const
{
    QStringList output;
    output.push_back(i18nc("Description of a tips", "<p>... skrooge can search and automatically process some operations.</p>"));
    output.push_back(i18nc("Description of a tips", "<p>... you can create alarms based on searches.</p>"));
    return output;
}

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

SKGAdviceList SKGSearchPlugin::advice(const QStringList& iIgnoredAdvice)
{
    SKGTRACEINFUNC(10);
    SKGAdviceList output;

    // Alarms
    if (!iIgnoredAdvice.contains("skgsearchplugin_alarm")) {
        SKGObjectBase::SKGListSKGObjectBase rules;
        SKGError err = m_currentBankDocument->getObjects("v_rule", "t_action_type='A' ORDER BY i_ORDER", rules);
        int nb = rules.count();
        if (nb) {
            SKGServices::SKGUnitInfo primary = m_currentBankDocument->getPrimaryUnit();

            for (int i = 0; !err && i < nb; ++i) {
                SKGRuleObject rule(rules.at(i));
                SKGRuleObject::SKGAlarmInfo alarm = rule.getAlarmInfo();
                if (alarm.Raised) {
                    double percent = 100 * alarm.Amount / alarm.Limit;
                    if (percent >= 70) {
                        SKGAdvice ad;
                        ad.setUUID("skgsearchplugin_alarm|" % SKGServices::intToString(rule.getID()));
                        ad.setPriority(percent >= 90 ? 9 : 6);
                        QString msg = alarm.Message.arg(m_currentBankDocument->formatMoney(alarm.Amount, primary, false)).arg(m_currentBankDocument->formatMoney(alarm.Limit, primary, false)).arg(m_currentBankDocument->formatMoney(alarm.Amount - alarm.Limit, primary, false));
                        ad.setShortMessage(msg);
                        ad.setLongMessage(i18nc("Advice on making the best (long)", "Take care to your alarms.<br> %1.", msg));
                        QList<SKGAdvice::SKGAdviceAction> autoCorrections;
                        {
                            SKGAdvice::SKGAdviceAction a;
                            a.Title = i18nc("Advice on making the best (action)", "Open operations corresponding to this alarm");
                            a.IconName = "skg_open";
                            a.IsRecommended = false;
                            autoCorrections.push_back(a);
                        }
                        ad.setAutoCorrections(autoCorrections);
                        output.push_back(ad);
                    }
                }
            }
        }
    }

    return output;
}

SKGError SKGSearchPlugin::executeAdviceCorrection(const QString& iAdviceIdentifier, int iSolution)
{
    if (m_currentBankDocument && iAdviceIdentifier.startsWith(QLatin1String("skgsearchplugin_alarm|"))) {
        // Get parameters
        QString id = iAdviceIdentifier.right(iAdviceIdentifier.length() - 22);
        SKGSearchPluginWidget::open(SKGRuleObject(m_currentBankDocument, SKGServices::stringToInt(id)));
        return SKGError();
    }

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