/***************************************************************************
 *   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 defines classes SKGRuleObject.
 *
 *
 * @author Stephane MANKOWSKI / Guillaume DE BURE
 */
#include "skgruleobject.h"

#include <klocalizedstring.h>

#include <QDomDocument>

#include "skgunitvalueobject.h"
#include "skgoperationobject.h"
#include "skgdocumentbank.h"
#include "skgtraces.h"
#include "skgtransactionmng.h"

SKGRuleObject::SKGRuleObject(SKGDocument* iDocument, int iID)
    : SKGObjectBase(iDocument, "v_rule", iID)
{
}

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

SKGRuleObject::SKGRuleObject(const SKGObjectBase& iObject)
{
    if (iObject.getRealTable() == "rule") {
        copyFrom(iObject);
    } else {
        *this = SKGObjectBase(iObject.getDocument(), "v_rule", iObject.getID());
    }
}

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

SKGRuleObject::~SKGRuleObject()
{}

QString SKGRuleObject::getDisplayName() const
{
    return getSearchDescription();
}

SKGError SKGRuleObject::bookmark(bool iBookmark)
{
    return setAttribute("t_bookmarked", iBookmark ? "Y" : "N");
}

bool SKGRuleObject::isBookmarked() const
{
    return (getAttribute("t_bookmarked") == "Y" ? true : false);
}

SKGError SKGRuleObject::save(bool iInsertOrUpdate, bool iReloadAfterSave)
{
    // Do the save
    SKGError err = SKGObjectBase::save(iInsertOrUpdate, iReloadAfterSave);

    // Raise alarm
    if (!err && getActionType() == ALARM) {
        err = execute();
    }

    return err;
}

SKGError SKGRuleObject::setXMLSearchDefinition(const QString& iXml)
{
    setSearchDescription(SKGRuleObject::getDescriptionFromXML(getDocument(), iXml, false, SEARCH));
    return setAttribute("t_definition", iXml);
}

QString SKGRuleObject::getXMLSearchDefinition() const
{
    return getAttribute("t_definition");
}

SKGError SKGRuleObject::setXMLActionDefinition(const QString& iXml)
{
    setActionDescription(SKGRuleObject::getDescriptionFromXML(getDocument(), iXml, false, getActionType()));
    return setAttribute("t_action_definition", iXml);
}

QString SKGRuleObject::getXMLActionDefinition() const
{
    return getAttribute("t_action_definition");
}

SKGError SKGRuleObject::setSearchDescription(const QString& iDescription)
{
    return setAttribute("t_description", iDescription);
}

QString SKGRuleObject::getSearchDescription() const
{
    return getAttribute("t_description");
}

SKGError SKGRuleObject::setActionDescription(const QString& iDescription)
{
    return setAttribute("t_action_description", iDescription);
}

QString SKGRuleObject::getActionDescription() const
{
    return getAttribute("t_action_description");
}

SKGError SKGRuleObject::setActionType(SKGRuleObject::ActionType iType)
{
    SKGError err = setAttribute("t_action_type", (iType == SEARCH ? "S" : (iType == UPDATE ? "U" : (iType == APPLYTEMPLATE ? "T" : "A"))));
    return err;
}

SKGRuleObject::ActionType SKGRuleObject::getActionType() const
{
    QString typeString = getAttribute("t_action_type");
    return (typeString == "S" ? SEARCH : (typeString == "U" ? UPDATE : (typeString == "T" ? APPLYTEMPLATE : ALARM)));
}

SKGError SKGRuleObject::setOrder(double iOrder)
{
    SKGError err;
    double order = iOrder;
    if (order == -1) {
        order = 1;
        SKGStringListList result;
        err = getDocument()->executeSelectSqliteOrder("SELECT max(f_sortorder) from rule", result);
        if (!err && result.count() == 2) {
            order = SKGServices::stringToDouble(result.at(1).at(0)) + 1;
        }
    }
    IFOKDO(err, setAttribute("f_sortorder", SKGServices::doubleToString(order)))
    return err;
}

