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

#include <Darkstar/Graphic System/Effect/effect.hpp>
#include <Darkstar/Kernel/logh.hpp>
#include <Darkstar/Graphic System/render_state.hpp>
#include <Darkstar/Graphic System/Effect/effect_manager.hpp>
#include <Darkstar/Graphic System/Direct3D/graphicDriver.hpp>

using namespace gs;
using ker::Log;

Technique::Technique()
{
	const_table.reset(new ConstantTable());
};

Effect::Effect()
{
	cur_ce_index = 0;
	need_to_get_ce_index = true;
	cur_technique.reset(new Technique());
};

Effect::~Effect()
{
	techniques.clear();
};

void Effect::addNewTechnique(const std::string& name, const shared_ptr<Technique>& technique)
{
	techniques.insert(std::pair<std::string, shared_ptr<Technique> >(name, technique));
};

bool Effect::setCurTechnique(const std::string& tname)
{
	hmt_it it = techniques.find(tname);
	if(it == techniques.end())
	{
		lwar << "Cannot find technique: " << tname;
		return false;
	};
	cur_technique = it->second;
	need_to_get_ce_index = true;
	return true;
};

bool Effect::getIsTechniqueExist(const std::string& tname) const
{
	const_hmt_it it = techniques.find(tname);
	return it != techniques.end();
};

void Effect::setMacro(const std::string& name, short new_value)
{
	stdext::hash_map<std::string, unsigned int>::iterator it = cur_technique->macros_num_map.find(name);
	if(it == cur_technique->macros_num_map.end())
	{
		lwar << "Cannot find macros: " << name;
		return;
	};
	Macro nm = cur_technique->macros[it->second];
	if(nm.max_value < new_value || nm.min_value > new_value)
	{	
		lwar << "Cannot set new value for macros: " << name << " value is out of range";
		return;
	};
	if(cur_technique->macros[it->second].cur_value == new_value)
		return;
	cur_technique->macros[it->second].cur_value = new_value;
	need_to_get_ce_index = true;
	return;
};

bool Effect::getIsMacroExist(const std::string& name) const
{
	stdext::hash_map<std::string, unsigned int>::iterator it = cur_technique->macros_num_map.find(name);
	return it != cur_technique->macros_num_map.end();
};

short Effect::getMacroCurValue(const std::string& name) const
{
	stdext::hash_map<std::string, unsigned int>::iterator it = cur_technique->macros_num_map.find(name);
	if(it == cur_technique->macros_num_map.end())
	{
		lmes << "Try to get current value of not exist macros " << name;
		return -100;
	};
	return cur_technique->macros[it->second].cur_value;
};

short Effect::getMacroMaxValue(const std::string& name) const
{
	stdext::hash_map<std::string, unsigned int>::iterator it = cur_technique->macros_num_map.find(name);
	if(it == cur_technique->macros_num_map.end())
	{
		lmes << "Try to get max value of not exist macros: " << name;
		return -100;
	};
	return cur_technique->macros[it->second].max_value;
};

short Effect::getMacroMinValue(const std::string& name) const
{
	stdext::hash_map<std::string, unsigned int>::iterator it = cur_technique->macros_num_map.find(name);
	if(it == cur_technique->macros_num_map.end())
	{
		lmes << "Try to get min value of not exist macros " << name;
		return -100;
	};
	return cur_technique->macros[it->second].min_value;
};

void Effect::_getCurCeIndex()
{
	unsigned int number = 0;
	std::vector<Macro>::const_iterator it;
	for(it = cur_technique->macros.begin(); it != cur_technique->macros.end(); ++it)
		number += it->contribution*(it->cur_value - it->min_value);
	cur_ce_index = number;
	need_to_get_ce_index = false;
};

bool Effect::setBool(const std::string& name, const bool* value)
{
	return cur_technique->const_table->setBoolArray(name, value, 1);
};

bool Effect::setBoolArray(const std::string& name, const bool* values, const unsigned int count)
{
	return cur_technique->const_table->setBoolArray(name, values, count);
};

bool Effect::setFloat(const std::string& name, const float* value)
{
	return cur_technique->const_table->setFloatArray(name, value, 1);
};

bool Effect::setFloatArray(const std::string& name, const float* values, const unsigned int count)
{
	return cur_technique->const_table->setFloatArray(name, values, count);
};

bool Effect::setInt(const std::string& name, const int* value)
{
	return cur_technique->const_table->setIntArray(name, value, 1);
};

bool Effect::setIntArray(const std::string& name, const int* values, const unsigned int count)
{
	return cur_technique->const_table->setIntArray(name, values, count);
};

