/****************************************************************************
**
** 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_CLASS_H_
#define _QX_CLASS_H_

#ifdef _MSC_VER
#pragma once
#endif

/*!
 * \file QxClass.h
 * \author XDL Team
 * \ingroup QxRegister
 * \brief Concrete class registered into QxOrm context
 */

#include <QxRegister/IxClass.h>

#include <QxDataMember/QxDataMemberX.h>

#include <QxFunction/QxFunctionInclude.h>

#include <QxSingleton/QxSingleton.h>

#include <QxTraits/get_class_name.h>
#include <QxTraits/get_base_class.h>
#include <QxTraits/get_primary_key.h>

#include <QxValidator/QxValidatorX.h>

namespace qx
{

   namespace trait
   {
      template <typename T>
      struct is_ix_persistable;
   } // namespace trait

   /*!
    * \ingroup QxRegister
    * \brief qx::register_class<T>(T & t) : specialize this template to register a class of type T into QxOrm context
    */
   template <class T>
   void register_class(T &t)
   {
      Q_UNUSED(t);
      qAssert(false);
   };

   /*!
    * \ingroup QxRegister
    * \brief qx::QxClass<T> : concrete class of type T registered into QxOrm context (this class is a singleton and is thread-safe)
    */
   template <class T>
   class QxClass : public IxClass, public QxSingleton<QxClass<T>>
   {

      friend class QxSingleton<QxClass<T>>;

   public:
      typedef typename qx::trait::get_primary_key<T>::type type_primary_key;
      typedef typename qx::trait::get_base_class<T>::type type_base_class;
      typedef IxFunction::type_any_params type_any_params;

      enum
      {
         is_valid_base_class = ((!std::is_same<type_base_class, T>::value) && (std::is_base_of<type_base_class, T>::value || std::is_same<type_base_class, qx::trait::no_base_class_defined>::value))
      };

   protected:
      QMutex m_oMutexClass; //!< Mutex -> 'QxClass' is thread-safe

   protected:
      QxClass() : IxClass(), QxSingleton<QxClass<T>>(QString("qx::QxClass_") + qx::trait::get_class_name<T>::get_xml_tag()) { init(); }
      virtual ~QxClass() { ; }

   public:
      QxDataMemberX<T> *dataMemberX() const { return static_cast<QxDataMemberX<T> *>(this->getDataMemberX()); }
      IxFunctionX *fctMemberX() const { return this->getFctMemberX(); }
      IxFunctionX *fctStaticX() const { return this->getFctStaticX(); }

      IxDataMember *id(type_primary_key T::*pDataMemberId, const QString &sKey, long lVersion = 0);
      IxDataMember *id(const QString &sKey, long lVersion);
      IxDataMember *data(const QString &sKey, long lVersion);

      template <typename V, typename U>
      IxDataMember *data(V U::*pData, const QString &sKey, long lVersion = 0, bool bSerialize = true, bool bDao = true);
      template <typename V, typename U>
      IxSqlRelation *relationOneToOne(V U::*pData, const QString &sKey, long lVersion = 0);
      template <typename V, typename U>
      IxSqlRelation *relationManyToOne(V U::*pData, const QString &sKey, long lVersion = 0);
      template <typename V, typename U>
      IxSqlRelation *relationOneToMany(V U::*pData, const QString &sKey, const QString &sForeignKey, long lVersion = 0);
      template <typename V, typename U>
      IxSqlRelation *relationManyToMany(V U::*pData, const QString &sKey, const QString &sExtraTable, const QString &sForeignKeyOwner, const QString &sForeignKeyDataType, long lVersion = 0);

      template <typename V, typename U>
      IxDataMember *pimpl(V U::*pData, const QString &sKey = QString("_PIMPL_"));
      template <typename U>
      IxDataMember *id(type_primary_key U::*pDataMemberId, const QString &sKey, long lVersion, IxDataMember *pImpl);
      template <typename V, typename U>
      IxDataMember *data(V U::*pData, const QString &sKey, long lVersion, bool bSerialize, bool bDao, IxDataMember *pImpl);
      template <typename V, typename U>
      IxSqlRelation *relationOneToOne(V U::*pData, const QString &sKey, long lVersion, IxDataMember *pImpl);
      template <typename V, typename U>
      IxSqlRelation *relationManyToOne(V U::*pData, const QString &sKey, long lVersion, IxDataMember *pImpl);
      template <typename V, typename U>
      IxSqlRelation *relationOneToMany(V U::*pData, const QString &sKey, const QString &sForeignKey, long lVersion, IxDataMember *pImpl);
      template <typename V, typename U>
      IxSqlRelation *relationManyToMany(V U::*pData, const QString &sKey, const QString &sExtraTable, const QString &sForeignKeyOwner, const QString &sForeignKeyDataType, long lVersion, IxDataMember *pImpl);

