/***************************************************************************
 *   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 to generate report.
 *
 * @author Stephane MANKOWSKI / Guillaume DE BURE
 */
#include "skgreportpluginwidget.h"
#include "skgmainpanel.h"
#include "skgbankincludes.h"
#include "skgtraces.h"
#include "skgcolorbutton.h"
#include "skgreport_settings.h"

#include <klocale.h>
#include <kmenu.h>

#include <QtGui/QGraphicsScene>

/**
 * The size of the forecast
 */
static const int FORECASTMAX = 100;

SKGReportPluginWidget::SKGReportPluginWidget(SKGDocumentBank* iDocument, bool iMinimmumMode)
    : SKGTabPage(iDocument), m_nbLevelLines(0), m_nbLevelColumns(0), m_refreshNeeded(true)
{
    SKGTRACEIN(10, "SKGReportPluginWidget::SKGReportPluginWidget");
    if (!iDocument) return;

    m_timer.setSingleShot(true);
    connect(&m_timer, SIGNAL(timeout()), this, SLOT(refreshActivated()));

    ui.setupUi(this);

    ui.kTitle->hide();
    ui.kResetInternalFilter->hide();
    ui.kResetInternalFilter->setIcon(KIcon("dialog-cancel"));

    ui.kCorrectedBy->setDocument(iDocument);
    ui.kCorrectedBy->setCurrentIndex(0);
    ui.kCorrectedBy->setWhereClauseCondition("t_type='I'");

    ui.kLineLabel->setText("");
    ui.kPeriod->setCurrentIndex(1);
    ui.kInterval->setCurrentIndex(2);

    ui.kLineUp->setIcon(KIcon("list-add"));
    ui.kLineDown->setIcon(KIcon("list-remove"));
    ui.kColUp->setIcon(KIcon("list-add"));
    ui.kColDown->setIcon(KIcon("list-remove"));

    ui.kLineAdd->setIcon(KIcon("arrow-right"));
    ui.kLineRemove->setIcon(KIcon("edit-delete"));

    // FIXME: Icons are not related to the text...
    ui.kMode->addItem(KIcon("office-chart-pie"), i18nc("Display graph values as the sum of operations amount", "Sum of operations"), 0);
    ui.kMode->addItem(KIcon("office-chart-line"), i18nc("Display graph values as the cumulated sum of operations amount", "Cumulated sum of operations"), 1);
    ui.kMode->addItem(KIcon("office-chart-area-percentage"), i18nc("Display graph values in base 100", "Base 100"), 7);
    ui.kMode->addItem(KIcon("office-chart-area-percentage"), i18nc("Display graph values in base 100", "Cumulated sum in base 100"), 8);
    ui.kMode->addItem(KIcon("office-chart-area-percentage"), i18nc("Display graph values in percentage", "Percent of columns"), 2);
    ui.kMode->addItem(KIcon("office-chart-area-percentage"), i18nc("Display graph values in percentage", "Absolute percent of columns"), 5);
    ui.kMode->addItem(KIcon("office-chart-area-percentage"), i18nc("Display graph values in percentage", "Percent of lines"), 4);
    ui.kMode->addItem(KIcon("office-chart-area-percentage"), i18nc("Display graph values in percentage", "Absolute percent of lines"), 6);
    ui.kMode->addItem(KIcon("milestone"), i18nc("Display graph values as a number of operations (or transaction)", "Count number of operations"), 3);

    ui.kForecastCmb->addItem(i18nc("Noun", "None"), 0);
    ui.kForecastCmb->addItem(KIcon("view-financial-forecast"), i18nc("Noun", "Moving average"), 2);
    ui.kForecastCmb->addItem(KIcon("view-financial-forecast"), i18nc("Noun", "Weighted moving average"), 3);
    //TODO: ui.kForecastCmb->addItem(KIcon("applications-education-mathematics"), i18nc("Noun", "Interest rate") , 5);
    ui.kForecastCmb->addItem(KIcon("chronometer"), i18nc("Noun", "Schedule"), 1);
    ui.kForecastCmb->addItem(KIcon("view-income-categories"), i18nc("Noun", "Budget"), 4);

    ui.kWidgetSelector->addButton(KIcon("configure"), i18n("Setup Report"), i18n("Display the edition panel for report"), ui.setupWidget);
    connect(ui.kWidgetSelector, SIGNAL(selectedModeChanged(int)), this, SLOT(onBtnModeClicked(int)));

    //Set colors
    setSettings();
    if (SKGMainPanel::getMainPanel()) {
        connect(SKGMainPanel::getMainPanel(), SIGNAL(settingsChanged()), this, SLOT(setSettings()));
    }

    //Build contextual menu
    KMenu* tableMenu = ui.kTableWithGraph->getTableContextualMenu();
    if (tableMenu) {
        tableMenu->addSeparator();
        QStringList overlayopen;
        overlayopen.push_back("skg_open");
        m_openReportAction = tableMenu->addAction(KIcon("view-investment", NULL, overlayopen), i18nc("Verb", "Open report..."));
        if (m_openReportAction) m_openReportAction->setEnabled(false);
    }

    KMenu* graphMenu = ui.kTableWithGraph->getGraphContextualMenu();
    if (graphMenu) {
        graphMenu->addSeparator();
        graphMenu->addAction(m_openReportAction);
    }

    connect(m_openReportAction, SIGNAL(triggered(bool)), this, SLOT(onOpenReport()));

    //Init comboboxes
    m_attsForColumns << "d_date" << "d_DATEWEEK" << "d_DATEMONTH" << "d_DATEQUARTER" << "d_DATESEMESTER" << "d_DATEYEAR";
    m_attsForLines << "#NOTHING#" <<
                   "t_ACCOUNTTYPE" << "t_ACCOUNT" <<
                   "t_REALCATEGORY" <<
                   "t_PAYEE" <<
                   "t_TYPEEXPENSENLS" <<
                   "t_mode" <<
                   "t_status" <<
                   "t_UNITTYPE" << "t_UNIT" <<
                   "t_REALREFUND";

    //Adding properties
    QStringList properties;
    iDocument->getDistinctValues("parameters", "t_name", "t_uuid_parent like '%-operation' AND t_name NOT LIKE 'SKG_%'", properties);
    int nb = properties.count();
    for (int i = 0; i < nb; ++i)
        m_attsForLines.push_back("p_" % properties.at(i));
    m_attsForColumns += m_attsForLines;

    if (iDocument) {
        foreach(const QString & att, m_attsForColumns)
        ui.kColumns->addItem(iDocument->getIcon(att), iDocument->getDisplay(att));

        foreach(const QString & att, m_attsForLines) {
            ui.kLines->addItem(iDocument->getIcon(att), iDocument->getDisplay(att));
        }

        ui.kColumns->setCurrentIndex(2);
    }

    connect(ui.kTableWithGraph, SIGNAL(selectionChanged()), this, SLOT(onSelectionChanged()));

    //Refresh
    connect(ui.kColumns, SIGNAL(currentIndexChanged(int)), this, SLOT(refresh()), Qt::QueuedConnection);
    connect(ui.kLines, SIGNAL(currentIndexChanged(int)), this, SLOT(refresh()), Qt::QueuedConnection);
    connect(ui.kMode, SIGNAL(currentIndexChanged(int)), this, SLOT(refresh()), Qt::QueuedConnection);
    connect(ui.kPeriod, SIGNAL(currentIndexChanged(int)), this, SLOT(refresh()), Qt::QueuedConnection);
    connect(ui.kTimeline, SIGNAL(valueChanged(int)), this, SLOT(refresh()), Qt::QueuedConnection);
    connect(ui.kInterval, SIGNAL(currentIndexChanged(int)), this, SLOT(refresh()), Qt::QueuedConnection);
    connect(ui.kDateBegin, SIGNAL(dateEntered(QDate)), this, SLOT(refresh()), Qt::QueuedConnection);
    connect(ui.kDateEnd, SIGNAL(dateEntered(QDate)), this, SLOT(refresh()), Qt::QueuedConnection);
    connect(ui.kNbIntervals, SIGNAL(valueChanged(int)), this, SLOT(refresh()), Qt::QueuedConnection);
    connect(ui.kIncomes, SIGNAL(stateChanged(int)), this, SLOT(refresh()), Qt::QueuedConnection);
    connect(ui.kExpenses, SIGNAL(stateChanged(int)), this, SLOT(refresh()), Qt::QueuedConnection);
    connect(ui.kTransfers, SIGNAL(stateChanged(int)) , this, SLOT(refresh()), Qt::QueuedConnection);
    connect(ui.kForecastCmb, SIGNAL(currentIndexChanged(int)) , this, SLOT(refresh()), Qt::QueuedConnection);
    connect(ui.kForecastValue, SIGNAL(valueChanged(int)) , this, SLOT(refresh()), Qt::QueuedConnection);
    connect(ui.kCorrectedBy, SIGNAL(currentIndexChanged(int)) , this, SLOT(refresh()), Qt::QueuedConnection);

    connect((const QObject*) getDocument(), SIGNAL(tableModified(QString,int)), this, SLOT(dataModified(QString,int)));
    connect(SKGMainPanel::getMainPanel(), SIGNAL(currentPageChanged()), this, SLOT(pageChanged()), Qt::QueuedConnection);

    if (iMinimmumMode) {
        ui.kTableWithGraph->getShowWidget()->setState("\"graph\"");
        ui.kWidgetSelector->setSelectedMode(-1);
    } else {
        ui.kWidgetSelector->setSelectedMode(0);
    }
    onBtnModeClicked(ui.kWidgetSelector->getSelectedMode());
}

