
#pragma once

#include <functional>
#include <unordered_map>

#include <QMap>
#include <QJSEngine>

#include "MsXpS/export-import-config.h"

namespace MsXpS
{

// This macro is fundamental for the registration of C++ QObject-derived
// classes that we want to expose to JavaScript (QJSEngine/QQmlEngine).
// This macros is added at the end of the class declaration in the
// corresponding namespace (MsXps::libXpertMassCore or MsXpS::libXpertMassGui,
// in the header file) in the form:
// MSXPS_REGISTER_JS_CLASS(MsXpS::libXpertMassCore, Isotope)
//
// This macro uses the getNameSpaceClassNameJsConstructorRegistrarMap()
// function in the pappso namespace which return a map of maps:
// The inner map maps the namespace with the class_name
// The out map maps the inner map to the class' static member function
// that performs the actual registration of the class to the QJSEngine
// that is passed as parameter. What the
// static void registerJsConstructor(QJSEngine *engine);
// function does is simply to register a constructor in the javascript
// global object that allows the JavaScript developer to call a constructor
// like so:
// var isotope = new Isotope(<any param>)

#define STRINGIFY_NS(ns) #ns
#define MSXPS_REGISTER_JS_CLASS(NS_IDENT, CLASS_NAME)                                \
  struct JsRegHelper_##CLASS_NAME                                              \
  {                                                                            \
    JsRegHelper_##CLASS_NAME()                                                 \
    {                                                                          \
      MsXpS::getNameSpaceClassNameJsConstructorRegistrarMap().insert(         \
        {{QStringLiteral(STRINGIFY_NS(NS_IDENT)), QStringLiteral(#CLASS_NAME)},\
         [](QJSEngine *engine) {                                               \
           NS_IDENT::CLASS_NAME::registerJsConstructor(engine);                \
         }});                                                                  \
    }                                                                          \
  };                                                                           \
  static JsRegHelper_##CLASS_NAME jsRegHelperInstance_##CLASS_NAME;


// Key type: pair of namespace and class name
using NamespaceClassnamePairAsKey   = std::pair<QString, QString>;
using RegisterFunc = std::function<void(QJSEngine *)>;

// Hash functor for NsClassKey
struct NamespaceClassnameAsKeyHash
{
  std::size_t
  operator()(const NamespaceClassnamePairAsKey &key) const noexcept
  {
    // Combine the hashes of the two QStrings
    return qHash(key.first) ^ (qHash(key.second) << 1);
  }
};

// Equality functor (needed because we use QString)
struct AreNamespaceClassnamePairsEqual
{
  bool
  operator()(const NamespaceClassnamePairAsKey &a, const NamespaceClassnamePairAsKey &b) const noexcept
  {
    return a.first == b.first && a.second == b.second;
  }
};

 DECLSPEC std::
  unordered_map<NamespaceClassnamePairAsKey, RegisterFunc, NamespaceClassnameAsKeyHash,
                          AreNamespaceClassnamePairsEqual> &
  getNameSpaceClassNameJsConstructorRegistrarMap();

  DECLSPEC void registerJsConstructorForNameSpaceClassNameInRegistrarMap(const QString &name_space, const QString &class_name, QJSEngine *engine);

DECLSPEC void registerJsConstructorForEachClassInRegistrarMap(QJSEngine *engine);


}// namespace MsXpS
