/***************************************************************************
 *   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 implements classes SKGRecurrentOperationObject.
 *
 * @author Stephane MANKOWSKI / Guillaume DE BURE
 */
#include "skgrecurrentoperationobject.h"
#include "skgoperationobject.h"
#include "skgsuboperationobject.h"
#include "skgservices.h"
#include "skgdocumentbank.h"
#include "skgtraces.h"

#include <klocale.h>

SKGRecurrentOperationObject::SKGRecurrentOperationObject(SKGDocument* iDocument, int iID): SKGObjectBase(iDocument, "v_recurrentoperation", iID)
{
}

SKGRecurrentOperationObject::~SKGRecurrentOperationObject()
{
}

SKGRecurrentOperationObject::SKGRecurrentOperationObject(const SKGRecurrentOperationObject& iObject)
    : SKGObjectBase(iObject)
{
}

SKGRecurrentOperationObject::SKGRecurrentOperationObject(const SKGObjectBase& iObject)
{
    if (iObject.getRealTable() == "recurrentoperation") {
        copyFrom(iObject);
    } else {
        *this = SKGObjectBase(iObject.getDocument(), "v_recurrentoperation", iObject.getID());
    }
}

const SKGRecurrentOperationObject& SKGRecurrentOperationObject::operator= (const SKGObjectBase& iObject)
{
    copyFrom(iObject);
    return *this;
}

SKGError SKGRecurrentOperationObject::getParentOperation(SKGOperationObject& oOperation) const
{
    SKGObjectBase objTmp;
    SKGError err = getDocument()->getObject("v_operation", "id=" % getAttribute("rd_operation_id"), objTmp);
    oOperation = objTmp;
    return err;
}

SKGError SKGRecurrentOperationObject::setParentOperation(const SKGOperationObject& iOperation)
{
    return setAttribute("rd_operation_id", SKGServices::intToString(iOperation.getID()));
}

SKGError SKGRecurrentOperationObject::setPeriodIncrement(int iIncrement)
{
    return setAttribute("i_period_increment", SKGServices::intToString(iIncrement));
}

int SKGRecurrentOperationObject::getPeriodIncrement() const
{
    return SKGServices::stringToInt(getAttribute("i_period_increment"));
}

SKGRecurrentOperationObject::PeriodUnit SKGRecurrentOperationObject::getPeriodUnit() const
{
    QString t_period_unit = getAttribute("t_period_unit");
    if (t_period_unit == "D") return SKGRecurrentOperationObject::DAY;
    else if (t_period_unit == "W") return SKGRecurrentOperationObject::WEEK;
    else if (t_period_unit == "M") return SKGRecurrentOperationObject::MONTH;
    return SKGRecurrentOperationObject::YEAR;
}

SKGError SKGRecurrentOperationObject::setPeriodUnit(SKGRecurrentOperationObject::PeriodUnit iPeriod)
{
    return setAttribute("t_period_unit", (iPeriod == SKGRecurrentOperationObject::DAY ? "D" : (iPeriod == SKGRecurrentOperationObject::WEEK ? "W" : (iPeriod == SKGRecurrentOperationObject::MONTH ? "M" : "Y"))));
}

SKGError SKGRecurrentOperationObject::setAutoWriteDays(int iDays)
{
    return setAttribute("i_auto_write_days", SKGServices::intToString(iDays));
}

int SKGRecurrentOperationObject::getAutoWriteDays() const
{
    return SKGServices::stringToInt(getAttribute("i_auto_write_days"));
}

SKGError SKGRecurrentOperationObject::setWarnDays(int iDays)
{
    return setAttribute("i_warn_days", SKGServices::intToString(iDays));
}

int SKGRecurrentOperationObject::getWarnDays() const
{
    return SKGServices::stringToInt(getAttribute("i_warn_days"));
}

bool SKGRecurrentOperationObject::hasTimeLimit() const
{
    return (getAttribute("t_times") == "Y" ? true : false);
}

SKGError SKGRecurrentOperationObject::timeLimit(bool iTimeLimit)
{
    return setAttribute("t_times", iTimeLimit ? "Y" : "N");
}

SKGError SKGRecurrentOperationObject::setTimeLimit(const QDate& iLastDate)
{
    //Get parameters
    QDate firstDate = this->getDate();
    SKGRecurrentOperationObject::PeriodUnit period = this->getPeriodUnit();
    int occu = this->getPeriodIncrement();

    //Compute nb time
    int nbd = firstDate.daysTo(iLastDate);
    if (period == SKGRecurrentOperationObject::DAY) nbd = nbd / occu;
    else if (period == SKGRecurrentOperationObject::WEEK) nbd = nbd / (7 * occu);
    else if (period == SKGRecurrentOperationObject::MONTH) {
        nbd = (iLastDate.day() >= firstDate.day() ? 0 : -1) + (iLastDate.year() - firstDate.year()) * 12 + (iLastDate.month() - firstDate.month());
    } else if (period == SKGRecurrentOperationObject::YEAR) nbd = nbd / (365 * occu);

    if (nbd < -1) nbd = -1;
    return setTimeLimit(nbd + 1);
}

SKGError SKGRecurrentOperationObject::setTimeLimit(int iTimeLimit)
{
    return setAttribute("i_nb_times", SKGServices::intToString(iTimeLimit));
}
int SKGRecurrentOperationObject::getTimeLimit() const
{
    return SKGServices::stringToInt(getAttribute("i_nb_times"));
}

