/**
\author		Alexey Shaydurov aka ASH
\date		28.1.2011 (c)Alexey Shaydurov

This file is a part of DGLE2 project and is distributed
under the terms of the GNU Lesser General Public License.
See "DGLE2.h" for more details.
*/

#if _MSC_VER > 1000
#pragma once
#endif

#ifndef _DGLE2_HELPERS_H
#define _DGLE2_HELPERS_H

#include "DGLE2.h"
#include <crtdbg.h>

namespace DGLE2
{

#define CHECK_HR(expr)\
{\
	HRESULT hr_ = expr;\
	if (FAILED(hr_)) throw hr_;\
}

#ifdef _DEBUG
#	ifdef _MSC_VER
#		define PARANOIC_CHECK_HR(hr) (hr != S_OK ? _RPT1(_CRT_ASSERT, "#### PARANOIC_CHECK_HR ####\n %s != S_OK", #hr), E_ABORT : S_OK)
#	elif __CODEGEARC__
#		define PARANOIC_CHECK_HR(hr) assert(hr != S_OK), S_OK
#	else
#		define PARANOIC_CHECK_HR(hr) hr
#	endif
#else
#	define PARANOIC_CHECK_HR(hr) hr
#endif

	namespace get_helpers
	{
		template<typename Enum, Enum enumeration, class IBase, class IObject, class IProvider, HRESULT (CALLBACK IProvider::*function)(Enum, IBase *&)>
		inline void Get(IProvider *provider, IObject *&object)
		{
			PARANOIC_CHECK_HR((provider->*function)(enumeration, reinterpret_cast<IBase *&>(object)));
			object = static_cast<IObject *>(reinterpret_cast<IBase *>(object));
		}

		/*
		TODO:
		Get specialization for GetDefaultResource trys to call Get corresponding to GetSubsystem.
		Currently Get replaced with GetSubsystem/GetDefaultResource to avoid conflict.
		Try with C++ 0x or other compilers.
		*/

		// GetSubsystem helper

		//template<E_ENGINE_SUB_SYSTEM subsystem, class Interface>
		//inline void GetSubsystem(IEngineCore *pEngineCore, Interface *&iface)
		//{
		//	PARANOIC_CHECK_HR(pEngineCore->GetSubSystem(subsystem, reinterpret_cast<IEngineSubSystem *&>(iface)))
		//	iface = static_cast<Interface *>(reinterpret_cast<IEngineSubSystem *>(iface));
		//}

		template<E_ENGINE_SUB_SYSTEM enumeration, class IObject>
		inline void GetSubsystem(IEngineCore *pEngineCore, IObject *&object)
		{
			Get<E_ENGINE_SUB_SYSTEM, enumeration, IEngineSubSystem, IObject, IEngineCore, &IEngineCore::GetSubSystem>(pEngineCore, object);
		}

		template<class IObject>
		inline void Get(IEngineCore *pEngineCore, IObject *&object);

		template<>
		inline void Get(IEngineCore *pEngineCore, IRender *&render)
		{
			GetSubsystem<ESS_RENDER>(pEngineCore, render);
		}

		template<>
		inline void Get(IEngineCore *pEngineCore, IInput *&input)
		{
			GetSubsystem<ESS_INPUT>(pEngineCore, input);
		}

		template<>
		inline void Get(IEngineCore *pEngineCore, ISound *&sound)
		{
			GetSubsystem<ESS_SOUND>(pEngineCore, sound);
		}

		template<>
		inline void Get(IEngineCore *pEngineCore, IResourceManager *&resMan)
		{
			GetSubsystem<ESS_RESOURCE_MANAGER>(pEngineCore, resMan);
		}

		template<>
		inline void Get(IEngineCore *pEngineCore, IMainFileSystem *&mainFS)
		{
			GetSubsystem<ESS_FILE_SYSTEM>(pEngineCore, mainFS);
		}

		// GetDefaultResource helper

		template<E_ENG_OBJ_TYPE enumeration, class IObject>
		inline void GetDefaultResource(IResourceManager *pResMan, IObject *&object)
		{
			Get<E_ENG_OBJ_TYPE, enumeration, IEngBaseObj, IObject, IResourceManager, &IResourceManager::GetDefaultResource>(pResMan, object);
		}

		template<class IObject>
		inline void Get(IResourceManager *pResMan, IObject *&object);

		template<>
		inline void Get(IResourceManager *pResMan, ITexture *&texture)
		{
			GetDefaultResource<EOT_TEXTURE>(pResMan, texture);
		}

