/***************************************************************************
 *   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 <KLineEdit>

#include <QDomDocument>
#include <QHeaderView>
#include <QKeyEvent>

#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);

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

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

    connect(ui.kView->getView(), SIGNAL(doubleClicked(QModelIndex)), SKGMainPanel::getMainPanel()->getGlobalAction("open"), SLOT(trigger()));
    connect(ui.kView->getView(), SIGNAL(selectionChangedDelayed()), this, SLOT(onSelectionChanged()));

    // Add registered global action in contextual menu
    if (SKGMainPanel::getMainPanel()) {
        ui.kView->getView()->insertGlobalAction();
        ui.kView->getView()->insertGlobalAction("open");

        m_jumpAction = new KAction(KIcon("skg_open"), ui.kJumpBtn->text(), this);
        connect(m_jumpAction, SIGNAL(triggered(bool)), this, SLOT(onJumpToTheOperation()));

        ui.kView->getView()->insertAction(0, m_jumpAction);
        ui.kView->getView()->insertGlobalAction();
        ui.kView->getView()->insertGlobalAction("edit_delete");
        ui.kView->getView()->insertGlobalAction();
    }

    // Add Standard KDE Icons to buttons to Operations
    ui.kModifyBtn->setIcon(KIcon("dialog-ok-apply"));
    ui.kProcessBtn->setIcon(KIcon("system-run"));
    connect(ui.kProcessBtn, SIGNAL(clicked(bool)), SLOT(onProcess()));

    KAction* processImmediatelyAction = new KAction(KIcon("system-run"), i18nc("User action", "Process immediately"), this);
    connect(processImmediatelyAction, SIGNAL(triggered(Qt::MouseButtons,Qt::KeyboardModifiers)), SLOT(onProcessImmediately()));
    KMenu* processMenu = new KMenu();
    processMenu->addAction(processImmediatelyAction);
    ui.kProcessBtn->setMenu(processMenu);

    ui.kJumpBtn->setIcon(KIcon("skg_open"));

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

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

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 false;
}

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

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

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

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

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

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

void SKGScheduledPluginWidget::onJumpToTheOperation()
{
    // Get selection
    SKGObjectBase::SKGListSKGObjectBase selection = getSelectedObjects();
    if (selection.count() > 0) {
        // Build where clause and title
        QString wc = "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.kFirstOccurenceDate->setEnabled(nb <= 1);
    ui.kModifyBtn->setEnabled(nb);
    ui.kProcessBtn->setEnabled(nb);
    ui.kJumpBtn->setEnabled(nb > 0);
    m_jumpAction->setEnabled(nb == 1);

    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);
    }

    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 (nb == 1) {
                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())

            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);
}

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);
}

#include "skgscheduledpluginwidget.moc"