QString SKGRuleObject::getSelectSqlOrder(const QString& iAdditionalCondition) const
{
    QString wc = iAdditionalCondition;
    QString wc2 = SKGRuleObject::getDescriptionFromXML(getDocument(), getXMLSearchDefinition(), true, SEARCH);
    if (!wc2.isEmpty()) {
        if (wc.isEmpty()) {
            wc = wc2;
        } else {
            wc = '(' % wc % ") AND (" % wc2 % ')';
        }
    }
    if (wc.isEmpty()) {
        wc = "1=1";
    }
    wc = "d_date!='0000-00-00' AND (" % wc % ')';
    return wc;
}

SKGRuleObject::SKGAlarmInfo SKGRuleObject::getAlarmInfo() const
{
    SKGTRACEINFUNC(10);
    SKGRuleObject::SKGAlarmInfo alarm;
    if (getActionType() == SKGRuleObject::ALARM) {
        // Alarm mode
        QString wc = getSelectSqlOrder();
        if (wc.isEmpty()) {
            wc = "1=1";
        }

        SKGDocument* doc = getDocument();

        QStringList list = SKGRuleObject::getFromXML(doc, getXMLActionDefinition(), true, ALARM, false);
        if (list.count()) {
            QString sql = list.at(0);
            sql.replace("#WC#", wc);

            SKGStringListList result;
            doc->executeSelectSqliteOrder(sql, result);
            if (result.count() == 2) {
                QStringList r = result.at(1);
                alarm.Raised = (r.at(0) == "1");
                alarm.Message = r.at(3);
                alarm.Amount = SKGServices::stringToDouble(r.at(1));
                alarm.Limit = SKGServices::stringToDouble(r.at(2));
            }
        }
    }
    return alarm;
}

SKGError SKGRuleObject::execute(ProcessMode iMode)
{
    SKGError err;
    SKGTRACEINFUNCRC(10, err);
    if (getActionType() == SKGRuleObject::UPDATE) {
        // Update mode
        QString addSql;
        if (iMode == IMPORTED) {
            addSql = "t_imported!='N'";
        } else if (iMode == IMPORTEDNOTVALIDATE) {
            addSql = "t_imported='P'";
        } else if (iMode == IMPORTING) {
            addSql = "t_imported='T'";
        }

        QString wc = getSelectSqlOrder(addSql);
        if (wc.isEmpty()) {
            wc = "1=1";
        }

        SKGDocument* doc = getDocument();
        if (doc) {
            QStringList list = SKGRuleObject::getFromXML(doc, getXMLActionDefinition(), true, UPDATE, true);  // SQL + SET clause
            int nb = list.count();
            err = doc->beginTransaction("#INTERNAL#" % i18nc("Progression step", "Apply rule"), nb);
            IFOK(err) {
                // All sql orders must be executed to be sure than i_tmp is reset
                SKGError err2;
                for (int i = 0; i < nb; ++i) {
                    QString sql = list.at(i);
                    sql.replace("#WC#", wc);
                    err2 = doc->executeSqliteOrder(sql);
                    if (!err2) {
                        err2 = doc->stepForward(i + 1);
                    }
                    if (err2 && !err) {
                        err = err2;
                    }
                }
            }

            IFOK(err) {
                SKGStringListList result;
                err = doc->executeSelectSqliteOrder("SELECT changes()", result);
                if (!err && result.count() == 2) {
                    int nbChanges = SKGServices::stringToInt(result.at(1).at(0));
                    if (nbChanges) {
                        doc->sendMessage(i18np("1 operation modified by %2", "%1 operations modified by %2", nbChanges, getAttribute("i_ORDER")));
                    }
                }
            }

            SKGENDTRANSACTION(doc,  err);
        }
    } else if (getActionType() == SKGRuleObject::ALARM) {
        // Alarm mode
        SKGDocumentBank* doc = qobject_cast<SKGDocumentBank*>(getDocument());
        if (doc) {
            SKGRuleObject::SKGAlarmInfo alarm = getAlarmInfo();
            if (!alarm.Message.isEmpty()) {
                SKGServices::SKGUnitInfo unit = doc->getPrimaryUnit();
                getDocument()->sendMessage(alarm.Message.arg(doc->formatMoney(alarm.Amount, unit, false)).arg(doc->formatMoney(alarm.Limit, unit, false)).arg(doc->formatMoney(alarm.Amount - alarm.Limit, unit, false)));
            }
        }
    } else if (getActionType() == SKGRuleObject::APPLYTEMPLATE) {
        // Template mode
        QString wc = getSelectSqlOrder();
        if (wc.isEmpty()) {
            wc = "1=1";
        }

        SKGDocument* doc = getDocument();
        if (doc) {
            QStringList list = SKGRuleObject::getFromXML(doc, getXMLActionDefinition(), true, APPLYTEMPLATE, false);
            if (list.count()) {
                QString id = list.at(0);

                // Get template
                SKGOperationObject templateToApply(doc, SKGServices::stringToInt(id));

                // Get operations to modify
                SKGObjectBase::SKGListSKGObjectBase objectsToModify;
                IFOKDO(err, doc->getObjects("v_operation", wc, objectsToModify))
                int nb = objectsToModify.count();
                for (int i = 0; !err && i < nb; ++i) {
                    SKGOperationObject operationObj(objectsToModify.at(i));

                    SKGOperationObject op;
                    IFOKDO(err, templateToApply.duplicate(op))
                    IFOKDO(err, op.mergeAttribute(operationObj, SKGOperationObject::PROPORTIONAL, false))
                }
            }
        }
    }
    IFKO(err) err.addError(ERR_FAIL, i18nc("Error message", "Rule %1 failed", getAttribute("i_ORDER")));
    return err;
}

