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

#include <Darkstar/Graphic System/Effect/effect_parser.hpp>
#include <Darkstar/Graphic System/Render State/render_state_manager.hpp>
#include <Darkstar/Kernel/logh.hpp>

using namespace gs;
using ker::DFile;
using ker::Log;
using ker::Parser;
using ker::Section;
using ker::PairInfo;
using ker::SystemTime;

EffectParser::EffectParser()
{

};

EffectParser::~EffectParser()
{

};

bool EffectParser::_parseTechnique(const shared_ptr<Section>& tech_section, _Technique& t)
{
	bool vs = false;
	bool ps = false;
	std::vector<shared_ptr<ker::Section> >::const_iterator tech_it;
	for(tech_it = tech_section->sections.begin(); tech_it != tech_section->sections.end(); ++tech_it)
	{
		if((*tech_it)->section_name == "macro")
		{
			if((*tech_it)->section_value == "")
			{
				lwar << "Each macros must have correct name. Line number: " << (*tech_it)->section_start_line;
				return false;
			};
			Macro new_m;
			if(!_parseMacro(*tech_it, new_m))
			{
				lwar << "Cannot parse macro";
				return false;
			};
			new_m.name = (*tech_it)->section_value;
			t.tech->macros_num_map.insert(std::pair<std::string, unsigned int>((*tech_it)->section_value, static_cast<unsigned int>(t.tech->macros.size())));
			t.tech->macros.push_back(new_m);
			continue;
		};
		if((*tech_it)->section_name == "sampler")
		{
			if((*tech_it)->section_value == "")
			{
				lwar << "Each sampler must have correct name. Line number " << (*tech_it)->section_start_line;
				return false;
			};
			shared_ptr<SamplerRenderState> new_s(new SamplerRenderState);
			if(!_parseSampler(*tech_it, new_s))
			{
				lwar << "Cannot parse sampler";
				return false;
			};
			t.tech->samplers.insert(std::pair<std::string, shared_ptr<SamplerRenderState> >((*tech_it)->section_value, new_s));
			continue;
		};
		if((*tech_it)->section_name == "vertex_shader")
		{
			if(vs)
			{
				lwar << "Each technique must have only one vertex shader";
				return false;
			};
			if((*tech_it)->section_value != "")
			{
				lwar << "Vertex shader have not name. Line number " << (*tech_it)->section_start_line;
				return false;
			};
			if(!_parseShader(*tech_it, t.vertex_shader_name, t.vertex_shader_target, t.vertex_shader_function, false))
			{
				lwar << "Cannot parse vertex shader";
				return false;
			};
			vs = true;
			continue;
		};
		if((*tech_it)->section_name == "pixel_shader")
		{
			if(ps)
			{
				lwar << "Each technique must have only one pixel shader";
				return false;
			};
			if((*tech_it)->section_value != "")
			{
				lwar << "Pixel shader have not name. Line number " << (*tech_it)->section_start_line;
				return false;
			};
			if(!_parseShader(*tech_it, t.pixel_shader_name, t.pixel_shader_target, t.pixel_shader_function, true))
			{
				lwar << "Cannot parse pixel shader";
				return false;
			};
			ps = true;
			continue;
		};
		if((*tech_it)->section_name == "include")
		{
			if(!_parseInclude(*tech_it, t.include_files))
			{
				lwar << "Cannot parse include section in effect file. Line number " << (*tech_it)->section_start_line;
				return false;
			};
			continue;
		};
		lwar << "Error in technique. It has section with name: " << (*tech_it)->section_name << " line: " << (*tech_it)->section_start_line;
		return false;
	};
	if(!vs)
	{
		lwar << "Each technique must have one vertex shader";
		return false;
	};
	if(!ps)
	{
		lwar << "Each technique must have one pixel shader";
		return false;
	};
	return true;
};