SKGError SKGRecurrentOperationObject::setDate(const QDate& iDate)
{
    return setAttribute("d_date", SKGServices::dateToSqlString(QDateTime(iDate)));
}

QDate SKGRecurrentOperationObject::getNextDate() const
{
    QDate nextDate = getDate();
    SKGRecurrentOperationObject::PeriodUnit punit = getPeriodUnit();
    int p = getPeriodIncrement();
    if (punit == SKGRecurrentOperationObject::DAY) nextDate = nextDate.addDays(p);
    else if (punit == SKGRecurrentOperationObject::WEEK) nextDate = nextDate.addDays(7 * p);
    else if (punit == SKGRecurrentOperationObject::MONTH) nextDate = nextDate.addMonths(p);
    else if (punit == SKGRecurrentOperationObject::YEAR) nextDate = nextDate.addYears(p);
    return nextDate;
}

QDate SKGRecurrentOperationObject::getDate() const
{
    return SKGServices::stringToTime(getAttribute("d_date")).date();
}

SKGError SKGRecurrentOperationObject::warnEnabled(bool iWarn)
{
    return setAttribute("t_warn", iWarn ? "Y" : "N");
}

bool SKGRecurrentOperationObject::isWarnEnabled() const
{
    return (getAttribute("t_warn") == "Y" ? true : false);
}

SKGError SKGRecurrentOperationObject::autoWriteEnabled(bool iAutoWrite)
{
    return setAttribute("t_auto_write", iAutoWrite ? "Y" : "N");

}

bool SKGRecurrentOperationObject::isAutoWriteEnabled() const
{
    return (getAttribute("t_auto_write") == "Y" ? true : false);
}

SKGError SKGRecurrentOperationObject::getRecurredOperations(SKGListSKGObjectBase& oOperations) const
{
    return getDocument()->getObjects("v_operation", "r_recurrentoperation_id=" % SKGServices::intToString(getID()), oOperations);
}

SKGError SKGRecurrentOperationObject::process(int& oNbInserted, bool iForce, const QDate& iDate)
{
    SKGError err;
    SKGTRACEINRC(10, "SKGRecurrentOperationObject::process", err);
    oNbInserted = 0;

    if (!hasTimeLimit() || getTimeLimit() > 0) {
        if (isAutoWriteEnabled() || iForce) {
            QDate nextDate = getDate();
            if (nextDate.isValid() && iDate >= nextDate.addDays(-getAutoWriteDays())) {
                SKGOperationObject op;
                err = getParentOperation(op);
                if (!err) {
                    //Create the duplicated operation
                    SKGOperationObject newOp;
                    err = op.duplicate(newOp, nextDate);

                    if (!op.isTemplate()) {
                        //Set old op as recurrent
                        if (!err) err = op.setAttribute("r_recurrentoperation_id", SKGServices::intToString(getID()));
                        if (!err) err = op.save();

                        //Set new operation as reference
                        if (!err) err = setParentOperation(newOp);
                    } else {
                        //Set new op as recurrent
                        if (!err) err = newOp.setAttribute("r_recurrentoperation_id", SKGServices::intToString(getID()));
                        if (!err) err = newOp.save();
                    }

                    if (!err) err = setDate(getNextDate());
                    if (!err && hasTimeLimit()) err = setTimeLimit(getTimeLimit() - 1);
                    if (!err) err = save();
                    if (!err) err = load();

                    //Process again in case of multi insert needed
                    int nbi = 0;
                    if (!err) err = process(nbi, iForce, iDate);
                    oNbInserted = oNbInserted + 1 + nbi;

                    //Send message
                    if (!err) err = newOp.load();
                    if (!err) {
                        err = getDocument()->sendMessage(i18nc("An information message", "Operation '%1' has been inserted", newOp.getDisplayName()));
                    }
                }
            }
        }

        if (isWarnEnabled() && !err) {
            QDate nextDate = getDate();
            if (QDate::currentDate() >= nextDate.addDays(-getWarnDays())) {
                SKGOperationObject op;
                err = getParentOperation(op);
                if (!err) {
                    int nbdays = QDate::currentDate().daysTo(nextDate);
                    if (nbdays > 0) {
                        QString objname = op.getDisplayName();
                        err = getDocument()->sendMessage(i18np("Operation '%2' will be inserted in one day", "Operation '%2' will be inserted in %1 days", nbdays, objname));
                    }
                }
            }
        }
    }
    return err;
}

SKGError SKGRecurrentOperationObject::process(SKGDocumentBank* iDocument, int& oNbInserted, bool iForce, const QDate& iDate)
{
    SKGError err;
    oNbInserted = 0;

    //Get all operation with auto_write
    SKGListSKGObjectBase recuOps;
    if (iDocument) err = iDocument->getObjects("v_recurrentoperation", "", recuOps);

    int nb = recuOps.count();
    for (int i = 0; !err && i < nb; ++i) {
        SKGRecurrentOperationObject recu = recuOps.at(i);
        int nbi = 0;
        err = recu.process(nbi, iForce, iDate);
        oNbInserted += nbi;
    }

    return err;
}

#include "skgrecurrentoperationobject.moc"