double SKGRuleObject::getOrder() const
{
    return SKGServices::stringToDouble(getAttribute("f_sortorder"));
}

QString SKGRuleObject::getDisplayForOperator(const QString& iOperator, const QString& iParam1, const QString& iParam2, const QString& iAtt2)
{
    QString output = iOperator;
    if (output == "#ATT# LIKE '%#V1S#%'") {
        output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "contains '#V1S#'").replace("#V1S#", iParam1);
    } else if (output == "#ATT# NOT LIKE '%#V1S#%'") {
        output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "does not contain '#V1S#'").replace("#V1S#", iParam1);
    } else if (output == "#ATT# LIKE '#V1S#%'") {
        output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "starts with '#V1S#'").replace("#V1S#", iParam1);
    } else if (output == "#ATT# NOT LIKE '#V1S#%'") {
        output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "does not start with '#V1S#'").replace("#V1S#", iParam1);
    } else if (output == "#ATT# LIKE '%#V1S#'") {
        output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "ends with '#V1S#'").replace("#V1S#", iParam1);
    } else if (output == "#ATT# NOT LIKE '%#V1S#'") {
        output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "does not end with '#V1S#'").replace("#V1S#", iParam1);
    } else if (output == "#ATT#=''") {
        output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "is empty");
    } else if (output == "#ATT#!=''") {
        output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "is not empty");
    } else if (output == "REGEXP('#V1S#', #ATT#)") {
        output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "regexp '#V1S#'").replace("#V1S#", iParam1);
    } else if (output == "NOT(REGEXP('#V1S#', #ATT#))") {
        output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "not regexp '#V1S#'").replace("#V1S#", iParam1);
    } else if (output == "WILDCARD('#V1S#', #ATT#)") {
        output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "wildcard '#V1S#'").replace("#V1S#", iParam1);
    } else if (output == "NOT(WILDCARD('#V1S#', #ATT#))") {
        output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "not wildcard '#V1S#'").replace("#V1S#", iParam1);
    } else if (output == "#ATT#=#V1#") {
        output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "=#V1#").replace("#V1#", iParam1);
    } else if (output == "#ATT#!=#V1#") {
        output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "!=#V1#").replace("#V1#", iParam1);
    } else if (output == "#ATT#>#V1#") {
        output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", ">#V1#").replace("#V1#", iParam1);
    } else if (output == "#ATT#<#V1#") {
        output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "<#V1#").replace("#V1#", iParam1);
    } else if (output == "#ATT#>=#V1#") {
        output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", ">=#V1#").replace("#V1#", iParam1);
    } else if (output == "#ATT#<=#V1#") {
        output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "<=#V1#").replace("#V1#", iParam1);
    } else if (output == "#ATT#='#V1S#'") {
        output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "='#V1S#'").replace("#V1S#", iParam1);
    } else if (output == "#ATT#!='#V1S#'") {
        output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "!='#V1S#'").replace("#V1S#", iParam1);
    } else if (output == "#ATT#>'#V1S#'") {
        output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", ">'#V1S#'").replace("#V1S#", iParam1);
    } else if (output == "#ATT#<'#V1S#'") {
        output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "<'#V1S#'").replace("#V1S#", iParam1);
    } else if (output == "#ATT#>='#V1S#'") {
        output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", ">='#V1S#'").replace("#V1S#", iParam1);
    } else if (output == "#ATT#<='#V1S#'") {
        output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "<='#V1S#'").replace("#V1S#", iParam1);
    } else if (output == "#ATT#>=#V1# AND #ATT#<=#V2#") {
        output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "is between #V1# and #V2#").replace("#V1#", iParam1).replace("#V2#", iParam2);
    } else if (output == "#ATT#>='#V1S#' AND #ATT#<='#V2S#'") {
        output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "is between '#V1S#' and '#V2S#'").replace("#V1S#", iParam1).replace("#V2S#", iParam2);
    } else if (output == "#ATT#=lower(#ATT#)") {
        output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "is set to lower");
    } else if (output == "#ATT#=upper(#ATT#)") {
        output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "is set to upper");
    } else if (output == "#ATT#=capitalize(#ATT#)") {
        output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "is set to capitalize");
    } else if (output == "#ATT#!=lower(#ATT#)") {
        output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "is not lower");
    } else if (output == "#ATT#!=upper(#ATT#)") {
        output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "is not upper");
    } else if (output == "#ATT#!=capitalize(#ATT#)") {
        output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "is not capitalize");
    } else if (output == "#ATT#= lower(#ATT#)") {
        output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "is lower");
    } else if (output == "#ATT#= upper(#ATT#)") {
        output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "is upper");
    } else if (output == "#ATT#= capitalize(#ATT#)") {
        output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "is capitalize");
    } else if (output == "#ATT#=replace(#ATT2#,'#V1S#','#V2S#')") {
        output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "=#ATT2# with '#V1S#' replaced by '#V2S#'").replace("#V1S#", iParam1).replace("#V2S#", iParam2).replace("#ATT2#", iAtt2);
    } else if (output == "#ATT#=substr(#ATT2#,'#V1#','#V2#')") {
        output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "=substring of #ATT2# from #V1# to #V2#").replace("#V1#", iParam1).replace("#V2#", iParam2).replace("#ATT2#", iAtt2);
    } else if (output == "#ATT#=#ATT2#") {
        output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "=#ATT2#").replace("#ATT2#", iAtt2);
    } else if (output == "#ATT#=WORD(#ATT2#,#V1S#)") {
        output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "=word(#ATT2#,#V1S#)").replace("#ATT2#", iAtt2).replace("#V1S#", iParam1);
    } else if (output == "#ATT#=todate(#ATT2#,'#DF#')") {
        output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "=#ATT2# as date with format #DF#").replace("#ATT2#", iAtt2).replace("#DF#", iParam2);
    } else if (output == "#ATT#=todate(WORD(#ATT2#,#V1S#),'#DF#')") {
        output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "=word(#ATT2#,#V1S#) as date with format #DF#").replace("#ATT2#", iAtt2).replace("#V1S#", iParam1).replace("#DF#", iParam2);
    } else if (output == "STRFTIME('%Y-%m',#ATT#)=STRFTIME('%Y-%m',date('now'))") {
        output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "is in current month");
    } else if (output == "STRFTIME('%Y-%m',#ATT#)=STRFTIME('%Y-%m',date('now','start of month','-1 month'))") {
        output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "is in previous month");
    } else if (output == "STRFTIME('%Y',#ATT#)=STRFTIME('%Y',date('now'))") {
        output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "is in current year");
    } else if (output == "STRFTIME('%Y',#ATT#)=STRFTIME('%Y',date('now','start of month','-1 year'))") {
        output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "is in previous year");
    } else if (output == "#ATT#>date('now','-30 day') AND #ATT#<=date('now')") {
        output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "is in last 30 days");
    } else if (output == "#ATT#>date('now','-3 month') AND #ATT#<=date('now')") {
        output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "is in last 3 months");
    } else if (output == "#ATT#>date('now','-6 month') AND #ATT#<=date('now')") {
        output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "is in last 6 months");
    } else if (output == "#ATT#>date('now','-12 month') AND #ATT#<=date('now')") {
        output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "is in last 12 months");
    } else if (output == "#ATT#>date('now','-2 year') AND #ATT#<=date('now')") {
        output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "is in last 2 years");
    } else if (output == "#ATT#>date('now','-3 year') AND #ATT#<=date('now')") {
        output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "is in last 3 years");
    } else if (output == "#ATT#>date('now','-5 year') AND #ATT#<=date('now')") {
        output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "is in last 5 years");
    } else if (output == "ABS(TOTAL(#ATT#))#OP##V1#,ABS(TOTAL(#ATT#)), #V1#, '#V2S#'") {
        output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "If total(#ATT#)#OP##V1# then send '#V2S#'").replace("#V1#", iParam1).replace("#V2S#", iParam2).replace("#OP#", iAtt2);
    } else if (output == "APPLYTEMPLATE(#V1#)") {
        output = i18nc("Description of a condition. Do not translate key words (#V1S#, #V1#, ...)", "Apply the template '#V2S#'").replace("#V2S#", iParam2);
    }

    return output;
}

