#pragma once

#include <Windows.Graphics.DirectX.Direct3D11.interop.h>
#include <Windows.Graphics.DirectX.Direct3D11.h>
#include <WindowsNumerics.h>
#include <windows.graphics.holographic.h>
#include <windows.perception.spatial.h>
#include <windows.ui.input.spatial.h>

#include <wrl.h>

#include "Dx11OglRenderer.hpp"

#include <memory>
#include <vector>

using namespace ABI::Windows::Foundation;
using namespace ABI::Windows::Perception::Spatial;
using namespace ABI::Windows::Graphics::Holographic;

using namespace Microsoft::WRL;
using namespace Microsoft::WRL::Wrappers;

using namespace ABI::Windows::Graphics::DirectX::Direct3D11;
using namespace ABI::Windows::Foundation::Numerics;
using namespace Windows::Foundation::Numerics;
using namespace ABI::Windows::UI::Input::Spatial;

class WMRimpl
{
public:
	bool init(HWND hwnd);

	void getLeftRightTextures(GLuint& left, GLuint& right);

	void getPerEyeResolution(uint32_t& width, uint32_t& height) const;

	void frameStart();
	void getTrackingData(float(&viewL)[16], float(&viewR)[16], float(&projL)[16], float(&projR)[16]);

	void submitFrame();

	void setNearFarDistance(float nearZ, float farZ);

protected:
	bool _initWrl();
	bool _createHolographicSpace(HWND hwnd);
	bool _createHolographicDisplay();
	bool _createSpatialLocator();
	bool _createRenderer();
		ComPtr<IDXGIDevice> _getRendererDxgiDevice();
		ComPtr<IDirect3DDevice> _convertDxgiDeviceToIDirect3DDevice(ComPtr<IDXGIDevice> device);
	bool _createSpatialInteractionManager(HWND hwnd);
		
		
	bool _getCameraParams();
	bool _updateRendererCameraById(ComPtr<IHolographicCameraRenderingParameters> renderParams, unsigned int cameraId);
	void _removeCameraByID(unsigned int id);

	void _registerCallbacks();
	void _unregisterCallbacks();

	ComPtr<IHolographicFrame> _beginFrame();
	void _presentFrame(ComPtr<IHolographicFrame> holographicFrame);

	void _convertMatrix(const Numerics::Matrix4x4& matrix, float(&destMatrix)[16]) const;

	//Callbacks
	HRESULT _onCameraAdded(IHolographicSpace* holographicSpace, IHolographicSpaceCameraAddedEventArgs* args);
	HRESULT _onCameraRemoved(IHolographicSpace* holographicSpace, IHolographicSpaceCameraRemovedEventArgs* args);
	HRESULT _onTrackingStateChanged(ISpatialLocator* locator, IInspectable* object);
	HRESULT _onHolographicDisplayChanged(IInspectable* object1, IInspectable* object2);
	
	HRESULT _onControllerDetected(ISpatialInteractionManager* manager, ISpatialInteractionSourceEventArgs* args);
		void _acquireControllerGLTFData(ComPtr<ISpatialInteractionSource> source, std::vector<unsigned char>& gltfData);
	HRESULT _onControllerLost(ISpatialInteractionManager* manager, ISpatialInteractionSourceEventArgs* args);
	HRESULT _onControllerPressed(ISpatialInteractionManager* manager, ISpatialInteractionSourceEventArgs* args);
	HRESULT _onControllerUpdated(ISpatialInteractionManager* manager, ISpatialInteractionSourceEventArgs* args);

private:
	std::unique_ptr<Dx11OglRenderer> _renderer = nullptr;

	ComPtr<IHolographicFrame> _currentFrame = nullptr;

	//UWP object handles acquired via WRL
	ComPtr<IHolographicSpace> _holographicSpace;
	ComPtr<IHolographicDisplay> _display = nullptr;
	ComPtr<ISpatialLocator>   _spatialLocator = nullptr;
	//ComPtr<IHolographicSpaceStatics2> _holographicStateStatics2 = nullptr;
	ComPtr<ISpatialStationaryFrameOfReference> _stationaryReferenceFrame = nullptr;
	ComPtr<ISpatialInteractionManager> _spatialInteractionManager = nullptr;

	//Tokens for event registration
	EventRegistrationToken _cameraAddedToken;
	EventRegistrationToken _cameraRemovedToken;
	EventRegistrationToken _trackingChangedToken;
	
	EventRegistrationToken _controllerAdded;
	EventRegistrationToken _controllerRemovedLost;
	EventRegistrationToken _controllerPressed;
	EventRegistrationToken _controllerTransformUpdated;

	float _nearZ = 0.1f;
	float _farZ = 100.0f;
};