SKGReportPluginWidget::~SKGReportPluginWidget()
{
    SKGTRACEIN(10, "SKGReportPluginWidget::~SKGReportPluginWidget");
    m_openReportAction = NULL;
}

void SKGReportPluginWidget::onResetInternalFilter()
{
    SKGTRACEIN(10, "SKGReportPluginWidget::onResetInternalFilter");

    //Initialisation
    m_lastState.clear();
    m_operationWhereClause = "";
    ui.kTitle->hide();
    ui.kResetInternalFilter->hide();

    dataModified("", 0);
}

QString SKGReportPluginWidget::getState()
{
    SKGTRACEIN(10, "SKGReportPluginWidget::getState");
    QDomDocument doc("SKGML");
    QDomElement root;
    if (m_lastState.hasChildNodes()) {
        doc = m_lastState;
        root = doc.documentElement();
    } else {
        root = doc.createElement("parameters");
        doc.appendChild(root);
    }

    root.setAttribute("columns", m_attsForColumns.value(ui.kColumns->currentIndex()));

    QString lineString;
    int nb = m_attsForLinesAdded.count();
    for (int i = 0; i < nb; ++i) {
        lineString += m_attsForLinesAdded.at(i);
        if (i < nb - 1) lineString += OBJECTSEPARATOR;
    }

    root.setAttribute("lines", lineString);
    root.setAttribute("lines2", m_attsForLines.value(ui.kLines->currentIndex()));
    root.setAttribute("mode", SKGServices::intToString(ui.kMode->itemData(ui.kMode->currentIndex()).toInt()));
    root.setAttribute("period", SKGServices::intToString(ui.kPeriod->currentIndex()));
    if (ui.kPeriod->currentIndex() == 4) {
        root.setAttribute("date_begin", SKGServices::intToString(ui.kDateBegin->date().toJulianDay()));
        root.setAttribute("date_end", SKGServices::intToString(ui.kDateEnd->date().toJulianDay()));
    }
    root.setAttribute("interval", SKGServices::intToString(ui.kInterval->currentIndex()));
    root.setAttribute("nb_intervals", SKGServices::intToString(ui.kNbIntervals->value()));
    root.setAttribute("timeline", SKGServices::intToString(ui.kTimeline->value()));
    root.setAttribute("incomes", ui.kIncomes->isChecked() ? "Y" : "N");
    root.setAttribute("expenses", ui.kExpenses->isChecked() ? "Y" : "N");
    root.setAttribute("transfers", ui.kTransfers->isChecked() ? "Y" : "N");
    root.setAttribute("currentPage", SKGServices::intToString(ui.kWidgetSelector->getSelectedMode()));
    root.setAttribute("tableAndGraphState", ui.kTableWithGraph->getState());
    root.setAttribute("nbLevelLines", SKGServices::intToString(m_nbLevelLines));
    root.setAttribute("nbLevelColumns", SKGServices::intToString(m_nbLevelColumns));
    root.setAttribute("forecast", SKGServices::intToString(ui.kForecastCmb->itemData(ui.kForecastCmb->currentIndex()).toInt()));
    root.setAttribute("forecastValue", SKGServices::intToString(ui.kForecastValue->value()));
    root.setAttribute("zoomPosition", SKGServices::intToString(zoomPosition()));
    root.setAttribute("correctedby", ui.kCorrectedBy->currentText());

    if (!m_operationWhereClause.isEmpty()) root.setAttribute("operationWhereClause", m_operationWhereClause);

    return doc.toString();
}