QStringList SKGRuleObject::getListOfOperators(SKGServices::AttributeType iAttributeType, SKGRuleObject::ActionType iType)
{
    QStringList output;
    if (iType == UPDATE) {
        // Mode update
        if (iAttributeType == SKGServices::TEXT) {
            output.push_back("#ATT#='#V1S#'");
            output.push_back("#ATT#=lower(#ATT#)");
            output.push_back("#ATT#=upper(#ATT#)");
            output.push_back("#ATT#=capitalize(#ATT#)");
            output.push_back("#ATT#=replace(#ATT2#,'#V1S#','#V2S#')");
        } else if (iAttributeType == SKGServices::INTEGER || iAttributeType == SKGServices::FLOAT) {
            output.push_back("#ATT#=#V1#");
        } else if (iAttributeType == SKGServices::DATE || iAttributeType == SKGServices::BOOL || iAttributeType == SKGServices::TRISTATE) {
            output.push_back("#ATT#='#V1S#'");
        }
        if (iAttributeType == SKGServices::DATE) {
            output.push_back("#ATT#=todate(#ATT2#,'#DF#')");
            output.push_back("#ATT#=todate(WORD(#ATT2#,#V1S#),'#DF#')");
        } else if (iAttributeType != SKGServices::BOOL && iAttributeType != SKGServices::TRISTATE) {
            output.push_back("#ATT#=substr(#ATT2#,'#V1#','#V2#')");
            output.push_back("#ATT#=#ATT2#");
            output.push_back("#ATT#=WORD(#ATT2#,#V1S#)");
        }
    } else if (iType == SEARCH) {
        // Mode query
        if (iAttributeType == SKGServices::TEXT) {
            output.push_back("#ATT# LIKE '%#V1S#%'");
            output.push_back("#ATT# NOT LIKE '%#V1S#%'");
            output.push_back("#ATT# LIKE '#V1S#%'");
            output.push_back("#ATT# NOT LIKE '#V1S#%'");
            output.push_back("#ATT# LIKE '%#V1S#'");
            output.push_back("#ATT# NOT LIKE '%#V1S#'");
            output.push_back("#ATT#=''");
            output.push_back("#ATT#!=''");
            output.push_back("#ATT#= lower(#ATT#)");
            output.push_back("#ATT#!=lower(#ATT#)");
            output.push_back("#ATT#= upper(#ATT#)");
            output.push_back("#ATT#!=upper(#ATT#)");
            output.push_back("#ATT#= capitalize(#ATT#)");
            output.push_back("#ATT#!=capitalize(#ATT#)");
            output.push_back("REGEXP('#V1S#', #ATT#)");
            output.push_back("NOT(REGEXP('#V1S#', #ATT#))");
            output.push_back("WILDCARD('#V1S#', #ATT#)");
            output.push_back("NOT(WILDCARD('#V1S#', #ATT#))");
        }

        if (iAttributeType == SKGServices::INTEGER || iAttributeType == SKGServices::FLOAT) {
            output.push_back("#ATT#=#V1#");
            output.push_back("#ATT#!=#V1#");
            output.push_back("#ATT#>#V1#");
            output.push_back("#ATT#<#V1#");
            output.push_back("#ATT#>=#V1#");
            output.push_back("#ATT#<=#V1#");
            output.push_back("#ATT#>=#V1# AND #ATT#<=#V2#");
        }

        if (iAttributeType == SKGServices::BOOL || iAttributeType == SKGServices::TRISTATE || iAttributeType == SKGServices::TEXT || iAttributeType == SKGServices::DATE) {
            output.push_back("#ATT#='#V1S#'");
        }

        if (iAttributeType == SKGServices::TRISTATE || iAttributeType == SKGServices::TEXT || iAttributeType == SKGServices::DATE) {
            output.push_back("#ATT#!='#V1S#'");
        }

        if (iAttributeType == SKGServices::TEXT || iAttributeType == SKGServices::DATE) {
            output.push_back("#ATT#>'#V1S#'");
            output.push_back("#ATT#<'#V1S#'");
            output.push_back("#ATT#>='#V1S#'");
            output.push_back("#ATT#<='#V1S#'");
            output.push_back("#ATT#>='#V1S#' AND #ATT#<='#V2S#'");
        }

        if (iAttributeType == SKGServices::DATE) {
            output.push_back("STRFTIME('%Y-%m',#ATT#)=STRFTIME('%Y-%m',date('now'))");
            output.push_back("STRFTIME('%Y-%m',#ATT#)=STRFTIME('%Y-%m',date('now','start of month','-1 month'))");
            output.push_back("STRFTIME('%Y',#ATT#)=STRFTIME('%Y',date('now'))");
            output.push_back("STRFTIME('%Y',#ATT#)=STRFTIME('%Y',date('now','start of month','-1 year'))");
            output.push_back("#ATT#>date('now','-30 day') AND #ATT#<=date('now')");
            output.push_back("#ATT#>date('now','-3 month') AND #ATT#<=date('now')");
            output.push_back("#ATT#>date('now','-6 month') AND #ATT#<=date('now')");
            output.push_back("#ATT#>date('now','-12 month') AND #ATT#<=date('now')");
            output.push_back("#ATT#>date('now','-2 year') AND #ATT#<=date('now')");
            output.push_back("#ATT#>date('now','-3 year') AND #ATT#<=date('now')");
            output.push_back("#ATT#>date('now','-5 year') AND #ATT#<=date('now')");
        }
    } else if (iType == ALARM) {
        output.push_back("ABS(TOTAL(#ATT#))#OP##V1#,ABS(TOTAL(#ATT#)), #V1#, '#V2S#'");
    } else if (iType == APPLYTEMPLATE) {
        output.push_back("APPLYTEMPLATE(#V1#)");
    }
    return output;
}

