/////////////////////////////////////////////////////////////////////////////////////////
//
// Please refer to "COPYRIGHT.md" for the relevant copyright statement of this software.
//
/////////////////////////////////////////////////////////////////////////////////////////
//
#pragma once

class CGcExtensionModule
{
public:
  CGcExtensionModule();
  ~CGcExtensionModule();

protected:
  bool m_bAttached;
  HINSTANCE m_hDefaultResource;
  HINSTANCE m_hModuleResource;
#ifndef _GSOFT_MAC_
  AFX_EXTENSION_MODULE m_module;
#endif

public:
  bool Attached() const;
  HINSTANCE DefaultResourceInstance() const;
  HINSTANCE ModuleResourceInstance() const;
  bool AttachInstance(HINSTANCE hExtInst, HINSTANCE hResInst);
  bool AttachInstance(HINSTANCE hExtInst)
  {
    return this->AttachInstance(hExtInst, hExtInst);
  }
  void DetachInstance();
};

inline CGcExtensionModule::CGcExtensionModule() : m_bAttached(false), m_hDefaultResource(NULL), m_hModuleResource(NULL)
{
#ifndef _GSOFT_MAC_
  m_module.bInitialized = FALSE;
  m_module.hModule = NULL;
  m_module.hResource = NULL;
  m_module.pFirstSharedClass = NULL;
  m_module.pFirstSharedFactory = NULL;
#endif
}

inline CGcExtensionModule::~CGcExtensionModule()
{
}

inline bool CGcExtensionModule::Attached() const
{
  return m_bAttached;
}

inline bool CGcExtensionModule::AttachInstance(HINSTANCE hExtInst, HINSTANCE hResInst)
{
  ASSERT(hExtInst != nullptr);
  ASSERT(hResInst != nullptr);
  if (m_bAttached)
    return false;
#ifndef _GSOFT_MAC_
  ASSERT(!m_module.bInitialized);
  m_bAttached = AfxInitExtensionModule(m_module, hExtInst) != 0;
#else
  m_bAttached = true;
#endif
  ASSERT(this->m_hDefaultResource == nullptr);
  ASSERT(this->m_hModuleResource == nullptr);
  if (m_bAttached)
  {
    this->m_hDefaultResource = ::AfxGetResourceHandle();
    this->m_hModuleResource = hResInst;
#ifndef _GSOFT_MAC_
    ASSERT(this->m_module.hModule == hExtInst);
    ASSERT(this->m_module.hResource == hExtInst);
    this->m_module.hResource = hResInst;
    new CDynLinkLibrary(m_module);
#endif
  }
  return m_bAttached;
}

inline HINSTANCE CGcExtensionModule::DefaultResourceInstance() const
{
  return m_hDefaultResource;
}

inline void CGcExtensionModule::DetachInstance()
{
  if (m_bAttached)
  {
#ifndef _GSOFT_MAC_
    AfxTermExtensionModule(m_module);
#endif
    this->m_hDefaultResource = nullptr;
    this->m_hModuleResource = nullptr;
    m_bAttached = FALSE;
  }
}

inline HINSTANCE CGcExtensionModule::ModuleResourceInstance() const
{
  return m_hModuleResource;
}

class CGcModuleResourceOverride
{
public:
  CGcModuleResourceOverride();
  CGcModuleResourceOverride(HINSTANCE hInst);
  ~CGcModuleResourceOverride();
  static HINSTANCE ResourceInstance()
  {
    return m_extensionModule.ModuleResourceInstance();
  }

private:
  static CGcExtensionModule& m_extensionModule;
  HINSTANCE m_previousResourceInstance;
};

inline CGcModuleResourceOverride::CGcModuleResourceOverride() : CGcModuleResourceOverride(NULL)
{
}

inline CGcModuleResourceOverride::CGcModuleResourceOverride(HINSTANCE hInst)
{
  m_previousResourceInstance = AfxGetResourceHandle();
  HINSTANCE hInstanceToSet = m_extensionModule.ModuleResourceInstance();
  if (hInst)
    hInstanceToSet = hInst;
  AfxSetResourceHandle(hInstanceToSet);
}

inline CGcModuleResourceOverride::~CGcModuleResourceOverride()
{
  ASSERT(m_previousResourceInstance);
  AfxSetResourceHandle(m_previousResourceInstance);
  m_previousResourceInstance = NULL;
}

#define GC_DECLARE_EXTENSION_MODULE(exm) extern CGcExtensionModule exm;

#define GC_IMPLEMENT_EXTENSION_MODULE(exm) \
  CGcExtensionModule exm;                  \
  CGcExtensionModule& CGcModuleResourceOverride::m_extensionModule = exm;
