//	Darkstar Engine  (c) UnderWorld Interactive Studio (all coding by Svarog    2007-2008)
//
//      Данный файл является частью Darkstar Engine. Любое использование кода файла
//      без согласия компании, является нарушением закона об авторском праве (в слу-
//      чае если не оговорено обратное).
//
//      Имя файла: render_state_manager_d3d9.cpp
//      Краткое описание: manager for render state blocks in DirecX 9
//

#ifdef GD_D3D9

#include <Darkstar/Graphic System/Render State/render_state_manager.hpp>
#include <Darkstar/Graphic System/Direct3D/graphicDriver.hpp>
#include <Darkstar/math.hpp>
#include <Darkstar/Kernel/logh.hpp>

using gs::GraphicDriver;
using gs::RenderStateManager;
using gs::DRSCMPFUNC;
using gs::DBLEND;
using gs::DBLENDOP;
using gs::DCULL;
using gs::DFILLMODE;
using gs::DTEXADDRESS;
using gs::DTEXFILTER;
using ker::Log;
using gs::FillModeRenderState;
using gs::CullModeRenderState;
using gs::DepthRenderState;
using gs::SamplerRenderState;
using gs::ColorWriteRenderState;
using gs::BlendRenderState;
using gs::SAMPLER_TYPE;

RenderStateManager::RenderStateManager()
{
	cur_blend_state.reset(new BlendRenderState());
	_setAllBlendState(cur_blend_state);
	new_blend_state = cur_blend_state;

	cur_cull_state.reset(new CullModeRenderState());
	_setAllCullModeState(cur_cull_state);
	new_cull_state = cur_cull_state;

	cur_depth_state.reset(new DepthRenderState());
	_setAllDepthState(cur_depth_state);
	new_depth_state = cur_depth_state;

	cur_color_write_state.reset(new ColorWriteRenderState());
	_setAllColorWriteState(cur_color_write_state);
	new_color_write_state = cur_color_write_state;

	cur_fill_mode_state.reset(new FillModeRenderState());
	_setAllFillModeState(cur_fill_mode_state);
	new_fill_mode_state = cur_fill_mode_state;

	for(unsigned int i = 0; i < sampler_num; ++i)
	{
		cur_sampler_state[i].reset(new SamplerRenderState());
		_setAllSamplerState(i, cur_sampler_state[i]);
		new_sampler_state[i] = cur_sampler_state[i];
	};
};

RenderStateManager::~RenderStateManager()
{

};

void RenderStateManager::_setAllBlendState(const shared_ptr<BlendRenderState>& state)
{
	IDirect3DDevice9* device = GraphicDriver::ptr()->getDevice();
	device->SetRenderState(D3DRS_BLENDOP, state->getBlendOp());
	device->SetRenderState(D3DRS_BLENDOPALPHA, state->getBlendOpAlpha());
	if(FAILED(device->SetRenderState(D3DRS_SRCBLEND, state->getSrcBlend())))
		lwar << "Cannot set src blend with value = " << state->getSrcBlend();
	if(FAILED(device->SetRenderState(D3DRS_DESTBLEND, state->getDstBlend())))
		lwar << "Cannot set dest blend with value = " << state->getDstBlend();
	if(FAILED(device->SetRenderState(D3DRS_SRCBLENDALPHA, state->getSrcBlendAlpha())))
		lwar << "Cannot set src blend alpha with value = " << state->getSrcBlendAlpha();
	if(FAILED(device->SetRenderState(D3DRS_DESTBLENDALPHA, state->getDstBlendAlpha())))
		lwar << "Cannot set dest blend alpha with value = " << state->getDstBlendAlpha();
	device->SetRenderState(D3DRS_ALPHABLENDENABLE, state->getBlendEnable(0));
	device->SetRenderState(D3DRS_SEPARATEALPHABLENDENABLE, state->getBlendEnable(0));
};

void RenderStateManager::_setAllColorWriteState(const shared_ptr<ColorWriteRenderState>& state)
{
	IDirect3DDevice9* device = GraphicDriver::ptr()->getDevice();
	device->SetRenderState(D3DRS_COLORWRITEENABLE, (DWORD)state->getColorWriteFlag(0));
};

void RenderStateManager::_setAllCullModeState(const shared_ptr<CullModeRenderState>& state)
{
	IDirect3DDevice9* device = GraphicDriver::ptr()->getDevice();
	device->SetRenderState(D3DRS_CULLMODE, state->getCullMode());
};

void RenderStateManager::_setAllDepthState(const shared_ptr<DepthRenderState>& state)
{
	IDirect3DDevice9* device = GraphicDriver::ptr()->getDevice();
	device->SetRenderState(D3DRS_ZENABLE, state->getIsDepthEnable());
	device->SetRenderState(D3DRS_ZFUNC, state->getDepthFunction());
	device->SetRenderState(D3DRS_ZWRITEENABLE, (DWORD)state->getDepthWriteMask());
};

void RenderStateManager::_setAllFillModeState(const shared_ptr<FillModeRenderState>& state)
{
	IDirect3DDevice9* device = GraphicDriver::ptr()->getDevice();
	device->SetRenderState(D3DRS_FILLMODE, state->getFillMode());
};

struct D3DFILTER
{
	D3DTEXTUREFILTERTYPE minf, maxf, mipf;
};