bool Effect::setMatrix4x4(const std::string& name, const math::Matr4* value)
{
	return cur_technique->const_table->setFloatArray(name, &math::Matr4(*value)[0][0], 16);
};
bool Effect::setMatrix4x4Array(const std::string& name, const math::Matr4* values, const unsigned int count)
{
	unsigned int float_number = count << 4;
	shared_array<float> nv;
	try
	{
		nv.reset(new float[float_number]);
	}
	catch(std::bad_alloc&)
	{
		lerr << "Cannot find enough memory to allocate. Size = " << (float_number << 2) << " bytes";
		return false;
	};
	for(unsigned int i = 0; i < count; ++i)
	{
		unsigned int pitch = i << 4;
		for(unsigned int j = 0; j < 4; ++j)
		{
			math::Vect4 row = values[i][j];
			unsigned int npitch = pitch + (j << 2);
			nv[npitch] = row.x;
			nv[npitch + 1] = row.y;
			nv[npitch + 2] = row.z;
			nv[npitch + 3] = row.w;
		};
	};
	return cur_technique->const_table->setFloatArray(name, nv.get(), float_number);
};

bool Effect::setVect2(const std::string& name, const math::Vect2* value)
{
	return cur_technique->const_table->setFloatArray(name, &(math::Vect2(*value))[0], 2);
};

bool Effect::setVect3(const std::string& name, const math::Vect3* value)
{
	return cur_technique->const_table->setFloatArray(name, &(math::Vect3(*value))[0], 3);
};

bool Effect::setVect3Array(const std::string& name, const math::Vect3* values, const unsigned int count)
{
	shared_array<float> nv;
	unsigned int float_num = count*3;
	try
	{
		nv.reset(new float[float_num]);
	}
	catch(std::bad_alloc&)
	{
		lerr << "Cannot find enough memory to allocate. Size = " << (float_num << 2) << " bytes";
		return false;
	};
	unsigned int index = 0;
	for(unsigned int i = 0; i < count; ++i)
	{
		index = i*3;
		nv[index] = values[i].x;
		nv[index + 1] = values[i].y;
		nv[index + 2] = values[i].z;
	};
	return cur_technique->const_table->setFloatArray(name, nv.get(), float_num);
};

bool Effect::setVect4(const std::string& name, const math::Vect4* value)
{
	return cur_technique->const_table->setFloatArray(name, &math::Vect4(*value)[0], 4);
};

bool Effect::setVect4Array(const std::string& name, const math::Vect4* values, const unsigned int count)
{
	unsigned int float_num = count << 2;
	shared_array<float> nv;
	try
	{
		nv.reset(new float[float_num]);
	}
	catch(std::bad_alloc&)
	{
		lerr << "Cannot find enough memory to allocate. Size = " << (float_num << 2) << " bytes";
		return false;
	};
	unsigned int index = 0;
	for(unsigned int i = 0 ; i < count; ++i)
	{
		index = i << 2;
		nv[index] = values[i].x;
		nv[index + 1] = values[i].y;
		nv[index + 2] = values[i].z;
		nv[index + 3] = values[i].w;
	};
	return cur_technique->const_table->setFloatArray(name, nv.get(), float_num);
};

bool Effect::setTexture(const std::string& name, const shared_ptr<rs::BaseTexture>& texture)
{
#ifdef GD_D3D10
	if(!getIsConstantExist(name))
		return false;
	short ci = getConstantIndex(name);
	CONSTANT_TYPE type = cur_technique->const_table->getConstantType(name);
	ID3D10ShaderResourceView* srv = texture->getShaderResourceView();
	switch(type)
	{
	case VS_CONST:
		{
			GraphicDriver::ptr()->getDevice()->VSSetShaderResources(ci, 1, &srv);
		};break;
	case PS_CONST:
		{
			GraphicDriver::ptr()->getDevice()->PSSetShaderResources(ci, 1, &srv);
		};break;
	default:
		{
			lwar << "Error type of sample with name = " << name;
			return false;
		};
	};
#endif
	return true;
};

bool Effect::setSamplers()
{
	stdext::hash_map<std::string, shared_ptr<SamplerRenderState> >::iterator it;
	for(it = cur_technique->samplers.begin(); it != cur_technique->samplers.end(); ++it)
	{
		if(!getIsConstantExist(it->first))
			continue;
		short ci = getConstantIndex(it->first);
		it->second->set(ci, (SAMPLER_TYPE)cur_technique->const_table->getConstantType(it->first));
	};
	return true;
};

bool Effect::getIsConstantExist(const std::string& name) const
{
	return cur_technique->const_table->getIsConstantExist(name);
};

short Effect::getConstantIndex(const std::string& name) const
{
	return cur_technique->const_table->getConstantIndex(name);
};

void Effect::set()
{
	if(need_to_get_ce_index)
		_getCurCeIndex();
#ifdef _NOTFINAL
	if(cur_ce_index >= cur_technique->ce.size() || cur_technique->ce.size() == 0)
	{
		lwar << "Cur Concrete effect index greater then concrete effects size";
		return;
	};
	if(!cur_technique->ce[cur_ce_index].ps || !cur_technique->ce[cur_ce_index].vs)
	{
		lwar << "Pixel or vertex shader in conrete effect is NULL";
		return;
	};
#endif
	cur_technique->ce[cur_ce_index].ps->set();
	cur_technique->ce[cur_ce_index].vs->set();
};

const InputSignature& Effect::getEffectInputSignature() const
{
	return input_signature;
};

void Effect::setEffectInputSignature(const InputSignature& input_signature) 
{
	this->input_signature = input_signature;
};


	