bool EffectParser::_parseInclude(const shared_ptr<ker::Section>& include_section, std::vector<std::string>& inc_names)
{
	std::map<std::string, PairInfo> values = include_section->values;
	std::map<std::string, PairInfo>::iterator it;
	std::string names;
	it = values.find("names");
	if(it == values.end())
	{
		lwar << "In each include section must be names field";
		return false;
	}
	else
		names = it->second.value;
	if(names[0] != '(' || names[names.length()-1] != ')')
	{
		lwar << "Names field has incorrect format";
		return false;
	};
	names = names.substr(1, names.length()-2);
	std::stringstream nstream(names);
	std::string tmp;
	while(std::getline(nstream, tmp, ','))
	{
		tmp = tmp.substr(tmp.find_first_not_of(' '));
		inc_names.push_back(tmp);
	};
	return true;
};

bool EffectParser::_parseMacro(const shared_ptr<ker::Section>& macro_section, Macro& m)
{
	std::map<std::string, PairInfo> values = macro_section->values;
	std::map<std::string, PairInfo>::iterator it;
	it = values.find("min_value");
	if(it == values.end())
	{
		lwar << "Each macro must have one min_value";
		return false;
	}
	else
		m.min_value = atoi(it->second.value.c_str());

	it = values.find("max_value");
	if(it == values.end())
	{
		lwar << "Each macro must have one max_value";
		return false;
	}
	else
		m.max_value = atoi(it->second.value.c_str());

	it = values.find("def_value");
	if(it == values.end())
	{
		lwar << "Each macro must have one def_value";
		return false;
	}
	else
		m.cur_value = atoi(it->second.value.c_str());

	it = values.find("type");

	if(it == values.end())
		m.type = MT_SHARED;
	else
	{
		if(it->second.value == "vertex")
			m.type = MT_VERTEX;
		else
			if(it->second.value == "pixel")
				m.type = MT_PIXEL;
			else
				if(it->second.value == "shared")
					m.type = MT_SHARED;
				else
				{
					lwar << "Macro type has incompatible value. Line number " << it->second.line_in_file;
					return false;
				};
	};
	return true;
};

bool getFilterFromString(const std::string& str, DTEXFILTER& filter)
{
	if(str == "min_mag_mip_point")
	{
		filter = DTEXF_MIN_MAG_MIP_POINT;
		return true;
	};
	if(str == "min_mag_point_mip_linear")
	{
		filter = DTEXF_MIN_MAG_POINT_MIP_LINEAR;
		return true;
	};
	if(str == "min_mip_point_mag_linear")
	{
		filter = DTEXF_MIN_POINT_MAG_LINEAR_MIP_POINT;
		return true;
	};
	if(str == "min_point_mag_mip_linear")
	{
		filter = DTEXF_MIN_POINT_MAG_MIP_LINEAR;
		return true;
	};
	if(str == "mag_mip_point_min_linear")
	{
		filter = DTEXF_MIN_LINEAR_MAG_MIP_POINT;
		return true;
	};
	if(str == "mag_point_min_mip_linear")
	{
		filter = DTEXF_MIN_LINEAR_MAG_POINT_MIP_LINEAR;
		return true;
	};
	if(str == "mip_point_min_mag_linear")
	{
		filter = DTEXF_MIN_MAG_LINEAR_MIP_POINT;
		return true;
	};
	if(str == "min_mag_mip_linear")
	{
		filter = DTEXF_MIN_MAG_MIP_LINEAR;
		return true;
	};
	if(str == "anisotropic")
	{
		filter = DTEXF_ANISOTROPIC;
		return true;
	};
	return false;
};

bool getAddressTypeFromString(const std::string& str, DTEXADDRESS& add)
{
	if(str == "wrap")
	{
		add = DTEXADD_WRAP;
		return true;
	};
	if(str == "clamp")
	{
		add = DTEXADD_CLAMP;
		return true;
	};
	if(str == "mirror")
	{
		add = DTEXADD_MIRROR;
		return true;
	};
	if(str == "mirroronce")
	{
		add = DTEXADD_MIRRORONCE;
		return true;
	};
	if(str == "border")
	{
		add = DTEXADD_BORDER;
		return true;
	};
	return false;
};