      template <typename R>
      IxFunction *fct_0(const typename QxFunction_0<T, R>::type_fct &fct, const QString &sKey);
      template <typename R, typename P1>
      IxFunction *fct_1(const typename QxFunction_1<T, R, P1>::type_fct &fct, const QString &sKey);
      template <typename R, typename P1, typename P2>
      IxFunction *fct_2(const typename QxFunction_2<T, R, P1, P2>::type_fct &fct, const QString &sKey);
      template <typename R, typename P1, typename P2, typename P3>
      IxFunction *fct_3(const typename QxFunction_3<T, R, P1, P2, P3>::type_fct &fct, const QString &sKey);
      template <typename R, typename P1, typename P2, typename P3, typename P4>
      IxFunction *fct_4(const typename QxFunction_4<T, R, P1, P2, P3, P4>::type_fct &fct, const QString &sKey);
      template <typename R, typename P1, typename P2, typename P3, typename P4, typename P5>
      IxFunction *fct_5(const typename QxFunction_5<T, R, P1, P2, P3, P4, P5>::type_fct &fct, const QString &sKey);
      template <typename R, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6>
      IxFunction *fct_6(const typename QxFunction_6<T, R, P1, P2, P3, P4, P5, P6>::type_fct &fct, const QString &sKey);
      template <typename R, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7>
      IxFunction *fct_7(const typename QxFunction_7<T, R, P1, P2, P3, P4, P5, P6, P7>::type_fct &fct, const QString &sKey);
      template <typename R, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7, typename P8>
      IxFunction *fct_8(const typename QxFunction_8<T, R, P1, P2, P3, P4, P5, P6, P7, P8>::type_fct &fct, const QString &sKey);
      template <typename R, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7, typename P8, typename P9>
      IxFunction *fct_9(const typename QxFunction_9<T, R, P1, P2, P3, P4, P5, P6, P7, P8, P9>::type_fct &fct, const QString &sKey);

      template <typename R>
      IxFunction *fctStatic_0(const typename QxFunction_0<void, R>::type_fct &fct, const QString &sKey);
      template <typename R, typename P1>
      IxFunction *fctStatic_1(const typename QxFunction_1<void, R, P1>::type_fct &fct, const QString &sKey);
      template <typename R, typename P1, typename P2>
      IxFunction *fctStatic_2(const typename QxFunction_2<void, R, P1, P2>::type_fct &fct, const QString &sKey);
      template <typename R, typename P1, typename P2, typename P3>
      IxFunction *fctStatic_3(const typename QxFunction_3<void, R, P1, P2, P3>::type_fct &fct, const QString &sKey);
      template <typename R, typename P1, typename P2, typename P3, typename P4>
      IxFunction *fctStatic_4(const typename QxFunction_4<void, R, P1, P2, P3, P4>::type_fct &fct, const QString &sKey);
      template <typename R, typename P1, typename P2, typename P3, typename P4, typename P5>
      IxFunction *fctStatic_5(const typename QxFunction_5<void, R, P1, P2, P3, P4, P5>::type_fct &fct, const QString &sKey);
      template <typename R, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6>
      IxFunction *fctStatic_6(const typename QxFunction_6<void, R, P1, P2, P3, P4, P5, P6>::type_fct &fct, const QString &sKey);
      template <typename R, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7>
      IxFunction *fctStatic_7(const typename QxFunction_7<void, R, P1, P2, P3, P4, P5, P6, P7>::type_fct &fct, const QString &sKey);
      template <typename R, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7, typename P8>
      IxFunction *fctStatic_8(const typename QxFunction_8<void, R, P1, P2, P3, P4, P5, P6, P7, P8>::type_fct &fct, const QString &sKey);
      template <typename R, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7, typename P8, typename P9>
      IxFunction *fctStatic_9(const typename QxFunction_9<void, R, P1, P2, P3, P4, P5, P6, P7, P8, P9>::type_fct &fct, const QString &sKey);

