#include "stdafx.h"
#include "StlMesh.h"

#include "../BaseMesh.h"

#include <C2/util/stream_util.h>

#include <set>
#include <fstream>


namespace lib_geo
{


StlMesh::StlMesh(void)
{
	for( int i = 0 ; i < MSG_LENGTH ; ++i )
	{
		m_Message[i] = 0;
	}
}

void StlMesh::Clear(void)
{
	ClearMessage();
	m_Faces.clear();
}

void StlMesh::ClearMessage(void)
{
	for( int i = 0 ; i < MSG_LENGTH ; ++i )
	{
		m_Message[i] = 0;
	}
}

bool StlMesh::LoadBinary(std::istream& in)
{
	Clear();

	size_t fileSize = util::GetStreamSize(in);

	in.read(m_Message, MSG_LENGTH);

	unsigned int NumFaces;
	in.read((char*)&NumFaces , sizeof(unsigned int));

	const size_t expectedBinaryFileSize = NumFaces * 50 + 84;

	if (expectedBinaryFileSize != fileSize)
		return false;

	m_Faces.resize( NumFaces );
	for (size_t i = 0; i < NumFaces; ++i)
	{
		char padding[2] = { 0 , 0 };

		StlFace& f = m_Faces[i];
		in.read( (char*)f.m_Normal.v()   , sizeof(float)*3 );
		in.read( (char*)f.m_Verts[0].v() , sizeof(float)*3 );
		in.read( (char*)f.m_Verts[1].v() , sizeof(float)*3 );
		in.read( (char*)f.m_Verts[2].v() , sizeof(float)*3 );
		in.read( padding , 2 );
	}

	return true;
}

bool StlMesh::LoadText(std::istream& in)
{
	if (!IsExistValidAsciiHeader(in))
		return false;

	Clear();

	in.seekg( 0 , std::ios::beg );

	StlFace f;
	int vcount = 0;

	std::string ws;
	std::getline(in, ws);
	while(!in.eof())
	{
		std::getline(in, ws);
		if (ws.empty())
			break;

		std::istringstream is(ws);
		std::string s;
		is >> s;

		if (s == "facet")
		{
			lm::vec3f o(0.0f, 0.0f, 0.0f);
			f.m_Normal   = o;
			f.m_Verts[0] = o;
			f.m_Verts[1] = o;
			f.m_Verts[2] = o;
			vcount = 0;

			lm::vec3f& n = f.m_Normal;
			is >> n.x >> n.y >> n.z;
		}
		else if (s == "endloop")
		{
			m_Faces.push_back(f);
		}
		else if (s == "vertex")
		{
			lm::vec3f& v = f.m_Verts[vcount++];
			is >> v.x >> v.y >> v.z;
		}
		else if (s == "endfacet")
		{
		}
		else if (s == "outer")
		{
		}
		else if (s == "endsolid")
		{
		}
		else
		{
			return false;
		}
	}

	return true;
}

bool StlMesh::IsExistValidAsciiHeader(std::istream& in)
{
	in.read( m_Message , MSG_LENGTH );

	m_Message[5] = '\0';
	std::string top = m_Message;
	ClearMessage();

	return top == "solid";
}

bool StlMesh::Load(std::istream& ist)
{
	if (LoadBinary(ist))
		return true;

	ist.seekg(0, std::ios::beg);

	if (LoadText(ist))
		return true;

	return false;

}

bool StlMesh::Load( const std::string& i_Filename )
{
	std::ifstream ifs( i_Filename.c_str() , std::ios::binary );
	if( !ifs.is_open() )
		return false;

	return Load(ifs);
}

bool StlMesh::Save( std::ostream& ost ) const
{
	unsigned int NumFaces = (unsigned int)m_Faces.size();
	ost.write( m_Message , MSG_LENGTH );
	ost.write( (char*)&NumFaces , sizeof(unsigned int) );

	for( size_t i = 0 ; i < NumFaces ; ++i )
	{
		char padding[2] = { 0 , 0 };

		ost.write( (char*)m_Faces[i].m_Normal.v()   , sizeof(float)*3 );
		ost.write( (char*)m_Faces[i].m_Verts[0].v() , sizeof(float)*3 );
		ost.write( (char*)m_Faces[i].m_Verts[1].v() , sizeof(float)*3 );
		ost.write( (char*)m_Faces[i].m_Verts[2].v() , sizeof(float)*3 );
		ost.write( padding , 2 );
	}

	return true;
}

bool StlMesh::Save( const std::string& i_Filename ) const
{
	std::ofstream ofs( i_Filename.c_str() , std::ios::binary );
	if( !ofs.is_open() )
		return false;

	return Save( ofs );
}

bool StlMesh::ConvertToBaseMesh( BaseMesh& o_mesh ) const
{
	o_mesh.Clear();

	o_mesh.m_Verts.resize( m_Faces.size() * 3 );
	o_mesh.m_Normals.resize( m_Faces.size() );
	o_mesh.m_Faces.resize( m_Faces.size() );

	for( size_t i = 0 ; i < m_Faces.size() ; ++i )
	{
		const StlFace& f = m_Faces[i];

		o_mesh.m_Verts[ i * 3 + 0 ] = f.m_Verts[0];
		o_mesh.m_Verts[ i * 3 + 1 ] = f.m_Verts[1];
		o_mesh.m_Verts[ i * 3 + 2 ] = f.m_Verts[2];

		o_mesh.m_Normals[ i ] = f.m_Normal;

		o_mesh.m_Faces[i].m_VertIds.resize(3);
		o_mesh.m_Faces[i].m_NormIds.resize(3);

		o_mesh.m_Faces[i].m_VertIds[0] = (int)(i * 3 + 0);
		o_mesh.m_Faces[i].m_VertIds[1] = (int)(i * 3 + 1);
		o_mesh.m_Faces[i].m_VertIds[2] = (int)(i * 3 + 2);

		o_mesh.m_Faces[i].m_NormIds[0] = (int)i;
		o_mesh.m_Faces[i].m_NormIds[1] = (int)i;
		o_mesh.m_Faces[i].m_NormIds[2] = (int)i;
	}

	return true;
}


//! サイズ0の法線を含む場合にtrueを返す
bool StlMesh::ExistZeroLengthNormal(void) const
{
	for( size_t i = 0 ; i < m_Faces.size() ; ++i )
	{
		if( m_Faces[i].m_Normal.is_zero() )
			return true;
	}

	return false;
}


}
