/***************************************************************************
 *   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 period editor.
 *
 * @author Stephane MANKOWSKI / Guillaume DE BURE
 */
#include "skgperiodedit.h"

#include <QDomDocument>

#include "skgservices.h"
#include "skgtraces.h"

SKGPeriodEdit::SKGPeriodEdit(QWidget* iParent)
    : QWidget(iParent)
{
    ui.setupUi(this);

    ui.kPeriod->addItem(i18nc("Period mode", "All Dates"), SKGPeriodEdit::ALL);
    ui.kPeriod->addItem(i18nc("Period mode", "Current..."), SKGPeriodEdit::CURRENT);
    ui.kPeriod->addItem(i18nc("Period mode", "Previous..."), SKGPeriodEdit::PREVIOUS);
    ui.kPeriod->addItem(i18nc("Period mode", "Last..."), SKGPeriodEdit::LAST);
    ui.kPeriod->addItem(i18nc("Period mode", "Custom..."), SKGPeriodEdit::CUSTOM);
    ui.kPeriod->addItem(i18nc("Period mode", "Timeline..."), SKGPeriodEdit::TIMELINE);

    ui.kInterval->addItem(i18nc("Period interval", "day(s)"), 0);
    ui.kInterval->addItem(i18nc("Period interval", "week(s)"), 1);
    ui.kInterval->addItem(i18nc("Period interval", "month(s)"), 2);
    ui.kInterval->addItem(i18nc("Period interval", "quarter(s)"), 4);
    ui.kInterval->addItem(i18nc("Period interval", "semester(s)"), 5);
    ui.kInterval->addItem(i18nc("Period interval", "year(s)"), 3);

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

    connect(ui.kPeriod, SIGNAL(currentIndexChanged(int)), this, SLOT(refresh()));
    connect(ui.kInterval, SIGNAL(currentIndexChanged(int)), this, SLOT(refresh()));
    connect(ui.kDateBegin, SIGNAL(dateEntered(QDate)), this, SLOT(refresh()));
    connect(ui.kDateEnd, SIGNAL(dateEntered(QDate)), this, SLOT(refresh()));
    connect(ui.kNbIntervals, SIGNAL(valueChanged(int)), this, SLOT(refresh()));
    connect(ui.kTimeline, SIGNAL(valueChanged(int)), this, SLOT(refresh()));
    connect(ui.kFuture, SIGNAL(stateChanged(int)), this, SLOT(refresh()));
}

SKGPeriodEdit::~SKGPeriodEdit()
{}

QString SKGPeriodEdit::getState()
{
    SKGTRACEINFUNC(10);
    QDomDocument doc("SKGML");
    QDomElement root = doc.createElement("parameters");
    doc.appendChild(root);

    root.setAttribute("period", SKGServices::intToString(mode()));
    if (mode() == CUSTOM) {
        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->itemData(ui.kInterval->currentIndex()).toInt()));
    root.setAttribute("nb_intervals", SKGServices::intToString(ui.kNbIntervals->value()));
    root.setAttribute("timeline", SKGServices::intToString(ui.kTimeline->value()));
    root.setAttribute("future", ui.kFuture->isChecked() ? "Y" : "N");

    return doc.toString();
}

void SKGPeriodEdit::setState(const QString& iState)
{
    SKGTRACEINFUNC(10);

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

    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 future = root.attribute("future");

    // Default values
    if (period.isEmpty()) {
        period = '1';
    }
    if (interval.isEmpty()) {
        interval = '2';
    }
    if (nb_interval.isEmpty()) {
        nb_interval = '1';
    }
    if (timeline.isEmpty()) {
        timeline = '1';
    }
    ui.kPeriod->setCurrentIndex(ui.kPeriod->findData(SKGServices::stringToInt(period)));
    ui.kInterval->setCurrentIndex(ui.kInterval->findData(SKGServices::stringToInt(interval)));
    ui.kTimeline->setValue(SKGServices::stringToInt(timeline));
    ui.kNbIntervals->setValue(SKGServices::stringToInt(nb_interval));
    ui.kFuture->setChecked(future == "Y");
    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)));
    }

    refresh();
}

