#include "HiddenGlWindow.h"

namespace
{
constexpr const char* kWindowClassName = "RenderCadenceCompositorHiddenGlWindow";
}

HiddenGlWindow::~HiddenGlWindow()
{
	Destroy();
}

bool HiddenGlWindow::Create(unsigned width, unsigned height, std::string& error)
{
	return CreateShared(width, height, nullptr, nullptr, error);
}

bool HiddenGlWindow::CreateShared(unsigned width, unsigned height, HDC sharedDeviceContext, HGLRC sharedContext, std::string& error)
{
	Destroy();

	mInstance = GetModuleHandle(nullptr);

	WNDCLASSA wc = {};
	wc.style = CS_OWNDC;
	wc.lpfnWndProc = HiddenGlWindow::WindowProc;
	wc.hInstance = mInstance;
	wc.lpszClassName = kWindowClassName;

	mClassAtom = RegisterClassA(&wc);
	if (mClassAtom == 0 && GetLastError() != ERROR_CLASS_ALREADY_EXISTS)
	{
		error = "RegisterClassA failed for hidden OpenGL window.";
		return false;
	}

	mWindow = CreateWindowA(
		kWindowClassName,
		"Render Cadence Compositor Hidden GL",
		WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT,
		CW_USEDEFAULT,
		static_cast<int>(width),
		static_cast<int>(height),
		nullptr,
		nullptr,
		mInstance,
		nullptr);
	if (!mWindow)
	{
		error = "CreateWindowA failed for hidden OpenGL window.";
		return false;
	}

	mDc = GetDC(mWindow);
	if (!mDc)
	{
		error = "GetDC failed for hidden OpenGL window.";
		return false;
	}

	PIXELFORMATDESCRIPTOR 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 = 0;
	pfd.iLayerType = PFD_MAIN_PLANE;

	int pixelFormat = 0;
	if (sharedDeviceContext != nullptr)
		pixelFormat = GetPixelFormat(sharedDeviceContext);
	if (pixelFormat == 0)
		pixelFormat = ChoosePixelFormat(mDc, &pfd);
	if (pixelFormat == 0 || !SetPixelFormat(mDc, pixelFormat, &pfd))
	{
		error = "Could not choose/set pixel format for hidden OpenGL window.";
		return false;
	}

	mGlrc = wglCreateContext(mDc);
	if (!mGlrc)
	{
		error = "wglCreateContext failed for hidden OpenGL window.";
		return false;
	}
	if (sharedContext != nullptr && wglShareLists(sharedContext, mGlrc) != TRUE)
	{
		error = "wglShareLists failed for hidden OpenGL shared context.";
		return false;
	}

	return true;
}

bool HiddenGlWindow::MakeCurrent() const
{
	return mDc != nullptr && mGlrc != nullptr && wglMakeCurrent(mDc, mGlrc) == TRUE;
}

void HiddenGlWindow::ClearCurrent() const
{
	wglMakeCurrent(nullptr, nullptr);
}

void HiddenGlWindow::Destroy()
{
	ClearCurrent();

	if (mGlrc)
	{
		wglDeleteContext(mGlrc);
		mGlrc = nullptr;
	}
	if (mWindow && mDc)
	{
		ReleaseDC(mWindow, mDc);
		mDc = nullptr;
	}
	if (mWindow)
	{
		DestroyWindow(mWindow);
		mWindow = nullptr;
	}

	mInstance = nullptr;
	mClassAtom = 0;
}

LRESULT CALLBACK HiddenGlWindow::WindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	return DefWindowProc(hwnd, message, wParam, lParam);
}