void SKGReportPluginWidget::setState(const QString& iState)
{
    SKGTRACEIN(10, "SKGReportPluginWidget::setState");
    m_timer.stop();

    QDomDocument doc("SKGML");
    doc.setContent(iState);
    QDomElement root = doc.documentElement();

    QString columns = root.attribute("columns");
    QString lines = root.attribute("lines");
    QString lines2 = root.attribute("lines2");
    QString mode = root.attribute("mode");
    QString period = root.attribute("period");
    QString interval = root.attribute("interval");
    QString nb_interval = root.attribute("nb_intervals");
    QString timeline = root.attribute("timeline");
    QString date_begin = root.attribute("date_begin");
    QString date_end = root.attribute("date_end");
    QString incomes = root.attribute("incomes");
    QString expenses = root.attribute("expenses");
    QString transfers = root.attribute("transfers");
    QString currentPage = root.attribute("currentPage");
    QString forecast = root.attribute("forecast");
    QString forecastValue = root.attribute("forecastValue");
    QString tableAndGraphState = root.attribute("tableAndGraphState");
    QString title = root.attribute("title");
    QString title_icon = root.attribute("title_icon");
    m_operationWhereClause = root.attribute("operationWhereClause");
    QString nbLevelLinesString = root.attribute("nbLevelLines");
    QString nbLevelColumnsString = root.attribute("nbLevelColumns");
    QString zoomPosition = root.attribute("zoomPosition");
    QString correctedby = root.attribute("correctedby");

    //Default values
    if (nbLevelLinesString.isEmpty()) nbLevelLinesString = '0';
    if (nbLevelColumnsString.isEmpty()) nbLevelColumnsString = '0';
    if (columns.isEmpty()) columns = m_attsForColumns.at(2);
    if (lines2.isEmpty()) lines2 = m_attsForLines.at(0);
    if (mode.isEmpty()) mode = '0';
    if (period.isEmpty()) period = '1';
    if (interval.isEmpty()) interval = '2';
    if (nb_interval.isEmpty()) nb_interval = '1';
    if (timeline.isEmpty()) timeline = '1';
    if (incomes.isEmpty()) incomes = 'Y';
    if (expenses.isEmpty()) expenses = 'Y';
    if (transfers.isEmpty()) transfers = 'Y';
    if (currentPage.isEmpty()) currentPage = '0';
    if (forecast.isEmpty()) forecast = '0';
    if (forecastValue.isEmpty()) forecastValue = '0';

    m_nbLevelLines = SKGServices::stringToInt(nbLevelLinesString);
    m_nbLevelColumns = SKGServices::stringToInt(nbLevelColumnsString);
    ui.kColumns->setCurrentIndex(m_attsForColumns.indexOf(columns));
    ui.kLines->setCurrentIndex(m_attsForLines.indexOf(lines2));
    m_attsForLinesAdded.clear();
    if (!lines.isEmpty()) m_attsForLinesAdded = lines.split(OBJECTSEPARATOR);
    ui.kMode->setCurrentIndex(ui.kMode->findData(SKGServices::stringToInt(mode)));
    ui.kPeriod->setCurrentIndex(SKGServices::stringToInt(period));
    ui.kInterval->setCurrentIndex(SKGServices::stringToInt(interval));
    ui.kTimeline->setValue(SKGServices::stringToInt(timeline));
    ui.kNbIntervals->setValue(SKGServices::stringToInt(nb_interval));
    if (!date_begin.isEmpty()) ui.kDateBegin->setDate(QDate::fromJulianDay(SKGServices::stringToInt(date_begin)));
    if (!date_end.isEmpty()) ui.kDateEnd->setDate(QDate::fromJulianDay(SKGServices::stringToInt(date_end)));
    ui.kIncomes->setChecked(incomes != "N");
    ui.kExpenses->setChecked(expenses != "N");
    ui.kTransfers->setChecked(transfers != "N");
    int currentPageInt = SKGServices::stringToInt(currentPage);
    ui.kWidgetSelector->setSelectedMode(currentPageInt);
    ui.kLine->setVisible(currentPageInt >= -1);
    ui.kForecastCmb->setCurrentIndex(ui.kForecastCmb->findData(SKGServices::stringToInt(forecast)));
    ui.kForecastValue->setValue(SKGServices::stringToInt(forecastValue));
    if (!zoomPosition.isEmpty()) setZoomPosition(SKGServices::stringToInt(zoomPosition));
    if (!correctedby.isEmpty()) ui.kCorrectedBy->setCurrentIndex(ui.kCorrectedBy->findText(correctedby));

    refresh();

    ui.kTableWithGraph->setState(tableAndGraphState);
    if (!title.isEmpty()) {
        ui.kTitle->setComment("<html><body><b>" % SKGServices::stringToHtml(title) % "</b></body></html>");
        ui.kTitle->show();
        ui.kResetInternalFilter->show();
    } else {
        ui.kTitle->hide();
        ui.kResetInternalFilter->hide();
    }
    if (!title_icon.isEmpty()) ui.kTitle->setPixmap(KIcon(title_icon).pixmap(22, 22), KTitleWidget::ImageLeft);
    if (!m_operationWhereClause.isEmpty()) {
        //We keep a copy of given state in case of bookmark
        m_lastState = doc;
        dataModified("", 0);
    } else m_lastState.clear();
    m_previousParametersUsed = "";
}

QString SKGReportPluginWidget::getDefaultStateAttribute()
{
    return "SKGREPORT_DEFAULT_PARAMETERS";
}

QWidget* SKGReportPluginWidget::zoomableWidget() const
{
    return ui.kTableWithGraph->table();
}

QList< QWidget* > SKGReportPluginWidget::printableWidgets() const
{
    QList<QWidget*> output;
    if (ui.kTableWithGraph->isTableVisible()) output.push_back(ui.kTableWithGraph->table());
    if (ui.kTableWithGraph->isGraphVisible()) output.push_back(ui.kTableWithGraph->graph()->graphicsView());
    if (ui.kTableWithGraph->isTextReportVisible()) output.push_back(ui.kTableWithGraph->textReport());
    return output;
}

void SKGReportPluginWidget::getWhereClauseAndTitleForSelection(int row, int column, QString& oWc, QString& oTitle)
{
    //Build where clause and title
    QString currentComment = ui.kTitle->comment();
    if (!currentComment.isEmpty()) {
        currentComment.remove("<html><body><b>");
        currentComment.remove("</b></body></html>");
        oTitle = SKGServices::htmlToString(currentComment) % ". ";
    }

    //Condition on line attribute
    oTitle += i18nc("Noun, a list of items", "Sub operations with ");
    SKGColorButton* btn = qobject_cast<SKGColorButton*>(ui.kTableWithGraph->table()->cellWidget(row, 0));

    QString attLine;
    QStringList listAtt = m_attsForLinesAdded;
    if (listAtt.count() == 0 || ui.kLines->currentIndex() > 0) listAtt.push_back(m_attsForLines.at(ui.kLines->currentIndex()));
    int nb = listAtt.count();
    for (int i = 0; i < nb; ++i) {
        QString att = listAtt.at(i);
        if (att == "#NOTHING#") att = "";
        if (att.startsWith(QLatin1String("p_"))) {
            QString propertyName = att.right(att.length() - 2);
            att = "IFNULL((SELECT t_value FROM parameters WHERE t_uuid_parent=v_operation_consolidated.id||'-operation' AND t_name='" % propertyName % "'), '#NULL#')";
        }
        if (att.isEmpty()) att = '\'' % i18nc("Noun", "All") % '\'';

        if (!attLine.isEmpty()) attLine += "||'" % OBJECTSEPARATOR % "'||";
        attLine += att;
    }

    oWc = attLine;
    QString lineVal = (btn ? btn->text() : ui.kTableWithGraph->table()->item(row, 0)->data(1).toString());
    QString title = ui.kTableWithGraph->table()->horizontalHeaderItem(0)->text();
    if (lineVal.isEmpty() && row == ui.kTableWithGraph->table()->rowCount() - 1) {
        //This is the last sum
        oWc = "";
        if (!attLine.isEmpty()) oWc = attLine % " NOT LIKE '%#NULL#%'";
    } else {
        //This is not the last sum
        if (lineVal.isEmpty()) oWc += " IS NULL OR " % attLine;
        oWc += " = '" % SKGServices::stringToSqlString(lineVal) % "' OR " %
               attLine % " like '" % SKGServices::stringToSqlString(lineVal) % OBJECTSEPARATOR % "%'";
        oWc = '(' % oWc % ')';
        oTitle += i18nc("Noun",  "'%1' with '%2'", title, lineVal);
    }

    //Condition on column attribute
    int nbCol = ui.kTableWithGraph->getNbColumns();
    QString att = m_attsForColumns[ui.kColumns->currentIndex()];
    if (att == "#NOTHING#") att = "";
    if (att.startsWith(QLatin1String("p_"))) {
        QString propertyName = att.right(att.length() - 2);
        att = "IFNULL((SELECT t_value FROM parameters WHERE t_uuid_parent=v_operation_consolidated.id||'-operation' AND t_name='" % propertyName % "'), '#NULL#')";
    }
    if (!att.isEmpty()) {
        if (column != 0 && column < nbCol) {
            if (!oWc.isEmpty()) {
                oWc += " AND ";
                oTitle += i18nc("Noun",  " and ");
            }

            oWc += '(' % att;
            QString val = ui.kTableWithGraph->table()->horizontalHeaderItem(column)->text();
            if (val.isEmpty()) {
                oWc += " IS NULL OR " % att % "=''";
                oTitle += i18nc("Noun",  "'%1' are empty", ui.kColumns->currentText());
            } else {
                oWc += " = '" % SKGServices::stringToSqlString(val) % "' OR " %
                       att % " like '" % SKGServices::stringToSqlString(val) % OBJECTSEPARATOR % "%'";
                oTitle += i18nc("Noun",  "'%1' with '%2'", ui.kColumns->currentText(), val);
            }
            oWc += ')';
        } else {
            oWc = '(' % oWc % ") AND " % att % " NOT LIKE '%#NULL#%'";
        }
    }

    //Condition on other attribute
    if (!oWc.isEmpty()) {
        oWc += " AND ";
        oTitle += i18nc("Noun",  " and ");
    }
    oWc += getConsolidatedWhereClause();

    QString during = ui.kPeriod->text();
    if (ui.kPeriod->currentIndex() == 4) {
        during += SKGServices::dateToSqlString(QDateTime(ui.kDateBegin->date())) % ' ' % SKGServices::dateToSqlString(QDateTime(ui.kDateEnd->date()));
    } else if (ui.kPeriod->currentIndex() > 0) {
        if (ui.kNbIntervals->isVisible()) during += ' ' % SKGServices::intToString(ui.kNbIntervals->value()) % ' ' ;
        during += ui.kInterval->text();
    }

    QStringList types;
    if (ui.kIncomes->isChecked()) types.push_back(i18nc("Noun",  "incomes"));
    if (ui.kExpenses->isChecked()) types.push_back(i18nc("Noun",  "expenses"));
    if (ui.kTransfers->isChecked()) types.push_back(i18nc("Noun",  "transfers"));

    oTitle += i18nc("Noun",  "during '%1' for '%2'", during, types.join(" "));
}

