/***************************************************************************
 *   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 operation management.
 *
 * @author Stephane MANKOWSKI / Guillaume DE BURE
 */
#include "skgoperationboardwidget.h"

#include <qdom.h>
#include <qparallelanimationgroup.h>
#include <qpropertyanimation.h>

#include <qaction.h>
#include <qwidgetaction.h>

#include "skgdocumentbank.h"
#include "skgreportbank.h"
#include "skgtraces.h"
#include "skgservices.h"
#include "skgobjectbase.h"
#include "skgmainpanel.h"
#include "skgperiodedit.h"

SKGOperationBoardWidget::SKGOperationBoardWidget(SKGDocument* iDocument)
    : SKGBoardWidget(iDocument, i18nc("Dashboard widget title", "Income & Expenditure")),
      m_periodEdit1(nullptr), m_periodEdit2(nullptr), m_anim(nullptr)
{
    SKGTRACEINFUNC(10);

    m_timer.setSingleShot(true);
    connect(&m_timer, &QTimer::timeout, this, [ = ]() {
        this->dataModified();
    }, Qt::QueuedConnection);

    // Initialize animation group
    m_anim = new QParallelAnimationGroup(this);

    auto f = new QFrame();
    ui.setupUi(f);
    setMainWidget(f);

    ui.kIncomeLabel->setText("<a href=\"IC\">" % ui.kIncomeLabel->text() % "</a>");
    ui.kExpenseLabel->setText("<a href=\"EC\">" % ui.kExpenseLabel->text() % "</a>");
    ui.kSavingLabel->setText("<a href=\"SC\">" % ui.kSavingLabel->text() % "</a>");
    ui.kIncome_previousLabel->setText("<a href=\"IP\">" % ui.kIncome_previousLabel->text() % "</a>");
    ui.kExpense_previousLabel->setText("<a href=\"EP\">" % ui.kExpense_previousLabel->text() % "</a>");
    ui.kSaving_previousLabel->setText("<a href=\"SP\">" % ui.kSaving_previousLabel->text() % "</a>");

    connect(ui.kIncomeLabel, &QLabel::linkActivated, this, &SKGOperationBoardWidget::onOpen);
    connect(ui.kExpenseLabel, &QLabel::linkActivated, this, &SKGOperationBoardWidget::onOpen);
    connect(ui.kSavingLabel, &QLabel::linkActivated, this, &SKGOperationBoardWidget::onOpen);
    connect(ui.kIncome_previousLabel, &QLabel::linkActivated, this, &SKGOperationBoardWidget::onOpen);
    connect(ui.kExpense_previousLabel, &QLabel::linkActivated, this, &SKGOperationBoardWidget::onOpen);
    connect(ui.kSaving_previousLabel, &QLabel::linkActivated, this, &SKGOperationBoardWidget::onOpen);

    // Create menu
    setContextMenuPolicy(Qt::ActionsContextMenu);

    QStringList overlayopen;
    overlayopen.push_back(QStringLiteral("quickopen"));
    m_menuOpen = new QAction(SKGServices::fromTheme(QStringLiteral("view-statistics"), overlayopen), i18nc("Verb", "Open report..."), this);
    connect(m_menuOpen, &QAction::triggered, SKGMainPanel::getMainPanel(), [ = ]() {
        SKGMainPanel::getMainPanel()->SKGMainPanel::openPage();
    });
    addAction(m_menuOpen);

    {
        auto sep = new QAction(this);
        sep->setSeparator(true);
        addAction(sep);
    }

    m_menuTransfert = new QAction(i18nc("Noun, a type of operations", "Transfers"), this);
    m_menuTransfert->setCheckable(true);
    m_menuTransfert->setChecked(false);
    connect(m_menuTransfert, &QAction::triggered, this, &SKGOperationBoardWidget::refreshDelayed);
    addAction(m_menuTransfert);

    m_menuTracked = new QAction(i18nc("Noun, a type of operations", "Tracked"), this);
    m_menuTracked->setCheckable(true);
    m_menuTracked->setChecked(true);
    connect(m_menuTracked, &QAction::triggered, this, &SKGOperationBoardWidget::refreshDelayed);
    addAction(m_menuTracked);

    m_menuSuboperation = new QAction(i18nc("Noun, a type of operations", "On suboperations"), this);
    m_menuSuboperation->setCheckable(true);
    m_menuSuboperation->setChecked(false);
    connect(m_menuSuboperation, &QAction::triggered, this, &SKGOperationBoardWidget::refreshDelayed);
    addAction(m_menuSuboperation);

    {
        auto sep = new QAction(this);
        sep->setSeparator(true);
        addAction(sep);
    }

    m_periodEdit1 = new SKGPeriodEdit(this, true);
    m_periodEdit1->setObjectName(QStringLiteral("m_periodEdit1"));
    {
        // Set default
        QDomDocument doc(QStringLiteral("SKGML"));
        QDomElement root = doc.createElement(QStringLiteral("parameters"));
        doc.appendChild(root);
        root.setAttribute(QStringLiteral("period"), SKGServices::intToString(static_cast<int>(SKGPeriodEdit::CURRENT)));
        m_periodEdit1->setState(doc.toString());

        // Add widget in menu
        auto periodEditWidget = new QWidgetAction(this);
        periodEditWidget->setObjectName(QStringLiteral("m_periodEdit1Action"));
        periodEditWidget->setDefaultWidget(m_periodEdit1);

        addAction(periodEditWidget);
    }

    {
        auto sep = new QAction(this);
        sep->setSeparator(true);
        addAction(sep);
    }

    m_periodEdit2 = new SKGPeriodEdit(this, true);
    m_periodEdit2->setObjectName(QStringLiteral("m_periodEdit2"));
    {
        // Set default
        QDomDocument doc(QStringLiteral("SKGML"));
        QDomElement root = doc.createElement(QStringLiteral("parameters"));
        doc.appendChild(root);
        root.setAttribute(QStringLiteral("period"), SKGServices::intToString(static_cast<int>(SKGPeriodEdit::PREVIOUS)));
        m_periodEdit2->setState(doc.toString());

        // Add widget in menu
        auto periodEditWidget = new QWidgetAction(this);
        periodEditWidget->setObjectName(QStringLiteral("m_periodEdit2Action"));
        periodEditWidget->setDefaultWidget(m_periodEdit2);

        addAction(periodEditWidget);
    }

    // Refresh
    connect(m_periodEdit1, &SKGPeriodEdit::changed, this, &SKGOperationBoardWidget::refreshDelayed, Qt::QueuedConnection);
    connect(m_periodEdit2, &SKGPeriodEdit::changed, this, &SKGOperationBoardWidget::refreshDelayed, Qt::QueuedConnection);
    connect(getDocument(), &SKGDocument::tableModified, this, &SKGOperationBoardWidget::dataModified, Qt::QueuedConnection);
}

