#include "stdafx.h"
#include "ObjMesh.h"

#include <fstream>

#include <LibGeo/Mesh/BaseMesh.h>

#include "ObjMeshIO.h"
#include "ObjMeshWriter.h"

#include <C2/util/container_cast.h>



namespace lib_geo
{


//! ファイル読込
bool ObjMesh::Load( const std::string& filename )
{
	return ObjMeshReader().Load(*this, filename);
}

//! ファイル出力
bool ObjMesh::Save( const std::string& filename ) const
{
	return ObjMeshWriter().Save(*this, filename);
}

//! ストリームからの形状読み込み. マテリアルの読み込み機能は無し
bool ObjMesh::LoadStream(std::istream& ist)
{
	return ObjMeshReader().LoadStream(*this, ist);
}

//! ストリームへの形状出力
bool ObjMesh::SaveStream(std::ostream& ost) const
{
	return ObjMeshWriter().SaveStream(*this, ost);
}


//! BaseMeshに変換する
void ObjMesh::ConvertToBaseMesh(BaseMesh& o_mesh)
{
	ConvertToBaseMeshMain(o_mesh, false);
}

void ObjMesh::ConvertToBaseMeshSwap(BaseMesh& o_mesh)
{
	ConvertToBaseMeshMain(o_mesh, true);
}

void ObjMesh::ConvertToBaseMeshMain(BaseMesh& o_mesh, bool forSwap)
{
	o_mesh.Clear();

	CopyMaterials(o_mesh);

	if (forSwap)
		SwapVerts(o_mesh);
	else
		CopyVerts(o_mesh);

	CopyFaces(o_mesh);

	CopyPolyline(o_mesh);
}

void ObjMesh::ConvertToBaseMeshEachGroup(std::vector<BaseMesh>& o_mesh) const
{
	o_mesh.clear();

	size_t NumGroups = m_Groups.size();
	o_mesh.resize(NumGroups);

	if(NumGroups <= 0)
		return;

	std::vector< std::vector<size_t> > FidMap(NumGroups);
	std::vector< std::vector<size_t> > PolylineMap(NumGroups);
	for (size_t i = 0; i < m_Faces.size(); ++i)
	{
		FidMap[m_Faces[i].m_GroupIdx].push_back(i);
	}

	for (size_t i = 0; i < m_Polylines.size(); ++i)
	{
		PolylineMap[m_Polylines[i].m_GroupIdx].push_back(i);
	}

	for (size_t i = 0; i < NumGroups; ++i)
	{
		BaseMesh& bm = o_mesh[i];

		CopyMaterials(bm);
		CopyVerts(bm);
		CopyFaces(bm, FidMap[i]);
		CopyPolylines(bm, PolylineMap[i]);

		bm.RemoveNotReferencedVertex();

		bm.MergeLinkedPolylines();
	}
}

void ObjMesh::ConvertToBaseMeshEachObject(std::vector<BaseMesh>& o_mesh) const
{
	o_mesh.clear();

	size_t NumObjects = m_Objects.size();
	o_mesh.resize(NumObjects);

	for (size_t i = 0; i < NumObjects; ++i)
	{
		const SubObject& obj_info = m_Objects[i];
		BaseMesh& dst = o_mesh[i];

		CopyMaterials(dst);
		CopyVerts(dst);
		CopyFaces(dst, (size_t)obj_info.m_FaceRange.Offset, (size_t)obj_info.m_FaceRange.NumElems);

		// TODO : Polyline未対応

		dst.RemoveNotReferencedVertex();
	}
}

void ObjMesh::ConvertToBaseMeshSeparated( ObjSplitMode splitMode, std::vector<BaseMesh>& o_mesh ) const
{
	if (splitMode == ObjSplitMode::Object)
		ConvertToBaseMeshEachObject(o_mesh);
	else if (splitMode == ObjSplitMode::Group)
		ConvertToBaseMeshEachGroup(o_mesh);
}


const std::vector<ObjMaterial>* ObjMesh::GetPrimaryMaterials(void) const
{
	// TOOD : 複数マテリアルグループには未対応. マテリアルグループがある場合, 先頭の要素を使用する

	if( m_MaterialGroups.empty() )
		return NULL;

	const ObjMaterialGroup& top_group = m_MaterialGroups.front();
	return &top_group.m_Materials;
}

void ObjMesh::CopyVerts(BaseMesh& o_mesh) const
{
	o_mesh.m_Verts   = m_Verts;
	o_mesh.m_Col     = m_Col;
	o_mesh.m_Normals = m_Normals;
	o_mesh.m_UVs     = m_UVs;
}

void ObjMesh::SwapVerts(BaseMesh& o_mesh)
{
	o_mesh.m_Verts.swap(m_Verts);
	o_mesh.m_Col.swap(m_Col);
	o_mesh.m_Normals.swap(m_Normals);
	o_mesh.m_UVs.swap(m_UVs);
}

void ObjMesh::CopyMaterials(BaseMesh& o_mesh) const
{
	const std::vector<ObjMaterial>* materials = GetPrimaryMaterials();
	if (materials == NULL)
		return;

	o_mesh.m_Materials.resize( materials->size() );
	for (size_t i = 0; i < materials->size(); ++i)
	{
		CopyMaterial(o_mesh.m_Materials[i], materials->at(i));
	}
}

void ObjMesh::CopyMaterial(BaseMaterial& mat_dst, const ObjMaterial& mat_obj) const
{
	CopyColor(mat_dst.m_Ambient, mat_obj.m_Ambient);
	CopyColor(mat_dst.m_Diffuse, mat_obj.m_Diffuse);
	CopyColor(mat_dst.m_Specular, mat_obj.m_Specular);
	mat_dst.m_Shininess = mat_obj.m_Shininess;
	
	mat_dst.m_Ambient.a()  = mat_obj.m_Transparent;
	mat_dst.m_Diffuse.a()  = mat_obj.m_Transparent;
	mat_dst.m_Specular.a() = mat_obj.m_Transparent;
	mat_dst.m_Name         = mat_obj.m_Name;
}

void ObjMesh::CopyColor(lgr::color4f& col, const lm::vec3f& v_col) const
{
	col.set(v_col.x, v_col.y, v_col.z, 1.0f);
}

void ObjMesh::CopyFaces(BaseMesh& o_mesh) const
{
	CopyFaces(o_mesh, 0, m_Faces.size());
}

void ObjMesh::CopyFaces( BaseMesh& o_mesh , size_t offset , size_t num_faces ) const
{
	// objのマテリアル名に対するインデックスからマテリアルへのインデックスへの変換テーブル
	std::vector<int> MatNameIdxToID;
	CreateMatIndexMap(MatNameIdxToID);

	o_mesh.m_Faces.reserve( m_Faces.size() );
	size_t fidx_end = offset + num_faces;
	for (size_t i = offset; i <fidx_end; ++i)
	{
		const ObjFace& f = m_Faces[i];

		o_mesh.m_Faces.push_back(BaseFace());
		BaseFace& bf = o_mesh.m_Faces.back();

		CopyFaceIndexBuffer(bf, f);

		if (f.m_MatNameIdx == -1)
			bf.m_MatIdx = -1;
		else
			bf.m_MatIdx = MatNameIdxToID[f.m_MatNameIdx];
	}
}

void ObjMesh::CopyFaces(BaseMesh& o_mesh, const std::vector<size_t>& fids) const
{
	// objのマテリアル名に対するインデックスからマテリアルへのインデックスへの変換テーブル
	std::vector<int> MatNameIdxToID;
	CreateMatIndexMap(MatNameIdxToID);

	o_mesh.m_Faces.reserve(m_Faces.size());
	for (size_t i = 0; i < fids.size(); ++i)
	{
		const ObjFace& f = m_Faces[fids[i]];

		o_mesh.m_Faces.push_back(BaseFace());
		BaseFace& bf = o_mesh.m_Faces.back();

		CopyFaceIndexBuffer(bf, f);

		if (f.m_MatNameIdx == -1)
			bf.m_MatIdx = -1;
		else
			bf.m_MatIdx = MatNameIdxToID[f.m_MatNameIdx];
	}
}

void ObjMesh::CopyFaceIndexBuffer(BaseFace& bf, const ObjFace& of) const
{
	of.m_IdxV.ToVec(m_FaceBuf.m_FaceIdxV, bf.m_VertIds);
	of.m_IdxUV.ToVec(m_FaceBuf.m_FaceIdxUV, bf.m_UVIds);
	of.m_IdxN.ToVec(m_FaceBuf.m_FaceIdxN, bf.m_NormIds);
}

void ObjMesh::CopyPolylines(BaseMesh& o_mesh, const std::vector<size_t>& lids) const
{
	o_mesh.m_Polylines.reserve(m_Polylines.size());
	for (size_t i = 0; i < lids.size(); ++i)
	{
		const ObjPolyline& l = m_Polylines[lids[i]];

		o_mesh.m_Polylines.push_back(BasePolyline());
		BasePolyline& bl = o_mesh.m_Polylines.back();

		CopyPolylineIndexBuffer(bl, l);
	}
}

void ObjMesh::CopyPolyline(BaseMesh& o_mesh) const
{
	o_mesh.m_Polylines.resize(m_Polylines.size());
	for (size_t i = 0; i < m_Polylines.size(); ++i)
	{
		o_mesh.m_Polylines[i].m_PLVids = m_Polylines[i].m_IdxV;
	}
}

void ObjMesh::CopyPolylineIndexBuffer(BasePolyline& bl, const ObjPolyline& ol) const
{
	bl.m_PLVids = ol.m_IdxV;
}


//! マテリアルのインデックスからIDを求めるマップを生成する.
void ObjMesh::CreateMatIndexMap( std::vector<int>& MatNameIdxToID ) const
{
	MatNameIdxToID.resize( m_MaterialNames.size() , -1 );

	const std::vector<ObjMaterial>* materials = GetPrimaryMaterials();
	if (materials == NULL)
		return;

	// マテリアル名からその名称に対するインデックスを取得するためのテーブル
	std::map<std::string, int> name_to_id;
	for (size_t i = 0; i < materials->size(); ++i)
	{
		name_to_id[ materials->at(i).m_Name ] = (int)i;
	}

	// 各マテリアル名に対応するマテリアルidxを求める
	for (size_t i = 0; i < m_MaterialNames.size(); ++i)
	{
		std::map<std::string, int>::const_iterator found;
		found = name_to_id.find(m_MaterialNames[i]);
		if (found == name_to_id.end())
			MatNameIdxToID[i] = -1;
		else
			MatNameIdxToID[i] = found->second;
	}
}

ObjSplitMode ObjMesh::GetSeparateMode(void) const
{
	if (!m_Objects.empty())
		return ObjSplitMode::Object;
	else if (!m_Groups.empty())
		return ObjSplitMode::Group;

	return ObjSplitMode::None;
}


}