void SKGReportPluginWidget::onDoubleClick(int row, int column)
{
    _SKGTRACEIN(10, "SKGReportPluginWidget::onDoubleClick");

    QString wc;
    QString title;
    getWhereClauseAndTitleForSelection(row, column, wc, title);

    //Open
    if (QApplication::keyboardModifiers() &Qt::ControlModifier && QApplication::keyboardModifiers() &Qt::ShiftModifier) {
        //Call debug plugin
        QDomDocument doc("SKGML");
        QDomElement root = doc.createElement("parameters");
        doc.appendChild(root);
        root.setAttribute("sqlOrder", "SELECT * from v_operation_consolidated WHERE " % wc);

        SKGMainPanel::getMainPanel()->openPage(SKGMainPanel::getMainPanel()->getPluginByName("Debug plugin"), -1, doc.toString());
    } else {
        //Call operation plugin
        QDomDocument doc("SKGML");
        doc.setContent(getDocument()->getParameter("SKGOPERATION_CONSOLIDATED_DEFAULT_PARAMETERS"));
        QDomElement root = doc.documentElement();
        if (root.isNull()) {
            root = doc.createElement("parameters");
            doc.appendChild(root);
        }

        root.setAttribute("operationTable", "v_operation_consolidated");
        root.setAttribute("operationWhereClause", wc);
        root.setAttribute("title", title);
        root.setAttribute("title_icon", "view-investment");
        root.setAttribute("currentPage", "-1");

        SKGMainPanel::getMainPanel()->openPage(SKGMainPanel::getMainPanel()->getPluginByName("Skrooge operation plugin"), -1, doc.toString());
    }
}

void SKGReportPluginWidget::onBtnModeClicked(int mode)
{
    ui.kTableWithGraph->setFilterVisibility(mode == 0);
}

void SKGReportPluginWidget::onOpenReport()
{
    SKGError err;
    SKGTRACEINRC(10, "SKGReportPluginWidget::onOpenReport", err);
    QList<QTableWidgetItem*> selection = ui.kTableWithGraph->table()->selectedItems();
    int nb = selection.count();
    if (nb) {
        QString wc;
        QString title;
        for (int i = 0; i < nb; ++i) {
            QString wc2;
            QString title2;
            getWhereClauseAndTitleForSelection(selection.at(i)->row(), selection.at(i)->column(), wc2, title2);
            if (!wc2.isEmpty()) {
                if (!wc.isEmpty()) wc = '(' % wc % ") OR (" % wc2 % ')';
                else wc = wc2;
            }
            if (!title2.isEmpty()) {
                if (!title.isEmpty()) title = i18n("(%1) or (%2)", title, title2);
                else title = title2;
            }
        }

        //Call report plugin
        QDomDocument doc("SKGML");
        doc.setContent(getState());
        QDomElement root = doc.documentElement();
        root.setAttribute("operationWhereClause", wc);
        root.setAttribute("title", title);
        root.setAttribute("title_icon", "view-investment");
        QString currentPage = root.attribute("currentPage");
        if (SKGServices::stringToInt(currentPage) < -1) root.setAttribute("currentPage", "-1");
        SKGMainPanel::getMainPanel()->openPage(SKGMainPanel::getMainPanel()->getPluginByName("Skrooge report plugin"), -1, doc.toString());
    }
}

void SKGReportPluginWidget::onOneLevelMore()
{
    _SKGTRACEIN(10, "SKGTableWithGraph::onOneLevelMore");
    if (sender() == ui.kLineUp) ++m_nbLevelLines;
    else ++m_nbLevelColumns;
    refresh();
}

void SKGReportPluginWidget::onOneLevelLess()
{
    _SKGTRACEIN(10, "SKGTableWithGraph::onOneLevelLess");
    if (sender() == ui.kLineDown) --m_nbLevelLines;
    else --m_nbLevelColumns;
    refresh();
}

void SKGReportPluginWidget::onAddLine()
{
    _SKGTRACEIN(10, "SKGTableWithGraph::onAddLine");
    m_attsForLinesAdded.push_back(m_attsForLines.value(ui.kLines->currentIndex()));
    ui.kLines->setCurrentIndex(0);
    refresh();
}

void SKGReportPluginWidget::onRemoveLine()
{
    _SKGTRACEIN(10, "SKGTableWithGraph::onRemoveLine");
    if (m_attsForLinesAdded.count()) m_attsForLinesAdded.pop_back();
    refresh();
}

