/***************************************************************************
 *   Copyright (C) 2010 by Guillaume DE BURE guillaume.debure@gmail.com        *
 *                                                                         *
 *   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/>  *
 ***************************************************************************/
#include "skgdataengine.h"

#include <sys/stat.h>
#include <QStringListModel>
#include <KMessageBox>
#include <Plasma/DataContainer>

#include "skgtraces.h"
#include "skgservices.h"
#include "skgmainpanel.h"
#include "skgdocumentbank.h"
#include "skgunitobject.h"
#include "skginterfaceplugin.h"
#include "skgaccountobject.h"
#include "skgruleobject.h"

/**
 * Data Sources names
 */
#define SRC_ACCOUNTS                "Accounts"
#define SRC_INCOMESANDEXPENDITURES  "IncomesExpenditures"
#define SRC_BANKS                   "Banks"
#define SRC_HIGHLIGHTED             "Highlighted Operations"
#define SRC_SCHEDULED               "Scheduled Operations"
#define SRC_UNITS                   "Units"
#define SRC_ADVICES                 "Advice"
#define SRC_INTERESTS               "Interests"
#define SRC_ALARMS                  "Alarms"

SKGDataEngine::SKGDataEngine(QObject* iParent, const QVariantList& args): DataEngine(iParent, args), document(NULL) {}

SKGDataEngine::~SKGDataEngine()
{
    document = NULL;
}

void SKGDataEngine::init()
{
    Plasma::DataEngine::init();

    // Get the document behind the current document
    SKGMainPanel* panel = SKGMainPanel::getMainPanel();
    if (panel) {
        setDocument(qobject_cast<SKGDocumentBank*> (panel->getDocument()));
    }
}

QStringList SKGDataEngine::sources() const
{
    QStringList sources;
    if (this->getDocument()) {
        sources << SRC_ACCOUNTS << SRC_INCOMESANDEXPENDITURES << SRC_BANKS << SRC_HIGHLIGHTED << SRC_SCHEDULED << SRC_UNITS << SRC_ADVICES;
    }
    return sources;
}

bool SKGDataEngine::updateSourceEvent(const QString& source)
{
    if (!document) {
        return false;
    }
    if (source == SRC_ACCOUNTS) {
        return getAccountData();
    } else if (source == SRC_INCOMESANDEXPENDITURES) {
        return getIncomeExpenditureData();
    } else if (source == SRC_HIGHLIGHTED) {
        return getHighlightedOperationData();
    } else if (source == SRC_SCHEDULED) {
        return getScheduledOperationData();
    } else if (source == SRC_UNITS) {
        return getUnitsData();
    } else if (source == SRC_ADVICES) {
        return getAdviceData();
    } else if (source == SRC_INTERESTS) {
        return getInterestsData();
    }
    return false;
}

bool SKGDataEngine::sourceRequestEvent(const QString& source)
{
    return updateSourceEvent(source);
}

bool SKGDataEngine::getAccountData()
{
    removeAllData(SRC_ACCOUNTS);
    if (!document) {
        return false;
    }

    SKGStringListList sqlResult;

    // Fetch results in the database
    SKGError err = document->executeSelectSqliteOrder("SELECT t_name, t_bank, t_typenls, f_CURRENTAMOUNT, t_close FROM v_account_display ORDER BY t_type,t_name", sqlResult);

    // We may receive an error from querying the SQLite database
    IFKO(err) return false;

    // Put the data in the dataengine
    int nb = sqlResult.count();
    for (int i = 1 ; i < nb ; ++i) {
        QStringList values;
        values << sqlResult.at(i).at(1);  // Account type
        values << sqlResult.at(i).at(2);  // Bank name
        values << sqlResult.at(i).at(3);  // Amount of money
        values << sqlResult.at(i).at(4);  // Account closed

        setData(SRC_ACCOUNTS, sqlResult.at(i).at(0), values);
    }
    return true;
}