SKGOperationBoardWidget::~SKGOperationBoardWidget()
{
    SKGTRACEINFUNC(10);
    m_menuTransfert = nullptr;
    m_menuTracked = nullptr;
    m_anim = nullptr;
}

QString SKGOperationBoardWidget::getState()
{
    QDomDocument doc(QStringLiteral("SKGML"));
    doc.setContent(SKGBoardWidget::getState());
    QDomElement root = doc.documentElement();

    root.setAttribute(QStringLiteral("menuTransfert"), m_menuTransfert && m_menuTransfert->isChecked() ? QStringLiteral("Y") : QStringLiteral("N"));
    root.setAttribute(QStringLiteral("menuTracked"), m_menuTracked && m_menuTracked->isChecked() ? QStringLiteral("Y") : QStringLiteral("N"));
    root.setAttribute(QStringLiteral("menuSuboperation"), m_menuSuboperation && m_menuSuboperation->isChecked() ? QStringLiteral("Y") : QStringLiteral("N"));
    root.setAttribute(QStringLiteral("period1"), m_periodEdit1 ? m_periodEdit1->getState() : QStringLiteral(""));
    root.setAttribute(QStringLiteral("period2"), m_periodEdit2 ? m_periodEdit2->getState() : QStringLiteral(""));
    return doc.toString();
}

void SKGOperationBoardWidget::setState(const QString& iState)
{
    SKGBoardWidget::setState(iState);

    QDomDocument doc(QStringLiteral("SKGML"));
    doc.setContent(iState);
    QDomElement root = doc.documentElement();
    if (m_menuTransfert) {
        m_menuTransfert->setChecked(root.attribute(QStringLiteral("menuTransfert")) == QStringLiteral("Y"));
    }
    if (m_menuTracked) {
        m_menuTracked->setChecked(root.attribute(QStringLiteral("menuTracked")) != QStringLiteral("N"));
    }
    if (m_menuSuboperation) {
        m_menuSuboperation->setChecked(root.attribute(QStringLiteral("menuSuboperation")) == QStringLiteral("Y"));
    }
    QString period1 = root.attribute(QStringLiteral("period1"));
    if (m_periodEdit1 && !period1.isEmpty()) {
        m_periodEdit1->setState(period1);
    }
    QString period2 = root.attribute(QStringLiteral("period2"));
    if (m_periodEdit2 && !period2.isEmpty()) {
        m_periodEdit2->setState(period2);
    }
    refreshDelayed();
}