QString SKGPeriodEdit::text() const
{
    QString during = ui.kPeriod->text().remove("...");

    SKGPeriodEdit::PeriodMode m = mode();
    if (m == CUSTOM) {
        during = SKGServices::dateToSqlString(QDateTime(ui.kDateBegin->date())) % ' ' % SKGServices::dateToSqlString(QDateTime(ui.kDateEnd->date()));
    } else if (m != ALL) {
        if ((m == PREVIOUS || m == LAST) && ui.kNbIntervals->value() != 1) {
            during += ' ' % SKGServices::intToString(ui.kNbIntervals->value());
        }
        during += ' ' % ui.kInterval->text();
    }
    return during;
}


SKGPeriodEdit::PeriodMode SKGPeriodEdit::mode() const
{
    return static_cast<PeriodMode>(ui.kPeriod->itemData(ui.kPeriod->currentIndex()).toInt());
}

QString SKGPeriodEdit::getWhereClause(bool iForecast, QString* oWhereClausForPreviousData, QString*  oWhereClausForForecastData) const
{
    // Build where clause
    QString wc;

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

    switch (ui.kInterval->itemData(ui.kInterval->currentIndex()).toInt()) {
    case 0:
        // Interval is in days
        strfFormat = "'D'";
        sqlInterval = "DAY";
        break;
    case 1:
        // Interval is in weeks
        strfFormat = "'W'";
        sqlInterval = "DAY";
        val *= 7;
        one *= 7;
        break;
    case 2:
    default:
        // Interval is in months
        strfFormat = "'M'";
        sqlInterval = "MONTH";
        break;
    case 4:
        // Interval is in quater
        strfFormat = "'Q'";
        sqlInterval = "MONTH";
        val *= 3;
        one *= 3;
        break;
    case 5:
        // Interval is in semester
        strfFormat = "'S'";
        sqlInterval = "MONTH";
        val *= 6;
        one *= 6;
        break;
    case 3:
        // Interval is in years
        strfFormat = "'Y'";
        sqlInterval = "YEAR";
        break;
    }

    switch (mode()) {
    case CURRENT:
        // Current Interval
        switch (ui.kInterval->itemData(ui.kInterval->currentIndex()).toInt()) {
        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:
        default:
            // 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;
        case 4:
            // Interval is in quarter
            a = a.addDays(1 - a.day()).addMonths(1 - (a.month() % 4));
            b = a.addMonths(3).addDays(-1);
            break;
        case 5:
            // Interval is in semester
            a = a.addDays(1 - a.day()).addMonths(1 - (a.month() % 6));
            b = a.addMonths(6).addDays(-1);
            break;
        }
        if (ui.kFuture->isChecked()) {
            b = QDate(2099, 12, 31);
        }

        ui.kDateBegin->setDate(a);
        ui.kDateEnd->setDate(b);
        wc = "period(d_date, " % strfFormat % ") = (SELECT period(date('now'), " % strfFormat % "))";
        if (ui.kFuture->isChecked()) {
            wc +=  " OR d_date > (SELECT date('now'))";
        }
        if (oWhereClausForPreviousData) {
            *oWhereClausForPreviousData = "period(d_date, " % strfFormat % ") < (SELECT period(date('now'), " % strfFormat % "))";
        }
        if (oWhereClausForForecastData) {
            *oWhereClausForForecastData = "d_date > (SELECT date('now'))";
        }
        break;
    case PREVIOUS:
        // Previous Interval
        switch (ui.kInterval->itemData(ui.kInterval->currentIndex()).toInt()) {
        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:
        default:
            // Interval is in months
            b = b.addDays(-b.day());
            a = b.addDays(1).addMonths(-val);
            break;
        case 3:
            // Interval is in years
            b = b.addMonths(1 - b.month()).addDays(-b.day());
            a = b.addDays(1).addYears(-val);
            break;
        case 4:
            // Interval is in quarter
            b = b.addMonths(1 - (b.month() % 4)).addDays(-b.day());
            a = b.addDays(1).addMonths(-val);
            break;
        case 5:
            // Interval is in semester
            b = b.addMonths(1 - (b.month() % 6)).addDays(-b.day());
            a = b.addDays(1).addMonths(-val);
            break;
        }

        ui.kDateBegin->setDate(a);
        ui.kDateEnd->setDate(b);
        wc = "period(d_date, " % strfFormat % ")>=(SELECT period(date('now','start of month', '-" % SKGServices::intToString(val) % ' ' % sqlInterval % "')," % strfFormat % "))";
        if (iForecast) {
            wc += " AND period(d_date, " % strfFormat % ")<=(SELECT period(date('now','start of month', '-" % SKGServices::intToString(one) % ' ' % sqlInterval % "')," % strfFormat % "))";    // For forecast based on scheduled operations

            if (oWhereClausForPreviousData) {
                *oWhereClausForPreviousData = "period(d_date, " % strfFormat % ") < (SELECT period(date('now','start of month', '-" % SKGServices::intToString(val) % ' ' % sqlInterval % "')," % strfFormat % "))";
            }
            if (oWhereClausForForecastData) {
                *oWhereClausForForecastData = "period(d_date, " % strfFormat % ") > (SELECT period(date('now','start of month', '-" % SKGServices::intToString(one) % ' ' % sqlInterval % "')," % strfFormat % "))";
            }
            break;
        case LAST:
            // Last Interval
            switch (ui.kInterval->itemData(ui.kInterval->currentIndex()).toInt()) {
            case 0:
                // Interval is in days
                a = a.addDays(-val);
                break;
            case 1:
                // Interval is in weeks
                a = a.addDays(-val);
                break;
            case 2:
            default:
                // Interval is in months
                a = a.addMonths(-val);
                break;
            case 3:
                // Interval is in years
                a = a.addYears(-val);
                break;
            case 4:
                // Interval is in quarter
                a = a.addMonths(-val);
                break;
            case 5:
                // Interval is in semester
                a = a.addMonths(-val);
                break;
            }
            a = a.addDays(1);
            if (ui.kFuture->isChecked()) {
                b = QDate(2099, 12, 31);
            }

            ui.kDateBegin->setDate(a);
            ui.kDateEnd->setDate(b);

            wc = "d_date > (SELECT date('now','-" % SKGServices::intToString(val) % ' ' % sqlInterval % "'))";
            if (iForecast) {
                wc += " AND d_date<=(SELECT date('now'))";    // For forecast based on scheduled operations
            }
        }
        if (ui.kFuture->isChecked()) {
            wc += " OR d_date > (SELECT date('now'))";
        }
        if (oWhereClausForPreviousData) {
            *oWhereClausForPreviousData = "d_date <= (SELECT date('now','-" % SKGServices::intToString(val) % ' ' % sqlInterval % "'))";
        }
        if (oWhereClausForForecastData) {
            *oWhereClausForForecastData = "d_date > (SELECT date('now'))";
        }
        break;
    case CUSTOM:
        // Custom Date
        wc = "d_date>='" % SKGServices::dateToSqlString(QDateTime(ui.kDateBegin->date())) % '\'';
        if (iForecast) {
            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 > (SELECT date('now'))";
        }
        break;
    case TIMELINE:
        // Timeline
        val = 13 - ui.kTimeline->value();
        switch (ui.kInterval->itemData(ui.kInterval->currentIndex()).toInt()) {
        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:
        default:
            // 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;
        case 4:
            // Interval is in quarter
            b = b.addMonths(1 - (b.month() % 4)).addDays(-b.day()).addMonths(3 * (2 - val));;
            a = b.addDays(1).addMonths(-3);
            break;
        case 5:
            // Interval is in semester
            b = b.addMonths(1 - (b.month() % 6)).addDays(-b.day()).addMonths(6 * (2 - val));
            a = b.addDays(1).addMonths(-6);
            break;
        }
        ui.kDateBegin->setDate(a);
        ui.kDateEnd->setDate(b);
        wc = "d_date>='" % SKGServices::dateToSqlString(QDateTime(ui.kDateBegin->date())) % '\'';
        if (iForecast) {
            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 > (SELECT 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')";
    }

    return wc;
}

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

    // Check dates
    ui.kDateSelect->setVisible(p != ALL);
    ui.kDateSelect->setEnabled(p == CUSTOM);
    ui.kTimeline->setVisible(p == TIMELINE);
    ui.kFuture->setVisible(p == CURRENT || p == LAST);

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

    ui.kInterval->setVisible(p == CURRENT || p == PREVIOUS || p == LAST || p == TIMELINE);
    ui.kNbIntervals->setVisible(p == PREVIOUS || p == LAST);

    // Needed to refresh dates
    getWhereClause();

    emit changed();
}
#include "skgperiodedit.moc"