      static qx_bool invoke(const QString &sKey, T *pOwner, const QString &params = QString(), qx::any *ret = NULL)
      {
         IxFunctionX *pFctX = QxClass<T>::getSingleton()->fctMemberX();
         IxFunction_ptr pFct = ((pFctX && pFctX->exist(sKey)) ? pFctX->getByKey(sKey) : IxFunction_ptr());
         return invokeHelper<T, type_base_class, 0>::invoke(sKey, pOwner, params, ret, pFct);
      }
      static qx_bool invoke(const QString &sKey, T *pOwner, const type_any_params &params, qx::any *ret = NULL)
      {
         IxFunctionX *pFctX = QxClass<T>::getSingleton()->fctMemberX();
         IxFunction_ptr pFct = ((pFctX && pFctX->exist(sKey)) ? pFctX->getByKey(sKey) : IxFunction_ptr());
         return invokeHelper<T, type_base_class, 0>::invoke(sKey, pOwner, params, ret, pFct);
      }
      static qx_bool invokeStatic(const QString &sKey, const QString &params = QString(), qx::any *ret = NULL)
      {
         IxFunctionX *pFctX = QxClass<T>::getSingleton()->fctStaticX();
         IxFunction_ptr pFct = ((pFctX && pFctX->exist(sKey)) ? pFctX->getByKey(sKey) : IxFunction_ptr());
         return invokeHelper<T, type_base_class, 0>::invoke(sKey, params, ret, pFct);
      }
      static qx_bool invokeStatic(const QString &sKey, const type_any_params &params, qx::any *ret = NULL)
      {
         IxFunctionX *pFctX = QxClass<T>::getSingleton()->fctStaticX();
         IxFunction_ptr pFct = ((pFctX && pFctX->exist(sKey)) ? pFctX->getByKey(sKey) : IxFunction_ptr());
         return invokeHelper<T, type_base_class, 0>::invoke(sKey, params, ret, pFct);
      }

      virtual bool isAbstract() const
      {
         return std::is_abstract<T>::value;
      }

      virtual bool implementIxPersistable() const
      {
         return implementIxPersistable_Helper<T, 0>::get();
      }

#ifndef _QX_NO_RTTI
      virtual const std::type_info &typeInfo() const
      {
         return typeid(T);
      }
#endif // _QX_NO_RTTI

      virtual IxClass *getBaseClass() const
      {
         return (std::is_same<type_base_class, qx::trait::no_base_class_defined>::value ? NULL : QxClass<type_base_class>::getSingleton());
      }

#if _QX_SUPPORT_COVARIANT_RETURN_TYPE
      virtual QxValidatorX<T> *getAllValidator()
      {
         if (!this->getAllValidatorRef())
         {
            this->getAllValidatorRef().reset(new QxValidatorX<T>());
            IxClass::getAllValidator();
         }
         return static_cast<QxValidatorX<T> *>(this->getAllValidatorRef().get());
      }
#else  // _QX_SUPPORT_COVARIANT_RETURN_TYPE
      virtual IxValidatorX *getAllValidator()
      {
         if (!this->getAllValidatorRef())
         {
            this->getAllValidatorRef().reset(new QxValidatorX<T>());
            IxClass::getAllValidator();
         }
         return this->getAllValidatorRef().get();
      }
#endif // _QX_SUPPORT_COVARIANT_RETURN_TYPE

   private:
      void init();
      IxFunction *insertFct(IxFunction_ptr pFct, const QString &sKey);
      IxFunction *insertFctStatic(IxFunction_ptr pFct, const QString &sKey);

      void registerClass() { qx::register_class<QxClass<T>>(*this); }

      void beforeRegisterClass()
      {
         static_assert(is_valid_base_class, "is_valid_base_class");
         QMutexLocker locker(&m_oMutexClass);
         QxClass<type_base_class>::getSingleton();
         bool bNeedReg = (!this->isRegistered());
         this->setRegistered(true);
         if (bNeedReg)
         {
            registerClass();
         }
      }

      template <typename U, typename V, int dummy>
      struct invokeHelper
      {
         static qx_bool invoke(const QString &sKey, U *pOwner, const QString &params, qx::any *ret, IxFunction_ptr pFct)
         {
            return ((pOwner && pFct) ? pFct->invoke(pOwner, params, ret) : QxClass<V>::invoke(sKey, static_cast<V *>(pOwner), params, ret));
         }

         static qx_bool invoke(const QString &sKey, U *pOwner, const type_any_params &params, qx::any *ret, IxFunction_ptr pFct)
         {
            return ((pOwner && pFct) ? pFct->invoke(pOwner, params, ret) : QxClass<V>::invoke(sKey, static_cast<V *>(pOwner), params, ret));
         }

         static qx_bool invoke(const QString &sKey, const QString &params, qx::any *ret, IxFunction_ptr pFct)
         {
            return (pFct ? pFct->invoke(params, ret) : QxClass<V>::invokeStatic(sKey, params, ret));
         }

         static qx_bool invoke(const QString &sKey, const type_any_params &params, qx::any *ret, IxFunction_ptr pFct)
         {
            return (pFct ? pFct->invoke(params, ret) : QxClass<V>::invokeStatic(sKey, params, ret));
         }
      };

