/***************************************************************************
 *   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 a plugin for undo and redo operation.
 *
 * @author Stephane MANKOWSKI / Guillaume DE BURE
 */
#include "skgundoredoplugin.h"
#include "skgtraces.h"
#include "skgerror.h"
#include "skgmainpanel.h"
#include "skgundoredoplugindockwidget.h"
#include "skgservices.h"
#include "skgundoredo_settings.h"

#include <QtGui/QWidget>

#include <kactioncollection.h>
#include <kstandardaction.h>
#include <ktoolbarpopupaction.h>
#include <kaboutdata.h>

/**
 * This plugin factory.
 */
K_PLUGIN_FACTORY(SKGUndoRedoPluginFactory, registerPlugin<SKGUndoRedoPlugin>();)
/**
 * This plugin export.
 */
K_EXPORT_PLUGIN(SKGUndoRedoPluginFactory("skg_undoredo", "skg_undoredo"))

SKGUndoRedoPlugin::SKGUndoRedoPlugin(QObject* iParent, const QVariantList& /*iArg*/) : SKGInterfacePlugin(iParent), m_dockWidget(NULL)
{
    SKGTRACEIN(10, "SKGUndoRedoPlugin::SKGUndoRedoPlugin");
}

SKGUndoRedoPlugin::~SKGUndoRedoPlugin()
{
    SKGTRACEIN(10, "SKGUndoRedoPlugin::~SKGUndoRedoPlugin");
    m_currentDocument = NULL;
    m_dockWidget = NULL;
    m_undoSaveAction = NULL;
    m_undoAction = NULL;
    m_redoAction = NULL;
    m_undoMenu = NULL;
    m_redoMenu = NULL;
}

bool SKGUndoRedoPlugin::setupActions(SKGDocument* iDocument, const QStringList& iArgument)
{
    SKGTRACEIN(10, "SKGUndoRedoPlugin::setupActions");
    Q_UNUSED(iArgument);

    m_currentDocument = iDocument;

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

    m_dockWidget = new QDockWidget(SKGMainPanel::getMainPanel());
    m_dockWidget->setObjectName(QString::fromUtf8("skg_undoredo_docwidget"));
    m_dockWidget->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
    m_dockWidget->setWindowTitle(title());
    m_dockWidget->setWidget(new SKGUndoRedoPluginDockWidget(m_currentDocument));

    //Menu
    m_undoSaveAction = new KAction(KIcon("document-revert"), i18nc("Verb, action to cancel previous action", "Undo document"), this);
    connect(m_undoSaveAction, SIGNAL(triggered(bool)), this, SLOT(actionUndoSave()));
    actionCollection()->addAction(QLatin1String("edit_undolastsave"), m_undoSaveAction);
    m_undoSaveAction->setShortcut(Qt::CTRL + Qt::ALT + Qt::Key_Z);
    if (SKGMainPanel::getMainPanel()) SKGMainPanel::getMainPanel()->registerGlobalAction("edit_undolastsave", m_undoSaveAction);

    m_undoAction = new KToolBarPopupAction(KIcon("edit-undo"), i18nc("Verb, action to cancel previous action", "Undo"), this);
    connect(m_undoAction, SIGNAL(triggered(bool)), this, SLOT(actionUndo()));
    actionCollection()->addAction(QLatin1String("edit_undo"), m_undoAction);
    m_undoAction->setShortcut(Qt::CTRL + Qt::Key_Z);
    m_undoAction->setPriority(QAction::LowPriority);

    m_undoMenu = static_cast<KMenu*>(m_undoAction->menu());
    connect(m_undoMenu , SIGNAL(aboutToShow()), this, SLOT(onShowUndoMenu()));

    m_undoAction->setStickyMenu(false);
    m_undoAction->setData(1);

    //undoAction=KStandardAction::undo(this, SLOT(actionUndo()), actionCollection());
    if (SKGMainPanel::getMainPanel()) SKGMainPanel::getMainPanel()->registerGlobalAction("edit-undo", m_undoAction);

    m_redoAction = new KToolBarPopupAction(KIcon("edit-redo"), i18nc("Verb, action to redo previous cancelled action", "Redo"), this);
    connect(m_redoAction, SIGNAL(triggered(bool)), this, SLOT(actionRedo()));
    actionCollection()->addAction(QLatin1String("edit_redo"), m_redoAction);
    m_redoAction->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_Z);
    m_redoAction->setPriority(QAction::LowPriority);

    m_redoMenu = static_cast<KMenu*>(m_redoAction->menu());
    connect(m_redoMenu , SIGNAL(aboutToShow()), this, SLOT(onShowRedoMenu()));

    m_redoAction->setStickyMenu(false);
    m_redoAction->setData(1);

    //redoAction=KStandardAction::redo(this, SLOT(actionRedo()), actionCollection());
    if (SKGMainPanel::getMainPanel()) SKGMainPanel::getMainPanel()->registerGlobalAction("edit-redo", m_redoAction);

    // add action to control hide / display of history
    m_dockWidget->toggleViewAction()->setShortcut(Qt::SHIFT + Qt::Key_F11);
    actionCollection()->addAction("view_transactions", m_dockWidget->toggleViewAction());

    return true;
}