bool EffectParser::_parseSampler(const shared_ptr<Section>& sampler_section, shared_ptr<SamplerRenderState>& s)
{
	std::map<std::string, PairInfo> values = sampler_section->values;
	std::map<std::string, PairInfo>::iterator it;
	std::string addr[3] = {"addressu", "addressv", "addressw"};
	boost::function1<void, const DTEXADDRESS&> func[3];
	func[0] = boost::bind(&SamplerRenderState::setAddressU, s.get(), _1);
	func[1] = boost::bind(&SamplerRenderState::setAddressV, s.get(), _1);
	func[2] = boost::bind(&SamplerRenderState::setAddressW, s.get(), _1);
	for(unsigned int i = 0; i < 3; ++i)
	{
		it = values.find(addr[i]);
		if(it != values.end())
		{
			DTEXADDRESS add;
			if(!getAddressTypeFromString(it->second.value, add))
			{
				lwar << "Error value for " << it->first << ". Line number " << it->second.line_in_file;
				return false;
			};
			func[i](add);
		};
	};

	it = values.find("filter");
	if(it != values.end())
	{
		DTEXFILTER filter;
		if(!getFilterFromString(it->second.value, filter))
		{
			lwar << "Error value for " << it->first << " in line " << it->second.line_in_file;
			return false;
		};
		s->setFilter(filter);
	};

	it = values.find("max_anisotropy");
	if(it != values.end())
		s->setMaxAnisotropy(atoi(it->second.value.c_str()));

	it = values.find("mip_lod_bias");
	if(it != values.end())
		s->setMipLODBias((float)atof(it->second.value.c_str()));
	return true;
};

bool EffectParser::_parseShader(const shared_ptr<Section>& shader_section, std::string& shader_name, std::string& shader_target, std::string& shader_function, bool is_pixel)
{
	std::map<std::string, PairInfo> values = shader_section->values;
	std::map<std::string, PairInfo>::iterator it;

	it = values.find("name");
	if(it != values.end())
		shader_name = it->second.value;
	else
	{
		lwar << "Each shader must have one name";
		return false;
	};

	it = values.find("target");
	if(it != values.end())
		shader_target = it->second.value;
	else
		if(is_pixel)
			shader_target = "ps_4_0";
		else
			shader_target = "vs_4_0";

	it = values.find("function");
	if(it != values.end())
		shader_function = it->second.value;
	else
		if(is_pixel)
			shader_function = "ps_main";
		else
			shader_function = "vs_main";
	return true;	
};

bool EffectParser::parseFile(const std::string& filename)
{
	DFile file(filename);
	if(!file.getIsLoaded())
	{
		lwar << "Cannot open file " << filename;
		return false;
	};
	effect_last_write_time = file.getLastWriteTime();
	effect_file_path = file.getPath();
	Parser file_parser(file);
	if(!file_parser.getIsParsedSuccessfully())
	{
		lwar << "Cannot parse file with effect";
		return false;
	};
	shared_ptr<Section> root_section = file_parser.getRootSection();
	std::vector<shared_ptr<ker::Section> >::const_iterator tech_it;
	for(tech_it = root_section->sections.begin(); tech_it != root_section->sections.end(); ++tech_it)
	{
		_Technique t;
		t.tech.reset(new Technique);
		if((*tech_it)->section_name != "technique" || (*tech_it)->section_value == "")
		{
			lwar << "Effect file format error. File " << file.getFilename() << " line: " << (*tech_it)->section_start_line;
			return false;
		};
		t.tech_name = (*tech_it)->section_value;
		if(!_parseTechnique(*tech_it, t))
		{
			lwar << "Error in effect: " << file.getFilename() << " cannot parse technique: " << t.tech_name;
			return false;
		};
		_tech.push_back(t);
	};
	return true;
};

const std::list<_Technique>& EffectParser::getFileData() const
{
	return _tech;
};

const shared_ptr<SystemTime>& EffectParser::getEffectFileLastWriteTime() const
{
	return effect_last_write_time;
};

const std::string& EffectParser::getEffectFilePath() const
{
	return effect_file_path;
};