#pragma once

#include <iostream>
#include <vector>
#include <string>

#include <C2/lm/vector2.h>
#include <C2/lm/vector3.h>

#include <C2/graph/material.h>

#include "ObjPrimitive.h"

#include <LibGeo/Mesh/SubObject.h>



enum class ObjSplitMode
{
	None,
	Object,
	Group,
};


namespace lib_geo
{


class BaseMesh;
class BaseFace;
class BasePolyline;
class BaseMaterial;


class ObjGroupInfo
{
public:
	std::string m_Name;
};


//! Obj形式のマテリアル
class ObjMaterial
{
public:
	ObjMaterial(void)
	{
		m_FileDirPath.clear();
		m_Name.clear();
		m_TextureFileName.clear();
		m_NormalMapFileName.clear();

		m_Ambient.set( 0.0f , 0.0f , 0.0f );
		m_Diffuse.set( 0.0f , 0.0f , 0.0f );
		m_Specular.set( 0.0f , 0.0f , 0.0f );
		m_Shininess   = 1.0f;
		m_Ni          = 0.0f;
		m_Transparent = 1.0f;
		m_Illum       = 0;
	}

	void Clear(void)
	{
		ObjMaterial default_mat;
		(*this) = default_mat;
	}

	std::string GetTextureFilePath(void) const
	{
		return GetAbsPath(m_TextureFileName);
	}

	std::string GetNormalMapFilePath(void) const
	{
		return GetAbsPath(m_NormalMapFileName);
	}

	std::string GetAbsPath(const std::string& path) const
	{
		if (path.empty())
			return "";

		if (IsAbsolutePath(path))
			return path;

		return m_FileDirPath + path;
	}

	bool IsAbsolutePath(const std::string& path) const
	{
		if (path.size() <= 2)
			return false;

		if (path[1] == ':')
			return true;

		if (path[0] == '\\' && path[1] == '\\')
			return true;

		return false;
	}

public:
	std::string   m_Name;               //!< 読み込み元mtlファイル名
	std::string   m_FileDirPath;        //!< 読み込み元mtlが保存されているディレクトリパス
	std::string   m_TextureFileName;    //!< テクスチャファイル名
	std::string   m_NormalMapFileName;  //!< 法線マップテクスチャファイル名

	lm::vec3f     m_Ambient;
	lm::vec3f     m_Diffuse;
	lm::vec3f     m_Specular;
	float         m_Shininess;
	float         m_Ni;
	float         m_Transparent;
	int           m_Illum;
};


//! Obj形式マテリアルセットファイルの情報
class ObjMaterialGroup
{
public:
	std::string               m_Filename;
	std::vector<ObjMaterial>  m_Materials;
};


//! Obj形式のメッシュ
class ObjMesh
{
public:
	ObjMesh(void)
	{}

	void Clear(void)
	{
		m_Verts.clear();
		m_Normals.clear();
		m_UVs.clear();
		m_Faces.clear();
		m_FaceBuf.Clear();

		m_Objects.clear();
		m_MaterialNames.clear();

		m_MaterialGroups.clear();
	}

	bool Load( const std::string& i_Filename );
	bool Save( const std::string& i_Filename ) const;
	bool LoadStream( std::istream& ist );
	bool SaveStream( std::ostream& ost ) const;

	void ConvertToBaseMesh( BaseMesh& o_mesh );
	void ConvertToBaseMeshSwap( BaseMesh& o_mesh );
	void ConvertToBaseMeshEachGroup( std::vector<BaseMesh>& o_mesh ) const;
	void ConvertToBaseMeshEachObject( std::vector<BaseMesh>& o_mesh ) const;
	void ConvertToBaseMeshSeparated( ObjSplitMode splitMode, std::vector<BaseMesh>& o_mesh ) const;

	void CreateMatIndexMap( std::vector<int>& MatNameIdxToID ) const;

	ObjSplitMode GetSeparateMode(void) const;

protected:
	void ConvertToBaseMeshMain(BaseMesh& o_mesh, bool forSwap);

	const std::vector<ObjMaterial>* GetPrimaryMaterials(void) const;

	void CopyVerts( BaseMesh& o_mesh ) const;
	void SwapVerts( BaseMesh& o_mesh );

	void CopyMaterials( BaseMesh& o_mesh ) const;
	void CopyMaterial( BaseMaterial& mat_dst , const ObjMaterial& mat_obj ) const;
	void CopyColor(lgr::color4f& col, const lm::vec3f& v_col) const;

	void CopyFaces(BaseMesh& o_mesh) const;
	void CopyPolyline(BaseMesh& o_mesh) const;
	void CopyFaces(BaseMesh& o_mesh, size_t offset, size_t num_faces) const;
	void CopyFaces(BaseMesh& o_mesh, const std::vector<size_t>& fids) const;

	void CopyPolylines(BaseMesh& o_mesh, const std::vector<size_t>& lids) const;
	void CopyPolylineIndexBuffer(BasePolyline& bl, const ObjPolyline& ol) const;

	void CopyFaceIndexBuffer(BaseFace& bf, const ObjFace& of) const;

public:
	std::vector<lm::vec3f> m_Verts;
	std::vector<lgr::color3f> m_Col;
	std::vector<lm::vec3f> m_Normals;
	std::vector<lm::vec2f> m_UVs;

	std::vector<ObjFace> m_Faces;
	ObjFaceBuf m_FaceBuf;

	std::vector<ObjPolyline> m_Polylines;

	std::vector<SubObject>    m_Objects;
	std::vector<ObjGroupInfo> m_Groups;
	std::vector<std::string>  m_MaterialNames;

	std::vector<ObjMaterialGroup> m_MaterialGroups;
};


}