void SKGUndoRedoPlugin::refresh()
{
    SKGTRACEIN(10, "SKGUndoRedoPlugin::refresh");

    if (m_currentDocument) {
        bool undoPossible = (m_currentDocument->getNbTransaction(SKGDocument::UNDO) > 0);
        if (m_undoSaveAction) m_undoSaveAction->setEnabled(undoPossible);
        if (m_undoAction) m_undoAction->setEnabled(undoPossible);
        if (m_redoAction) m_redoAction->setEnabled(m_currentDocument->getNbTransaction(SKGDocument::REDO) > 0);

        //Refresh undo redo
        QString name;
        m_currentDocument->getTransactionToProcess(SKGDocument::UNDO, &name);
        QString message = i18nc("Verb",  "Undo operation '%1'.", name);
        if (name.isEmpty()) message = "";
        if (m_undoAction) m_undoAction->setStatusTip(message);

        m_currentDocument->getTransactionToProcess(SKGDocument::REDO, &name);
        message = i18nc("Verb",  "Redo operation '%1'.", name);
        if (name.isEmpty()) message = "";
        if (m_redoAction) m_redoAction->setStatusTip(message);
    }
}


QWidget* SKGUndoRedoPlugin::getPreferenceWidget()
{
    SKGTRACEIN(10, "SKGUndoRedoPlugin::getPreferenceWidget");
    //Read Setting
    if (m_currentDocument) {
        KSharedConfigPtr config = KSharedConfig::openConfig();
        KConfigGroup pref = config->group("skg_undoredo");
        pref.writeEntry("maxNumberOfUndo", SKGServices::stringToInt(m_currentDocument->getParameter("SKG_UNDO_MAX_DEPTH")));
        pref.writeEntry("cleanHistoryOnSave", (m_currentDocument->getParameter("SKG_UNDO_CLEAN_AFTER_SAVE") == "Y"));
    }

    //Create widget
    QWidget* widget = new QWidget();
    ui.setupUi(widget);
    return widget;
}

KConfigSkeleton* SKGUndoRedoPlugin::getPreferenceSkeleton()
{
    return skgundoredo_settings::self();
}

SKGError SKGUndoRedoPlugin::savePreferences() const
{
    SKGError err;
    if (m_currentDocument) {
        //Read Setting
        QString max = SKGServices::intToString(skgundoredo_settings::maxNumberOfUndo());
        QString clean = (skgundoredo_settings::cleanHistoryOnSave() ? "Y" : "N");

        //Save setting in document
        if (max != m_currentDocument->getParameter("SKG_UNDO_MAX_DEPTH")) err = m_currentDocument->setParameter("SKG_UNDO_MAX_DEPTH", max);
        if (clean != m_currentDocument->getParameter("SKG_UNDO_CLEAN_AFTER_SAVE")) err = m_currentDocument->setParameter("SKG_UNDO_CLEAN_AFTER_SAVE", clean);
    }
    return err;
}

QString SKGUndoRedoPlugin::title() const
{
    return i18nc("Noun", "History");
}

QString SKGUndoRedoPlugin::icon() const
{
    return "edit-undo";
}

QString SKGUndoRedoPlugin::toolTip() const
{
    return i18nc("Noun", "History");
}

QStringList SKGUndoRedoPlugin::tips() const
{
    QStringList output;
    output.push_back(i18nc("Description of a tips", "<p>... you can undo and redo your modifications.</p>"));
    output.push_back(i18nc("Description of a tips", "<p>... you can modify the maximum size of the undo/redo stack in the settings.</p>"));
    return output;
}

int SKGUndoRedoPlugin::getOrder() const
{
    return 4;
}

SKGAdviceList SKGUndoRedoPlugin::advices() const
{
    SKGTRACEIN(10, "SKGUndoRedoPlugin::advices");
    SKGAdviceList output;
    //Get nb transaction
    int nbObject = m_currentDocument->getNbTransaction();
    int priority = qMin(10, nbObject / 50);
    if (priority > 0) {
        SKGAdvice ad;
        ad.setUUID("skgundoredoplugin_too_big");
        ad.setPriority(priority);
        ad.setShortMessage(i18nc("Advice on making the best (short)", "History is too large"));
        ad.setLongMessage(i18nc("Advice on making the best (long)", "You can improve performances by reducing your history size in settings."));
        QStringList autoCorrections;
        autoCorrections.push_back(i18nc("Advice on making the best (action)", "Clear history"));
        autoCorrections.push_back(i18nc("Advice on making the best (action)", "Open settings panel"));
        ad.setAutoCorrections(autoCorrections);
        output.push_back(ad);
    }

    return output;
}

