
#include "glad_wgl.h"

#include <Platform/PlatformDisplay.h>
#include <Spectre/System/Log.h>

#include "Win32Internal.h"
#include "Win32GLContext.h"

// Ensure that OpenGL extensions are loaded.
static void ensureExtensionsLoaded(HDC dc)
{
	static bool init = false;

	if (!init) {
		init = true;

		if (!gladLoadWGL(dc)) {
			log("Win32: Could not load WGL extensions\n");
		}

		if (!gladLoadGL()) {
			log("Win32: Could not load OpenGL extensions\n");
		}
	}
}

Win32GLContext::Win32GLContext() :
m_wnd		(NULL),
m_deviceContext (NULL),
m_renderContext (NULL)
{
}

Win32GLContext::~Win32GLContext()
{
	destroy();
}

bool Win32GLContext::create(const PlatformDisplay* display)
{
	// If created. destroy old handles.
	destroy();

	m_wnd = (HWND) display->getHandle();

	// Should have a valid handle here. trigger error.
	if (!m_wnd) {
		log("Win32 - Could not create GL context: Invalid display handle\n");
		return false;
	}

	m_deviceContext = ::GetDC(m_wnd);

	createGLContext();

	if (!m_deviceContext || !m_renderContext) {

		// Make sure we release all handles.
		destroy();

		log("Win32 - Could not create GL context: %s", Win32GetMessage(GetLastError()));
		return false;
	}
	return true;
}

void Win32GLContext::destroy()
{
	if (m_wnd && m_deviceContext) {
		::ReleaseDC(m_wnd, m_deviceContext);
		m_deviceContext = NULL;
		m_wnd = NULL;
	}

	if (m_renderContext) {
		::wglDeleteContext(m_renderContext);
		m_renderContext = NULL;
	}
}

void Win32GLContext::createGLContext()
{
	HGLRC tmpDC;

	// First set pixel format.
	if (!setPixelFormat()) {
		return;
	}

	// This is one reason why Win32 context creation is wierd.
	// I need to create some context first so that GLEW knows if
	// It supports the "wglCreateContextAttribsARB". That is a 3.2+ Core function.
	tmpDC = ::wglCreateContext(m_deviceContext);
	::wglMakeCurrent(m_deviceContext, tmpDC);

	ensureExtensionsLoaded(m_deviceContext);

	// Dont need to old one anymore.
	wglMakeCurrent(m_deviceContext, NULL);
	::wglDeleteContext(tmpDC);

	// TODO: For now.. We force 3.2 Core but this should not be implementation specific.
	// The Display class should force that for all GLContext Implementations.

	int attriblist[] = {
		WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
		WGL_CONTEXT_MINOR_VERSION_ARB, 2,
		WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
		0, 0
	};

	// Create real context.
	m_renderContext = ::wglCreateContextAttribsARB(m_deviceContext, 0, attriblist);
}

bool Win32GLContext::activate()
{
	// Make sure we have dc and rc before calling MakeCurrent.
	if (m_deviceContext && m_renderContext) {
		return ::wglMakeCurrent(m_deviceContext, m_renderContext);
	}
	return false;
}

bool Win32GLContext::deactivate()
{
	return ::wglMakeCurrent(NULL, NULL);
}

bool Win32GLContext::isActive() const
{
	return ::wglGetCurrentContext() == m_renderContext;
}

bool Win32GLContext::setSwapInterval(int interval)
{
	ensureExtensionsLoaded(m_deviceContext);

	return wglSwapIntervalEXT(interval);
}

void Win32GLContext::setSize(unsigned int width, unsigned int height)
{
	if (activate()) {
		glViewport(0, 0, width, height);
	}
}

void Win32GLContext::swapBuffers()
{
	if (m_deviceContext) {
		::SwapBuffers(m_deviceContext);
	}
}

bool Win32GLContext::setPixelFormat()
{
	PIXELFORMATDESCRIPTOR pfd;
	int format;

	// For now, always create a RGBA pixel format with double buffer.
	ZeroMemory(&pfd, sizeof(pfd));
	pfd.nSize = sizeof(pfd);
	pfd.nVersion = 1;
	pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL |
		      PFD_DOUBLEBUFFER;
	pfd.iPixelType = PFD_TYPE_RGBA;
	pfd.cColorBits = 32;
	pfd.cDepthBits = 16;
	pfd.iLayerType = PFD_MAIN_PLANE;

	format = ::ChoosePixelFormat(m_deviceContext, &pfd);
	if (format) {
		return ::SetPixelFormat(m_deviceContext, format, &pfd);
	}
	return false;
}