QString SKGReportPluginWidget::getConsolidatedWhereClause(QString* oWhereClausForPreviousData, QString*  oWhereClausForForecastData)
{
    //Build where clause
    QString wc;

    int forecastmode = ui.kForecastCmb->itemData(ui.kForecastCmb->currentIndex()).toInt();

    QString strfFormat;
    QString sqlInterval;
    double val = ui.kNbIntervals->value();
    double one = 1;
    QDate a = QDate::currentDate();
    QDate b = a;

    switch (ui.kInterval->currentIndex()) {
    case 0:
        // Interval is in days
        strfFormat = "'%Y-%m-%d'";
        sqlInterval = "DAY";
        break;
    case 1:
        // Interval is in weeks
        strfFormat = "'%Y-%W'";
        sqlInterval = "DAY";
        val *= 7;
        one *= 7;
        break;
    case 2:
        // Interval is in months
        strfFormat = "'%Y-%m'";
        sqlInterval = "MONTH";
        break;
    case 3:
        // Interval is in years
        strfFormat = "'%Y'";
        sqlInterval = "YEAR";
        break;
    }

    switch (ui.kPeriod->currentIndex()) {
    case 1:
        // Current Interval
        switch (ui.kInterval->currentIndex()) {
        case 0:
            // Interval is in days
            break;
        case 1:
            // Interval is in weeks
            a = a.addDays(1 - a.dayOfWeek());
            b = a.addDays(7 - 1);
            break;
        case 2:
            // Interval is in months
            a = a.addDays(1 - a.day());
            b = a.addMonths(1).addDays(-1);
            break;
        case 3:
            // Interval is in years
            a = a.addDays(1 - a.day()).addMonths(1 - a.month());
            b = a.addYears(1).addDays(-1);
            break;
        }
        ui.kDateBegin->setDate(a);
        ui.kDateEnd->setDate(b);
        wc = "strftime(" % strfFormat % ",d_date) = strftime(" % strfFormat % ",'now')";
        if (oWhereClausForPreviousData) *oWhereClausForPreviousData = "strftime(" % strfFormat % ",d_date) < strftime(" % strfFormat % ",'now')";
        if (oWhereClausForForecastData) *oWhereClausForForecastData = "d_date > date('now')";
        break;
    case 2:
        // Previous Interval
        switch (ui.kInterval->currentIndex()) {
        case 0:
            // Interval is in days
            b = b.addDays(-1);
            a = b.addDays(-val + 1);
            break;
        case 1:
            // Interval is in weeks
            b = b.addDays(-a.dayOfWeek());
            a = b.addDays(-val + 1);
            break;
        case 2:
            // Interval is in months
            b = b.addDays(-b.day());
            a = a.addDays(1 - a.day()).addMonths(-val);
            break;
        case 3:
            // Interval is in years
            b = b.addDays(-b.day()).addMonths(-b.month());
            a = a.addMonths(1 - a.month()).addDays(1 - a.day()).addYears(-val);
            break;
        }
        ui.kDateBegin->setDate(a);
        ui.kDateEnd->setDate(b);
        wc = "strftime(" % strfFormat % ",d_date)>=strftime(" % strfFormat % ",date('now','start of month', '-" % SKGServices::intToString(val) % ' ' % sqlInterval % "'))";
        if (forecastmode != 1) wc += " AND strftime(" % strfFormat % ",d_date)<=strftime(" % strfFormat % ",date('now','start of month', '-" % SKGServices::intToString(one) % ' ' % sqlInterval % "'))"; //For forecast based on scheduled operations
        if (oWhereClausForPreviousData) *oWhereClausForPreviousData = "strftime(" % strfFormat % ",d_date) < strftime(" % strfFormat % ",date('now','start of month', '-" % SKGServices::intToString(val) % ' ' % sqlInterval % "'))";
        if (oWhereClausForForecastData) *oWhereClausForForecastData = "strftime(" % strfFormat % ",d_date) > strftime(" % strfFormat % ",date('now','start of month', '-" % SKGServices::intToString(one) % ' ' % sqlInterval % "'))";
        break;
    case 3:
        //Last Interval
        switch (ui.kInterval->currentIndex()) {
        case 0:
            // Interval is in days
            a = a.addDays(-val);
            break;
        case 1:
            // Interval is in weeks
            a = a.addDays(-val);
            break;
        case 2:
            // Interval is in months
            a = a.addMonths(-val);
            break;
        case 3:
            // Interval is in years
            a = a.addYears(-val);
            break;
        }
        a = a.addDays(1);
        ui.kDateBegin->setDate(a);
        ui.kDateEnd->setDate(b);
        wc = "d_date > date('now','-" % SKGServices::intToString(val) % ' ' % sqlInterval % "')";
        if (forecastmode != 1) wc += " AND d_date<=date('now')"; //For forecast based on scheduled operations
        if (oWhereClausForPreviousData) *oWhereClausForPreviousData = "d_date <= date('now','-" % SKGServices::intToString(val) % ' ' % sqlInterval % "')";
        if (oWhereClausForForecastData) *oWhereClausForForecastData = "d_date > date('now')";
        break;
    case 4:
        // Custom Date
        wc = "d_date>='" % SKGServices::dateToSqlString(QDateTime(ui.kDateBegin->date())) % '\'';
        if (forecastmode != 1) wc += "AND d_date<='" % SKGServices::dateToSqlString(QDateTime(ui.kDateEnd->date())) % '\''; //For forecast based on scheduled operations
        if (oWhereClausForPreviousData) *oWhereClausForPreviousData = "d_date<'" % SKGServices::dateToSqlString(QDateTime(ui.kDateBegin->date())) % '\'';
        if (oWhereClausForForecastData) *oWhereClausForForecastData = "d_date > date('now')";
        break;
    case 5:
        // Timeline
        val = 13 - ui.kTimeline->value();
        switch (ui.kInterval->currentIndex()) {
        case 0:
            // Interval is in days
            a = a.addDays(1 - val);
            b = a.addDays(1);
            break;
        case 1:
            // Interval is in weeks
            a = a.addDays(8 - 7 * val - a.dayOfWeek());
            b = a.addDays(6);
            break;
        case 2:
            // Interval is in months
            a = a.addDays(1 - a.day()).addMonths(1 - val);
            b = a.addMonths(1).addDays(-1);
            break;
        case 3:
            // Interval is in years
            a = a.addDays(1 - a.day()).addMonths(1 - a.month()).addYears(1 - val);
            b = a.addYears(1).addDays(-1);
            break;
        }
        ui.kDateBegin->setDate(a);
        ui.kDateEnd->setDate(b);
        wc = "d_date>='" % SKGServices::dateToSqlString(QDateTime(ui.kDateBegin->date())) % '\'';
        if (forecastmode != 1) wc += "AND d_date<='" % SKGServices::dateToSqlString(QDateTime(ui.kDateEnd->date())) % '\''; //For forecast based on scheduled operations
        if (oWhereClausForPreviousData) *oWhereClausForPreviousData = "d_date<'" % SKGServices::dateToSqlString(QDateTime(ui.kDateBegin->date())) % '\'';
        if (oWhereClausForForecastData) *oWhereClausForForecastData = "d_date>'" % SKGServices::dateToSqlString(QDateTime(ui.kDateEnd->date())) % '\'';
        break;
    default:
        // Take all dates
        a = a.addYears(-1);
        ui.kDateBegin->setDate(a);
        ui.kDateEnd->setDate(b);
        wc = "1=1";
        if (oWhereClausForPreviousData) *oWhereClausForPreviousData = "1=0";
        if (oWhereClausForForecastData) *oWhereClausForForecastData = "d_date > date('now')";
        break;
    }

    wc = "((" % wc % ") OR d_date='0000') AND d_date!='0000-00-00'";
    if (oWhereClausForPreviousData) *oWhereClausForPreviousData = "((" % *oWhereClausForPreviousData % ") OR d_date='0000-00-00')";

    QString operationTypes;
    if (ui.kIncomes->isChecked() && !ui.kExpenses->isChecked()) {
        operationTypes = "t_TYPEEXPENSE='+'";
    } else if (ui.kExpenses->isChecked() && !ui.kIncomes->isChecked()) {
        operationTypes = "t_TYPEEXPENSE='-'";
    }
    if (!operationTypes.isEmpty()) {
        QString condition = " AND " % operationTypes;
        wc += condition;
        if (oWhereClausForPreviousData) *oWhereClausForPreviousData += condition;
    }

    if (ui.kTransfers->isChecked() == false) {
        QString condition = " AND t_TRANSFER='N'";
        wc += condition;
        if (oWhereClausForPreviousData) *oWhereClausForPreviousData += condition;
    }

    if (!m_operationWhereClause.isEmpty()) {
        QString condition = " AND (" % m_operationWhereClause % ')';
        wc += condition;
        if (oWhereClausForPreviousData) *oWhereClausForPreviousData += condition;
    }

    return wc;
}

