/*
/ BSTMDEVICE.H
/ ************
/ Direct3D9 renderer that creates and manages
/ the device as well as handling the rendering
/ of displays and post-processing effects.
/
/ Will Beddow 2010 - http://343gs.com/brainstem
/
/ This software is currently under development
/ and is subject to change and improvement. Some
/ aspects of the system are incomplete and/or 
/ unstable due to ongoing implementation.
/
*/

#ifndef __BSTMDEVICE_H__
#define __BSTMDEVICE_H__

#include "Compile.h"
#include "Types.h"
#include "Matrix.h"
#include "Device.h"
#include "Kernel.h"
#include "Resource.h"

#include "BstmDisplay.h"

#include <Windows.h>
#include <algorithm>
#include <iostream>
#include <string>
#include <sstream>
#include <d3dx9.h>
#include <DxErr.h>

extern "C" IMAGE_DOS_HEADER __ImageBase;

namespace Brainstem
{
	struct Vertex
	{
		Vertex() { }
		Vertex(Vector3<float> position, float u, float v)
		{
			Position = D3DXVECTOR3(position.X, position.Y, position.Z);
			tU = u;   tV = v;
		}

		D3DXVECTOR3 Position;
		D3DXVECTOR3 Normal;
		float tU;
		float tV;

		static const DWORD FVF = D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1;
	};

	struct Material
	{
		D3DXCOLOR Ambient;
		D3DXCOLOR Diffuse;
		D3DXCOLOR Emissive;
		D3DXCOLOR Specular;
		float Shininess;
	};

	class BstmDevice : public IDevice
	{
	public:
		BstmDevice(IKernel* kernel, HWND hwnd, int width, int height, bool fullscreen);
		~BstmDevice();

		bool Init();
		void Update();
		void Release();

		void BeginRender();
		void BeginRender(IDisplay* display);
		void EndRender();
		void Present();

		Vector3<float> UnProject(Vector3<float> source, Matrix4<float> view, Matrix4<float> projection);
		HRESULT CompileEffectFromResources(int resource, LPD3DXEFFECT &effect);

		void DrawString(const char* text, Vector2<int> position);

		void DrawDisplay(Display* display);
		void* GetDevicePtr();

		Color Background;

		AUTO_OBJECT_SIZE;
		virtual const char* GetName() { return "Direct3D Renderer"; }
	private:

#define REPORT_ERRORS(hr) { if(FAILED(hr)) return ReportInitialisationError(hr); }

		IKernel* Kernel;

		HWND hWnd;
		int Width;
		int Height;
		bool Fullscreen;
		bool WaitForVerticalSync;
		DWORD MSAASamples;

		void ChooseBestCSAAMode(IDirect3D9* d3d, D3DFORMAT backBufferFmt, D3DFORMAT depthStencilFmt,
							BOOL windowed, D3DMULTISAMPLE_TYPE &type,
							DWORD &qualityLevels);

		void ChooseBestMSAAMode(IDirect3D9* d3d, D3DFORMAT backBufferFmt, D3DFORMAT depthStencilFmt,
							BOOL windowed, D3DMULTISAMPLE_TYPE &type,
							DWORD &qualityLevels, DWORD &samplesPerPixel);

		bool MSAAModeSupported(IDirect3D9* d3d, D3DMULTISAMPLE_TYPE type, D3DFORMAT backBufferFmt,
                       D3DFORMAT depthStencilFmt, BOOL windowed,
                       DWORD &qualityLevels);

		HRESULT CreateDisplayCanvas();
		void ApplyEffects();

		bool ReportInitialisationError(HRESULT hr);
		bool ReportInitialisationError(const char* message);

		IDirect3DDevice9* Device;
		ID3DXFont* Font;
		IDirect3DSurface9* SurfaceBuffer;
		IDirect3DTexture9* TextureBuffer;

		ID3DXMesh* Canvas;
		ID3DXEffect* Effect;

		std::vector<Display*> Displays;
		IPtr<Display> Active;
		IPtr<Display> Root;
	};
}

#endif