/****************************************************************************
**
** 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
**
****************************************************************************/

#include <QxPrecompiled.h>

#include <stdlib.h>

#include <QtCore/qcoreapplication.h>

#include <QxSingleton/QxSingletonX.h>

#include <QxMemLeak/mem_leak.h>

#ifdef _QX_STATIC_BUILD
#undef _QX_USE_QX_SINGLETON_X
#define _QX_USE_QX_SINGLETON_X 0
#endif // _QX_STATIC_BUILD

QX_DLL_EXPORT_QX_SINGLETON_CPP(qx::QxSingletonX)

namespace qx
{

      /* -- We replaced these static class members by static class methods to avoid (with some compilers) the 'static initialization order fiasco' --
      QHash<QString, IxSingleton *> QxSingletonX::m_mapSingletonX; // replaced by 'getMapSingletonX()'
      QMutex QxSingletonX::m_oMutexSingletonX; // replaced by 'getMutexSingletonX()'
      bool QxSingletonX::m_bOnClearSingletonX = false; // replaced by 'getOnClearSingletonX()'
      */

      QxSingletonX::QxSingletonX() : QxSingleton<QxSingletonX>("qx::QxSingletonX")
      {
#if _QX_USE_QX_SINGLETON_X
            int iResult = std::atexit(&QxSingletonX::deleteAllSingleton);
            Q_UNUSED(iResult);
            QString sAssertMsg = QString("cannot register 'qx::QxSingletonX::deleteAllSingleton()' function at exit program (using 'std::atexit')");
            Q_UNUSED(sAssertMsg);
            qAssertMsg((iResult == 0), "[QxOrm] qx::QxSingletonX() constructor", qPrintable(sAssertMsg));
#endif // _QX_USE_QX_SINGLETON_X
      }

      QHash<QString, IxSingleton *> &QxSingletonX::getMapSingletonX()
      {
            // There is a 'very small' memory leak here : this is to avoid (with some compilers) the 'static initialization order fiasco'
            // More details here : https://isocpp.org/wiki/faq/ctors#static-init-order
            static QHash<QString, IxSingleton *> *p = new QHash<QString, IxSingleton *>();
            return (*p);
      }

      QMutex *QxSingletonX::getMutexSingletonX()
      {
            // There is a 'very small' memory leak here : this is to avoid (with some compilers) the 'static initialization order fiasco'
            // More details here : https://isocpp.org/wiki/faq/ctors#static-init-order
            static QMutex *p = new QMutex();
            return p;
      }

      bool &QxSingletonX::getOnClearSingletonX()
      {
            // There is a 'very small' memory leak here : this is to avoid (with some compilers) the 'static initialization order fiasco'
            // More details here : https://isocpp.org/wiki/faq/ctors#static-init-order
            static bool *p = new bool(false);
            return (*p);
      }

      bool QxSingletonX::addSingleton(const QString &sKey, IxSingleton *pSingleton)
      {
#if _QX_USE_QX_SINGLETON_X
#ifdef _QX_TRACE_CONSTRUCTOR_DESTRUCTOR
            qDebug("[QxOrm] qx::QxSingletonX::addSingleton() : '%s'", qPrintable(sKey));
#endif // _QX_TRACE_CONSTRUCTOR_DESTRUCTOR
            QMutex *pMutex = (QCoreApplication::instance() ? getMutexSingletonX() : NULL);
            QMutexLocker locker(pMutex);
            bool bExist = getMapSingletonX().contains(sKey);
            QString sAssertMsg = QString("singleton key '%1' already exists or is empty").arg(sKey);
            Q_UNUSED(sAssertMsg);
            qAssertMsg((!bExist && !sKey.isEmpty()), "[QxOrm] qx::QxSingletonX::addSingleton()", qPrintable(sAssertMsg));
            if (!pSingleton || bExist || sKey.isEmpty())
            {
                  return false;
            }
            getMapSingletonX().insert(sKey, pSingleton);
            return true;
#else  // _QX_USE_QX_SINGLETON_X
            Q_UNUSED(sKey);
            Q_UNUSED(pSingleton);
            return false;
#endif // _QX_USE_QX_SINGLETON_X
      }

      bool QxSingletonX::removeSingleton(const QString &sKey)
      {
#if _QX_USE_QX_SINGLETON_X
            bool &bOnClearSingletonX = getOnClearSingletonX();
            if (bOnClearSingletonX)
            {
                  return false;
            }
#ifdef _QX_TRACE_CONSTRUCTOR_DESTRUCTOR
            qDebug("[QxOrm] qx::QxSingletonX::removeSingleton() : '%s'", qPrintable(sKey));
#endif // _QX_TRACE_CONSTRUCTOR_DESTRUCTOR
            QMutex *pMutex = (QCoreApplication::instance() ? getMutexSingletonX() : NULL);
            QMutexLocker locker(pMutex);
            QString sAssertMsg = QString("singleton key '%1' doesn't exist in the singleton manager").arg(sKey);
            Q_UNUSED(sAssertMsg);
            qAssertMsg((getMapSingletonX().contains(sKey)), "[QxOrm] qx::QxSingletonX::removeSingleton()", qPrintable(sAssertMsg));
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
            bool bRemoveOk = getMapSingletonX().remove(sKey);
#else  // (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
            bool bRemoveOk = (getMapSingletonX().remove(sKey) > 0);
#endif // (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
            return bRemoveOk;
#else  // _QX_USE_QX_SINGLETON_X
            Q_UNUSED(sKey);
            return false;
#endif // _QX_USE_QX_SINGLETON_X
      }

      void QxSingletonX::deleteAllSingleton()
      {
#if _QX_USE_QX_SINGLETON_X
#ifdef _QX_TRACE_CONSTRUCTOR_DESTRUCTOR
            qDebug("[QxOrm] qx::QxSingletonX : %s", "execute deleteAllSingleton() function");
#endif // _QX_TRACE_CONSTRUCTOR_DESTRUCTOR
            QMutex *pMutex = (QCoreApplication::instance() ? getMutexSingletonX() : NULL);
            QMutexLocker locker(pMutex);
            bool &bOnClearSingletonX = getOnClearSingletonX();
            bOnClearSingletonX = true;

            QHash<QString, IxSingleton *> &mapSingletonX = getMapSingletonX();
            QHashIterator<QString, IxSingleton *> itr(mapSingletonX);
            while (itr.hasNext())
            {
                  itr.next();
                  IxSingleton *pSingleton = itr.value();
                  if (pSingleton && (pSingleton != QxSingletonX::getSingleton()))
                        pSingleton->deleteInstance();
            }

            QxSingletonX::deleteSingleton();
            mapSingletonX.clear();
            bOnClearSingletonX = false;
#endif // _QX_USE_QX_SINGLETON_X
      }

} // namespace qx