void SKGReportPluginWidget::onSelectionChanged()
{
    if (m_openReportAction) m_openReportAction->setEnabled(ui.kTableWithGraph->table()->selectedItems().count() > 0);
}

void SKGReportPluginWidget::refresh()
{
    int p = ui.kPeriod->currentIndex();

    bool dateCol = (m_attsForColumns.value(ui.kColumns->currentIndex()).startsWith(QLatin1String("d_")));
    if (!dateCol) ui.kForecastCmb->setCurrentIndex(0);
    ui.kForecastCmb->setEnabled(dateCol);
    ui.kForecastValue->setEnabled(ui.kForecastCmb->currentIndex() > 0);
    ui.kLineRemove->setEnabled(m_attsForLinesAdded.count());

    int mode = ui.kMode->itemData(ui.kMode->currentIndex()).toInt();
    ui.kCorrectedBy->setEnabled(mode != 3);

    //Check dates
    ui.kDateSelect->setVisible(p > 0);
    ui.kDateSelect->setEnabled(p == 4);
    ui.kTimeline->setVisible(p == 5);

    QDate d1 = ui.kDateBegin->date();
    QDate d2 = ui.kDateEnd->date();
    if (d1 > d2) {
        ui.kDateBegin->setDate(d2);
        ui.kDateEnd->setDate(d1);
    }

    //Check income & expense
    if (!ui.kIncomes->isChecked() && !ui.kExpenses->isChecked()) {
        if (sender() == ui.kIncomes) ui.kExpenses->setChecked(true);
        else ui.kIncomes->setChecked(true);
    }

    bool current = (p == 1);
    bool previous = (p == 2);
    bool last = (p == 3);
    bool timeline = (p == 5);

    ui.kInterval->setVisible(current || previous || last || timeline);
    ui.kNbIntervals->setVisible(last || previous);
    ui.kForecastFrm->setEnabled(!timeline);
    if (timeline) ui.kForecastCmb->setCurrentIndex(0);

    m_timer.start(300);
}

void SKGReportPluginWidget::refreshActivated()
{
    dataModified("", 0);
}

void SKGReportPluginWidget::pageChanged()
{
    if (m_refreshNeeded) dataModified("", 0);
}

void SKGReportPluginWidget::dataModified(const QString& iTableName, int iIdTransaction)

