// Copyright 2012 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.

#include <string>
#include <windows.h>

#include "Common/GL/GLInterface/WGL.h"
#include "Common/Logging/Log.h"
#include "Common/MsgHandler.h"

static HDC hDC = nullptr;              // Private GDI Device Context
static HGLRC hRC = nullptr;            // Permanent Rendering Context
static HINSTANCE dllHandle = nullptr;  // Handle to OpenGL32.dll

// typedef from wglext.h
typedef BOOL(WINAPI* PFNWGLSWAPINTERVALEXTPROC)(int interval);
static PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = nullptr;

void cInterfaceWGL::SwapInterval(int Interval)
{
  if (wglSwapIntervalEXT)
    wglSwapIntervalEXT(Interval);
  else
    ERROR_LOG(VIDEO, "No support for SwapInterval (framerate clamped to monitor refresh rate).");
}
void cInterfaceWGL::Swap()
{
  SwapBuffers(hDC);
}

void* cInterfaceWGL::GetFuncAddress(const std::string& name)
{
  void* func = (void*)wglGetProcAddress((LPCSTR)name.c_str());
  if (func == nullptr)
    func = (void*)GetProcAddress(dllHandle, (LPCSTR)name.c_str());
  return func;
}

// Draw messages on top of the screen
bool cInterfaceWGL::PeekMessages()
{
  // TODO: peekmessage
  MSG msg;
  while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
  {
    if (msg.message == WM_QUIT)
      return FALSE;
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }
  return TRUE;
}

// Create rendering window.
// Call browser: Core.cpp:EmuThread() > main.cpp:Video_Initialize()
bool cInterfaceWGL::Create(void* window_handle, bool core)
{
  if (window_handle == nullptr)
    return false;

  HWND window_handle_reified = reinterpret_cast<HWND>(window_handle);
  RECT window_rect = {0};

  if (!GetClientRect(window_handle_reified, &window_rect))
    return false;

  // Control window size and picture scaling
  int twidth = (window_rect.right - window_rect.left);
  int theight = (window_rect.bottom - window_rect.top);
  s_backbuffer_width = twidth;
  s_backbuffer_height = theight;

  m_window_handle = window_handle_reified;

  dllHandle = LoadLibrary(TEXT("OpenGL32.dll"));

  PIXELFORMATDESCRIPTOR pfd =  // pfd Tells Windows How We Want Things To Be
      {
          sizeof(PIXELFORMATDESCRIPTOR),  // Size Of This Pixel Format Descriptor
          1,                              // Version Number
          PFD_DRAW_TO_WINDOW |            // Format Must Support Window
              PFD_SUPPORT_OPENGL |        // Format Must Support OpenGL
              PFD_DOUBLEBUFFER,           // Must Support Double Buffering
          PFD_TYPE_RGBA,                  // Request An RGBA Format
          32,                             // Select Our Color Depth
          0,
          0, 0, 0, 0, 0,   // Color Bits Ignored
          0,               // 8bit Alpha Buffer
          0,               // Shift Bit Ignored
          0,               // No Accumulation Buffer
          0, 0, 0, 0,      // Accumulation Bits Ignored
          0,               // 0Bit Z-Buffer (Depth Buffer)
          0,               // 0bit Stencil Buffer
          0,               // No Auxiliary Buffer
          PFD_MAIN_PLANE,  // Main Drawing Layer
          0,               // Reserved
          0, 0, 0          // Layer Masks Ignored
      };

  int PixelFormat;  // Holds The Results After Searching For A Match

  if (!(hDC = GetDC(window_handle_reified)))
  {
    PanicAlert("(1) Can't create an OpenGL Device context. Fail.");
    return false;
  }

  if (!(PixelFormat = ChoosePixelFormat(hDC, &pfd)))
  {
    PanicAlert("(2) Can't find a suitable PixelFormat.");
    return false;
  }

  if (!SetPixelFormat(hDC, PixelFormat, &pfd))
  {
    PanicAlert("(3) Can't set the PixelFormat.");
    return false;
  }

  if (!(hRC = wglCreateContext(hDC)))
  {
    PanicAlert("(4) Can't create an OpenGL rendering context.");
    return false;
  }

  return true;
}

bool cInterfaceWGL::MakeCurrent()
{
  bool success = wglMakeCurrent(hDC, hRC) ? true : false;
  if (success)
  {
    // Grab the swap interval function pointer
    wglSwapIntervalEXT =
        (PFNWGLSWAPINTERVALEXTPROC)GLInterface->GetFuncAddress("wglSwapIntervalEXT");
  }
  return success;
}

bool cInterfaceWGL::ClearCurrent()
{
  return wglMakeCurrent(hDC, nullptr) ? true : false;
}

// Update window width, size and etc. Called from Render.cpp
void cInterfaceWGL::Update()
{
  RECT rcWindow;
  GetClientRect(m_window_handle, &rcWindow);

  // Get the new window width and height
  s_backbuffer_width = (rcWindow.right - rcWindow.left);
  s_backbuffer_height = (rcWindow.bottom - rcWindow.top);
}

// Close backend
void cInterfaceWGL::Shutdown()
{
  if (hRC)
  {
    if (!wglMakeCurrent(nullptr, nullptr))
      NOTICE_LOG(VIDEO, "Could not release drawing context.");

    if (!wglDeleteContext(hRC))
      ERROR_LOG(VIDEO, "Attempt to release rendering context failed.");

    hRC = nullptr;
  }

  if (hDC && !ReleaseDC(m_window_handle, hDC))
  {
    ERROR_LOG(VIDEO, "Attempt to release device context failed.");
    hDC = nullptr;
  }
  FreeLibrary(dllHandle);
}
