#pragma once

#include <memory>

#ifdef _WIN32
#include <Windows.h>
#else
#define HWND void*
#endif

#include <GL/gl.h>

#include "3dmath.h"

#include "IWMR_Wrapper.h"

namespace VRUT
{
	class WindowsMixedRealityWrapper
	{
	public:
		WindowsMixedRealityWrapper() = default;
		~WindowsMixedRealityWrapper()
		{
#ifdef _WIN32
			if (_dllhandle)
			{
				FreeLibrary(_dllhandle);
				_dllhandle = nullptr;
			}

			if (_hwnd)
			{
				DestroyWindow(_hwnd);
				_hwnd = nullptr;
			}
#endif
		}
		WindowsMixedRealityWrapper(const WindowsMixedRealityWrapper& other) = delete;


		bool init()
		{
#ifdef _WIN32
			if (!_createFakeWindow())
				return false;

			HINSTANCE _dllhandle = LoadLibraryA("WMR_Wrapper.dll");
			if (!_dllhandle)
				return false;

			IWMR_Wrapper*(*CreateWrapper)();
			CreateWrapper = (IWMR_Wrapper*(*)())GetProcAddress(_dllhandle, "CreateWrapper");
			if (!CreateWrapper)
				return false;

			_wrapper.reset(CreateWrapper());
			_isInitialized = _wrapper->init(_hwnd);
			return _isInitialized;
#endif
			return false;
		}

		bool isInitialized() const { return _isInitialized; }

		void beginFrameReadTracking()
		{
			if (_wrapper)
				_wrapper->frameStartGetTracking(_viewL._m, _viewR._m, _projL._m, _projR._m);
		}

		void sumbitFrame()
		{
			if (_wrapper)
				_wrapper->submitFrameToHmd();
		}

		void setNearFar(float nearZ, float farZ)
		{
			if (_wrapper)
				_wrapper->setNearFarDistance(nearZ, farZ);
		}

		void getGlTextureHandles(GLuint& left, GLuint& right) const
		{
			if (_wrapper)
				_wrapper->getTextures(left, right);
		}

		void getEyeWidthHeight(unsigned int* width, unsigned int* height) const
		{
			if (_wrapper)
				_wrapper->getPerEyeResolution(*width, *height);
		}

		Matrix getCameraWorldMatrixL() const { return _viewL.Inverse(); };
		Matrix getCameraWorldMatrixR() const { return _viewR.Inverse(); };
		Matrix getCameraViewMatrixL() const { return _viewL; };
		Matrix getCameraViewMatrixR() const { return _viewR; };
		Matrix getCameraProjectionMatrixL() const { return _projL; };
		Matrix getCameraProjectionMatrixR() const { return _projR; };

	protected:
		bool _createFakeWindow()
		{
#ifdef _WIN32
			WNDCLASSEX wc;

			//Step 1: Registering the Window Class
			wc.cbSize = sizeof(WNDCLASSEX);
			wc.style = 0;
			wc.lpfnWndProc = DefWindowProc;
			wc.cbClsExtra = 0;
			wc.cbWndExtra = 0;
			wc.hInstance = GetModuleHandle(0);
			wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
			wc.hCursor = LoadCursor(NULL, IDC_ARROW);
			wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
			wc.lpszMenuName = NULL;
			wc.lpszClassName = L"myWindowClass";
			wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

			if (!RegisterClassEx(&wc))
				return false;

			// Step 2: Creating the Window
			_hwnd = CreateWindowEx(
				WS_EX_CLIENTEDGE,
				L"myWindowClass",
				L"Dummy WMR Window",
				WS_OVERLAPPEDWINDOW,
				CW_USEDEFAULT, CW_USEDEFAULT, 10, 10,
				NULL, NULL, GetModuleHandle(0), NULL);

			if (_hwnd == NULL)
				return false;
			return true;
#endif
			return false;
		}

	private:
		std::unique_ptr<IWMR_Wrapper> _wrapper;

		Matrix _viewL, _viewR, _projL, _projR;
		bool _isInitialized = false;
#ifdef _WIN32
		HINSTANCE _dllhandle = nullptr;
		HWND _hwnd = nullptr;
#endif
	};
};