D3DFILTER _getFilters(const DTEXFILTER& filter)
{
	D3DFILTER fil;
	switch(filter)
	{
	case gs::DTEXF_MIN_MAG_MIP_POINT:
		{
			fil.maxf = D3DTEXF_POINT;
			fil.minf = D3DTEXF_POINT;
			fil.mipf = D3DTEXF_POINT;
		};break;
	case gs::DTEXF_MIN_POINT_MAG_LINEAR_MIP_POINT:
		{
			fil.minf = D3DTEXF_POINT;
			fil.maxf = D3DTEXF_LINEAR;
			fil.mipf = D3DTEXF_POINT;
		};break;
	case gs::DTEXF_MIN_MAG_POINT_MIP_LINEAR:
		{
			fil.minf = D3DTEXF_POINT;
			fil.maxf = D3DTEXF_POINT;
			fil.mipf = D3DTEXF_LINEAR;
		};break;
	case gs::DTEXF_MIN_LINEAR_MAG_MIP_POINT:
		{
			fil.minf = D3DTEXF_LINEAR;
			fil.maxf = D3DTEXF_POINT;
			fil.mipf = D3DTEXF_POINT;
		};break;
	case gs::DTEXF_MIN_LINEAR_MAG_POINT_MIP_LINEAR:
		{
			fil.minf = D3DTEXF_LINEAR;
			fil.maxf = D3DTEXF_POINT;
			fil.mipf = D3DTEXF_LINEAR;
		};break;
	case gs::DTEXF_MIN_POINT_MAG_MIP_LINEAR:
		{
			fil.minf = D3DTEXF_POINT;
			fil.maxf = D3DTEXF_LINEAR;
			fil.mipf = D3DTEXF_LINEAR;
		};break;
	case gs::DTEXF_MIN_MAG_LINEAR_MIP_POINT:
		{
			fil.minf = D3DTEXF_LINEAR;
			fil.maxf = D3DTEXF_LINEAR;
			fil.mipf = D3DTEXF_POINT;
		};break;
	case gs::DTEXF_MIN_MAG_MIP_LINEAR:
		{
			fil.minf = D3DTEXF_LINEAR;
			fil.maxf = D3DTEXF_LINEAR;
			fil.mipf = D3DTEXF_LINEAR;
		};break;
	case gs::DTEXF_ANISOTROPIC:
		{
			fil.minf = D3DTEXF_ANISOTROPIC;
			fil.maxf = D3DTEXF_ANISOTROPIC;
			fil.mipf = D3DTEXF_ANISOTROPIC;
		};break;
	default:
		{
			fil.minf = D3DTEXF_LINEAR;
			fil.maxf = D3DTEXF_LINEAR;
			fil.mipf = D3DTEXF_LINEAR;
			lwar << "Filter format does not support by DirectX 9 " << filter;
		};
	};
	return fil;
};

void RenderStateManager::_setAllSamplerState(const unsigned int index, const shared_ptr<SamplerRenderState>& state)
{
	IDirect3DDevice9* device = GraphicDriver::ptr()->getDevice();
	device->SetSamplerState(index, D3DSAMP_ADDRESSU, state->getAddressU());
	device->SetSamplerState(index, D3DSAMP_ADDRESSV, state->getAddressV());
	device->SetSamplerState(index, D3DSAMP_ADDRESSW, state->getAddressW());
	device->SetSamplerState(index, D3DSAMP_MIPMAPLODBIAS, (DWORD)state->getMipLODBias());
	device->SetSamplerState(index, D3DSAMP_MAXANISOTROPY, state->getMaxAnisotropy());
	D3DFILTER fil = _getFilters(state->getFilter());
	device->SetSamplerState(index, D3DSAMP_MINFILTER, fil.minf);
	device->SetSamplerState(index, D3DSAMP_MAGFILTER, fil.maxf);
	device->SetSamplerState(index, D3DSAMP_MIPFILTER, fil.mipf);
};

void RenderStateManager::setSamplerRenderState(const SamplerRenderState& sampler_render_state, const unsigned int sampler_index, const SAMPLER_TYPE& type)
{
	new_sampler_state[sampler_index].reset(new SamplerRenderState(sampler_render_state));
};

const shared_ptr<SamplerRenderState>& RenderStateManager::getCurSamplerRenderState(const unsigned int sampler_index, const SAMPLER_TYPE& type) const
{
	return cur_sampler_state[sampler_index];
};

void RenderStateManager::flush()
{
	if(!(*cur_cull_state == *new_cull_state))
	{
		_setAllCullModeState(new_cull_state);
		cur_cull_state = new_cull_state;
	};

	if(!(*cur_depth_state == *new_depth_state))
	{
		_setAllDepthState(new_depth_state);
		cur_depth_state = new_depth_state;
	};

	if(!(*cur_blend_state == *new_blend_state))
	{
		_setAllBlendState(new_blend_state);
		cur_blend_state = new_blend_state;
	};

	if(!(*cur_color_write_state == *new_color_write_state))
	{
		_setAllColorWriteState(new_color_write_state);
		cur_color_write_state = new_color_write_state;
	};

	if(!(*cur_fill_mode_state == *new_fill_mode_state))
	{
		_setAllFillModeState(new_fill_mode_state);
		cur_fill_mode_state = new_fill_mode_state;
	};

	for(unsigned int i = 0; i < sampler_num; ++i)
	{
		if(!(*cur_sampler_state[i] == *new_sampler_state[i]))
		{
			_setAllSamplerState(i, new_sampler_state[i]);
			cur_sampler_state[i] = new_sampler_state[i];
		};
	};
};

#endif