/***************************************************************************
 *   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());
}

void SKGPeriodEdit::getDates(SKGPeriodEdit::PeriodMode iPeriod, SKGPeriodEdit::PeriodInterval iInterval, int iValue, QDate& oBeginDate, QDate& oEndDate, const QDate& iDate)
{
    QDate a = iDate;
    QDate b = a;
    int val = iValue;

    switch (iInterval) {
    case DAY:
        // Interval is in days
        break;
    case WEEK:
        // Interval is in weeks
        val *= 7;
        break;
    case MONTH:
    default:
        // Interval is in months
        break;
    case QUARTER:
        // Interval is in quater
        val *= 3;
        break;
    case SEMESTER:
        // Interval is in semester
        val *= 6;
        break;
    case YEAR:
        // Interval is in years
        break;
    }

    switch (iPeriod) {
    case CURRENT:
        // Current Interval
        switch (iInterval) {
        case DAY:
            // Interval is in days
            break;
        case WEEK:
            // Interval is in weeks
            a = a.addDays(1 - a.dayOfWeek());
            b = a.addDays(7 - 1);
            break;
        case MONTH:
        default:
            // Interval is in months
            a = a.addDays(1 - a.day());
            b = a.addMonths(1).addDays(-1);
            break;
        case YEAR:
            // Interval is in years
            a = a.addDays(1 - a.day()).addMonths(1 - a.month());
            b = a.addYears(1).addDays(-1);
            break;
        case QUARTER:
            // Interval is in quarter
            a = a.addDays(1 - a.day()).addMonths(1 - ((a.month() - 1) % 4));
            b = a.addMonths(3).addDays(-1);
            break;
        case SEMESTER:
            // Interval is in semester
            a = a.addDays(1 - a.day()).addMonths(- ((a.month() - 1) % 6));
            b = a.addMonths(6).addDays(-1);
            break;
        }
        break;
    case PREVIOUS:
        // Previous Interval
        switch (iInterval) {
        case DAY:
            // Interval is in days
            b = b.addDays(-1);
            a = b.addDays(-val + 1);
            break;
        case WEEK:
            // Interval is in weeks
            b = b.addDays(-a.dayOfWeek());
            a = b.addDays(-val + 1);
            break;
        case MONTH:
        default:
            // Interval is in months
            b = b.addDays(-b.day());
            a = b.addDays(1).addMonths(-val);
            break;
        case YEAR:
            // Interval is in years
            b = b.addMonths(1 - b.month()).addDays(-b.day());
            a = b.addDays(1).addYears(-val);
            break;
        case QUARTER:
            // Interval is in quarter
            b = b.addMonths(1 - ((b.month() - 1) % 4)).addDays(-b.day());
            a = b.addDays(1).addMonths(-val);
            break;
        case SEMESTER:
            // Interval is in semester
            b = b.addMonths(- ((b.month() - 1) % 6)).addDays(-b.day());
            a = b.addDays(1).addMonths(-val);
            break;
        }
        break;
    case LAST:
        // Last Interval
        switch (iInterval) {
        case DAY:
            // Interval is in days
            a = a.addDays(-val);
            break;
        case WEEK:
            // Interval is in weeks
            a = a.addDays(-val);
            break;
        case MONTH:
        default:
            // Interval is in months
            a = a.addMonths(-val);
            break;
        case YEAR:
            // Interval is in years
            a = a.addYears(-val);
            break;
        case QUARTER:
            // Interval is in quarter
            a = a.addMonths(-val);
            break;
        case SEMESTER:
            // Interval is in semester
            a = a.addMonths(-val);
            break;
        }
        a = a.addDays(1);
        break;
    case TIMELINE:
        // Timeline
        switch (iInterval) {
        case DAY:
            // Interval is in days
            a = a.addDays(iValue - 12);
            b = a;
            break;
        case WEEK:
            // Interval is in weeks
            a = a.addDays(1 - a.dayOfWeek()).addDays((iValue - 12) * 7);
            b = a.addDays(7 - 1);
            break;
        case MONTH:
        default:
            // Interval is in months
            a = a.addDays(1 - a.day()).addMonths(iValue - 12);
            b = a.addMonths(1).addDays(-1);
            break;
        case YEAR:
            // Interval is in years
            a = a.addDays(1 - a.day()).addMonths(1 - a.month()).addYears(iValue - 12);
            b = a.addYears(1).addDays(-1);
            break;
        case QUARTER:
            // Interval is in quarter
            a = a.addDays(1 - a.day()).addMonths(1 - ((a.month() - 1) % 4)).addMonths(3 * (iValue - 12));
            b = a.addMonths(3).addDays(-1);
            break;
        case SEMESTER:
            // Interval is in semester
            a = a.addDays(1 - a.day()).addMonths(- ((a.month() - 1) % 6)).addMonths(6 * (iValue - 12));
            b = a.addMonths(6).addDays(-1);
            break;
        }
        break;
    default:
        // Take all dates
        a = a.addYears(-1);
        break;
    }

    oBeginDate = a;
    oEndDate = b;
}

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

    SKGPeriodEdit::PeriodInterval interval = (SKGPeriodEdit::PeriodInterval) ui.kInterval->itemData(ui.kInterval->currentIndex()).toInt();
    QString strfFormat;
    QString sqlInterval;
    int val = ui.kNbIntervals->value();
    int one = 1;
    QDate a;
    QDate b;

    getDates(mode(), interval, mode() == TIMELINE ? ui.kTimeline->value() : val, a, b);

    switch (interval) {
    case DAY:
        // Interval is in days
        strfFormat = "'D'";
        sqlInterval = "DAY";
        break;
    case WEEK:
        // Interval is in weeks
        strfFormat = "'W'";
        sqlInterval = "DAY";
        val *= 7;
        one *= 7;
        break;
    case MONTH:
    default:
        // Interval is in months
        strfFormat = "'M'";
        sqlInterval = "MONTH";
        break;
    case QUARTER:
        // Interval is in quater
        strfFormat = "'Q'";
        sqlInterval = "MONTH";
        val *= 3;
        one *= 3;
        break;
    case SEMESTER:
        // Interval is in semester
        strfFormat = "'S'";
        sqlInterval = "MONTH";
        val *= 6;
        one *= 6;
        break;
    case YEAR:
        // Interval is in years
        strfFormat = "'Y'";
        sqlInterval = "YEAR";
        break;
    }

    switch (mode()) {
    case CURRENT:
        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:
        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:
        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
        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"
