/****************************************************************************
**
** https://www.qxorm.com/
** Copyright (C) 2013 XDL Team (ic-east.com)
**
** This file is part of the QxOrm library
**
** This software is provided 'as-is', without any express or implied
** warranty. In no event will the authors be held liable for any
** damages arising from the use of this software
**
** Commercial Usage
** Licensees holding valid commercial QxOrm licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and XDL Team
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file 'license.gpl3.txt' included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met : http://www.gnu.org/copyleft/gpl.html
**
** If you are unsure which license is appropriate for your use, or
** if you have questions regarding the use of this file, please contact :
** ic-east.com
**
****************************************************************************/

#ifndef _QX_DAO_ASYNC_H_
#define _QX_DAO_ASYNC_H_

#ifdef _MSC_VER
#pragma once
#endif

/*!
 * \file QxDaoAsync.h
 * \author XDL Team
 * \ingroup QxDao
 * \brief Helper class to execute SQL queries in another thread (asynchronous way) using qx::IxPersistable interface
 */

#ifdef _QX_NO_PRECOMPILED_HEADER
#ifndef Q_MOC_RUN
#include <QxPrecompiled.h> // Need to include precompiled header for the generated moc file
#endif                     // Q_MOC_RUN
#endif                     // _QX_NO_PRECOMPILED_HEADER

#include <QtCore/qqueue.h>

#include <QtSql/qsqlerror.h>

#ifndef Q_MOC_RUN
#include <QxDao/IxPersistable.h>
#include <QxDao/QxSqlQuery.h>
#endif // Q_MOC_RUN

namespace qx
{
   namespace dao
   {
      namespace detail
      {

         /*!
          * \ingroup QxDao
          * \brief qx::dao::detail::QxDaoAsyncParams : all parameters for qx::QxDaoAsync class to execute queries
          */
         struct QxDaoAsyncParams
         {

            enum dao_action
            {
               dao_none,
               dao_count,
               dao_fetch_by_id,
               dao_fetch_all,
               dao_fetch_by_query,
               dao_insert,
               dao_update,
               dao_save,
               dao_delete_by_id,
               dao_delete_all,
               dao_delete_by_query,
               dao_destroy_by_id,
               dao_destroy_all,
               dao_destroy_by_query,
               dao_execute_query,
               dao_call_query
            };

            dao_action daoAction;                         //!< Action to execute into the thread (asynchronous way)
            QString className;                            //!< Classname parameter to execute action (must implement qx::IxPersistable interface)
            qx::QxSqlQuery query;                         //!< Query parameter to execute action
            QSqlDatabase *pDatabase;                      //!< Database parameter to execute action
            IxPersistable_ptr pInstance;                  //!< Current instance parameter to execute action
            IxPersistableCollection_ptr pListOfInstances; //!< List of instances fetched by query
            QStringList listColumns;                      //!< List of columns parameter to execute action
            QStringList listRelations;                    //!< List of relationships parameter to execute action
            QVariant id;                                  //!< Current instance id parameter to execute action
            long daoCount;                                //!< Dao count value returned by qx::dao::count query
            bool useExecBatch;                            //!< If true then use the QSqlQuery::execBatch() method to improve performance inserting/updating/deleting a list of instances to database (but doesn't fill the last inserted identifier in the C++ instances)

            QxDaoAsyncParams() : daoAction(dao_none), pDatabase(NULL), daoCount(0), useExecBatch(false) { ; }
            ~QxDaoAsyncParams() { ; }
         };

         typedef std::shared_ptr<QxDaoAsyncParams> QxDaoAsyncParams_ptr;

         /*!
          * \ingroup QxDao
          * \brief qx::dao::detail::QxDaoAsyncRunner : class with a slot to execute queries in another thread
          */
         class QX_DLL_EXPORT QxDaoAsyncRunner : public QObject
         {

            Q_OBJECT

         public:
            QxDaoAsyncRunner();
            virtual ~QxDaoAsyncRunner();

         protected:
            QSqlError runQuery(qx::dao::detail::QxDaoAsyncParams_ptr pDaoParams);

         Q_SIGNALS:

            void queryFinished(const QSqlError &daoError, qx::dao::detail::QxDaoAsyncParams_ptr pDaoParams);

         public Q_SLOTS:

            void onQueryStarted(qx::dao::detail::QxDaoAsyncParams_ptr pDaoParams);
         };

      } // namespace detail
   } // namespace dao