void SKGOperationBoardWidget::onOpen(const QString& iLink)
{
    // Call operation plugin
    QString wc;
    if (iLink.endsWith(QLatin1String("C"))) {
        wc = m_periodEdit1->getWhereClause() % " AND t_TYPEACCOUNT<>'L'";
    } else {
        wc = m_periodEdit2->getWhereClause() % " AND t_TYPEACCOUNT<>'L'";
    }

    if (iLink.startsWith(QLatin1String("I"))) {
        wc = wc % " AND t_TYPEEXPENSE='+'";
    } else if (iLink.startsWith(QLatin1String("E"))) {
        wc = wc % " AND t_TYPEEXPENSE='-'";
    }

    wc = wc % (m_menuTransfert && m_menuTransfert->isChecked() ? "" : " AND t_TRANSFER='N'") % (m_menuTracked && m_menuTracked->isChecked() ? "" : " AND t_REFUND=''");

    QString title;
    if (iLink == QStringLiteral("IC")) {
        title = i18nc("Title of a list of operations", "Incomes of %1", ui.kLabel->text());
    } else if (iLink == QStringLiteral("EC")) {
        title = i18nc("Title of a list of operations", "Expenses of %1", ui.kLabel->text());
    } else if (iLink == QStringLiteral("SC")) {
        title = i18nc("Title of a list of operations", "Savings of %1", ui.kLabel->text());
    } else if (iLink == QStringLiteral("IP")) {
        title = i18nc("Title of a list of operations", "Incomes of %1", ui.kLabel_previous->text());
    } else if (iLink == QStringLiteral("EP")) {
        title = i18nc("Title of a list of operations", "Expenses of %1", ui.kLabel_previous->text());
    } else if (iLink == QStringLiteral("SP")) {
        title = i18nc("Title of a list of operations", "Savings of %1", ui.kLabel_previous->text());
    }

    bool onSubOperation = (m_menuSuboperation && m_menuSuboperation->isChecked());
    SKGMainPanel::getMainPanel()->openPage("skg://skrooge_operation_plugin/" % QString(onSubOperation ? QStringLiteral("SKGOPERATION_CONSOLIDATED_DEFAULT_PARAMETERS/") : QStringLiteral("")) % "?operationTable=" % SKGServices::encodeForUrl(onSubOperation ? QStringLiteral("v_suboperation_consolidated") : QStringLiteral("v_operation_display")) % "&title_icon=view-financial-list&currentPage=-1&title=" % SKGServices::encodeForUrl(title) %
                                           "&operationWhereClause=" % SKGServices::encodeForUrl(wc));
}

void SKGOperationBoardWidget::refreshDelayed()
{
    m_timer.start(300);
}