SKGError SKGUndoRedoPlugin::executeAdviceCorrection(const QString& iAdviceIdentifier, int iSolution) const
{
    SKGError err;
    if (m_currentDocument && iAdviceIdentifier == "skgundoredoplugin_too_big") {
        if (iSolution == 0) {
            QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
            err = m_currentDocument->removeAllTransactions();
            QApplication::restoreOverrideCursor();

            //status bar
            if (!err) err = SKGError(0, i18nc("Message for successful user action", "Clear history successfully done."));
            else err.addError(ERR_FAIL, i18nc("Error message", "Clear history failed"));

            //Display error
            SKGMainPanel::displayErrorMessage(err);
        } else SKGMainPanel::getMainPanel()->optionsPreferences(this->objectName());
        return err;
    }
    return SKGInterfacePlugin::executeAdviceCorrection(iAdviceIdentifier, iSolution);
}

void SKGUndoRedoPlugin::actionUndoSave()
{
    SKGError err;
    SKGTRACEINRC(10, "SKGUndoRedoPlugin::actionUndoSave", err);
    if (m_currentDocument && SKGMainPanel::getMainPanel()) {
        QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
        err = m_currentDocument->undoRedoTransaction(SKGDocument::UNDOLASTSAVE);
        QApplication::restoreOverrideCursor();

        //status bar
        if (!err) err = SKGError(0, i18nc("Successful message after an user action", "Undo successfully done."));
        else err.addError(ERR_FAIL, i18nc("Error message",  "Undo failed"));

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

void SKGUndoRedoPlugin::actionUndo()
{
    SKGError err;
    SKGTRACEINRC(10, "SKGUndoRedoPlugin::actionUndo", err);
    if (m_currentDocument && SKGMainPanel::getMainPanel()) {
        QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
        int pos = ((QAction*) sender())->data().toInt();
        for (int i = 1 ; !err && i <= pos; ++i)
            err = m_currentDocument->undoRedoTransaction(SKGDocument::UNDO);
        QApplication::restoreOverrideCursor();

        //status bar
        if (!err) err = SKGError(0, i18nc("Successful message after an user action", "Undo successfully done."));
        else err.addError(ERR_FAIL, i18nc("Error message",  "Undo failed"));

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

void SKGUndoRedoPlugin::onShowUndoMenu()
{
    if (m_undoMenu && m_currentDocument) {
        m_undoMenu->clear();
        SKGStringListList listTmp;
        m_currentDocument->executeSelectSqliteOrder(
            "SELECT t_name, t_savestep FROM doctransaction WHERE t_mode='U' ORDER BY d_date DESC LIMIT 7",
            listTmp);
        int nb = listTmp.count();
        for (int i = 1; i < nb; ++i) {
            QAction* act = m_undoMenu->addAction(listTmp.at(i).at(1) == "Y" ?  KIcon("document-revert") : KIcon("edit-undo"), listTmp.at(i).at(0));
            if (act) {
                act->setData(i);
                connect(act, SIGNAL(triggered()), this, SLOT(actionUndo()));
            }
        }
    }
}
void SKGUndoRedoPlugin::actionRedo()
{
    SKGError err;
    SKGTRACEINRC(10, "SKGUndoRedoPlugin::actionRedo", err);
    if (m_currentDocument && SKGMainPanel::getMainPanel()) {
        QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
        int pos = ((QAction*) sender())->data().toInt();
        for (int i = 1 ; !err && i <= pos; ++i)
            err = m_currentDocument->undoRedoTransaction(SKGDocument::REDO);
        QApplication::restoreOverrideCursor();

        //status bar
        if (!err) err = SKGError(0, i18nc("Successful message after an user action", "Redo successfully done."));
        else err.addError(ERR_FAIL, i18nc("Error message",  "Redo failed"));

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

void SKGUndoRedoPlugin::onShowRedoMenu()
{
    if (m_redoMenu && m_currentDocument) {
        m_redoMenu->clear();
        SKGStringListList listTmp;
        m_currentDocument->executeSelectSqliteOrder(
            "SELECT t_name FROM doctransaction WHERE t_mode='R' ORDER BY d_date ASC LIMIT 7",
            listTmp);
        int nb = listTmp.count();
        for (int i = 1; i < nb; ++i) {
            QAction* act = m_redoMenu->addAction(KIcon("edit-redo"), listTmp.at(i).at(0));
            if (act) {
                act->setData(i);
                connect(act, SIGNAL(triggered()), this, SLOT(actionRedo()));
            }
        }
    }
}

QDockWidget* SKGUndoRedoPlugin::getDockWidget()
{
    return m_dockWidget;
}
#include "skgundoredoplugin.moc"
