#ifndef SERVICE_H
#define SERVICE_H

#include <QList>
#include <QSharedPointer>
#include <QString>

#include <odb/core.hxx>
#include <odb/transaction.hxx>
#include <odb/database.hxx>
#include <odb/result.hxx>

#include "core_global.h"
#include "context.h"
#include "iservice.h"

#include "transaction.h"

template<class T>
class Service : public IService
{
public:
    explicit Service(QObject *parent = NULL) :IService(parent) { }

    explicit Service(const QString &pluginId) {
        m_pluginId = pluginId;
    }

    QList<QSharedPointer<T> > all() {
        odb::database *db = Context::instance().db();

        Q_ASSERT(db);

        Transaction tx;
        QList<QSharedPointer<T> > ret;

        try
        {
            odb::result<T> res = db->template query<T>();

            for (typename odb::result<T>::iterator it = res.begin(); it != res.end(); it++) {
                ret.append(it.load());
            }

            tx.commit();
        }
        catch (const odb::exception &ex)
        {
            emit dbError(ex.what());
        }

        return ret;
    }

    void save(QSharedPointer<T> entity) {
        odb::database *db = Context::instance().db();

        Q_ASSERT(db);

        Transaction tx;

        try
        {
            db->persist(entity);
            tx.commit();
        }
        catch (const odb::exception &ex)
        {
            emit dbError(ex.what());
            emit dbErrorUpdate(ex.what());
            return;
        }

        emit dataChanged();
    }

    void update(QSharedPointer<T> entity) {
        odb::database *db = Context::instance().db();

        Q_ASSERT(db);

        Transaction tx;

        try
        {
            db->update(entity);
            tx.commit();
        }
        catch (const odb::exception &ex)
        {
            emit dbError(ex.what());
            emit dbErrorInsert(ex.what());
            return;
        }

        emit dataChanged();
    }

    QSharedPointer<T> loadById(int id) {
        odb::database *db = Context::instance().db();

        Q_ASSERT(db);

        Transaction tx;
        QSharedPointer<T> entity;

        try
        {
            entity = db->template load<T>(id);
            tx.commit();
        }
        catch (const odb::exception &ex)
        {
            emit dbError(ex.what());
            emit dbErrorRead(ex.what());
        }

        return entity;
    }

    QSharedPointer<T> reload(int id) {
        odb::database *db = Context::instance().db();

        Q_ASSERT(db);

        Context::instance().session().cache_erase<T>(*db, id);
        return loadById(id);
    }

    void erase(QSharedPointer<T> entity) {
        odb::database *db = Context::instance().db();

        Q_ASSERT(db);

        Transaction tx;

        try
        {
            db->erase(entity);
            tx.commit();
        }
        catch (const odb::exception &ex)
        {
            emit dbError(ex.what());
            emit dbErrorDelete(ex.what());
        }
    }

    void setPluginId(const QString &pluginId) {
        m_pluginId = pluginId;
    }

private:
    QString m_pluginId;
};

#endif // SERVICE_H