void SKGOperationBoardWidget::dataModified(const QString& iTableName, int iIdTransaction)
{
    SKGTRACEINFUNC(10);
    Q_UNUSED(iIdTransaction);
    QString table = (m_menuSuboperation && m_menuSuboperation->isChecked() ? QStringLiteral("v_suboperation_consolidated") : QStringLiteral("v_operation_display"));
    if (m_periodEdit1 && m_periodEdit2 && (iTableName == table || iTableName.isEmpty())) {
        // Set titles
        ui.kLabel->setText(m_periodEdit1->text());
        ui.kLabel_previous->setText(m_periodEdit2->text());

        SKGDocumentBank* doc = qobject_cast<SKGDocumentBank*>(getDocument());
        if (doc) {
            if (m_menuOpen) {
                QString url = QStringLiteral("skg://skrooge_report_plugin/?grouped=")
                              % (m_menuTransfert && m_menuTransfert->isChecked() ? QStringLiteral("Y") : QStringLiteral("N")) % "&transfers="
                              % (m_menuTransfert && m_menuTransfert->isChecked() ? QStringLiteral("Y") : QStringLiteral("N")) % "&tracked="
                              % (m_menuTracked && m_menuTracked->isChecked() ? QStringLiteral("Y") : QStringLiteral("N")) % "&expenses=Y&incomes=Y&lines2=t_TYPEEXPENSENLS&columns=d_DATEMONTH&currentPage=-1" %
                              "&mode=0&interval=3&period=3" %
                              "&tableAndGraphState.graphMode=1&tableAndGraphState.allPositive=Y&tableAndGraphState.show=graph&title=" %
                              SKGServices::encodeForUrl(i18nc("Noun, the title of a section", "Income & Expenditure"));
                m_menuOpen->setData(url);
            }

            SKGServices::SKGUnitInfo primary = doc->getPrimaryUnit();
            SKGServices::SKGUnitInfo secondary = doc->getSecondaryUnit();

            SKGReportBank* bk = qobject_cast< SKGReportBank* >(doc->getReport());
            if (bk) {
                QVariantList table2 = bk->getIncomeVsExpenditure(m_menuSuboperation && m_menuSuboperation->isChecked(),
                                      m_menuTransfert && m_menuTransfert->isChecked(),
                                      m_menuTracked && m_menuTracked->isChecked(),
                                      m_periodEdit1->getWhereClause(),
                                      m_periodEdit2->getWhereClause());
                delete bk;

                QVariantList l1 = table2.value(1).toList();
                QVariantList l2 = table2.value(2).toList();
                double income_previous_month = l1.value(2).toDouble();
                double expense_previous_month = l2.value(2).toDouble();
                double income_month = l1.value(3).toDouble();
                double expense_month = l2.value(3).toDouble();

                // Set Maximum
                int max = qMax(income_previous_month, qMax(expense_previous_month, qMax(income_month, expense_month)));
                if (max == 0) {
                    max = 100.0;
                }
                ui.kIncome->setMaximum(max);
                ui.kIncome_previous->setMaximum(max);
                ui.kExpense->setMaximum(max);
                ui.kExpense_previous->setMaximum(max);
                ui.kSaving->setMaximum(max);
                ui.kSaving_previous->setMaximum(max);

                // Set texts and tool tips
                ui.kIncome->setFormat(doc->formatMoney(income_month, primary, false));
                ui.kIncome_previous->setFormat(doc->formatMoney(income_previous_month, primary, false));
                ui.kExpense->setFormat(doc->formatMoney(expense_month, primary, false));
                ui.kExpense_previous->setFormat(doc->formatMoney(expense_previous_month, primary, false));
                ui.kSaving->setFormat(doc->formatMoney(income_month - expense_month, primary, false));
                ui.kSaving_previous->setFormat(doc->formatMoney(income_previous_month - expense_previous_month, primary, false));
                if (!secondary.Symbol.isEmpty() && secondary.Value) {
                    ui.kIncome->setToolTip(doc->formatMoney(income_month, secondary, false));
                    ui.kIncome_previous->setToolTip(doc->formatMoney(income_previous_month, secondary, false));
                    ui.kExpense->setToolTip(doc->formatMoney(expense_month, secondary, false));
                    ui.kExpense_previous->setToolTip(doc->formatMoney(expense_previous_month, secondary, false));
                    ui.kSaving->setToolTip(doc->formatMoney(income_month - expense_month, secondary, false));
                    ui.kSaving_previous->setToolTip(doc->formatMoney(income_previous_month - expense_previous_month, secondary, false));
                }

                // Change colors
                ui.kIncome->setLimits(0, 0, max);
                ui.kIncome_previous->setLimits(0, 0, max);
                ui.kExpense->setLimits(max, -1, -1);
                ui.kExpense_previous->setLimits(max, -1, -1);
                ui.kSaving->setLimits(income_month - expense_month < 0 ? max : 0, 0.1 * income_month, max);
                ui.kSaving_previous->setLimits(income_previous_month - expense_previous_month < 0 ? max : 0, 0.1 * income_previous_month, max);

                // Set values
                if (m_anim) {
                    m_anim->clear();
                }
                SKGOperationBoardWidget::setValue(ui.kIncome, income_month);
                SKGOperationBoardWidget::setValue(ui.kIncome_previous, income_previous_month);
                SKGOperationBoardWidget::setValue(ui.kExpense, expense_month);
                SKGOperationBoardWidget::setValue(ui.kExpense_previous, expense_previous_month);
                SKGOperationBoardWidget::setValue(ui.kSaving, qAbs(income_month - expense_month));
                SKGOperationBoardWidget::setValue(ui.kSaving_previous, qAbs(income_previous_month - expense_previous_month));
                if (m_anim) {
                    QTimer::singleShot(1000, Qt::CoarseTimer, m_anim, [ = ]() {
                        m_anim->start();
                    });
                }
            }

            // No widget if no account
            bool exist = false;
            doc->existObjects(QStringLiteral("account"), QStringLiteral(""), exist);
            if (parentWidget()) {
                setVisible(exist);
            }
        }
    }
}

void SKGOperationBoardWidget::setValue(SKGProgressBar* iWidget, double iValue)
{
    if (m_anim) {
        auto panim = new QPropertyAnimation(iWidget, "value");
        panim->setDuration(1000);
        panim->setStartValue(0);
        panim->setEndValue(static_cast<int>(iValue));
        m_anim->addAnimation(panim);
    } else {
        iWidget->setValue(iValue);
    }
}

