
#include "Window.h"
#include "FrameBuffer.h"
#include <Windows.h>

static const char* const WindowClassName = "Live++ examples";


static LRESULT CALLBACK WindowProcedure(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	Window* window = reinterpret_cast<Window*>(GetWindowLongPtrA(hwnd, GWLP_USERDATA));
	if (!window)
	{
		// the user pointer is not available during window creation
		return ::DefWindowProc(hwnd, uMsg, wParam, lParam);
	}

	FrameBuffer* frameBuffer = window->GetFrameBuffer();
	const unsigned int width = frameBuffer->GetWidth();
	const unsigned int height = frameBuffer->GetHeight();

	switch (uMsg)
	{
		case WM_DESTROY:
			::PostQuitMessage(0);
			return 0;

		case WM_PAINT:
		{
			BITMAPINFO info = {};
			info.bmiHeader.biBitCount = 32;
			info.bmiHeader.biWidth = width;
			info.bmiHeader.biHeight = -static_cast<int>(height);
			info.bmiHeader.biPlanes = 1;
			info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
			info.bmiHeader.biCompression = BI_RGB;

			PAINTSTRUCT ps = {};
			HDC hdc = ::BeginPaint(hwnd, &ps);
			::StretchDIBits(hdc, 0, 0, width, height, 0, 0, width, height, frameBuffer->GetRGBABuffer(), &info, DIB_RGB_COLORS, SRCCOPY);
			::EndPaint(hwnd, &ps);

			return 0;
		}
	}

	return ::DefWindowProc(hwnd, uMsg, wParam, lParam);
}


Window::Window(const char* windowTitle, FrameBuffer* frameBuffer)
	: m_frameBuffer(frameBuffer)
	, m_windowHandle(nullptr)
{
	HINSTANCE instance = ::GetModuleHandle(nullptr);

	WNDCLASSA windowClass = {};
	windowClass.lpfnWndProc = &WindowProcedure;
	windowClass.hInstance = instance;
	windowClass.lpszClassName = WindowClassName;
	::RegisterClassA(&windowClass);

	RECT rect = {};
	rect.top = 0;
	rect.left = 0;
	rect.right = frameBuffer->GetWidth();
	rect.bottom = frameBuffer->GetHeight();
	::AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE);

	// position the window at the center of the screen
	const int screenWidth = ::GetSystemMetrics(SM_CXSCREEN);
	const int screenHeight = ::GetSystemMetrics(SM_CYSCREEN);
	const int width = rect.right - rect.left;
	const int height = rect.bottom - rect.top;

	m_windowHandle = ::CreateWindowExA(0, WindowClassName, windowTitle, WS_OVERLAPPED | WS_MINIMIZEBOX | WS_SYSMENU, (screenWidth - width) >> 1u, (screenHeight - height) >> 1u, width, height, nullptr, nullptr, instance, 0);
	::SetWindowLongPtrA(m_windowHandle, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));

	::ShowWindow(m_windowHandle, SW_NORMAL);
}


Window::~Window(void)
{
	HINSTANCE instance = ::GetModuleHandle(nullptr);

	::DestroyWindow(m_windowHandle);
	::UnregisterClassA(WindowClassName, instance);
}


bool Window::PollMessages(void) const
{
	MSG msg = {};
	while (::PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
	{
		if (msg.message == WM_QUIT)
		{
			return false;
		}

		::TranslateMessage(&msg);
		::DispatchMessage(&msg);
	}

	return true;
}


void Window::Present(void) const
{
	// make the window repaint itself
	::InvalidateRect(m_windowHandle, nullptr, FALSE);
}


int Window::GetPositionX(void) const
{
	RECT rect = {};
	::GetWindowRect(m_windowHandle, &rect);

	return rect.left;
}


int Window::GetPositionY(void) const
{
	RECT rect = {};
	::GetWindowRect(m_windowHandle, &rect);

	return rect.top;
}


int Window::GetWidth(void) const
{
	RECT rect = {};
	::GetWindowRect(m_windowHandle, &rect);

	return rect.right - rect.left;
}


int Window::GetHeight(void) const
{
	RECT rect = {};
	::GetWindowRect(m_windowHandle, &rect);

	return rect.bottom - rect.top;
}