      template <typename U, int dummy>
      struct invokeHelper<U, qx::trait::no_base_class_defined, dummy>
      {
         static qx_bool invoke(const QString &sKey, U *pOwner, const QString &params, qx::any *ret, IxFunction_ptr pFct)
         {
            Q_UNUSED(sKey);
            return ((pOwner && pFct) ? pFct->invoke(pOwner, params, ret) : qx_bool(false));
         }

         static qx_bool invoke(const QString &sKey, U *pOwner, const type_any_params &params, qx::any *ret, IxFunction_ptr pFct)
         {
            Q_UNUSED(sKey);
            return ((pOwner && pFct) ? pFct->invoke(pOwner, params, ret) : qx_bool(false));
         }

         static qx_bool invoke(const QString &sKey, const QString &params, qx::any *ret, IxFunction_ptr pFct)
         {
            Q_UNUSED(sKey);
            return (pFct ? pFct->invoke(params, ret) : qx_bool(false));
         }

         static qx_bool invoke(const QString &sKey, const type_any_params &params, qx::any *ret, IxFunction_ptr pFct)
         {
            Q_UNUSED(sKey);
            return (pFct ? pFct->invoke(params, ret) : qx_bool(false));
         }
      };

      template <int dummy>
      struct invokeHelper<qx::trait::no_base_class_defined, qx::trait::no_base_class_defined, dummy>
      {
         static qx_bool invoke(const QString &sKey, qx::trait::no_base_class_defined *pOwner, const QString &params, qx::any *ret, IxFunction_ptr pFct)
         {
            Q_UNUSED(sKey);
            Q_UNUSED(pOwner);
            Q_UNUSED(params);
            Q_UNUSED(ret);
            Q_UNUSED(pFct);
            return qx_bool(false);
         }

         static qx_bool invoke(const QString &sKey, qx::trait::no_base_class_defined *pOwner, const type_any_params &params, qx::any *ret, IxFunction_ptr pFct)
         {
            Q_UNUSED(sKey);
            Q_UNUSED(pOwner);
            Q_UNUSED(params);
            Q_UNUSED(ret);
            Q_UNUSED(pFct);
            return qx_bool(false);
         }

         static qx_bool invoke(const QString &sKey, const QString &params, qx::any *ret, IxFunction_ptr pFct)
         {
            Q_UNUSED(sKey);
            Q_UNUSED(params);
            Q_UNUSED(ret);
            Q_UNUSED(pFct);
            return qx_bool(false);
         }

         static qx_bool invoke(const QString &sKey, const type_any_params &params, qx::any *ret, IxFunction_ptr pFct)
         {
            Q_UNUSED(sKey);
            Q_UNUSED(params);
            Q_UNUSED(ret);
            Q_UNUSED(pFct);
            return qx_bool(false);
         }
      };

      template <int dummy>
      struct invokeHelper<QObject, qx::trait::no_base_class_defined, dummy>
      {
         static qx_bool invoke(const QString &sKey, QObject *pOwner, const QString &params, qx::any *ret, IxFunction_ptr pFct)
         {
            Q_UNUSED(sKey);
            Q_UNUSED(pOwner);
            Q_UNUSED(params);
            Q_UNUSED(ret);
            Q_UNUSED(pFct);
            return qx_bool(false);
         }

         static qx_bool invoke(const QString &sKey, QObject *pOwner, const type_any_params &params, qx::any *ret, IxFunction_ptr pFct)
         {
            Q_UNUSED(sKey);
            Q_UNUSED(pOwner);
            Q_UNUSED(params);
            Q_UNUSED(ret);
            Q_UNUSED(pFct);
            return qx_bool(false);
         }

         static qx_bool invoke(const QString &sKey, const QString &params, qx::any *ret, IxFunction_ptr pFct)
         {
            Q_UNUSED(sKey);
            Q_UNUSED(params);
            Q_UNUSED(ret);
            Q_UNUSED(pFct);
            return qx_bool(false);
         }

         static qx_bool invoke(const QString &sKey, const type_any_params &params, qx::any *ret, IxFunction_ptr pFct)
         {
            Q_UNUSED(sKey);
            Q_UNUSED(params);
            Q_UNUSED(ret);
            Q_UNUSED(pFct);
            return qx_bool(false);
         }
      };

   private:
      template <typename U, int dummy>
      struct implementIxPersistable_Helper
      {
         static bool get() { return qx::trait::is_ix_persistable<U>::value; }
      };

      template <int dummy>
      struct implementIxPersistable_Helper<qx::trait::no_base_class_defined, dummy>
      {
         static bool get() { return false; }
      };

      template <int dummy>
      struct implementIxPersistable_Helper<QObject, dummy>
      {
         static bool get() { return false; }
      };
   };

} // namespace qx

#include "../../inl/QxRegister/QxClass.inl"

#endif // _QX_CLASS_H_