		template<>
		inline void Get(IResourceManager *pResMan, IMesh *&mesh)
		{
			GetDefaultResource<EOT_MESH>(pResMan, mesh);
		}

		template<>
		inline void Get(IResourceManager *pResMan, IBitmapFont *&bitmapFont)
		{
			GetDefaultResource<EOT_BITMAP_FONT>(pResMan, bitmapFont);
		}

		template<>
		inline void Get(IResourceManager *pResMan, ISoundSample *&soundSample)
		{
			GetDefaultResource<EOT_SOUND_SAMPLE>(pResMan, soundSample);
		}

		template<>
		inline void Get(IResourceManager *pResMan, IMusic *&music)
		{
			GetDefaultResource<EOT_MUSIC>(pResMan, music);
		}

		template<>
		inline void Get(IResourceManager *pResMan, IProgram *&program)
		{
			GetDefaultResource<EOT_PROGRAM>(pResMan, program);
		}

		template<>
		inline void Get(IResourceManager *pResMan, IShader *&shader)
		{
			GetDefaultResource<EOT_SHADER>(pResMan, shader);
		}

		template<>
		inline void Get(IResourceManager *pResMan, IParticleEffect *&particleEffect)
		{
			GetDefaultResource<EOT_PARTICLE_EFFECT>(pResMan, particleEffect);
		}
	}

#ifdef DGLE2_USE_COM

#define IDGLE2_BASE_IMPLEMENTATION(interface_name) \
	HRESULT CALLBACK GetGUID(GUID &guid)\
{\
	guid = IID_##interface_name;\
	return S_OK;\
}\
	HRESULT CALLBACK QueryInterface(REFIID riid, void __RPC_FAR *__RPC_FAR *ppvObject)\
{\
	*ppvObject = NULL;\
	if(::memcmp(&riid,&__uuidof(IUnknown),sizeof(GUID)) == 0) \
	*ppvObject = static_cast<IUnknown *>(this);\
	else if(::memcmp(&riid,&IID_##interface_name,sizeof(GUID)) == 0) \
	*ppvObject = static_cast<interface_name *>(this);\
	else return E_NOINTERFACE;\
	return S_OK;\
}\
	ULONG CALLBACK AddRef(){return 1;}\
	ULONG CALLBACK Release(){return 1;}

#define IDGLE2_BASE_IMPLEMENTATION1(interface1_name, interface2_name) \
	HRESULT CALLBACK GetGUID(GUID &guid)\
{\
	guid = IID_##interface1_name;\
	return S_OK;\
}\
	HRESULT CALLBACK QueryInterface(REFIID riid, void __RPC_FAR *__RPC_FAR *ppvObject)\
{\
	*ppvObject = NULL;\
	if(::memcmp(&riid,&__uuidof(IUnknown),sizeof(GUID)) == 0) \
	*ppvObject = static_cast<IUnknown *>(this);\
	else if(::memcmp(&riid,&IID_##interface1_name,sizeof(GUID)) == 0) \
	*ppvObject = static_cast<interface1_name *>(this);\
	else if(::memcmp(&riid,&IID_##interface2_name,sizeof(GUID)) == 0) \
	*ppvObject = static_cast<interface2_name *>(this);\
	else return E_NOINTERFACE;\
	return S_OK;\
}\
	ULONG CALLBACK AddRef(){return 1;}\
	ULONG CALLBACK Release(){return 1;}

#define IUNKNOWN_IMPLEMENTATION(interface_name) \
	HRESULT CALLBACK QueryInterface(REFIID riid, void __RPC_FAR *__RPC_FAR *ppvObject)\
{\
	*ppvObject = NULL;\
	if(::memcmp(&riid,&__uuidof(IUnknown),sizeof(GUID)) == 0)\
	*ppvObject = static_cast<IUnknown *>(this);\
	else if(::memcmp(&riid,&__uuidof(interface_name),sizeof(GUID)) == 0)\
	{\
	*ppvObject = static_cast<interface_name *>(this);\
	AddRef();\
}\
	else return E_NOINTERFACE;\
	return S_OK;\
}\
	ULONG CALLBACK AddRef() { return 1; }\
	ULONG CALLBACK Release() { return 1; }

#else//DGLE2_USE_COM

#define IDGLE2_BASE_IMPLEMENTATION(interface_name) \
	HRESULT CALLBACK GetGUID(GUID &guid)\
{\
	guid = IID_##interface_name;\
	return S_OK;\
}

#endif
}

#endif//_DGLE2_HELPERS_H