bool SKGDataEngine::getIncomeExpenditureData()
{
    removeAllData(SRC_INCOMESANDEXPENDITURES);
    if (!document) {
        return false;
    }

    QDate date = QDate::currentDate();

    QString month = date.toString("yyyy-MM");
    QString previousmonth = date.addMonths(-1).toString("yyyy-MM");

    SKGStringListList data;
    SKGError err = document->executeSelectSqliteOrder("SELECT d_DATEMONTH, t_TYPEEXPENSE, TOTAL(f_CURRENTAMOUNT) FROM v_operation_display WHERE t_TYPEEXPENSE IN ('+','-') AND d_DATEMONTH IN ('" % month % "', '" % previousmonth % "') AND t_TRANSFER='N' group by d_DATEMONTH, t_TYPEEXPENSE", data);

    // We may receive an error from querying the SQLite database
    IFKO(err) return false;

    // Put the data in the dataengine
    int nb = data.count();
    for (int i = 1 ; i < nb ; ++i) {
        QStringList values;
        values << data.at(i).at(0);  // month
        values << data.at(i).at(1);  // type (income or expense)
        values << data.at(i).at(2);  // amount
        setData(SRC_INCOMESANDEXPENDITURES, SKGServices::intToString(i), values);
    }
    return true;
}

bool SKGDataEngine::getHighlightedOperationData()
{
    removeAllData(SRC_HIGHLIGHTED);
    if (!document) {
        return false;
    }

    SKGStringListList sqlResult;

    // Fetch results in the database
    SKGError err = document->executeSelectSqliteOrder("SELECT id, d_date, t_payee, f_currentamount FROM v_operation_display WHERE t_bookmarked='Y' ORDER BY d_date", sqlResult);

    // We may receive an error from querying the SQLite database
    IFKO(err) return false;

    // Put the data in the dataengine
    int nb = sqlResult.count();
    for (int i = 1 ; i < nb ; ++i) {
        QStringList values;
        values << sqlResult.at(i).at(1);  // Date
        values << sqlResult.at(i).at(2);  // Payee
        values << sqlResult.at(i).at(3);  // Amount

        setData(SRC_HIGHLIGHTED, sqlResult.at(i).at(0), values);
    }

    return true;
}

bool SKGDataEngine::getScheduledOperationData()
{
    removeAllData(SRC_SCHEDULED);
    if (!document) {
        return false;
    }

    SKGStringListList sqlResult;

    // Fetch results in the database
    SKGError err = document->executeSelectSqliteOrder("SELECT id, d_date, t_payee, f_currentamount FROM v_recurrentoperation_display ORDER BY d_date LIMIT 5", sqlResult);

    // We may receive an error from querying the SQLite database
    IFKO(err) return false;

    // Put the data in the dataengine
    int nb = sqlResult.count();
    for (int i = 1 ; i < nb ; ++i) {
        QStringList values;
        values << sqlResult.at(i).at(1);  // Date
        values << sqlResult.at(i).at(2);  // Payee
        values << sqlResult.at(i).at(3);  // Amount

        setData(SRC_SCHEDULED, sqlResult.at(i).at(0), values);
    }

    return true;
}

bool SKGDataEngine::getUnitsData()
{
    removeAllData(SRC_UNITS);
    if (!document) {
        return false;
    }

    SKGStringListList sqlResult;

    // Fetch results in the database
    SKGError err = document->executeSelectSqliteOrder("SELECT id FROM v_unit_display WHERE t_type != 1 ORDER BY t_type ASC, t_name ASC", sqlResult);

    // We may receive an error from querying the SQLite database
    IFKO(err) return false;

    // Put the data in the dataengine
    int nb = sqlResult.count();
    for (int i = 1 ; i < nb ; ++i) {
        int unit_id = SKGServices::stringToInt(sqlResult.at(i).at(0));

        QStringList values;
        SKGUnitObject unit(document, unit_id);

        values << unit.getName();
        values << unit.getSymbol();
        values << SKGServices::doubleToString(unit.getAmount());
        values << unit.getAttribute("t_TYPENLS");
        values << document->formatPercentage(unit.getDailyChange());

        setData(SRC_UNITS, sqlResult.at(i).at(0), values);
    }

    return true;
}

