#include "StdAfx.h"
#include "ColladaFileLoader.h"

#include "TiUtility.h"



namespace lib_geo
{


bool ColladaFileLoader::Load(const char* filename, ColladaGeom& geom)
{
	m_TargetGeom = &geom;

	bool result = LoadMain(filename);

	m_TargetGeom = NULL;

	return result;
}

bool ColladaFileLoader::LoadMain(const char* filename)
{
	TiXmlDocument doc;
	if( !doc.LoadFile( filename ) )
		return false;

	TiXmlElement* elem_collada = doc.FirstChildElement( "COLLADA" );
	if( elem_collada == NULL )
		return false;

	TiXmlElement* lib_geometry     = elem_collada->FirstChildElement( "library_geometries"    );
	TiXmlElement* lib_visual_scene = elem_collada->FirstChildElement( "library_visual_scenes" );
	TiXmlElement* lib_controllers  = elem_collada->FirstChildElement( "library_controllers"   );

	if( lib_geometry != NULL )
	{
		TiElemForeach( elem_lib_geo , lib_geometry )
		{
			std::string geom_name = elem_lib_geo->Attribute( "id" );

			TiElemForeach( elem_mesh , elem_lib_geo )
			{
				if( strcmp( elem_mesh->Value() , "mesh" ) == 0 )
				{
					LoadMesh( elem_mesh );
				}
			}

			m_TargetGeom->m_Geoms.back().m_Name = geom_name;
		}
	}

	if( lib_visual_scene != NULL )
	{
		TiElemForeach( elem_lib_vs , lib_visual_scene )
		{
			LoadVisualScene(elem_lib_vs);
		}
	}

	if( lib_controllers != NULL )
	{
		TiElemForeach( elem_cont , lib_controllers )
		{
				LoadController(elem_cont);
		}
	}

	return true;
}

void ColladaFileLoader::LoadMesh(TiXmlElement* elem)
{
	DaeVertexCache vert_cache;

	TiElemForeach( elem_src , elem )
	{
		if( strcmp( elem_src->Value() , "source" ) == 0 )
		{
			vert_cache.m_Name = elem_src->Attribute("id");
			LoadMeshSource( elem_src , vert_cache );
		}
		else if( strcmp( elem_src->Value() , "vertices" ) == 0 )
		{
			std::string my_name = elem_src->Attribute("id");
			TiXmlElement* ref_elem = TiGetElem(elem_src->FirstChild());
			if( ref_elem == NULL )
				continue;

			std::string ref_name = ref_elem->Attribute("source");
			vert_cache.m_VertSources[ my_name ] = vert_cache.m_VertSources[ ref_name.substr(1) ];
		}
		else if( strcmp( elem_src->Value() , "polylist" ) == 0 )
		{
			LoadMeshPolylist( elem_src , vert_cache );
		}
	}
}

void ColladaFileLoader::LoadMeshSource(TiXmlElement* elem, DaeVertexCache& vert_cache)
{
	std::string name = elem->Attribute("id");

	DaeMeshSource& vert_source = vert_cache.m_VertSources[ name ];
	vert_source.m_Name = name;

	TiElemForeach( elem2 , elem )
	{
		if( strcmp( elem2->Value() , "float_array" ) == 0 )
		{
			std::istringstream s( TiGetFirstChildElementAsText(elem2) );

			int count = boost::lexical_cast<int>( elem2->Attribute("count") );
			vert_source.m_Values.resize( count );

			for( int i = 0 ; i < count ; ++i )
			{
				s >> vert_source.m_Values[i];
			}
		}
		else if( strcmp( elem2->Value() , "technique_common" ) == 0 )
		{
			TiXmlElement* accessor_elem = dynamic_cast<TiXmlElement*>( elem2->FirstChild() );
			vert_source.m_ElemCount = boost::lexical_cast<int>( accessor_elem->Attribute( "count"  ) );
			vert_source.m_Stride    = boost::lexical_cast<int>( accessor_elem->Attribute( "stride" ) );
		}
	}
}

void ColladaFileLoader::LoadMeshPolylist(TiXmlElement* elem, DaeVertexCache& vert_cache)
{
	m_TargetGeom->m_Geoms.push_back( ColladaMesh() );
	ColladaMesh& geom = m_TargetGeom->m_Geoms.back();

	int num_faces = boost::lexical_cast<int>( elem->Attribute( "count" ) );
	geom.m_Faces.resize( num_faces );

	TiElemForeach( elem2 , elem )
	{
		if( strcmp( elem2->Value() , "input" ) == 0 )
		{
			std::string semantic = elem2->Attribute( "semantic" );
			std::string source   = elem2->Attribute( "source"   );
			DaeMeshSource& source_buf = vert_cache.m_VertSources[ source.substr(1) ];

			if( semantic == "VERTEX" )
			{
				geom.m_Verts.resize( source_buf.m_ElemCount );
				for( int i = 0 ; i < source_buf.m_ElemCount ; ++i )
				{
					int stride = source_buf.m_Stride;
					lm::vec3f& vert = geom.m_Verts[i];
					if( stride >= 1 ) vert.x = source_buf.m_Values[ i * stride + 0 ];
					if( stride >= 2 ) vert.y = source_buf.m_Values[ i * stride + 1 ];
					if( stride >= 3 ) vert.z = source_buf.m_Values[ i * stride + 2 ];
				}
			}
			else if( semantic == "NORMAL" )
			{
				geom.m_Normals.resize( source_buf.m_ElemCount );
				for( int i = 0 ; i < source_buf.m_ElemCount ; ++i )
				{
					int stride = source_buf.m_Stride;
					lm::vec3f& nrm = geom.m_Normals[i];
					if( stride >= 1 ) nrm.x = source_buf.m_Values[ i * stride + 0 ];
					if( stride >= 2 ) nrm.y = source_buf.m_Values[ i * stride + 1 ];
					if( stride >= 3 ) nrm.z = source_buf.m_Values[ i * stride + 2 ];
				}
			}
		}
		else if( strcmp( elem2->Value() , "vcount" ) == 0 )
		{
			std::istringstream s( TiGetFirstChildElementAsText(elem2) );

			for( int i = 0 ; i < num_faces ; ++i )
			{
				int vcount;
				s >> vcount;
				geom.m_Faces[i].m_VertIds.resize(vcount);
				geom.m_Faces[i].m_NormIds.resize(vcount);
			}
		}
		else if( strcmp( elem2->Value() , "p" ) == 0 )
		{
			std::istringstream s( TiGetFirstChildElementAsText(elem2) );
			for( int i = 0 ; i < num_faces ; ++i )
			{
				lib_geo::BaseFace& f = geom.m_Faces[i];
				for( size_t j = 0 ; j < f.m_VertIds.size() ; ++j )
				{
					s >> f.m_VertIds[j];
					s >> f.m_NormIds[j];
				}
			}
		}
	}
}

void ColladaFileLoader::LoadVisualScene(TiXmlElement* elem)
{
	TiElemForeach( elem2 , elem )
	{
		if( strcmp( elem2->Value() , "node" ) == 0 )
		{
			m_TargetGeom->m_Nodes.push_back( ColladaNode() );
			LoadVisualSceneNode( elem2 , m_TargetGeom->m_Nodes.back() );
		}
	}

	for( size_t i = 0 ; i < m_TargetGeom->m_Nodes.size() ; ++i )
	{
		m_TargetGeom->m_Nodes[i].SetParent( NULL );
	}
}

void ColladaFileLoader::LoadVisualSceneNode(TiXmlElement* elem, ColladaNode& node)
{
	node.m_Name = elem->Attribute( "id" );

	const char* NodeTypeStr = elem->Attribute( "type" );
	if( strcmp( NodeTypeStr , "NODE" ) == 0 )
	{
		node.m_Type = ColladaNode::TYPE_NODE;
	}
	else if( strcmp( NodeTypeStr , "JOINT" ) == 0 )
	{
		node.m_Type = ColladaNode::TYPE_JOINT;
	}
	else
	{
		node.m_Type = ColladaNode::TYPE_NONE;
	}

	TiElemForeach( elem2 , elem )
	{
		if( strcmp( elem2->Value() , "translate" ) == 0 )
		{
			std::istringstream s( TiGetFirstChildElementAsText(elem2) );
			s >> node.m_Transform.m_Trans.x >> node.m_Transform.m_Trans.y >> node.m_Transform.m_Trans.z;
		}
		else if( strcmp( elem2->Value() , "rotate" ) == 0 )
		{
			std::istringstream s( TiGetFirstChildElementAsText(elem2) );

			AxisRotation rot;
			s >> rot.m_Axis.x >> rot.m_Axis.y >> rot.m_Axis.z >> rot.m_Angle;

			const char* sid = elem2->Attribute("sid");
			if( strcmp( sid , "rotationX" ) == 0 )
				node.m_Transform.m_RotX = rot;
			else if( strcmp( sid , "rotationY" ) == 0 )
				node.m_Transform.m_RotY = rot;
			else if( strcmp( sid , "rotationZ" ) == 0 )
				node.m_Transform.m_RotZ = rot;
		}
		else if( strcmp( elem2->Value() , "scale" ) == 0 )
		{
			std::istringstream s( TiGetFirstChildElementAsText(elem2) );
			s >> node.m_Transform.m_Scale.x >> node.m_Transform.m_Scale.y >> node.m_Transform.m_Scale.z;
		}
		else if( strcmp( elem2->Value() , "node" ) == 0 )
		{
			node.m_Childs.push_back( ColladaNode() );
			LoadVisualSceneNode( elem2 , node.m_Childs.back() );
		}
		else if( strcmp( elem2->Value() , "instance_geometry" ) == 0 )
		{
			std::string name = elem2->Attribute( "url" );
			node.m_BindTargetName = name.substr(1);
		}
		else if( strcmp( elem2->Value() , "instance_controller" ) == 0 )
		{
			std::string name = elem2->Attribute( "url" );
			node.m_BindTargetName = name.substr(1);
		}
	}
}

void ColladaFileLoader::LoadController(TiXmlElement* elem)
{
	TiXmlElement* elem_skin = elem->FirstChildElement( "skin" );
	if( elem_skin != NULL )
	{
		LoadControllerSkin(elem_skin);
	}

	m_TargetGeom->m_Bones.back().m_Name = elem->Attribute("id");
}

void ColladaFileLoader::LoadControllerSkin(TiXmlElement* elem)
{
	m_TargetGeom->m_Bones.push_back( ColladaBone() );
	ColladaBone& bone = m_TargetGeom->m_Bones.back();

	std::string mesh_name = elem->Attribute( "source" );
	mesh_name = mesh_name.substr(1);

	for( size_t i = 0 ; i < m_TargetGeom->m_Geoms.size() ; ++i )
	{
		if( m_TargetGeom->m_Geoms[i].m_Name == mesh_name )
		{
			bone.m_SrcMesh = &m_TargetGeom->m_Geoms[i];
			break;
		}
	}

	int joint_elem_count = 0;
	const char* joint_names = NULL;

	int joint_weight_value_count = 0;
	int joint_weight_elem_count = 0;
	const char* joint_weights = NULL;

	TiElemForeach( elem_source , elem )
	{
		if( strcmp( elem_source->Value() , "source" ) == 0 )
		{
			int value_cnt = 0;
			int elem_cnt = 0;
			const char* str = NULL;

			TiElemForeach( elem2 , elem_source )
			{
				if( strcmp( elem2->Value() , "Name_array" ) == 0 )
				{
					value_cnt = boost::lexical_cast<int>( elem2->Attribute("count") );
					str = TiGetFirstChildElementAsText(elem2);
				}
				else if( strcmp( elem2->Value() , "float_array" ) == 0 )
				{
					value_cnt = boost::lexical_cast<int>( elem2->Attribute("count") );
					str = TiGetFirstChildElementAsText(elem2);
				}
				else if( strcmp( elem2->Value() , "technique_common" ) == 0 )
				{
					TiElemForeach( elem3 , elem2 )
					{
						if( strcmp( elem3->Value() , "accessor" ) == 0 )
						{
							elem_cnt = boost::lexical_cast<int>( elem3->Attribute( "count" ) );

							TiElemForeach( elem4 , elem3 )
							{
								if( strcmp( elem4->Value() , "param" ) == 0 )
								{
									if( strcmp( elem4->Attribute("name") , "WEIGHT" ) == 0 )
									{
										joint_weight_value_count = value_cnt;
										joint_weight_elem_count = elem_cnt;
										joint_weights = str;
									}
									else if( strcmp( elem4->Attribute("name") , "JOINT" ) == 0 )
									{
										joint_elem_count = elem_cnt;
										joint_names = str;
									}
								}
							}
						}
					}
				}
			}
		}
		else if( strcmp( elem_source->Value() , "bind_shape_matrix" ) == 0 )
		{
			std::istringstream s(TiGetFirstChildElementAsText(elem_source) );
			for( size_t i = 0 ; i < 16 ; ++i )
				s >> bone.m_BindShapeMat.at(i);
		}
		else if( strcmp( elem_source->Value() , "vertex_weights" ) == 0 )
		{
			int w_counts = boost::lexical_cast<int>( elem_source->Attribute( "count") );

			std::vector<int> vcounts;
			std::vector<int> v_ary;

			TiElemForeach( elem2 , elem_source )
			{
				if( strcmp( elem2->Value() , "vcount" ) == 0 )
				{
					const char* vcout_str = TiGetFirstChildElementAsText(elem2);
					std::istringstream s(vcout_str);
					vcounts.resize(w_counts);
					for( int i = 0 ; i < w_counts ; ++i )
						s >> vcounts[i];
				}
				else if( strcmp( elem2->Value() , "v" ) == 0 )
				{
					const char* v_str = TiGetFirstChildElementAsText(elem2);
					std::istringstream s(v_str);
					while( s.eof() == false )
					{
						int i;
						s >> i;
						v_ary.push_back(i);
					}
				}
			}

			bone.m_Nodes.resize( joint_elem_count );
			{
				std::istringstream s( joint_names );
				for( int i = 0 ; i < joint_elem_count ; ++i )
					s >> bone.m_Nodes[i].m_Name;
			}

			std::vector<float> weight_ary( joint_weight_value_count );
			{
				std::istringstream s( joint_weights );
				for( int i = 0 ; i < joint_weight_value_count ; ++i )
					s >> weight_ary[i];
			}

			int v_ofs = 0;
			bone.m_Binds.resize( w_counts );
			for( int i = 0 ; i < w_counts ; ++i )
			{
				VertBondBinds& vb = bone.m_Binds[i];
				vb.m_Binds.resize( vcounts[i] );

				for( int j = 0 ; j < vcounts[i] ; ++j )
				{
					vb.m_Binds[j].m_JointIdx = v_ary[ v_ofs + 0 ];
					vb.m_Binds[j].m_Weight   = weight_ary[ v_ary[ v_ofs + 1 ] ];
					v_ofs += 2;
				}
			}
		}
	}
}


}
