/***************************************************************************
 *   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 plugin for advice.
 *
 * @author Stephane MANKOWSKI / Guillaume DE BURE
 */
#include "skgadviceboardwidget.h"

#include <QDomDocument>
#include <QLayoutItem>
#include <QToolButton>
#include <QPushButton>
#include <KMenu>

#include "skgdocumentbank.h"
#include "skgmainpanel.h"
#include "skgtraces.h"
#include "skgservices.h"
#include "skginterfaceplugin.h"
#include "skgtransactionmng.h"

SKGAdviceBoardWidget::SKGAdviceBoardWidget(SKGDocument* iDocument)
    : SKGBoardWidget(iDocument, i18nc("Dashboard widget title", "Advices")), m_maxAdvice(7), m_refreshNeeded(true), m_refresh(NULL)
{
    SKGTRACEINFUNC(10);

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

    QWidget* g = new QWidget(this);
    m_layout = new QFormLayout(g);
    m_layout->setContentsMargins(0, 0, 0, 0);
    m_layout->setObjectName(QString::fromUtf8("layout"));
    m_layout->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow);
    m_layout->setHorizontalSpacing(1);
    m_layout->setVerticalSpacing(1);
    setMainWidget(g);

    // menu
    KAction* menuResetAdvice = new KAction(KIcon("edit-undo"), i18nc("Noun, a user action", "Activate all advice"), this);
    connect(menuResetAdvice, SIGNAL(triggered(bool)), this, SLOT(activateAllAdvice()));
    addAction(menuResetAdvice);

    QAction* sep = new QAction(this);
    sep->setSeparator(true);
    addAction(sep);

    m_menuAuto = new KAction(i18nc("Noun, a type of refresh for advice", "Automatic refresh"), this);
    m_menuAuto->setCheckable(true);
    m_menuAuto->setChecked(true);
    connect(m_menuAuto, SIGNAL(triggered(bool)), this, SLOT(dataModified()));
    addAction(m_menuAuto);

    // Refresh
    connect(getDocument(), SIGNAL(transactionSuccessfullyEnded(int)), this, SLOT(dataModified()), Qt::QueuedConnection);
    connect(SKGMainPanel::getMainPanel(), SIGNAL(currentPageChanged()), this, SLOT(pageChanged()), Qt::QueuedConnection);
    connect(this, SIGNAL(refreshNeeded()), this, SLOT(dataModified()), Qt::QueuedConnection);
}

SKGAdviceBoardWidget::~SKGAdviceBoardWidget()
{
    SKGTRACEINFUNC(10);
    m_menuAuto = NULL;
    m_refresh = NULL;
}

QString SKGAdviceBoardWidget::getState()
{
    QDomDocument doc("SKGML");
    QDomElement root = doc.createElement("parameters");
    doc.appendChild(root);
    root.setAttribute("maxAdvice", SKGServices::intToString(m_maxAdvice));
    root.setAttribute("automatic", (m_menuAuto->isChecked() ? "Y" : "N"));
    return doc.toString();
}

void SKGAdviceBoardWidget::setState(const QString& iState)
{
    QDomDocument doc("SKGML");
    doc.setContent(iState);
    QDomElement root = doc.documentElement();

    QString maxAdviceS = root.attribute("maxAdvice");
    if (maxAdviceS.isEmpty()) {
        maxAdviceS = '7';
    }
    m_maxAdvice = SKGServices::stringToInt(maxAdviceS);

    QString automatic = root.attribute("automatic");
    if (automatic.isEmpty()) {
        automatic = 'Y';
    }

    if (m_menuAuto) {
        bool previous = m_menuAuto->blockSignals(true);
        m_menuAuto->setChecked(automatic == "Y");
        m_menuAuto->blockSignals(previous);
    }

    dataModified(true);
}

void SKGAdviceBoardWidget::pageChanged()
{
    if (m_refreshNeeded) {
        dataModified();
    }
}

void SKGAdviceBoardWidget::dataModifiedForce()
{
    dataModified(true);
}