QStringList SKGRuleObject::getFromXML(SKGDocument* iDocument, const QString& iXML, bool iSQL, SKGRuleObject::ActionType iType, bool iFullUpdate)
{
    QStringList output;
    if (iFullUpdate) {
        // Add markers
        output.push_back("UPDATE v_operation_prop set i_tmp=1 WHERE #WC#");
    }

    QDomDocument doc("SKGML");
    doc.setContent(iXML);
    QDomElement root = doc.documentElement();
    if (root.tagName() == "element" || iType != SEARCH) {
        // Mode advanced
        QDomNode l = root.firstChild();
        while (!l.isNull()) {
            QDomElement elementl = l.toElement();
            if (!elementl.isNull()) {
                QString lineDescription;

                QDomNode n = elementl.firstChild();
                bool parenthesisNeeded = false;
                while (!n.isNull()) {
                    QDomElement element = n.toElement();
                    if (!element.isNull()) {
                        // Build element description
                        QString elementDescription;
                        QString attribute = element.attribute("attribute");
                        QString propName;
                        if (iSQL) {
                            attribute = SKGServices::stringToSqlString(attribute);
                            if (attribute.startsWith(QLatin1String("p_"))) {
                                // Case property
                                propName = attribute.right(attribute.length() - 2);
                                if (iType == SEARCH) {
                                    attribute = "i_PROPPNAME='" % SKGServices::stringToSqlString(propName) % "' AND i_PROPVALUE";
                                } else {
                                    attribute = "t_value";
                                }
                            }

                            QString part = element.attribute("operator");
                            part = part.replace("#V1#", SKGServices::stringToSqlString(element.attribute("value")));
                            part = part.replace("#V1S#", SKGServices::stringToSqlString(element.attribute("value")));
                            part = part.replace("#V2#", SKGServices::stringToSqlString(element.attribute("value2")));
                            part = part.replace("#V2S#", SKGServices::stringToSqlString(element.attribute("value2")));
                            part = part.replace("#DF#", SKGServices::stringToSqlString(element.attribute("value2")));
                            part = part.replace("#OP#", SKGServices::stringToSqlString(element.attribute("operator2")));

                            elementDescription += part;
                        } else {
                            attribute = "operation." % attribute;
                            if (iDocument) {
                                attribute = iDocument->getDisplay(attribute);
                            }

                            if (iType != ALARM && iType != APPLYTEMPLATE) {
                                elementDescription = attribute;
                                elementDescription += ' ';
                            }

                            QString tmp = element.attribute("att2s");
                            if (tmp.isEmpty()) {
                                tmp = element.attribute("operator2");
                            }
                            QString part = SKGRuleObject::getDisplayForOperator(element.attribute("operator"),
                                           element.attribute("value"),
                                           element.attribute("value2"),
                                           tmp);

                            elementDescription += part;
                        }

                        elementDescription = elementDescription.replace("#ATT#", attribute);

                        // Att2
                        QString attribute2 = element.attribute("att2");
                        if (iSQL) {
                            attribute2 = SKGServices::stringToSqlString(attribute2);
                            if (attribute2.startsWith(QLatin1String("p_"))) {
                                QString propertyName = attribute2.right(attribute2.length() - 2);
                                attribute2 = "IFNULL((SELECT op2.i_PROPVALUE FROM v_operation_prop op2 WHERE op2.id=v_operation_prop.id AND op2.i_PROPPNAME='" % SKGServices::stringToSqlString(propertyName) % "'),'')";
                            }

                            if (element.attribute("attribute").startsWith(QLatin1String("p_"))) {
                                attribute2 = "(SELECT " % attribute2 %  " FROM v_operation_prop WHERE i_PROPPID=parameters.id)";
                            }
                        }
                        elementDescription = elementDescription.replace("#ATT2#", attribute2);

                        // Add it to line description
                        if (iSQL) {
                            if (iType == UPDATE) {
                                if (!iFullUpdate) {
                                    output.push_back(elementDescription);
                                } else {
                                    QString sqlOrder;
                                    if (attribute == "t_REALCATEGORY") {
                                        elementDescription.replace(attribute, "t_fullname");

                                        QString parentcat;
                                        QString cat = element.attribute("value");
                                        bool stop = false;
                                        while (!stop) {
                                            int posSeparator = cat.indexOf(OBJECTSEPARATOR);
                                            if (posSeparator == -1) {
                                                if (parentcat.isEmpty()) {
                                                    output.push_back("INSERT OR IGNORE INTO category (t_name, rd_category_id) VALUES ('" % SKGServices::stringToSqlString(cat) % "', 0)");
                                                } else {
                                                    output.push_back("INSERT OR IGNORE INTO category (t_name, rd_category_id) VALUES ('" % SKGServices::stringToSqlString(cat) % "',(SELECT id FROM category WHERE t_fullname='" % SKGServices::stringToSqlString(parentcat) % "'))");
                                                }
                                                stop = true;
                                            } else {
                                                // Get first and second parts of the branch
                                                QString first = cat.mid(0, posSeparator);
                                                QString second = cat.mid(posSeparator + QString(OBJECTSEPARATOR).length(), cat.length() - posSeparator - QString(OBJECTSEPARATOR).length());
                                                if (parentcat.isEmpty()) {
                                                    output.push_back("INSERT OR IGNORE INTO category (t_name, rd_category_id) VALUES ('" % SKGServices::stringToSqlString(first) % "', 0)");
                                                } else {
                                                    output.push_back("INSERT OR IGNORE INTO category (t_name, rd_category_id) VALUES ('" % SKGServices::stringToSqlString(first) % "',(SELECT id FROM category WHERE t_fullname='" % SKGServices::stringToSqlString(parentcat) % "'))");
                                                }

                                                if (parentcat.isEmpty()) {
                                                    parentcat = first;
                                                } else {
                                                    parentcat = parentcat % OBJECTSEPARATOR % first;
                                                }
                                                cat = second;
                                            }
                                        }
                                        output.push_back("UPDATE suboperation set r_category_id=(SELECT id FROM category WHERE " % elementDescription % ") WHERE i_tmp=1");
                                    } else if (element.attribute("attribute").startsWith(QLatin1String("p_"))) {
                                        output.push_back("INSERT OR IGNORE INTO parameters (t_uuid_parent, t_name, i_tmp) SELECT o.id||'-operation', '" % SKGServices::stringToSqlString(propName) % "', 1 FROM operation o WHERE o.i_tmp=1");
                                        output.push_back("UPDATE parameters set " % elementDescription % " WHERE i_tmp=1 AND t_name='" % SKGServices::stringToSqlString(propName) % "' AND NOT(" % elementDescription % ')');
                                        output.push_back("DELETE FROM parameters WHERE i_tmp=1 AND t_name='" % SKGServices::stringToSqlString(propName) % "' AND t_value=''");
                                    } else {
                                        output.push_back("UPDATE v_operation_prop set " % elementDescription % " WHERE i_tmp=1 AND NOT(" % elementDescription % ')');
                                    }
                                }
                            } else if (iType == ALARM) {
                                output.push_back("SELECT " % elementDescription % " FROM v_suboperation_consolidated WHERE #WC#");
                            } else if (iType == APPLYTEMPLATE) {
                                output.push_back(element.attribute("value"));
                            }
                        }

                        if (!lineDescription.isEmpty()) {
                            lineDescription += (iType == UPDATE ? " , " : (iSQL ? " AND " : i18nc("logical operator in a search query", " and ")));
                            parenthesisNeeded = true;
                        }
                        lineDescription += elementDescription;
                    }
                    n = n.nextSibling();
                }

                if (!(iType != SEARCH && iSQL) && !lineDescription.isEmpty()) {
                    if (iType == SEARCH && parenthesisNeeded) {
                        lineDescription = '(' % lineDescription % ')';
                    }
                    output.push_back(lineDescription);
                }
            }
            l = l.nextSibling();
        }
    } else {
        output.push_back(root.attribute(iSQL ? "sql" : "query"));
    }

    if (iFullUpdate) {
        // Remove markers
        output.push_back("UPDATE v_operation_prop set i_tmp=0 WHERE i_tmp=1");
    }
    return output;
}

QString SKGRuleObject::getDescriptionFromXML(SKGDocument* iDocument, const QString& iXML, bool iSQL, SKGRuleObject::ActionType iType)
{
    QString output;

    QStringList list = getFromXML(iDocument, iXML, iSQL, iType);
    int nb = list.count();
    for (int i = 0; i < nb; ++i) {
        output.append(list[i]);
        if (i < nb - 1) {
            output.append(iType != SEARCH ? " , " : (iSQL ? " OR " : i18nc("logical operator in a search query", " or ")));
        }
    }
    return output;
}

