#include <guiddef.h>
#include <objbase.h>
#include <shlobj.h>
#include <shlwapi.h>
#include <thumbcache.h>

#include <new>
#include <string>
#include <vector>

#include "F3DShellExtensionClassFactory.h"
#include "RegistryHelpers.h"

#include "F3DShellExtension.h"

// Unique F3D Thumbnail Provider GUID
static const GUID CLSID_F3DThumbnailProvider =
  { 0x0f3d0f3d0, 0x7d37, 0x4ccc, { 0x89, 0x99, 0x4f, 0xdd, 0xde, 0xbc, 0x5b, 0xe6 } };

// Handle the the DLL's module
HINSTANCE g_hInst = NULL;
long g_cDllRef = 0;

//------------------------------------------------------------------------------
// Standard DLL functions
STDAPI_(BOOL) DllMain(HINSTANCE hInstance, DWORD dwReason, void*)
{
  if (dwReason == DLL_PROCESS_ATTACH)
  {
    g_hInst = hInstance;
    DisableThreadLibraryCalls(hInstance);
  }
  return TRUE;
}

//------------------------------------------------------------------------------
STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void** ppv)
{
  HRESULT hr = CLASS_E_CLASSNOTAVAILABLE;

  if (IsEqualCLSID(CLSID_F3DThumbnailProvider, rclsid))
  {
    hr = E_OUTOFMEMORY;

    F3DShellExtensionClassFactory* pClassFactory = new F3DShellExtensionClassFactory();
    if (pClassFactory)
    {
      hr = pClassFactory->QueryInterface(riid, ppv);
      pClassFactory->Release();
    }
  }

  return hr;
}

//------------------------------------------------------------------------------
STDAPI DllCanUnloadNow()
{
  return (g_cDllRef == 0) ? S_OK : S_FALSE;
}

//------------------------------------------------------------------------------
// Register the COM server and the f3d thumbnail handler.
STDAPI DllRegisterServer()
{
  wchar_t szModuleName[MAX_PATH];
  if (GetModuleFileName(g_hInst, szModuleName, ARRAYSIZE(szModuleName)) == 0)
  {
    return HRESULT_FROM_WIN32(GetLastError());
  }

  // Register the component.
  HRESULT hr = RegisterInprocServer(szModuleName, CLSID_F3DThumbnailProvider,
    L"F3DShellExtension.F3DThumbnailProvider Class");
  if (!SUCCEEDED(hr))
  {
    return hr;
  }
  // Register the thumbnail handler. The thumbnail handler is associated
  // with the f3d file class.
  for (auto ext : extensions)
  {
    hr |= RegisterShellExtThumbnailHandler(ext.c_str(), CLSID_F3DThumbnailProvider);
  }
  if (SUCCEEDED(hr))
  {
    // Invalidate the thumbnail cache.
    SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
  }

  return hr;
}

//------------------------------------------------------------------------------
// Unregister the COM server and the f3d thumbnail handler.
STDAPI DllUnregisterServer()
{
  wchar_t szModuleName[MAX_PATH];
  if (GetModuleFileName(g_hInst, szModuleName, ARRAYSIZE(szModuleName)) == 0)
  {
    return HRESULT_FROM_WIN32(GetLastError());
  }

  // Unregister the component.
  HRESULT hr = UnregisterInprocServer(CLSID_F3DThumbnailProvider);
  if (SUCCEEDED(hr))
  {
    // Unregister the thumbnail handler.
    for (auto ext : extensions)
    {
      hr |= UnregisterShellExtThumbnailHandler(ext.c_str());
    }
  }

  return hr;
}

//------------------------------------------------------------------------------
// Notify the Windows Shell that file associations have changed.
STDAPI DllNotifyShell()
{
  // Invalidate the thumbnail cache.
  SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);

  return S_OK;
}

