#include "stdafx.h"

#include "MqoLoader.h"

#include <fstream>

#include <C2/util/string_util.h>

#include "../../Path.h"



namespace lib_geo
{


// ------------------------------------------------------------------------------------------------------------------------------------------------
// MqoElement

std::string MqoElement::GetValueName(void) const
{
	size_t i = m_str.find_first_of( "(" );
	if( i == m_str.npos )
		return m_str;
	else
		return m_str.substr( 0 , i );
}

void MqoElement::GetParams( StringAry& o_params ) const
{
	o_params.clear();
	size_t begin_idx = m_str.find_first_of("(");
	size_t end_idx   = m_str.find_last_of("(");
	if( begin_idx == m_str.npos || end_idx == m_str.npos ) return;
	std::string s = m_str.substr( begin_idx + 1 , end_idx - 1 );
	boost::algorithm::split( o_params , s , boost::is_any_of( " " ) );
}

void MqoElement::GetValueNameAndParams( std::string& o_value , StringAry& o_params ) const
{
	o_params.clear();
	size_t begin_idx = m_str.find_first_of("(");
	if( begin_idx == m_str.npos )
	{
		o_value = m_str;
	}
	else
	{
		o_value = m_str.substr( 0 , begin_idx );

		size_t end_idx = m_str.find_last_of(")");
		if( end_idx != m_str.npos )
		{
			std::string s = m_str.substr( begin_idx + 1 , end_idx - begin_idx - 1 );
			boost::algorithm::split( o_params , s , boost::is_any_of( " " ) );
		}
	}
}
	


// ------------------------------------------------------------------------------------------------------------------------------------------------
// MqoLineSeek

bool MqoLineSeek::LoadNext(void)
{
	if( m_ist.eof() ) return false;
	std::getline( m_ist , m_current_line );
	boost::trim( m_current_line );
	
	return true;
}

const std::string& MqoLineSeek::GetCurrent(void) const
{ 
	return m_current_line; 
}

std::string MqoLineSeek::GetTopToken(void) const
{
	size_t i = m_current_line.find_first_of( " " );
	if( i == m_current_line.npos )
		return m_current_line;
	else
		return m_current_line.substr( 0 , i );
}

void MqoLineSeek::SpaceSplit(StringAry& o_str_ary) const
{
	boost::algorithm::split( o_str_ary , m_current_line , boost::is_any_of(" ") );
}

void MqoLineSeek::ElementSplit(MqoElements& o_elems) const
{
	o_elems.clear();

	bool in_block = false;
	bool in_cyg   = false;
	size_t last_begin = 0;
	size_t last_end   = 0;
	for( size_t i = 0 ; i < m_current_line.size() ; ++i )
	{
		char c = m_current_line[i];
		if( in_block )
		{
			last_end = i+1;
			if( m_current_line[i] == ')' )
				in_block = false;
			in_cyg = false;
		}
		else
		{
			if( m_current_line[i] == '(' )
			{
				in_block = true;
			}
			else if( m_current_line[i] == ' ' )
			{
				if( in_cyg )
					last_end = i;
				in_cyg = false;
			}
			else
			{
				if( in_cyg == false )
				{
					if( last_begin != last_end )
						o_elems.push_back( MqoElement( m_current_line.substr( last_begin , last_end - last_begin ) ) );
					last_begin = i;
					last_end   = i;
				}
				in_cyg = true;
			}
		}
	}

	if( last_begin != last_end )
		o_elems.push_back( MqoElement( m_current_line.substr( last_begin ) ) );
}




// ------------------------------------------------------------------------------------------------------------------------------------------------
// MqoLoader

bool MqoLoader::Load( MqoObject& o_mqo , const std::string& filename )
{
	std::ifstream ist( filename.c_str() );
	if( ist.is_open() == false ) return false;

	if( !Load( o_mqo , ist ) )
		return false;

	std::string work_path = Path::GetParentDirPath(filename);

	for( size_t i = 0 ; i < o_mqo.m_Materials.size() ; ++i )
	{
		o_mqo.m_Materials[i].m_FileDirPath = work_path;

		util::String::Trim( o_mqo.m_Materials[i].m_TextureFileName , "\"" );
	}

	return true;
}

bool MqoLoader::Load( MqoObject& o_mqo , std::istream& ist )
{
	MqoLineSeek line_seek(ist);

	if( line_seek.LoadNext() == false ) return false;

	for(;;)
	{
		std::string top_token = line_seek.GetTopToken();

		if( top_token == "Eof" )  break;

		if      ( top_token == "Scene"    ) Load_Scene     ( o_mqo , line_seek );
		else if ( top_token == "Material" ) Load_Materials ( o_mqo , line_seek );
		else if ( top_token == "Object"   ) Load_Object    ( o_mqo , line_seek );

		if( line_seek.LoadNext() == false ) break;
	}

	InitNormals( o_mqo );

	return true;
}

void MqoLoader::Load_Scene( MqoObject& o_mqo , MqoLineSeek& line_seek )
{
	// 今のところSceneは利用しない
	for(;;)
	{
		if( line_seek.GetTopToken() == "}" ) break;
		if( line_seek.LoadNext() == false ) return;
	}
}

void MqoLoader::Load_Materials( MqoObject& o_mqo , MqoLineSeek& line_seek )
{
	// load root
	{
		StringAry vs;
		line_seek.SpaceSplit( vs );
		o_mqo.m_Materials.clear();
		o_mqo.m_Materials.reserve( boost::lexical_cast<size_t>( vs[1] ) );
		line_seek.LoadNext();
	}

	for(;;)
	{
		if( line_seek.GetTopToken() == "}" ) break;

		lib_graph::color4f col_base;
		o_mqo.m_Materials.push_back( MqoMaterial() );
		MqoMaterial& m = o_mqo.m_Materials.back();

		MqoElements elems;
		line_seek.ElementSplit( elems );
		for( size_t i = 0 ; i < elems.size() ; ++i )
		{
			MqoElement& elem = elems[i];
			std::string value_name;
			StringAry params;
			elem.GetValueNameAndParams( value_name , params );

			if( i == 0 )
			{
				m.m_Name = elems[i].m_str;
			}
			else if( value_name == "col" )
			{
				m.m_BaseColor.r() = boost::lexical_cast<float>( params[0] );
				m.m_BaseColor.g() = boost::lexical_cast<float>( params[1] );
				m.m_BaseColor.b() = boost::lexical_cast<float>( params[2] );
				m.m_BaseColor.a() = boost::lexical_cast<float>( params[3] );
			}
			else if( value_name == "dif" )
				m.m_Diffuse = boost::lexical_cast<float>( params[0] );
			else if( value_name == "amb" )
				m.m_Ambient = boost::lexical_cast<float>( params[0] );
			else if( value_name == "emi" )
				m.m_Emission = boost::lexical_cast<float>( params[0] );
			else if( value_name == "spc" )
				m.m_Specular = boost::lexical_cast<float>( params[0] );
			else if( value_name == "power" )
				m.m_Shininess = boost::lexical_cast<float>( params[0] );
			else if( value_name == "tex" )
				m.m_TextureFileName = params[0];
			else if( value_name == "aplane" )
				m.m_AlphaTextureFileName = params[0];
		}

		if( !line_seek.LoadNext() )
			return;
	}
}

void MqoLoader::Load_Object( MqoObject& o_mqo , MqoLineSeek& line_seek )
{
	// load top
	{
		StringAry vs;
		line_seek.SpaceSplit( vs );
		o_mqo.m_Meshs.push_back( MqoMesh() );
		o_mqo.m_Meshs.back().m_Name = vs[1];
	}

	if( line_seek.LoadNext() == false ) return;

	for(;;)
	{
		std::string top_token = line_seek.GetTopToken();
		if( top_token == "}" ) break;

		if      ( top_token == "vertex" ) Load_Vertices ( o_mqo.m_Meshs.back() , line_seek );
		else if ( top_token == "face"   ) Load_Faces    ( o_mqo.m_Meshs.back() , line_seek );

		if( line_seek.LoadNext() == false ) return;
	}
}

void MqoLoader::Load_Vertices( MqoMesh& o_obj , MqoLineSeek& line_seek )
{
	// load top
	{
		StringAry vs;
		line_seek.SpaceSplit( vs );
		o_obj.m_Vertices.clear();
		o_obj.m_Vertices.reserve( boost::lexical_cast<size_t>( vs[1] ) );
	}

	if( line_seek.LoadNext() == false ) return;

	for(;;)
	{
		if( line_seek.GetTopToken() == "}" ) break;

		StringAry v_pos;
		line_seek.SpaceSplit( v_pos );

		MqoVector vertex;
		vertex.x = boost::lexical_cast<float>( v_pos[0] );
		vertex.y = boost::lexical_cast<float>( v_pos[1] );
		vertex.z = boost::lexical_cast<float>( v_pos[2] );
		o_obj.m_Vertices.push_back( vertex );

		if( line_seek.LoadNext() == false ) return;
	}
}

void MqoLoader::Load_Faces( MqoMesh& o_obj , MqoLineSeek& line_seek )
{
	// load top
	{
		StringAry vs;
		line_seek.SpaceSplit( vs );
		o_obj.m_Faces.reserve( boost::lexical_cast<size_t>( vs[1] ) );
	}

	if( line_seek.LoadNext() == false ) return;

	for(;;)
	{
		if( line_seek.GetTopToken() == "}" ) break;

		MqoElements elems;
		line_seek.ElementSplit( elems );

		o_obj.m_Faces.push_back( MqoFace() );
		MqoFace& face = o_obj.m_Faces.back();

		face.m_NumVertex = (unsigned int)boost::lexical_cast<unsigned int>( elems[0].m_str );

		for( size_t i = 0 ; i < elems.size() ; ++i )
		{
			MqoElement& elem = elems[i];
			StringAry params;
			std::string value_name;
			elem.GetValueNameAndParams( value_name , params );

			if( value_name == "V" )
			{
				for( size_t j = 0 ; j < face.m_NumVertex ; ++j )
				{
					face.m_VertexIdx[j] = boost::lexical_cast<int>(params[j]);
				}
			}
			else if( value_name == "M" )
			{
				face.m_MaterialIdx = boost::lexical_cast<int>(params[0]);
			}
			else if( value_name == "UV" )
			{
				for( size_t j = 0 ; j < face.m_NumVertex ; ++j )
				{
					face.m_UV[j].x = boost::lexical_cast<float>(params[j*2+0]);
					face.m_UV[j].y = boost::lexical_cast<float>(params[j*2+1]);
				}
			}
		}

		if( line_seek.LoadNext() == false ) return;
	}
}

void MqoLoader::InitNormals( MqoObject& o_mqo )
{
	for( size_t i = 0 ; i < o_mqo.m_Meshs.size() ; ++i)
	{
		MqoMesh& m = o_mqo.m_Meshs[i];
		m.m_Normals.clear();
		m.m_Normals.resize( m.m_Vertices.size() );

		m.UpdateNormals();
	}
}

}

