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

#include <Darkstar/Resource System/Text/text_manager.hpp>
#include <Darkstar/Graphic System/Direct3D/graphicDriver.hpp>
#include <Darkstar/Kernel/logh.hpp>
#include <Darkstar/Graphic System/Camera/fpcamera_controller.hpp>
#include <Darkstar/Graphic System/Render State/render_state_manager.hpp>

using gs::GraphicDriver;
using rs::TextManager;
using rs::Type;
using rs::TextureChar;
using rs::BaseTexture;
using gs::Effect;
using rs::VertexBuffer;
using rs::IndexBuffer;
using rs::InputLayout;
using math::Matr4;
using math::Vect2;
using math::Vect3;
using ker::Log;
using gs::FPCameraController;
using gs::RenderStateManager;
using gs::BlendRenderState;
using gs::DepthRenderState;

TextManager::TextManager()
{
	znear = 0.01f;
	zfar = 10.0f;
	boost::function2<void, const unsigned int, const unsigned int> new_function;
	new_function = boost::bind(&TextManager::onResize, this, _1, _2);
	GraphicDriver::ptr()->addMessageListenerOnResize(new_function);
	current_type.reset();

	input_layout.reset(new InputLayout);
	input_layout->addElement("POSITION", 0, 0, 0, DFMT_R32G32B32_FLOAT);
	input_layout->addElement("TEXCOORD", 0, 0, 12, DFMT_R32G32_FLOAT);
};

TextManager::~TextManager()
{
	
};

bool TextManager::init(const shared_ptr<gs::Effect>& effect, const unsigned int width, const unsigned int height)
{
	if(!effect)
	{
		lwar << "Effect is NULL";
		return false;
	};
	effect->setCurTechnique("base");
	_updateMatrix(width, height);
	this->effect = effect;
	if(!input_layout->create(effect->getEffectInputSignature().code.get(), (unsigned int)(effect->getEffectInputSignature().size)))
	{
		lwar << "Cannot create input layout";
		return false;
	};
	return true;
};

void TextManager::_clearAllBuffers()
{
	type_iterator it;
	for(it = types.begin(); it != types.end(); ++it)
	{
		it->second->usage_buffers.clear();
		std::vector<std::vector<Vertex> >::iterator buf_it;
		for(buf_it = it->second->buffers.begin(); buf_it != it->second->buffers.end(); ++buf_it)
			buf_it->clear();
		std::vector<std::vector<unsigned short> >::iterator ibuf_it;
		for(ibuf_it = it->second->ibuffers.begin(); ibuf_it != it->second->ibuffers.end(); ++ibuf_it)
			ibuf_it->clear();
	};
};

void TextManager::_updateMatrix(const unsigned int new_width, const unsigned int new_height)
{
	width = new_width;
	height = new_height;
	ortho_projection_matrix = Matr4(2.0f / (float)width, 0, 0, -1, 
									0, -2.0f/(float)height, 0, 1, 
									0, 0, 1.0f/(zfar - znear), znear/(znear - zfar),
									0, 0, 0, 1);
	FPCameraController camera(shared_ptr<gs::Camera>(new gs::Camera()), false);
	camera.move(-2.0f);
	view_matrix = camera.getCamera()->getViewMatrix();
	ov_matrix = ortho_projection_matrix * view_matrix;
};

bool TextManager::_resizeBuffers(shared_ptr<TypeDescriptor>& td, unsigned int new_num_of_char)
{
	// one char - four vertices
	td->vertex_number_in_buffer = new_num_of_char << 2;
	// one char - six indices
	unsigned int number_of_ind = new_num_of_char * 6;
	for(unsigned int i = 0; i < td->num_type_textures; ++i)
	{
		td->index_buffers[i].reset(new IndexBuffer);
		if(!td->index_buffers[i]->create(number_of_ind, rs::DSEIB_16))
		{
			lwar << "Cannot create index buffer for type";
			return false;
		};
		td->vertex_buffers[i].reset(new VertexBuffer(input_layout));
		if(td->vertex_buffers[i]->createNextStream(sizeof(Vertex), td->vertex_number_in_buffer) == -1)
		{
			lwar << "Cannot create vertex buffer for type";
			return false;
		};
	};
	return true;
};