void SKGAdviceBoardWidget::dataModified(bool iForce)
{
    SKGTRACEINFUNC(10);
    SKGTabPage* page = SKGTabPage::parentTabPage(this);
    if (!iForce && ((page != NULL && page != SKGMainPanel::getMainPanel()->currentPage()) || !m_menuAuto->isChecked())) {
        m_refreshNeeded = true;
        if (m_refresh) {
            m_refresh->show();
        }
        return;
    }
    QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
    m_refreshNeeded = false;

    // Remove all item of the layout
    while (m_layout->count()) {
        QLayoutItem* child = m_layout->takeAt(0);
        if (child) {
            QWidget* w = child->widget();
            delete w;
            delete child;
        }
    }

    // Get list of advice
    double elapse = SKGServices::getMicroTime();
    SKGAdviceList globalAdviceList = SKGMainPanel::getMainPanel()->getAdvice();
    elapse = SKGServices::getMicroTime() - elapse;

    if (elapse > 3000 && m_menuAuto->isChecked()) {
        SKGAdvice ad;
        ad.setUUID("skgadviceboardwidget_verylong");
        ad.setPriority(2);
        ad.setShortMessage(i18nc("Advice on making the best (short)", "Advice are very long to compute"));
        ad.setLongMessage(i18nc("Advice on making the best (long)", "To improve performances, you should switch the widget in 'Manual refresh' (see contextual menu)."));
        globalAdviceList.push_back(ad);
    }

    // Fill layout
    int nbDisplayedAdvice = 0;
    int nb = globalAdviceList.count();
    for (int i = 0; i < m_maxAdvice && i < nb; ++i) {
        // Get advice
        SKGAdvice ad = globalAdviceList.at(i);

        // Create icon
        QString iconName = (ad.getPriority() == -1 ? "dialog-information" : ad.getPriority() >= 8 ? "security-low" : ad.getPriority() <= 4 ? "security-high" : "security-medium");
        QString toolTipString = i18n("<p><b>Priority %1:</b>%2</p>", SKGServices::intToString(ad.getPriority()), ad.getLongMessage());

        // Add ignored action
        QStringList autoCorrections = ad.getAutoCorrections();
        autoCorrections.push_back(i18nc("Dismiss an advice provided", "Dismiss"));
        autoCorrections.push_back(i18nc("Dismiss an advice provided", "Dismiss during current month"));
        autoCorrections.push_back(i18nc("Dismiss an advice provided", "Dismiss this kind"));
        autoCorrections.push_back(i18nc("Dismiss an advice provided", "Dismiss this kind during current month"));

        int nbSolution = autoCorrections.count();

        // Build button
        QStringList overlays;
        overlays.push_back(nbSolution > 2 ? "system-run" : "edit-delete");
        QToolButton*  icon = new QToolButton(this);
        icon->setObjectName(ad.getUUID());
        icon->setIcon(KIcon(iconName, NULL, overlays));
        icon->setIconSize(QSize(22, 22));
        icon->setMaximumSize(QSize(22, 22));
        icon->setCursor(Qt::PointingHandCursor);
        icon->setAutoRaise(true);

        KMenu* menu = new KMenu(this);
        menu->setIcon(icon->icon());
        for (int k = 0; k < nbSolution; ++k) {
            QString actionText = autoCorrections.at(k);
            QAction* action = SKGMainPanel::getMainPanel()->getGlobalAction(QString(actionText).remove("skg://"), false);
            if (action) {
                // This is an action
                QAction* act = menu->addAction(action->icon(), action->text(), SKGMainPanel::getMainPanel(), SLOT(openPage()));
                act->setData(actionText);
            } else {
                // This is a text
                QAction* act = menu->addAction(KIcon(k < nbSolution - 4 ?  "system-run" : "edit-delete"), autoCorrections.at(k), this, SLOT(adviceClicked()));
                if (act) {
                    act->setProperty("id", ad.getUUID());
                    act->setProperty("solution", k < nbSolution - 4 ? k : k - nbSolution);
                }
            }
        }

        icon->setMenu(menu);
        icon->setPopupMode(QToolButton::InstantPopup);

        icon->setToolTip(toolTipString);

        // Create text
        QLabel* label = new QLabel(this);
        label->setText(ad.getShortMessage());
        label->setToolTip(toolTipString);

        // Add them
        m_layout->addRow(icon, label);

        ++nbDisplayedAdvice;
    }

    // Add more
    if (nb > m_maxAdvice) {
        QPushButton*  more = new QPushButton(this);
        more->setIcon(KIcon("arrow-down-double"));
        more->setIconSize(QSize(22, 22));
        more->setMaximumSize(QSize(22, 22));
        more->setCursor(Qt::PointingHandCursor);
        connect(more, SIGNAL(clicked(bool)), this, SLOT(moreAdvice()), Qt::QueuedConnection);

        m_layout->addRow(more, new QLabel(this));
    } else if (nbDisplayedAdvice > 7) {
        // Add less
        QPushButton*  less = new QPushButton(this);
        less->setIcon(KIcon("arrow-up-double"));
        less->setIconSize(QSize(22, 22));
        less->setMaximumSize(QSize(22, 22));
        less->setCursor(Qt::PointingHandCursor);
        connect(less, SIGNAL(clicked(bool)), this, SLOT(lessAdvice()), Qt::QueuedConnection);

        m_layout->addRow(less, new QLabel(this));
    }

    // Add manual refresh button
    m_refresh = new QPushButton(this);
    m_refresh->setIcon(KIcon("view-refresh"));
    m_refresh->setIconSize(QSize(22, 22));
    m_refresh->setMaximumSize(QSize(22, 22));
    m_refresh->setCursor(Qt::PointingHandCursor);
    m_refresh->hide();
    connect(m_refresh, SIGNAL(clicked(bool)), this, SLOT(dataModifiedForce()), Qt::QueuedConnection);

    m_layout->addRow(m_refresh, new QLabel(this));

    QApplication::restoreOverrideCursor();
}

