#include "StdAfx.h"
#include "MeshBuf.h"

#include <map>
using namespace std;

#include <C2/graph/MaterialSamples.h>

#include "GeomObject.h"



namespace geom
{


void MeshBuf::CreateAdjBufOnce(void)
{
	if (m_Mesh.HasVertAdj())
		return;

	m_Mesh.CreateAdjBuffers();
}

bool MeshBuf::IsValidMaterialIdx(int idx) const
{
	return !(idx < 0 || (int)m_Mesh.m_Materials.size() <= idx);
}

void MeshBuf::ClearTexture(void)
{
	for (GeomTextureSet& t : m_Textures)
	{
		t.ReleaseAll();
	}
	m_Textures.clear();
}

void MeshBuf::CreateFaceMatGroup(void)
{
	m_FaceMatGroup.clear();

	for(size_t i = 0; i < m_Mesh.m_Faces.size(); ++i)
	{
		int mat_idx = m_Mesh.m_Faces[i].m_MatIdx;

		FaceGroup& mg = m_FaceMatGroup[mat_idx];
		mg.m_GroupID = mat_idx;
		mg.m_Fids.push_back((int)i);
	}
}

void MeshBuf::ClearSelect(void)
{
	m_SelVerts.clear();
}

void MeshBuf::ClearAllBuf(void)
{
	m_Mesh.Clear();

	ClearTexture();

	ClearSelect();

	m_FaceMatGroup.clear();

	m_VertLinkers.clear();

	m_Bones.Clear();
}

bool MeshBuf::HasTexture(void) const
{
	return !m_Textures.empty();
}

GeomTextureSet* MeshBuf::GetTextureFromFace(const lib_geo::BaseFace& f)
{
	return GetTexture(f.m_MatIdx);
}

GeomTextureSet* MeshBuf::GetTexture(int idx)
{
	if (m_Textures.empty())
		return NULL;

	if (!IsValidMaterialIdx(idx))
		return NULL;

	return &m_Textures[idx];
}

GeomTextureSet* MeshBuf::GeSeltTexture(void)
{
	int idx = GetSelMatIdx();
	if (idx == -1)
		return NULL;

	return GetTexture(idx);
}

lib_geo::BaseMaterial* MeshBuf::GetMaterial(int idx)
{
	return &m_Mesh.m_Materials[idx];
}

lib_geo::BaseMaterial* MeshBuf::GetSelMaterial(void)
{
	int idx = GetSelMatIdx();
	if (idx == -1)
		return NULL;

	return GetMaterial(idx);
}

void MeshBuf::CreateTextureBuf(size_t size)
{
	m_Textures.clear();
	for (size_t i = 0; i < size; ++i)
	{
		m_Textures.push_back(new GeomTextureSet(this));
	}
}

void MeshBuf::UpdateBBox(void)
{
	m_BBox = m_Mesh.CalcAABB();
}

void MeshBuf::ResetIniBBox(void)
{
	m_BBoxIni = m_BBox;
}


void MeshBuf::FlipFace(bool normal_only)
{
	for (lm::vec3f& n : m_Mesh.m_Normals)
	{
		n *= -1.0f;
	}

	if (normal_only)
		return;

	for (lib_geo::BaseFace& f : m_Mesh.m_Faces)
	{
		std::reverse(f.m_VertIds.begin(), f.m_VertIds.end());
		std::reverse(f.m_NormIds.begin(), f.m_NormIds.end());
		std::reverse(f.m_UVIds.begin(), f.m_UVIds.end());
		std::reverse(f.m_AdjEids.begin(), f.m_AdjEids.end());
	}

	for (lib_geo::VertAdj& a : m_Mesh.m_VertAdj)
	{
		std::reverse(a.m_AdjEids.begin(), a.m_AdjEids.end());
		std::reverse(a.m_AdjFids.begin(), a.m_AdjFids.end());
		a.m_NormalAvg *= -1.0f;
	}
}


void MeshBuf::SwapVertSelect(int vidx)
{
	map<int, bool>::iterator i;
	i = m_SelVerts.find(vidx);
	if (i == m_SelVerts.end())
		m_SelVerts[vidx] = true;
	else
		m_SelVerts.erase(i);
}

void MeshBuf::SetVertSelect(int vidx, bool select)
{
	map<int, bool>::iterator i;
	i = m_SelVerts.find(vidx);
	if(select)
	{
		if(i == m_SelVerts.end())
			m_SelVerts[vidx] = true;
	}
	else
	{
		if(i != m_SelVerts.end())
			m_SelVerts.erase(i);
	}
}

bool MeshBuf::GetVertSelect(int vidx) const
{
	map<int, bool>::const_iterator i;
	i = m_SelVerts.find(vidx);
	if(i == m_SelVerts.end())
		return false;

	return i->second;
}


void MeshBuf::UpdateCrossSection(bool SplitGroup, const lib_geo::Plane& cutplane)
{
	lib_geo::BaseMesh& m = m_Mesh;

	if (!m.HasEdge())
	{
		m.CreateEdgeFromFace();
		m.CreateAdjBuffers();
	}

	if (SplitGroup)
		m_CrossSection.CreateCrossSectionEachGroup(m, cutplane);
	else
		m_CrossSection.CreateCrossSectionWhole(m, cutplane);
}


void MeshBuf::InitializeBuffer(void)
{
	lib_geo::BaseMesh& base_mesh = m_Mesh;

	// create uv
	if (!base_mesh.HasUV())
		base_mesh.CreateUVsEachVerts();

	if (base_mesh.m_Materials.empty())
	{
		base_mesh.m_Materials.push_back(lib_graph::MaterialSamples::GetSilver());
		for (size_t i = 0; i < base_mesh.m_Faces.size(); ++i)
		{
			base_mesh.m_Faces[i].m_MatIdx = 0;
		}

		CreateTextureBuf(1);
	}

	UpdateBBox();
	ResetIniBBox();

	CreateFaceMatGroup();
}


void MeshBuf::Triangulate(void)
{
	m_Mesh.Triangulate();

	CreateFaceMatGroup();
}


void MeshBuf::InitColorTexture(int idx, const std::string& filepath, const std::string& name, const gl::TextureConfig& config)
{
	m_Textures[idx].TexColor = GetInitTexture(filepath, name, config);
}

void MeshBuf::InitNormalTexture(int idx, const std::string& filepath, const std::string& name, const gl::TextureConfig& config)
{
	m_Textures[idx].TexNormal = GetInitTexture(filepath, name, config);
}

gl::GlTexture* MeshBuf::GetInitTexture(const std::string& filepath, const std::string& name, const gl::TextureConfig& config)
{
	if (filepath.empty())
		return NULL;

	TextureLib& tex_lib = m_Parent->m_TextureLib;

	gl::GlTexture* tex = tex_lib.FindTexture(filepath);
	if (tex != NULL)
		return tex;

	gl::GlTexture* ctex = tex_lib.LoadFromFile(filepath);
	if (ctex == NULL)
		return NULL;

	ctex->InitializeTexture();
	ctex->SetTextureGLAndReleaseImage(config);
	ctex->SetName(name);

	return ctex;
}

void MeshBuf::ReleaseTextureUnit(int mat_idx, TextureType type)
{
	assert(IsValidMaterialIdx(mat_idx));
	GeomTextureSet& tex = m_Textures[mat_idx];
	ReleaseTextureUnit(&tex, type);
}

void MeshBuf::ReleaseTextureUnit(GeomTextureSet* ts, TextureType type)
{
	gl::GlTexture* tex_buf = NULL;
	if (type == TextureType::Color)
		std::swap(tex_buf, ts->TexColor);
	else if (type == TextureType::Normal)
		std::swap(tex_buf, ts->TexNormal);

	if (!IsExistTextureLink(tex_buf))
		tex_buf->FinalizeTexture();
}

bool MeshBuf::IsExistTextureLink(gl::GlTexture* tex)
{
	for (GeomTextureSet& ts : m_Textures)
	{
		if (ts.TexColor == tex)
			return true;
		if (ts.TexNormal == tex)
			return true;
	}

	return false;
}


GeomObject* MeshBuf::GetParent(void)
{
	return m_Parent;
}

const GeomObject* MeshBuf::GetParent(void) const
{
	return m_Parent;
}


//! MeshBufのIDをオブジェクト, メッシュのインデックスから作成
int MeshBuf::GetDecWholeIdx(void) const
{
	int oidx = GetParent()->GetObjectIndex();
	return oidx * 0x10000 + m_MBufIdx;
}

int MeshBuf::DecodeWholeIdx_Obj(int id)
{
	return id / 0x10000;
}

int MeshBuf::DecodeWholeIdx_Mesh(int id)
{
	return id % 0x10000;
}


bool MeshBuf::IsVisible(void) const
{
	if (!m_Visible)
		return false;
	if (!GetParent()->m_Visible)
		return false;

	return true;
}


// マテリアルが非選択であれば, 適当(=先頭)に選択
void MeshBuf::SelectMatAuto(void)
{
	if (m_Mesh.m_Materials.empty())
		return;

	if (IsMatSelected())
		return;

	SetSelMatIdx(0);
}

bool MeshBuf::IsMatSelected(void) const
{
	return GetSelMatIdx() != -1;
}

int MeshBuf::GetSelMatIdx(void) const
{
	if (!IsValidMaterialIdx(m_SelMatIdx))
		return -1;

	return m_SelMatIdx;
}

void MeshBuf::SetSelMatIdx(int mat_idx)
{
	m_SelMatIdx = mat_idx;
}

bool MeshBuf::IsSelectedMat(int mat_idx) const
{
	return m_SelMatIdx == mat_idx;
}


}
