/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "CEHHelper.h"

#ifdef SHOW_CONSOLE
#include <io.h> // _open_osfhandle
#endif

HANDLE sCon;
LPCWSTR metroDX10Available = L"MetroD3DAvailable";

typedef HRESULT (WINAPI*D3D10CreateDevice1Func)
  (IDXGIAdapter *, D3D10_DRIVER_TYPE, HMODULE, UINT,
   D3D10_FEATURE_LEVEL1, UINT, ID3D10Device1 **);
typedef HRESULT(WINAPI*CreateDXGIFactory1Func)(REFIID , void **);

void
Log(const wchar_t *fmt, ...)
{
#if !defined(SHOW_CONSOLE)
  return;
#endif
  va_list a = NULL;
  wchar_t szDebugString[1024];
  if(!lstrlenW(fmt))
    return;
  va_start(a,fmt);
  vswprintf(szDebugString, 1024, fmt, a);
  va_end(a);
  if(!lstrlenW(szDebugString))
    return;

  DWORD len;
  WriteConsoleW(sCon, szDebugString, lstrlenW(szDebugString), &len, NULL);
  WriteConsoleW(sCon, L"\n", 1, &len, NULL);

  if (IsDebuggerPresent()) {  
    OutputDebugStringW(szDebugString);
    OutputDebugStringW(L"\n");
  }
}

#if defined(SHOW_CONSOLE)
void
SetupConsole()
{
  FILE *fp;
  AllocConsole();
  sCon = GetStdHandle(STD_OUTPUT_HANDLE); 
  int fd = _open_osfhandle(reinterpret_cast<intptr_t>(sCon), 0);
  fp = _fdopen(fd, "w");
  *stdout = *fp;
  setvbuf(stdout, NULL, _IONBF, 0);
}
#endif

bool
IsDX10Available()
{
  DWORD isDX10Available;
  if (GetDWORDRegKey(metroDX10Available, isDX10Available)) {
    return isDX10Available;
  }

  HMODULE dxgiModule = LoadLibraryA("dxgi.dll");
  if (!dxgiModule) {
    SetDWORDRegKey(metroDX10Available, 0);
    return false;
  }
  CreateDXGIFactory1Func createDXGIFactory1 =
    (CreateDXGIFactory1Func) GetProcAddress(dxgiModule, "CreateDXGIFactory1");
  if (!createDXGIFactory1) {
    SetDWORDRegKey(metroDX10Available, 0);
    return false;
  }

  HMODULE d3d10module = LoadLibraryA("d3d10_1.dll");
  if (!d3d10module) {
    SetDWORDRegKey(metroDX10Available, 0);
    return false;
  }
  D3D10CreateDevice1Func createD3DDevice =
    (D3D10CreateDevice1Func) GetProcAddress(d3d10module,
                                            "D3D10CreateDevice1");
  if (!createD3DDevice) {
    SetDWORDRegKey(metroDX10Available, 0);
    return false;
  }

  CComPtr<IDXGIFactory1> factory1;
  if (FAILED(createDXGIFactory1(__uuidof(IDXGIFactory1), (void**)&factory1))) {
    SetDWORDRegKey(metroDX10Available, 0);
    return false;
  }

  CComPtr<IDXGIAdapter1> adapter1;
  if (FAILED(factory1->EnumAdapters1(0, &adapter1))) {
    SetDWORDRegKey(metroDX10Available, 0);
    return false;
  }

  CComPtr<ID3D10Device1> device;
  // Try for DX10.1
  if (FAILED(createD3DDevice(adapter1, D3D10_DRIVER_TYPE_HARDWARE, NULL,
                             D3D10_CREATE_DEVICE_BGRA_SUPPORT |
                             D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS,
                             D3D10_FEATURE_LEVEL_10_1,
                             D3D10_1_SDK_VERSION, &device))) {
    // Try for DX10
    if (FAILED(createD3DDevice(adapter1, D3D10_DRIVER_TYPE_HARDWARE, NULL,
                               D3D10_CREATE_DEVICE_BGRA_SUPPORT |
                               D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS,
                               D3D10_FEATURE_LEVEL_10_0,
                               D3D10_1_SDK_VERSION, &device))) {
      // Try for DX9.3 (we fall back to cairo and cairo has support for D3D 9.3)
      if (FAILED(createD3DDevice(adapter1, D3D10_DRIVER_TYPE_HARDWARE, NULL,
                                 D3D10_CREATE_DEVICE_BGRA_SUPPORT |
                                 D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS,
                                 D3D10_FEATURE_LEVEL_9_3,
                                 D3D10_1_SDK_VERSION, &device))) {

        SetDWORDRegKey(metroDX10Available, 0);
        return false;
      }
    }
  }
  

  SetDWORDRegKey(metroDX10Available, 1);
  return true;
}

bool
GetDWORDRegKey(LPCWSTR name, DWORD &value)
{
  HKEY key;
  LONG result = RegOpenKeyExW(HKEY_CURRENT_USER, L"SOFTWARE\\Mozilla\\Firefox",
                              0, KEY_READ, &key);
  if (result != ERROR_SUCCESS) {
    return false;
  }

  DWORD bufferSize = sizeof(DWORD);
  DWORD type;
  result = RegQueryValueExW(key, name, nullptr, &type,
                            reinterpret_cast<LPBYTE>(&value),
                            &bufferSize);
  RegCloseKey(key);
  return result == ERROR_SUCCESS;
}

bool
SetDWORDRegKey(LPCWSTR name, DWORD value)
{
  HKEY key;
  LONG result = RegOpenKeyExW(HKEY_CURRENT_USER, L"SOFTWARE\\Mozilla\\Firefox",
                              0, KEY_WRITE, &key);
  if (result != ERROR_SUCCESS) {
    return false;
  }

  result = RegSetValueEx(key, name, 0, REG_DWORD,
                         reinterpret_cast<LPBYTE>(&value),
                         sizeof(DWORD));
  RegCloseKey(key);
  return result == ERROR_SUCCESS;
}