void SKGAdviceBoardWidget::moreAdvice()
{
    m_maxAdvice = 9999999;
    dataModifiedForce();
}

void SKGAdviceBoardWidget::lessAdvice()
{
    m_maxAdvice = 7;
    dataModifiedForce();
}

void SKGAdviceBoardWidget::activateAllAdvice()
{
    SKGError err;
    {
        SKGBEGINTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Activate all advice"), err);
        err = getDocument()->executeSqliteOrder("DELETE FROM parameters WHERE t_uuid_parent='advice'");
    }

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

    SKGMainPanel::displayErrorMessage(err);
}

void SKGAdviceBoardWidget::adviceClicked()
{
    // Get advice identifier
    QAction* act = qobject_cast< QAction* >(sender());
    if (act) {
        QString uuid = act->property("id").toString();
        if (!uuid.isEmpty()) {
            // Get solution clicker
            int solution = sender()->property("solution").toInt();

            if (solution < 0) {
                // We have to ignore this advice
                SKGError err;
                {
                    SKGBEGINLIGHTTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Dismiss advice"), err);
                    QString currentMonth = QDate::currentDate().toString("yyyy-MM");

                    // Create dismiss
                    if (solution == -1 || solution == -2) {
                        uuid = SKGServices::splitCSVLine(uuid, '|').at(0);
                    }
                    err = getDocument()->setParameter(uuid, solution == -2 || solution == -4 ? "I" : QString("I_" % currentMonth), QVariant(), "advice");

                    // Delete useless dismiss
                    IFOKDO(err, getDocument()->executeSqliteOrder("DELETE FROM parameters WHERE t_uuid_parent='advice' AND t_value like 'I_ % ' AND t_value!='I_" % currentMonth % '\''))
                }

                // status bar
                IFOKDO(err, SKGError(0, i18nc("Successful message after an user action", "Advice dismissed.")))
                else {
                    err.addError(ERR_FAIL, i18nc("Error message",  "Advice dismiss failed"));
                }
            } else {
                // Get last transaction id
                int previous = getDocument()->getTransactionToProcess();

                // Execute the advice correction on all plugin
                QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
                int index = 0;
                while (index >= 0) {
                    SKGInterfacePlugin* plugin = SKGMainPanel::getMainPanel()->getPluginByIndex(index);
                    if (plugin) {
                        SKGError err = plugin->executeAdviceCorrection(uuid, solution);
                        if (!err || err.getReturnCode() != ERR_NOTIMPL) {
                            // The correction has been done or failed. This is the end.
                            index = -2;
                        }
                    } else {
                        index = -2;
                    }
                    ++index;
                }

                // Get last transaction id
                int next = getDocument()->getTransactionToProcess();

                // If this is the same transaction, it means that an action has been done outside the document ==> a refresh is needed
                if (next == previous) {
                    emit refreshNeeded();
                }

                QApplication::restoreOverrideCursor();
            }
        }
    }
}

#include "skgadviceboardwidget.moc"