{
    SKGTRACEIN(10, "SKGReportPluginWidget::dataModified");
    Q_UNUSED(iIdTransaction);

    //Refresh panel
    QSqlDatabase* db = getDocument()->getDatabase();
    setEnabled(db != NULL);
    //Check if needed
    if (db != NULL && (iTableName == "v_operation_consolidated" || iTableName.isEmpty())) {
        SKGError err;
        SKGDocumentBank* doc = static_cast<SKGDocumentBank*>(getDocument());
        if (doc) {
            //Check if update is needed
            SKGTabPage* page = SKGTabPage::parentTabPage(this->parentWidget());
            SKGTabPage* cpage = SKGMainPanel::getMainPanel()->currentPage();
            if (page != NULL && page != cpage) {
                m_refreshNeeded = true;
                return;
            }

            QString ParametersUsed = getState() % ';' % SKGServices::intToString(doc->getTransactionToProcess());
            if (ParametersUsed == m_previousParametersUsed) {
                SKGTRACEL(10) << "Same parameters. Refresh ignored" << endl;
                return;
            }

            QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
            m_refreshNeeded = false;
            m_previousParametersUsed = ParametersUsed;

            //Fill line and create title
            QString lineString;
            QString title;
            int nb = m_attsForLinesAdded.count();
            for (int i = 0; i < nb; ++i) {
                lineString += doc->getDisplay(m_attsForLinesAdded.at(i));
                if (i < nb - 1) lineString += OBJECTSEPARATOR;
            }
            if (ui.kLines->currentIndex() > 0 && nb) {
                lineString += OBJECTSEPARATOR;
            }
            title = lineString;
            if (ui.kLines->currentIndex() > 0 || nb == 0) {
                title += ui.kLines->text();
            }
            ui.kLineLabel->setVisible(nb > 0);
            ui.kLineLabel->setText(lineString);
            ui.kLineLabel->setToolTip(lineString);

            //Get parameters
            int col = ui.kColumns->currentIndex();
            int line = ui.kLines->currentIndex();

            if (col >= 0 && line >= 0) {
                int mode = ui.kMode->itemData(ui.kMode->currentIndex()).toInt();

                //Mode history ?
                bool modeHistory = (mode == 1 || mode == 8);

                //Get condition (must be done before forecast on scheduled operations)
                int nbVirtualColumn = 0;
                QString conditionPrevious;
                QString conditionForecast;
                QString condition = getConsolidatedWhereClause(&conditionPrevious, &conditionForecast);
                int forecastMode = ui.kForecastCmb->itemData(ui.kForecastCmb->currentIndex()).toInt();

                //Compute forecast on scheduled operations
                if (forecastMode == 1 || forecastMode == 4) {
                    //Create operations
                    QDate lastDate = QDate::currentDate().addDays(ui.kForecastValue->value() * 365.0 / ui.kForecastValue->maximum());
                    if (forecastMode == 1) {
                        //Create scheduled operations
                        doc->beginTransaction("#INTERNAL#" % i18nc("Progression step", "Create scheduled operations"));
                        int nbInserted = 0;
                        SKGRecurrentOperationObject::process(doc, nbInserted, true, lastDate);
                    } else {
                        //Create budgeted operations
                        SKGObjectBase::SKGListSKGObjectBase budgets;
                        if (!err) err = doc->getObjects("v_budget",
                                                            "t_PERIOD>STRFTIME('%Y-%m', date('now')) AND "
                                                            "t_PERIOD<=STRFTIME('%Y-%m', '" % SKGServices::dateToSqlString(QDateTime(lastDate)) % "')", budgets);
                        int nb = budgets.count();
                        if (nb) {
                            //Get most active account
                            SKGStringListList list;
                            err = doc->executeSelectSqliteOrder("SELECT account.id FROM account, operation WHERE operation.rd_account_id=account.id AND d_date>date('now', '-3 month') GROUP BY (t_name) ORDER BY COUNT(1) DESC LIMIT 1", list);
                            if (!err && list.count() == 2) {
                                SKGAccountObject account(doc, SKGServices::stringToInt(list.at(1).at(0)));
                                err = account.load();

                                //Get unit
                                SKGUnitObject unit(doc);
                                if (!err) err = unit.setName(doc->getPrimaryUnit().Name);
                                if (!err) err = unit.setSymbol(doc->getPrimaryUnit().Symbol);
                                if (!err) err = unit.load();


                                doc->beginTransaction("#INTERNAL#" % i18nc("Progression step", "Create budgeted operations"), nb);
                                for (int i = 0; !err && i < nb; ++i) {
                                    SKGBudgetObject budget = budgets.at(i);

                                    SKGCategoryObject cat;
                                    budget.getCategory(cat);

                                    SKGOperationObject op;
                                    if (!err) err = account.addOperation(op);
                                    if (!err) err = op.setDate(QDate(budget.getYear(), qMax(budget.getMonth(), 1), 1));
                                    if (!err) err = op.setUnit(unit);
                                    if (!err) err = op.save();

                                    SKGSubOperationObject sop;
                                    if (!err) err = op.addSubOperation(sop);
                                    if (!err) err = sop.setCategory(cat);
                                    if (!err) err = sop.setQuantity(budget.getBudgetedAmount());
                                    if (!err) err = sop.save();

                                    if (!err) err = doc->stepForward(i + 1);
                                }
                            }
                        }
                    }
                    ui.kForecastValue->setToolTip(SKGServices::dateToSqlString(QDateTime(lastDate)));
                } else {
                    ui.kForecastValue->setToolTip("");
                    conditionForecast = "1=0";
                }

                //Execute sql order
                SKGStringListList table;
                QString tableName = "v_operation_consolidated";
                QString attCol = m_attsForColumns[col ];
                if (attCol == "#NOTHING#") attCol = "";
                if (attCol.startsWith(QLatin1String("p_"))) {
                    QString propertyName = attCol.right(attCol.length() - 2);
                    attCol = "IFNULL((SELECT t_value FROM parameters WHERE t_uuid_parent=v_operation_consolidated.id||'-operation' AND t_name='" % propertyName % "'), '#NULL#')";
                }

                QStringList listAtt = m_attsForLinesAdded;
                if (listAtt.count() == 0 || line > 0) listAtt.push_back(m_attsForLines.at(line));
                QString attLine;
                int nb = listAtt.count();
                for (int i = 0; i < nb; ++i) {
                    QString att = listAtt.at(i);
                    if (att == "#NOTHING#") att = "";
                    if (att.startsWith(QLatin1String("p_"))) {
                        QString propertyName = att.right(att.length() - 2);
                        att = "IFNULL((SELECT t_value FROM parameters WHERE t_uuid_parent=v_operation_consolidated.id||'-operation' AND t_name='" % propertyName % "'), '#NULL#')";
                    }
                    if (att.isEmpty()) att = '\'' % i18nc("Noun", "All") % '\'';

                    if (!attLine.isEmpty()) attLine += "||'" % OBJECTSEPARATOR % "'||";
                    attLine += att;
                }

                if ((modeHistory || forecastMode) && !attCol.isEmpty()) {
                    tableName = "(SELECT ";
                    if (!modeHistory) {
                        tableName += "d_date, ";
                        tableName += attCol % "||(CASE WHEN " % conditionForecast % " THEN '999' ELSE '' END) AS " % m_attsForColumns[col ] % ",* FROM v_operation_consolidated) as v_operation_consolidated";
                    } else {
                        if (attCol != "d_date") tableName += "(CASE WHEN (" % conditionPrevious % ") AND f_REALQUANTITY<>0 THEN '0000' ELSE d_date END) AS d_date, ";
                        tableName += "(CASE WHEN (" % conditionPrevious % ") AND f_REALQUANTITY<>0 THEN '0000' ELSE " % attCol % "||(CASE WHEN " % conditionForecast % " THEN '999' ELSE '' END) END) AS " % m_attsForColumns[col ] % ",* FROM v_operation_consolidated) as v_operation_consolidated";
                    }
                }

                //Remove #NULL# columns and lines
                if (!attLine.isEmpty()) condition = '(' % condition % ") AND " % attLine % " NOT LIKE '%#NULL#%'";
                if (!attCol.isEmpty()) condition = '(' % condition % ") AND " % attCol % " NOT LIKE '%#NULL#%'";

                QString coef;
                if (ui.kCorrectedBy->currentIndex() > 0 && mode != 3) {
                    SKGUnitObject unit = ui.kCorrectedBy->getUnit();
                    QString id = SKGServices::intToString(unit.getID());
                    coef = "*COALESCE((SELECT u1.f_quantity FROM unitvalue u1 where u1.rd_unit_id=" % id % " and u1.d_date=(SELECT MAX(u2.d_date) FROM unitvalue u2 where u2.rd_unit_id=" % id % " and u2.d_date<=v_operation_consolidated.d_date)), (SELECT u1.f_quantity FROM unitvalue u1 where u1.rd_unit_id=" % id % " and u1.d_date=(SELECT MIN(u2.d_date) FROM unitvalue u2 where u2.rd_unit_id=" % id % ")),0)";
                }

                if (!err) err = doc->getConsolidatedView(tableName, attCol, attLine, "f_REALCURRENTAMOUNT" % coef,
                                    (mode == 3 ? "COUNT" : "TOTAL"), condition, table);
                int nbLines = table.count();
                IFSKGTRACEL(10) {
                    QStringList dump = SKGServices::tableToDump(table, SKGServices::DUMP_TEXT);
                    int nbl = dump.count();
                    for (int i = 0; i < nbl; ++i) {
                        SKGTRACE << dump[i] << endl;
                    }
                }

                if (forecastMode == 1 || forecastMode == 4) {
                    //Rollback create operations
                    doc->endTransaction(false);

                    //Compute nb date in futur
                    if (nbLines) {
                        QStringList line1 = table.at(0);
                        int nbCol = line1.count();
                        for (int i = 0; i < nbCol; ++i) {
                            QString title = line1.at(i);
                            if (title.endsWith(QLatin1String("999"))) {
                                title = title.left(title.count() - 3);
                                line1.replace(i, title);
                                ++nbVirtualColumn;
                            }
                        }
                        table.replace(0, line1);
                    }
                }

                if (!err) {
                    //Update title
                    if (nbLines) {
                        //Change title
                        QStringList line1 = table.at(0);
                        if (line1.count()) {
                            line1.replace(0, title);
                            table.replace(0, line1);
                        }
                    }

                    //Compute forecast on average
                    if (nbLines && (forecastMode == 2 || forecastMode == 3)) {
                        QStringList newLine = table.at(0);
                        int nbVals = newLine.count() - 1;
                        int percent = ui.kForecastValue->value();
                        if (nbVals >= 3 && percent > 0) {
                            //Compute nb value to add
                            nbVirtualColumn = percent * nbVals / FORECASTMAX;

                            //Build header
                            for (int i = 0; i < nbVirtualColumn; ++i) {
                                newLine.push_back(i18nc("Noun", "N %1", (i + 1)));
                            }
                            table.replace(0, newLine);

                            //Build values
                            for (int j = 1; j < nbLines; ++j) {
                                QStringList newLine = table.at(j);
                                for (int i = 0; i < nbVirtualColumn; ++i) {
                                    //Moving average
                                    double nv = 0;
                                    double weight = 0;
                                    for (int k = 0; k < nbVirtualColumn; ++k) {
                                        if (forecastMode == 2) {
                                            //Simple mode
                                            nv += SKGServices::stringToDouble(newLine.at(nbVals + i - k));
                                            ++weight;
                                        } else {
                                            //Weighted mode
                                            nv += (nbVirtualColumn - k) * SKGServices::stringToDouble(newLine.at(nbVals + i - k));
                                            weight += nbVirtualColumn - k;
                                        }
                                    }
                                    newLine.push_back(SKGServices::doubleToString(nv / weight));
                                }
                                table.replace(j, newLine);
                            }
                        }
                    }

                    //Create grouped by lines table
                    int nbLevelLineMax = 0;
                    {
                        SKGStringListList groupedByLineTable = table;
                        {
                            int nbCols = -1;
                            QString previousLineName = "###";
                            if (groupedByLineTable.count()) {
                                nbCols = groupedByLineTable.at(0).count();
                            }
                            for (int i = 1; nbCols && i < groupedByLineTable.count(); ++i) { //Dynamically modified
                                QStringList line = groupedByLineTable.at(i);
                                QString val = line.at(0);

                                //Rebuild val for the number of level
                                QStringList vals = val.split(OBJECTSEPARATOR);
                                int nbvals = vals.count();
                                if (nbvals > m_nbLevelLines + 1) {
                                    //Rebuild val
                                    val = "";
                                    for (int k = 0; k <= m_nbLevelLines; ++k) {
                                        val += vals[k];
                                        if (k != m_nbLevelLines) val += OBJECTSEPARATOR;
                                    }
                                }
                                nbLevelLineMax = qMax(nbLevelLineMax, nbvals - 1);

                                if (val == previousLineName) {
                                    //Current line is merged with previous one
                                    QStringList newLine;
                                    newLine.push_back(val);
                                    for (int k = 1; k < nbCols; ++k) {
                                        QString valstring1 = line.at(k);
                                        QString valstring2 = groupedByLineTable.at(i - 1).at(k);

                                        if (!valstring1.isEmpty() || !valstring2.isEmpty()) {
                                            double sum2 = 0;
                                            if (!valstring1.isEmpty()) sum2 += SKGServices::stringToDouble(valstring1);
                                            if (!valstring2.isEmpty()) sum2 += SKGServices::stringToDouble(valstring2);
                                            newLine.push_back(SKGServices::doubleToString(sum2));
                                        } else {
                                            newLine.push_back("");
                                        }
                                    }

                                    groupedByLineTable.replace(i - 1, newLine);

                                    //Remove current line
                                    groupedByLineTable.removeAt(i);
                                    --i;
                                } else {
                                    //Current line is just modified
                                    QStringList newLine;
                                    newLine.push_back(val);
                                    for (int k = 1; k < nbCols; ++k) {
                                        newLine.push_back(line.at(k));
                                    }
                                    groupedByLineTable.replace(i, newLine);

                                    previousLineName = val;
                                }
                            }
                        }
                        table = groupedByLineTable;
                    }

                    //Create grouped by columns table
                    int nbLevelColMax = 0;
                    {
                        SKGStringListList groupedByColTable = table;
                        int nbLines = groupedByColTable.count();
                        if (nbLines) {
                            QString previousColumnName = "###";
                            for (int i = 1; nbLines && i < groupedByColTable.at(0).count(); ++i) { //Dynamically modified
                                QString val = groupedByColTable.at(0).at(i);

                                //Rebuild val for the number of level
                                QStringList vals = val.split(OBJECTSEPARATOR);
                                int nbvals = vals.count();
                                if (nbvals > m_nbLevelColumns + 1) {
                                    //Rebuild val
                                    val = "";
                                    for (int k = 0; k <= m_nbLevelColumns; ++k) {
                                        val += vals[k];
                                        if (k != m_nbLevelColumns) val += OBJECTSEPARATOR;
                                    }
                                }
                                nbLevelColMax = qMax(nbLevelColMax, nbvals - 1);

                                if (val == previousColumnName) {
                                    //Current column is merged with previous one
                                    QStringList newLine = groupedByColTable.at(0);
                                    newLine.removeAt(i);
                                    groupedByColTable.replace(0, newLine);

                                    for (int k = 1; k < nbLines; ++k) {
                                        newLine = groupedByColTable.at(k);
                                        QString valstring1 = newLine.at(i);
                                        QString valstring2 = newLine.at(i - 1);

                                        if (!valstring1.isEmpty() || !valstring2.isEmpty()) {
                                            double sum2 = 0;
                                            if (!valstring1.isEmpty()) sum2 += SKGServices::stringToDouble(valstring1);
                                            if (!valstring2.isEmpty()) sum2 += SKGServices::stringToDouble(valstring2);
                                            newLine.replace(i - 1, SKGServices::doubleToString(sum2));
                                        } else {
                                            newLine.removeAt(i - 1);
                                        }
                                        newLine.removeAt(i);
                                        groupedByColTable.replace(k, newLine);
                                    }

                                    //Remove current line
                                    --i;
                                } else {
                                    //Current column is just modified
                                    QStringList newLine = groupedByColTable.at(0);
                                    newLine.replace(i, val);
                                    groupedByColTable.replace(0, newLine);

                                    previousColumnName = val;
                                }
                            }
                        }
                        table = groupedByColTable;
                    }

                    //Initialize parameters
                    SKGServices::SKGUnitInfo primaryUnit = doc->getPrimaryUnit();
                    SKGServices::SKGUnitInfo secondaryUnit = doc->getSecondaryUnit();

                    SKGTableWithGraph::DisplayAdditionalFlag dmode = SKGTableWithGraph::NONE;
                    if (m_attsForColumns.at(col).startsWith(QLatin1String("d_"))) dmode |= SKGTableWithGraph::AVERAGE | SKGTableWithGraph::LIMITS;

                    //Create history (must be done after groups)
                    if (modeHistory) table = SKGServices::getHistorizedTable(table);
                    else {
                        //Add SUM if not in HISTORY mode
                        dmode |= SKGTableWithGraph::SUM;
                    }

                    //Mode percent
                    bool modePercent = (mode == 2 || mode == 4 || mode == 5 || mode == 6);
                    if (modePercent) {
                        primaryUnit.Symbol = '%';
                        SKGServices::SKGUnitInfo empty;
                        secondaryUnit = empty;

                        table = SKGServices::getPercentTable(table, (mode == 2 || mode == 5), (mode == 5 || mode == 6));
                    }

                    //Mode base 100
                    if (mode == 7 || mode == 8) {
                        table = SKGServices::getBase100Table(table);
                    }

                    //Mode Count
                    if (mode == 3) {
                        primaryUnit.Symbol = ' ';
                        SKGServices::SKGUnitInfo empty;
                        secondaryUnit = empty;
                    }

                    //Set data
                    ui.kTableWithGraph->setData(table, primaryUnit, secondaryUnit, dmode, nbVirtualColumn);

                    //Enable/Disable buttons
                    m_nbLevelLines = qMin(nbLevelLineMax, m_nbLevelLines);
                    ui.kLineDown->setEnabled(m_nbLevelLines > 0);
                    ui.kLineUp->setEnabled(m_nbLevelLines < nbLevelLineMax);

                    m_nbLevelColumns = qMin(nbLevelColMax, m_nbLevelColumns);
                    ui.kColDown->setEnabled(m_nbLevelColumns > 0);
                    ui.kColUp->setEnabled(m_nbLevelColumns < nbLevelColMax);
                }
            }
            QApplication::restoreOverrideCursor();

        }

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

void SKGReportPluginWidget::setSettings()
{
    ui.kTableWithGraph->setAxisColor(skgreport_settings::axisColor());
    ui.kTableWithGraph->setGridColor(skgreport_settings::gridColor());
    ui.kTableWithGraph->setMinColor(skgreport_settings::minColor());
    ui.kTableWithGraph->setMaxColor(skgreport_settings::maxColor());
    ui.kTableWithGraph->setAverageColor(skgreport_settings::averageColor());
    ui.kTableWithGraph->setTendencyColor(skgreport_settings::tendencyColor());
    ui.kTableWithGraph->setBackgroundColor(skgreport_settings::backgroundColor());
    ui.kTableWithGraph->setTextColor(skgreport_settings::textColor());
    ui.kTableWithGraph->setOutlineColor(skgreport_settings::outlineColor());

    ui.kTableWithGraph->setAntialiasing(skgreport_settings::antialiasing());

    ui.kTableWithGraph->redrawGraphDelayed();
}

#include "skgreportpluginwidget.moc"
