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

#include <Darkstar/Resource System/Material/material_loader.hpp>
#include <Darkstar/Resource System/Model/model_loader.hpp>
#include <Darkstar/Resource System/Model/mesh.hpp>
#include <Darkstar/Resource System/Model/vertex_buffer.hpp>
#include <Darkstar/Resource System/Model/index_buffer.hpp>
#include <Darkstar/Resource System/Model/input_layout.hpp>
#include <Darkstar/Kernel/logh.hpp>
#include <Darkstar/Kernel/FileSystem/dfile.hpp>

using rs::ModelLoader;
using rs::StaticModel;
using rs::InputLayout;
using rs::IndexBuffer;
using rs::VertexBuffer;
using rs::Material;
using rs::Mesh;
using rs::MaterialLoader;
using ker::DFile;
using ker::Log;

ModelLoader::ModelLoader()
{

};

ModelLoader::~ModelLoader()
{

};

shared_ptr<StaticModel> ModelLoader::load(const std::string& filename)
{
	shared_ptr<StaticModel> model(new StaticModel);
	shared_ptr<StaticModel> dummy_mode;
	DFile file(filename);
	if(!file.getIsLoaded())
	{	
		lwar << "Cannot load model file " << filename;
		return model;
	};
	unsigned int file_size = file.getSize();
	shared_array<char> file_data;
	try
	{
		file_data.reset(new char[file_size+1]);
		memset(file_data.get(), 0, file_size);
		file_data[file_size] = '\0';
	}
	catch (std::bad_alloc&)
	{
		lwar << "Cannot find enough memory to load file " << filename;
		return model;
	};
	if(!file.read(file_size, file_data.get()))
	{
		lwar << "Cannot read " << file_size << " bytes from file " << filename;
		return model;
	};
	std::stringstream stream;
	stream.rdbuf()->sputn(file_data.get(), file_size+1);
	unsigned short material_name_length;
	stream.read((char*)&material_name_length, sizeof(unsigned short)); 
	shared_array<char> material_name_buffer(new char[material_name_length+1]);
	memset(material_name_buffer.get(), int('\0'), material_name_length+1);
	stream.read(material_name_buffer.get(), material_name_length);
	std::map<std::string, shared_ptr<Material> > materials = MaterialLoader::ptr()->load(material_name_buffer.get());
	if(materials.size() == 0)
	{
		lwar << "Cannot load material file for object " << filename;
		return model;
	};
	unsigned short mesh_number;
	stream.read((char*)&mesh_number, sizeof(unsigned short));
	unsigned short counter = 0;
	while(!stream.eof() && counter < mesh_number)
	{
		Matr4 transform;
		stream.read((char*)&transform.row0[0], 4*sizeof(float));
		stream.read((char*)&transform.row1[0], 4*sizeof(float));
		stream.read((char*)&transform.row2[0], 4*sizeof(float));
		stream.read((char*)&transform.row3[0], 4*sizeof(float));

		shared_ptr<IndexBuffer> index_buffer(new IndexBuffer());
		// download vertex buffer
		unsigned int vertex_buffer_size;
		stream.read((char*)&vertex_buffer_size, sizeof(unsigned int));
		unsigned int vertex_elements_number;
		stream.read((char*)&vertex_elements_number, sizeof(unsigned int));
		shared_array<char> vb(new char[vertex_buffer_size]);
		stream.read(vb.get(), vertex_buffer_size);
		// download index buffer
		unsigned char index_buffer_type;
		stream.read((char*)&index_buffer_type, sizeof(unsigned char));
		rs::IBTYPE ibtype;
		if(index_buffer_type == 2)
			ibtype = rs::DSEIB_16;
		else
			if(index_buffer_type == 4)
				ibtype = rs::DSEIB_32;
			else
			{
				lwar << "Cannot have recognized index buffer type. Size of one index is " << index_buffer_type;
				return dummy_mode;
			};
		unsigned int index_buffer_size;
		stream.read((char*)&index_buffer_size, sizeof(unsigned int));
		shared_array<char> ib(new char[index_buffer_size*index_buffer_type]);
		stream.read(ib.get(), index_buffer_size*index_buffer_type);
		if(!index_buffer->createWithData(index_buffer_size, ibtype, ib.get()))
		{
			lwar << "Cannot create index buffer " << filename;
			return dummy_mode;
		};
		// material name
		unsigned int mesh_material_name_size;
		stream.read((char*)&mesh_material_name_size, sizeof(unsigned int));
		shared_array<char> mmn(new char[mesh_material_name_size + 1]);
		memset(mmn.get(), int('\0'), mesh_material_name_size+1);
		stream.read(mmn.get(), mesh_material_name_size);
		std::map<std::string, shared_ptr<Material> >::iterator mat_it = materials.find(mmn.get());
		if(mat_it == materials.end())
		{
			lwar << "Mesh in file " << filename << " has material name which is not exist in material file";
			return dummy_mode;
		};
		shared_ptr<Material> mesh_material = mat_it->second;
		// download input layout
		unsigned char input_layout_string_number;
		stream.read((char*)&input_layout_string_number, sizeof(unsigned char));
		shared_ptr<InputLayout> input_layout(new InputLayout());
		for(unsigned int i = 0; i < input_layout_string_number; ++i)
		{
			unsigned short input_layout_string_size;
			stream.read((char*)&input_layout_string_size, sizeof(unsigned short));
			shared_array<char> ild(new char[input_layout_string_size + 1]);
			memset(ild.get(), int('\0'), input_layout_string_size + 1);
			stream.read(ild.get(), input_layout_string_size);
			std::string input_layout_string(ild.get());
			unsigned int first_space_pos = (unsigned int)input_layout_string.find_first_of(' ');
			if(first_space_pos == std::string::npos)
			{
				lwar << "File format error. Input layout description string has unsupported format " << input_layout_string;
				return dummy_mode;
			};
			std::string semantic = input_layout_string.substr(0, first_space_pos);
			unsigned int offset;
			unsigned int stream_num;
			unsigned int usage_index;
			unsigned int input_layout_elem_size;
			sscanf_s(input_layout_string.substr(first_space_pos+1).c_str(), "%d %d %d %d", &offset, &stream_num, &usage_index, &input_layout_elem_size);
			rs::DSEFORMAT elem_format;
			switch(input_layout_elem_size)
			{
			case 1:
				{
					elem_format = DFMT_R32_FLOAT;
				};break;
			case 2:
				{
					elem_format = DFMT_R32G32_FLOAT;
				};break;
			case 3:
				{
					elem_format = DFMT_R32G32B32_FLOAT;
				};break;
			case 4:
				{
					elem_format = DFMT_R32G32B32A32_FLOAT;
				};break;
			default:
				{
					lwar << "File format error. Error in input layout description. File " << filename;
					return dummy_mode;
				};
			};

			input_layout->addElement(semantic.c_str(), usage_index, stream_num, offset, elem_format);
		};
		if(!input_layout->create(mesh_material->getEffect()->getEffectInputSignature().code.get(), static_cast<unsigned int>(mesh_material->getEffect()->getEffectInputSignature().size)))
		{
			lwar << "Cannot create input layout. File " << filename;
			return dummy_mode;
		};
		shared_ptr<VertexBuffer> vertex_buffer(new VertexBuffer(input_layout));
		if(vertex_buffer->createNextStreamWithData((vertex_buffer_size/vertex_elements_number), vertex_elements_number, vb.get()) == -1)
		{
			lwar << "Cannot create vertex buffer in file " << filename;
			return dummy_mode;
		};
		shared_ptr<Mesh> new_mesh(new Mesh(vertex_buffer, index_buffer, mesh_material, transform));
		model->addNextMesh(new_mesh);
		++counter;
	};
	if(counter != mesh_number)
	{
		lwar << "Number of mesh in file less than a number of mesh that had saved in the file header";
		return dummy_mode;
	};
	return model;
};



		
	