bool TextManager::registerNewType(const shared_ptr<Type>& new_type, const unsigned int char_in_buf /* = 128 */)
{
	shared_ptr<TypeDescriptor> new_td(new TypeDescriptor);
	new_td->type = new_type;
	new_td->num_type_textures = new_type->getTypeTexturesNumber();
	new_td->buffers.resize(new_td->num_type_textures);
	new_td->ibuffers.resize(new_td->num_type_textures);
	new_td->index_buffers.resize(new_td->num_type_textures);
	new_td->vertex_buffers.resize(new_td->num_type_textures);
	if(!_resizeBuffers(new_td, char_in_buf))
		return false;
	if(!current_type)
		current_type = new_td;
	types.insert(std::pair<std::string, shared_ptr<TypeDescriptor> >(new_type->getName(), new_td));
	return true;
};

void TextManager::onResize(const unsigned int new_width, const unsigned int new_height)
{
	_updateMatrix(new_width, new_height);
};

void TextManager::setCurrentType(const std::string& type_name)
{
	type_iterator it = types.find(type_name);
	if(it == types.end())
		return;
	current_type = it->second;
};

void TextManager::addText(const std::string& str, const unsigned int x_pos, const unsigned int y_pos, const float scale_factor /* = 1 */)
{
	float tmp_xpos = (float)x_pos;
	float tmp_ypos = (float)y_pos;

	// rasterizer grid and texture sampler grid not the same. They are shift from each other by 
	// half of pixel. So we need to shift start x position by half pixel to the left and y position
	// by half of pixel to the down.
#ifdef GD_D3D9
	tmp_xpos -= 0.5f;
	tmp_ypos += 0.5f;
#endif
	for(unsigned int i = 0; i < str.length(); ++i)
	{
		char ch = str[i];
		TextureChar tc = current_type->type->getChar(ch);
		Vertex lu_cor, ld_cor, ru_cor, rd_cor;

		lu_cor.position = Vect3(tmp_xpos, tmp_ypos, 0);
		lu_cor.tex_coord = tc.tex_coord.lu_corner;

		float shift_tmp_xpos = tmp_xpos + scale_factor*tc.tex_coord.width;
		float shift_tmp_ypos = tmp_ypos + scale_factor*tc.tex_coord.height;

		ru_cor.position = Vect3(shift_tmp_xpos, tmp_ypos, 0);
		ru_cor.tex_coord = tc.tex_coord.ru_corner;

		ld_cor.position = Vect3(tmp_xpos, shift_tmp_ypos, 0);
		ld_cor.tex_coord = tc.tex_coord.ld_corner;

		rd_cor.position = Vect3(shift_tmp_xpos, shift_tmp_ypos, 0);
		rd_cor.tex_coord = tc.tex_coord.rd_corner;

		std::vector<Vertex>& cur_vlist = current_type->buffers[tc.texture_ref];
		std::vector<unsigned short>& cur_ilist = current_type->ibuffers[tc.texture_ref];

		cur_vlist.push_back(lu_cor);
		cur_vlist.push_back(ru_cor);
		cur_vlist.push_back(rd_cor);
		cur_vlist.push_back(ld_cor);

		unsigned short cur_number_ind = (unsigned short)cur_vlist.size() - 4;
		unsigned short cur_number_ind2 = cur_number_ind + 2;
		cur_ilist.push_back(cur_number_ind);
		cur_ilist.push_back(cur_number_ind + 1);
		cur_ilist.push_back(cur_number_ind2);
		cur_ilist.push_back(cur_number_ind2);
		cur_ilist.push_back(cur_number_ind + 3);
		cur_ilist.push_back(cur_number_ind);

		current_type->usage_buffers.insert(tc.texture_ref);

		tmp_xpos += tc.tex_coord.aw;
		if(tmp_xpos >= width || tmp_ypos >= height)
			break;
	};
};

