/***************************************************************************
 *   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 "skgscheduledpluginwidget.h"

#include <qlineedit.h>

#include <qdom.h>
#include <qheaderview.h>
#include <qevent.h>

#include "skgmainpanel.h"
#include "skgtraces.h"
#include "skgdocumentbank.h"
#include "skgobjectmodel.h"
#include "skgrecurrentoperationobject.h"
#include "skgtransactionmng.h"
#include "skgoperationobject.h"

SKGScheduledPluginWidget::SKGScheduledPluginWidget(SKGDocumentBank* iDocument)
    : SKGTabPage(iDocument)
{
    SKGTRACEINFUNC(1);
    if (!iDocument) {
        return;
    }

    ui.setupUi(this);

    // Define action
    if (SKGMainPanel::getMainPanel()) {
        auto actJumpToOperation = new QAction(SKGServices::fromTheme(QStringLiteral("quickopen")), ui.kJumpBtn->text(), this);
        connect(actJumpToOperation, &QAction::triggered, this, &SKGScheduledPluginWidget::onJumpToTheOperation);
        SKGMainPanel::getMainPanel()->registerGlobalAction(QStringLiteral("edit_reconciliate"), actJumpToOperation, true, QStringList() << QStringLiteral("recurrentoperation"), 1, -1, 160);
    }

    // Set show widget
    ui.kView->getShowWidget()->addGroupedItem(QStringLiteral("all"), i18n("All"), QStringLiteral(""), QStringLiteral(""), QStringLiteral(""), Qt::META + Qt::Key_A);
    ui.kView->getShowWidget()->addGroupedItem(QStringLiteral("ongoing"), i18n("Ongoing"), QStringLiteral("task-ongoing"), QStringLiteral("t_times='N' OR i_nb_times>0"), QStringLiteral(""), Qt::META + Qt::Key_O);
    ui.kView->getShowWidget()->addGroupedItem(QStringLiteral("finished"), i18n("Complete"), QStringLiteral("task-complete"), QStringLiteral("t_times='Y' AND i_nb_times=0"), QStringLiteral(""), Qt::META + Qt::Key_C);
    ui.kView->getShowWidget()->setDefaultState(QStringLiteral("all"));

    ui.kView->setModel(new SKGObjectModel(static_cast<SKGDocumentBank*>(getDocument()), QStringLiteral("v_recurrentoperation_display"), QStringLiteral(""), this, QStringLiteral(""), false));

    connect(ui.kView->getView(), &SKGTreeView::doubleClicked, SKGMainPanel::getMainPanel()->getGlobalAction(QStringLiteral("open")).data(), &QAction::trigger);
    connect(ui.kView->getView(), &SKGTreeView::selectionChangedDelayed, [ = ] {this->onSelectionChanged();});

    // Add Standard KDE Icons to buttons to Operations
    ui.kModifyBtn->setIcon(SKGServices::fromTheme(QStringLiteral("dialog-ok")));
    ui.kProcessBtn->setIcon(SKGServices::fromTheme(QStringLiteral("system-run")));
    connect(ui.kProcessBtn, &QToolButton::clicked, this, &SKGScheduledPluginWidget::onProcess);

    auto processImmediatelyAction = new QAction(SKGServices::fromTheme(QStringLiteral("system-run")), i18nc("User action", "Process immediately"), this);
    connect(processImmediatelyAction, &QAction::triggered, this, &SKGScheduledPluginWidget::onProcessImmediately);
    auto processMenu = new QMenu();
    processMenu->addAction(processImmediatelyAction);
    ui.kProcessBtn->setMenu(processMenu);

    ui.kJumpBtn->setIcon(SKGServices::fromTheme(QStringLiteral("quickopen")));

    ui.kTitle->setPixmap(SKGServices::fromTheme(QStringLiteral("dialog-information")).pixmap(22, 22), KTitleWidget::ImageLeft);
    bool exist = false;
    getDocument()->existObjects(QStringLiteral("recurrentoperation"), QStringLiteral(""), exist);
    ui.kTitle->setVisible(!exist);

    // Set Event filters to catch CTRL+ENTER or SHIFT+ENTER
    this->installEventFilter(this);

    connect(ui.kRemindMe, &QCheckBox::toggled, ui.kRemindMeVal, &QSpinBox::setEnabled);
    connect(ui.kRemindMe, &QCheckBox::toggled, ui.label_3, &QSpinBox::setEnabled);

    connect(ui.kNbTimes, &QCheckBox::toggled, ui.kNbTimesVal, &QSpinBox::setEnabled);
    connect(ui.kNbTimes, &QCheckBox::toggled, ui.kLastOccurenceDate, &QSpinBox::setEnabled);

    connect(ui.kAutoWrite, &QCheckBox::toggled, ui.kAutoWriteVal, &QSpinBox::setEnabled);
    connect(ui.kAutoWrite, &QCheckBox::toggled, ui.label_4, &QSpinBox::setEnabled);

    connect(ui.kModifyBtn, &QPushButton::clicked, this, &SKGScheduledPluginWidget::onUpdate);
    connect(ui.kJumpBtn, &QPushButton::clicked, this, &SKGScheduledPluginWidget::onJumpToTheOperation);

    connect(ui.kOnceEveryUnit, static_cast<void (KComboBox::*)(const QString&)>(&KComboBox::currentIndexChanged), this, &SKGScheduledPluginWidget::onNbOccurrenceChanged);
    connect(ui.kLastOccurenceDate, &SKGDateEdit::dateChanged, this, &SKGScheduledPluginWidget::onNbOccurrenceChanged);
    connect(ui.kNbTimesVal, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &SKGScheduledPluginWidget::onNbOccurrenceChanged);
    connect(ui.kOnceEveryVal, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &SKGScheduledPluginWidget::onNbOccurrenceChanged);
}

SKGScheduledPluginWidget::~SKGScheduledPluginWidget()
{
    SKGTRACEINFUNC(1);
}

bool SKGScheduledPluginWidget::eventFilter(QObject* iObject, QEvent* iEvent)
{
    if (iEvent && iEvent->type() == QEvent::KeyPress) {
        QKeyEvent* keyEvent = static_cast<QKeyEvent*>(iEvent);
        if ((keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter) && iObject == this) {
            if (QApplication::keyboardModifiers() &Qt::ShiftModifier && ui.kModifyBtn->isEnabled()) {
                ui.kModifyBtn->click();
            }
        }
    }

    return SKGTabPage::eventFilter(iObject, iEvent);
}

QString SKGScheduledPluginWidget::getState()
{
    SKGTRACEINFUNC(10);
    QDomDocument doc(QStringLiteral("SKGML"));
    QDomElement root = doc.createElement(QStringLiteral("parameters"));
    doc.appendChild(root);
    root.setAttribute(QStringLiteral("view"), ui.kView->getState());
    return doc.toString();
}

void SKGScheduledPluginWidget::setState(const QString& iState)
{
    SKGTRACEINFUNC(10);
    QDomDocument doc(QStringLiteral("SKGML"));
    doc.setContent(iState);
    QDomElement root = doc.documentElement();
    ui.kView->setState(root.attribute(QStringLiteral("view")));

    QString selection = root.attribute(QStringLiteral("selection"));

    if (!selection.isEmpty()) {
        QStringList uuids = SKGServices::splitCSVLine(selection);
        ui.kView->getView()->selectObjects(uuids, true);  // FIXME // TODO(Stephane MANKOWSKI)
        onSelectionChanged();
    }
}

QString SKGScheduledPluginWidget::getDefaultStateAttribute()
{
    return QStringLiteral("SKGSCHEDULED_DEFAULT_PARAMETERS");
}

QWidget* SKGScheduledPluginWidget::mainWidget()
{
    return ui.kView->getView();
}

void SKGScheduledPluginWidget::onJumpToTheOperation()
{
    // Get selection
    SKGObjectBase::SKGListSKGObjectBase selection = getSelectedObjects();
    if (!selection.isEmpty()) {
        // Build where clause and title
        QString wc = QStringLiteral("id IN (");
        QString title = i18nc("Noun, a list of items", "Operations of the schedule");
        int nb = selection.count();
        for (int i = 0; i < nb; ++i) {
            SKGRecurrentOperationObject recOp(selection.at(i));

            SKGOperationObject op;
            recOp.getParentOperation(op);

            wc += SKGServices::intToString(op.getID());
            if (i < nb - 1) {
                wc += ',';
            }
        }
        wc += ')';

        // Call operation plugin
        SKGMainPanel::getMainPanel()->openPage("skg://skrooge_operation_plugin/?template=Y&title_icon=chronometer&operationTable=v_operation_display_all&title=" %
                                               SKGServices::encodeForUrl(title) % "&operationWhereClause=" % SKGServices::encodeForUrl(wc));
    }
}

void SKGScheduledPluginWidget::onSelectionChanged()
{
    SKGTRACEINFUNC(10);

    int nb = getNbSelectedObjects();
    ui.kModifyBtn->setEnabled(nb);
    ui.kProcessBtn->setEnabled(nb);
    ui.kJumpBtn->setEnabled(nb > 0);

    if (nb == 1) {
        SKGRecurrentOperationObject recOp(ui.kView->getView()->getFirstSelectedObject());

        ui.kFirstOccurenceDate->setDate(recOp.getDate());
        ui.kOnceEveryVal->setValue(recOp.getPeriodIncrement());
        ui.kOnceEveryUnit->setCurrentIndex(static_cast<int>(recOp.getPeriodUnit()));

        ui.kRemindMeVal->setValue(recOp.getWarnDays());
        ui.kRemindMe->setCheckState(recOp.isWarnEnabled() ? Qt::Checked : Qt::Unchecked);

        ui.kAutoWriteVal->setValue(recOp.getAutoWriteDays());
        ui.kAutoWrite->setCheckState(recOp.isAutoWriteEnabled() ? Qt::Checked : Qt::Unchecked);

        ui.kNbTimesVal->setValue(recOp.getTimeLimit());
        ui.kNbTimes->setCheckState(recOp.hasTimeLimit() ? Qt::Checked : Qt::Unchecked);
    } else if (nb > 1) {
        ui.kFirstOccurenceDate->setEditText(NOUPDATE);
    }

    Q_EMIT selectionChanged();
}

void SKGScheduledPluginWidget::onNbOccurrenceChanged()
{
    QDate firstDate = ui.kFirstOccurenceDate->date();
    SKGRecurrentOperationObject::PeriodUnit punit = static_cast<SKGRecurrentOperationObject::PeriodUnit>(ui.kOnceEveryUnit->currentIndex());
    int p = ui.kOnceEveryVal->value();

    if (ui.kLastOccurenceDate == this->sender()) {
        // End date has been modified.
        // We must set the number of occurrence
        QDate lastDate = ui.kLastOccurenceDate->date();
        if (lastDate <= firstDate) {
            ui.kLastOccurenceDate->setDate(firstDate);
            ui.kNbTimesVal->setValue(1);
        } else {
            int nbd = firstDate.daysTo(lastDate);
            if (punit == SKGRecurrentOperationObject::DAY) {
                nbd = nbd / p;
            } else if (punit == SKGRecurrentOperationObject::MONTH) {
                nbd = (lastDate.day() >= firstDate.day() ? 0 : -1) + (lastDate.year() - firstDate.year()) * 12 + (lastDate.month() - firstDate.month());
            } else if (punit == SKGRecurrentOperationObject::YEAR) {
                nbd = nbd / (365 * p);
            }

            bool previous = ui.kNbTimesVal->blockSignals(true);
            ui.kNbTimesVal->setValue(nbd + 1);
            ui.kNbTimesVal->blockSignals(previous);
        }

    } else {
        // We must compute the date
        p *= (ui.kNbTimesVal->value() - 1);
        if (punit == SKGRecurrentOperationObject::DAY) {
            firstDate = firstDate.addDays(p);
        } else if (punit == SKGRecurrentOperationObject::MONTH) {
            firstDate = firstDate.addMonths(p);
        } else if (punit == SKGRecurrentOperationObject::YEAR) {
            firstDate = firstDate.addYears(p);
        }

        bool previous = ui.kLastOccurenceDate->blockSignals(true);
        ui.kLastOccurenceDate->setDate(firstDate);
        ui.kLastOccurenceDate->blockSignals(previous);
    }
}

void SKGScheduledPluginWidget::onUpdate()
{
    SKGError err;
    SKGTRACEINFUNCRC(10, err);
    {
        // Get Selection
        SKGObjectBase::SKGListSKGObjectBase selection = getSelectedObjects();

        int nb = selection.count();
        SKGBEGINPROGRESSTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Recurrent operation update"), err, nb);
        for (int i = 0; !err && i < nb; ++i) {
            // Get the real object, not the object from the view
            SKGRecurrentOperationObject recOp = SKGRecurrentOperationObject(selection.at(i).getDocument(), selection.at(i).getID());

            // Update it
            if (ui.kFirstOccurenceDate->currentText() != NOUPDATE) {
                err = recOp.setDate(ui.kFirstOccurenceDate->date());
            }
            IFOKDO(err, recOp.setPeriodIncrement(ui.kOnceEveryVal->value()))
            IFOKDO(err, recOp.setPeriodUnit(static_cast<SKGRecurrentOperationObject::PeriodUnit>(ui.kOnceEveryUnit->currentIndex())))
            IFOKDO(err, recOp.setWarnDays(ui.kRemindMeVal->value()))
            IFOKDO(err, recOp.warnEnabled(ui.kRemindMe->checkState() == Qt::Checked))
            IFOKDO(err, recOp.setAutoWriteDays(ui.kAutoWriteVal->value()))
            IFOKDO(err, recOp.autoWriteEnabled(ui.kAutoWrite->checkState() == Qt::Checked))
            IFOKDO(err, recOp.setTimeLimit(ui.kNbTimesVal->value()))
            IFOKDO(err, recOp.timeLimit(ui.kNbTimes->checkState() == Qt::Checked))
            IFOKDO(err, recOp.save())

            // Send message
            IFOKDO(err, getDocument()->sendMessage(i18nc("An information to the user", "The recurrent operation '%1' has been updated", recOp.getDisplayName()), SKGDocument::Hidden));

            IFOKDO(err, getDocument()->stepForward(i + 1))
        }
    }
    // status bar
    IFOKDO(err, SKGError(0, i18nc("Successful message after an user action", "Recurrent operation updated.")))
    else {
        err.addError(ERR_FAIL, i18nc("Error message",  "Update failed"));
    }

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

    // Set focus on table
    ui.kView->getView()->setFocus();
}

void SKGScheduledPluginWidget::onProcessImmediately()
{
    onProcess(true);
}

void SKGScheduledPluginWidget::onProcess(bool iImmediately)
{
    SKGError err;
    SKGTRACEINFUNCRC(10, err);
    {
        // Get Selection
        SKGObjectBase::SKGListSKGObjectBase selection = getSelectedObjects();

        int nb = selection.count();
        SKGBEGINPROGRESSTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Insert recurrent operations"), err, nb);
        for (int i = 0; !err && i < nb; ++i) {
            // Get the real object, not the object from the view
            SKGRecurrentOperationObject recOp = SKGRecurrentOperationObject(selection.at(i).getDocument(), selection.at(i).getID());

            // Process it
            int nbi = 0;
            err = recOp.process(nbi, true, (iImmediately ? recOp.getDate() : QDate::currentDate()));
            IFOKDO(err, getDocument()->stepForward(i + 1))
        }
    }
    // status bar
    IFOKDO(err, SKGError(0, i18nc("Successful message after an user action", "Recurrent operation inserted.")))
    else {
        err.addError(ERR_FAIL, i18nc("Error message",  "Insertion failed"));
    }

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