bool SKGDataEngine::getAdviceData()
{
    removeAllData(SRC_ADVICES);
    if (!document) {
        return false;
    }

    // Get list of ignored advice
    QString currentMonth = QDate::currentDate().toString("yyyy-MM");
    QStringList ignoredAdvice = getDocument()->getParameters("advice", "t_value='I' OR t_value='I_" % currentMonth % '\'');

    // Build the list of all advice by requesting all plugins
    int index = 0;
    while (index >= 0) {
        SKGInterfacePlugin* plugin = SKGMainPanel::getMainPanel()->getPluginByIndex(index);
        if (plugin) {
            foreach(const SKGAdvice & advice, plugin->advice(ignoredAdvice)) {
                if (!ignoredAdvice.contains(advice.getUUID()) && !ignoredAdvice.contains(SKGServices::splitCSVLine(advice.getUUID(), '|').at(0))) {
                    QVariantList values;
                    values.append(SKGServices::intToString(advice.getPriority()));
                    values.append(advice.getShortMessage());
                    values.append(advice.getLongMessage());
                    values.append(advice.getAutoCorrections());

                    setData(SRC_ADVICES, advice.getUUID(), values);
                }
            }
        } else {
            index = -2;
        }
        ++index;
    }

    return true;
}

bool SKGDataEngine::getInterestsData()
{
    removeAllData(SRC_INTERESTS);
    if (!document) {
        return false;
    }

    SKGStringListList sqlResult;

    // Fetch results in the database
    SKGError err = document->executeSelectSqliteOrder("SELECT id,t_name FROM v_account WHERE t_close='N' AND EXISTS(select 1 from interest where interest.rd_account_id=v_account.id) ORDER BY t_name", sqlResult);

    // We may receive an error from querying the SQLite database
    IFKO(err) return false;

    // Put the data in the dataengine
    int nb = sqlResult.count();
    for (int i = 1 ; i < nb ; ++i) {
        int account_id = SKGServices::stringToInt(sqlResult.at(i).at(0));

        QStringList values;
        SKGAccountObject account(document, account_id);

        QList<SKGAccountObject::SKGInterestItem> oInterestList;
        double oInterests = 0;
        account.getInterestItems(oInterestList, oInterests);

        values << sqlResult.at(i).at(1);  // Account name
        values << SKGServices::doubleToString(oInterests);  // Interests

        setData(SRC_INTERESTS, sqlResult.at(i).at(0), values);
    }

    return true;
}


bool SKGDataEngine::getAlarmsData()
{
    removeAllData(SRC_ALARMS);
    if (!document) {
        return false;
    }

    SKGStringListList sqlResult;

    // Fetch results in the database
    SKGError err = document->executeSelectSqliteOrder("SELECT id FROM v_rule WHERE t_action_type='A'", sqlResult);

    // We may receive an error from querying the SQLite database
    IFKO(err) return false;

    // Put the data in the dataengine
    int nb = sqlResult.count();
    for (int i = 1 ; i < nb ; ++i) {
        int alarm_id = SKGServices::stringToInt(sqlResult.at(i).at(0));

        QStringList values;
        SKGRuleObject rule(document, alarm_id);

        double alarm_limit = rule.getAlarmInfo().Amount;

        values << SKGServices::doubleToString(alarm_limit);  // Alarm limit

        setData(SRC_ALARMS, sqlResult.at(i).at(0), values);
    }

    return true;
}


void SKGDataEngine::setDocument(SKGDocumentBank* iDocument)
{
    document = iDocument;

    // Whenever a table is modified, update sources
    if (document) {
        connect(document, SIGNAL(transactionSuccessfullyEnded(int)), this, SLOT(updateAllSources()));    // TODO(Stephane MANKOWSKI) How to updale only modified sources
    }
}
/*
transactionEnded(int trans)
{
  .. get your list of sources by name ..
  foreach :: updateSourceEvent(name);
}
*/

SKGDocumentBank* SKGDataEngine::getDocument()  const
{
    return document;
}

// This does the magic that allows Plasma to load
// this plugin.  The first argument must match
// the X-Plasma-EngineName in the .desktop file.

K_EXPORT_PLASMA_DATAENGINE(dataengine_skrooge, SKGDataEngine)

// this is needed since skgdataengine is a QObject
#include "skgdataengine.moc"