void TextManager::clear()
{
	_clearAllBuffers();
};

void TextManager::drawAll()
{
	type_iterator it;
	if(effect->getIsConstantExist("ortho_view_matr"))
		effect->setMatrix4x4("ortho_view_matr", &ov_matrix);
	effect->setSamplers();
	effect->set();
#ifdef GD_D3D10
	GraphicDriver::ptr()->getDevice()->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
#endif
	BlendRenderState blend;
	blend.setSrcBlend(gs::DBLEND_SRC_ALPHA);
	blend.setBlendOp(gs::DBLENDOP_ADD);
	blend.setDstBlend(gs::DBLEND_INV_SRC_ALPHA);

	blend.setBlendEnable(0, true);

	blend.setSrcBlendAlpha(gs::DBLEND_ZERO);
	blend.setDstBlendAlpha(gs::DBLEND_ZERO);
	blend.setBlendOpAlpha(gs::DBLENDOP_ADD);
	blend.set();
	gs::CullModeRenderState culling;
	culling.setCullMode(gs::DCULL_BACK);
	culling.set();
	shared_ptr<DepthRenderState> cur_depth_state = RenderStateManager::ptr()->getCurDepthRenderState();
	DepthRenderState depth;
	depth.setDepthEnable(false);
	depth.set();
	for(it = types.begin(); it != types.end(); ++it)
	{
		shared_ptr<TypeDescriptor> cur_desc = it->second;
		if(cur_desc->usage_buffers.size() == 0)
			continue;
		std::set<unsigned int>::iterator set_it;
		for(set_it = cur_desc->usage_buffers.begin(); set_it != cur_desc->usage_buffers.end(); ++set_it)
		{
			unsigned int tex_ref = *set_it;
			std::vector<Vertex>& vlist = cur_desc->buffers[tex_ref];
			std::vector<unsigned short>& ilist = cur_desc->ibuffers[tex_ref];
			if(vlist.size() > cur_desc->vertex_number_in_buffer)
			{
				unsigned int char_num = (unsigned int)vlist.size() * 2;
				if(!_resizeBuffers(cur_desc, char_num))
					return;
			};
			shared_ptr<VertexBuffer> vb = cur_desc->vertex_buffers[tex_ref];
			shared_ptr<IndexBuffer> ib = cur_desc->index_buffers[tex_ref];
			if(!vb->uploadStream(0, (unsigned int)vlist.size()*sizeof(Vertex), &vlist[0]))
			{
				lwar << "Cannot upload stream in text manager. Type name is " << cur_desc->type->getName();
				continue;
			};
			if(!ib->upload((unsigned int)ilist.size()*sizeof(unsigned short), &ilist[0]))
			{
				lwar << "Cannot upload index buffer in text manager. Type name is " << cur_desc->type->getName();
				continue;
			};
			if(effect->getIsConstantExist("text"))
				effect->setTexture("text", cur_desc->type->getTypeTextureByRef(tex_ref));
			vb->set();
			ib->set();
			RenderStateManager::ptr()->flush();
#ifdef GD_D3D10
			GraphicDriver::ptr()->setConstantBuffers();
			GraphicDriver::ptr()->getDevice()->DrawIndexed((unsigned int)ilist.size(), 0, 0);
#endif
#ifdef GD_D3D9
			GraphicDriver::ptr()->getDevice()->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, (unsigned int)vlist.size(), 0, (unsigned int)ilist.size()/3);
#endif
		};
	};
	cur_depth_state->set();
	RenderStateManager::ptr()->flush();
};
			