   /*!
    * \ingroup QxDao
    * \brief qx::QxDaoAsync : helper class to execute SQL queries in another thread (asynchronous way) using qx::IxPersistable interface
    *
    * To use <i>qx::QxDaoAsync</i> helper class :
    * 1- be careful to work only with classes implementing <i>qx::IxPersistable</i> interface ;
    * 2- create an instance of <i>qx::QxDaoAsync</i> type (for example, a property of a QWidget derived class) ;
    * 3- connect a SLOT to the <i>qx::QxDaoAsync::queryFinished()</i> SIGNAL (for example, a SLOT of a QWidget derived class) ;
    * 4- run a query using one of <i>qx::QxDaoAsync::asyncXXXX()</i> methods.
    *
    * For example, with a <i>MyWidget</i> class :
    * \code
   class MyWidget : public QWidget
   {
      Q_OBJECT
      //...
      qx::QxDaoAsync m_daoAsync;
      //...
   Q_SLOTS:
      void onQueryFinished(const QSqlError & daoError, qx::dao::detail::QxDaoAsyncParams_ptr pDaoParams);
      //...
   };
    * \endcode
    * And here is the implementation of <i>MyWidget</i> class :
    * \code
   MyWidget::MyWidget() : QObject()
   {
      //...
      QObject::connect((& m_daoAsync), SIGNAL(queryFinished(const QSqlError &, qx::dao::detail::QxDaoAsyncParams_ptr)), this, SLOT(onQueryFinished(const QSqlError &, qx::dao::detail::QxDaoAsyncParams_ptr)));
      //...
   }
   void MyWidget::onQueryFinished(const QSqlError & daoError, qx::dao::detail::QxDaoAsyncParams_ptr pDaoParams)
   {
      if (! pDaoParams) { return; }
      qx::QxSqlQuery query = pDaoParams->query;
      if (! daoError.isValid()) { ; }
      // If the async query is associated to a simple object, just use 'pDaoParams->pInstance' method
      qx::IxPersistable_ptr ptr = pDaoParams->pInstance;
      // If the async query is associated to a list of objects, just use 'pDaoParams->pListOfInstances' method
      qx::IxPersistableCollection_ptr lst = pDaoParams->pListOfInstances;
      //...
   }
    * \endcode
    */
   class QX_DLL_EXPORT QxDaoAsync : public QThread
   {

      Q_OBJECT

   protected:
      QMutex m_mutex;                                     //!< Mutex => qx::QxDaoAsync is thread-safe
      qx::dao::detail::QxDaoAsyncParams_ptr m_pDaoParams; //!< Parameters to execute query

   public:
      QxDaoAsync();
      virtual ~QxDaoAsync();

      bool asyncCount(const QString &className, const qx::QxSqlQuery &query = qx::QxSqlQuery(), QSqlDatabase *pDatabase = NULL);
      bool asyncFetchById(IxPersistable_ptr pToFetch, const QVariant &id = QVariant(), const QStringList &columns = QStringList(), const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL);
      bool asyncFetchAll(const QString &className, const QStringList &columns = QStringList(), const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL);
      bool asyncFetchByQuery(const QString &className, const qx::QxSqlQuery &query, const QStringList &columns = QStringList(), const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL);
      bool asyncInsert(IxPersistable_ptr pToInsert, const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL);
      bool asyncUpdate(IxPersistable_ptr pToUpdate, const qx::QxSqlQuery &query = qx::QxSqlQuery(), const QStringList &columns = QStringList(), const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL);
      bool asyncSave(IxPersistable_ptr pToSave, const QStringList &relation = QStringList(), QSqlDatabase *pDatabase = NULL);
      bool asyncDeleteById(IxPersistable_ptr pToDelete, const QVariant &id = QVariant(), QSqlDatabase *pDatabase = NULL);
      bool asyncDeleteAll(const QString &className, QSqlDatabase *pDatabase = NULL);
      bool asyncDeleteByQuery(const QString &className, const qx::QxSqlQuery &query, QSqlDatabase *pDatabase = NULL);
      bool asyncDestroyById(IxPersistable_ptr pToDestroy, const QVariant &id = QVariant(), QSqlDatabase *pDatabase = NULL);
      bool asyncDestroyAll(const QString &className, QSqlDatabase *pDatabase = NULL);
      bool asyncDestroyByQuery(const QString &className, const qx::QxSqlQuery &query, QSqlDatabase *pDatabase = NULL);
      bool asyncExecuteQuery(const QString &className, qx::QxSqlQuery &query, QSqlDatabase *pDatabase = NULL);
      bool asyncCallQuery(qx::QxSqlQuery &query, QSqlDatabase *pDatabase = NULL);

      bool isQueryRunning() const { return (m_pDaoParams.get() != NULL); }

   protected:
      virtual void run();

      void startQuery();

   Q_SIGNALS:

      void queryStarted(qx::dao::detail::QxDaoAsyncParams_ptr pDaoParams);
      void queryFinished(const QSqlError &daoError, qx::dao::detail::QxDaoAsyncParams_ptr pDaoParams);

   private Q_SLOTS:

      void onQueryFinished(const QSqlError &daoError, qx::dao::detail::QxDaoAsyncParams_ptr pDaoParams);
   };

   typedef std::shared_ptr<QxDaoAsync> QxDaoAsync_ptr;

} // namespace qx

QX_DLL_EXPORT QDataStream &operator<<(QDataStream &stream, const qx::dao::detail::QxDaoAsyncParams &t) QX_USED;
QX_DLL_EXPORT QDataStream &operator>>(QDataStream &stream, qx::dao::detail::QxDaoAsyncParams &t) QX_USED;

#endif // _QX_DAO_ASYNC_H_
