#include "StdAfx.h"
#include "GeometryRender.h"

#include <LibQtGeoViewerCore/LazyDisp.h>
#include <LibQtGeoViewerCore/IndexColor.h>

#include <C2/gl/OpenGLUT/OpenGlutStringExt.h>
#include <C2/gl/MaterialSetter.h>
#include <C2/gl/GlGeometryFunctions.h>

#include <C2/graph/MaterialSamples.h>

#include <LibQtGeoViewerCore/VBOBuilder.h>

#include "Shader/ConstantShader.h"

using namespace lib_gl;
using namespace std;



GeometryRender::GeometryRender(void)
{
	m_Config = NULL;
}

GeometryRender::~GeometryRender(void)
{
	ReleaseAccBuffer();
}


void GeometryRender::InitializeRender(View3DConfig* config, ShaderLibrary* shaders)
{
	m_Config = config;
	m_Shaders = shaders;

	m_LastConfig = *config;
}

void GeometryRender::ReleaseAccBuffer(void)
{
	m_DispList_FaceWire.ReleaseList();
	m_DispList_Vert.ReleaseList();
	m_DispList_Vid.ReleaseList();
	m_DispList_Fid.ReleaseList();

	m_FaceGroupVBO.clear();
}


void GeometryRender::DrawSelVertAll(GeomObject& obj)
{
	glPushAttrib(GL_LINE_BIT | GL_POINT_BIT | GL_ENABLE_BIT);
	glDisable(GL_LIGHTING);
	glPointSize((float)m_Config->m_PointSize + 4.0f);
	glLineWidth(1.0f);

	obj.CreateAdjBufOnce();
	lib_geo::BaseMesh& m = obj.m_Mesh;

	for (auto& j : obj.m_SelVerts)
	{
		if (!j.second)
			continue;

		glColor3d(1,0.25,1);

		int vert_idx = j.first;
		if(vert_idx >= m.m_Verts.size())
			continue;

		const lm::vec3f& v0 = m.m_Verts[vert_idx];
		const lm::vec3f& n = m.m_VertAdj[vert_idx].m_NormalAvg;
		const lm::vec3f v1 = v0 + n * m_Config->m_IndexLineLen;

		glDrawSegment(v0, v1);
		glDrawPoint(v1);

		if(m_Config->m_ShowSelVertCoord || m_Config->m_ShowSelVertIdx)
		{
			std::ostringstream s;

			if(m_Config->m_ShowSelVertIdx)
				s << " " << vert_idx;

			if(m_Config->m_ShowSelVertCoord)
				s << " (" << v1.x << " , " << v1.y << " , " << v1.z << ")";

			glutBitmapString3f(v1, s.str().c_str());
		}
	}

	glPopAttrib();
}

void GeometryRender::DrawGeomVertPick(GeomObject& obj, SceneMain& scene)
{
	if( !IsVisibleObject(scene, obj) )
		return;

	if(!obj.m_VertexOnlyMode)
	{
		if(!m_Config->m_DrawVert)
			return;
	}

	const lib_geo::BaseMesh& mesh = obj.m_Mesh;

	for( size_t i = 0 ; i < mesh.m_Verts.size() ; ++i )
	{
		glPushName((GLuint)i);
		glDrawPoint(mesh.m_Verts[i]);
		glPopName();
	}
}

//! 選択中オブジェクトに輪郭を描画する
void GeometryRender::DrawGeomHighlight(GeomObject& obj, SceneMain& scene)
{
	const lib_geo::BaseMesh& mesh = obj.m_Mesh;

	glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_LINE_BIT);
	glDisable(GL_LIGHTING);
	glEnable(GL_STENCIL_TEST);

	glStencilFunc(GL_NOTEQUAL, obj.m_ObjectIndex + 1, ~0);

	glColor3d(1, 0.5, 0);
	glLineWidth(3.0f);
	for (const lib_geo::BaseFace& f : mesh.m_Faces)
	{
		glBegin(GL_LINE_LOOP);
		for (int vid : f.m_VertIds)
		{
			glVertex3fv(mesh.m_Verts[vid].v());
		}
		glEnd();
	}

	glPopAttrib();
}

//! ジオメトリをサーフェス描画する.
void GeometryRender::DrawMeshMain(GeomObject& obj, SceneMain& scene, ShaderInterface* shader)
{
	if(obj.m_VertexOnlyMode)
		return;

	if(m_Config->m_DrawFace && m_Config->m_DrawWire)
	{
		DrawMeshFaceMain(obj, scene, shader, MR_FILL);
		DrawMeshFaceMain(obj, scene, shader, MR_WIRE_OVERLAY);
	}
	else if(m_Config->m_DrawFace)
	{
		DrawMeshFaceMain(obj, scene, shader, MR_FILL);
	}
	else if(m_Config->m_DrawWire)
	{
		DrawMeshFaceMain(obj, scene, shader, MR_WIRE_SHADE);
	}
}

void GeometryRender::DrawMeshFaceMain(GeomObject& obj, SceneMain& scene, ShaderInterface* shader, MeshRenderMode mode)
{
	glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_POLYGON_BIT | GL_LIGHTING_BIT);

	if(m_Config->m_HighlightSelected)
	{
		glEnable(GL_STENCIL_TEST);

		glStencilFunc(GL_ALWAYS, obj.m_ObjectIndex + 1, ~0);
		glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
	}

	static const float OFFSET_UNIT = 0.8f;
	static const float O_1 = OFFSET_UNIT * 1.0f;
	static const float O_2 = OFFSET_UNIT * 2.0f;

	if(mode == MR_FILL)
	{
		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
		glEnable(GL_POLYGON_OFFSET_FILL);
		glPolygonOffset(O_2, O_2);
	}
	else
	{
		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
		glEnable(GL_POLYGON_OFFSET_LINE);
		glPolygonOffset(O_1, O_1);
	}

	if(mode == MR_WIRE_OVERLAY)
	{
		glDisable(GL_LIGHTING);
		glDepthFunc(GL_LEQUAL);

		glColor3ubv(m_Config->m_WireColor.v());
		DrawFace_WireConstantColor(obj, scene);
	}
	else
	{
		DrawFace_Fill(obj, scene, shader);
	}

	glPopAttrib();
}


void GeometryRender::DrawFace_WireConstantColor(GeomObject& obj, SceneMain& scene)
{
	if(m_Config->m_EnableWireVBO)
		DrawFace_WireConstantColorVBO(obj, scene);
	else
		DrawFace_WireConstantColorRawOrDispList(obj, scene);
}

void GeometryRender::DrawFace_WireConstantColorForCS(GeomObject& obj, SceneMain& scene)
{
	// 断面描画時は三角形化は関係ないので, Faceの設定を使う
	if(m_Config->m_EnableFaceVBO)
		DrawFace_WireConstantColorVBO(obj, scene);
	else
		DrawFace_WireConstantColorRawOrDispList(obj, scene);
}

void GeometryRender::DrawFace_WireConstantColorVBO(GeomObject& obj, SceneMain& scene)
{
	lib_geo::BaseMesh& mesh = obj.m_Mesh;
	if(mesh.m_Verts.empty())
		return;

	ConstantShader* shader = m_Shaders->GetConstantShader();
	if(shader == NULL)
		return;

	shader->BeginShader();

	glDisable(GL_LIGHTING);

	std::map<int, FaceGroup>::iterator it;
	for(it = obj.m_FaceMatGroup.begin(); it != obj.m_FaceMatGroup.end(); ++it)
	{
		const FaceGroup& fg = it->second;
		if(fg.IsEmpty())
			continue;

		DrawFaceGroupGeometry(mesh, fg, GL_POLYGON);
	}

	shader->EndShader();
}

void GeometryRender::DrawFace_WireConstantColorRawOrDispList(GeomObject& obj, SceneMain& scene)
{
	LazyDisp ld(m_DispList_FaceWire);

	if(m_Config->m_EnableWireDispList)
	{
		if(ld.CallOrBeginList() == LazyDisp::Called)
			return;
	}

	lib_geo::BaseMesh& mesh = obj.m_Mesh;

	for (const lib_geo::BaseFace& f : mesh.m_Faces)
	{
		glBegin(GL_POLYGON);
		for (int vid : f.m_VertIds)
		{
			glVertex3fv( mesh.m_Verts[vid].v() );
		}
		glEnd();
	}
}

void GeometryRender::DrawFace_Fill(GeomObject& obj, SceneMain& scene, ShaderInterface* shader)
{
	lib_geo::BaseMesh& mesh = obj.m_Mesh;
	if(mesh.m_Verts.empty())
		return;

	if(shader == NULL)
		return;

	shader->BeginShader();
	shader->SetDublesideEnabled(m_Config->m_DoubleSideShading);
	shader->SetLightingEnabled(m_Config->m_EnableLighting);
	shader->SetFlatShade(m_Config->m_EnableFlatShade);

	if(m_Config->m_DoubleSideShading)
		glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 1);

	std::map<int, FaceGroup>::iterator it;
	for(it = obj.m_FaceMatGroup.begin(); it != obj.m_FaceMatGroup.end(); ++it)
	{
		const FaceGroup& fg = it->second;
		if(fg.IsEmpty())
			continue;

		bool IsHilightMat = false;
		if(m_Config->m_HighlightMaterial)
		{
			bool SelObj = (obj.m_ObjectIndex == scene.m_SelectedObjectIdx);
			bool Selmat = (fg.m_GroupID == scene.m_SelectedMaterialIdx);
			IsHilightMat = (SelObj && Selmat);
		}

		const lib_geo::BaseFace& top_face = mesh.m_Faces[fg.GetTopFid()];

		AssignMaterialToFace(scene, obj, shader, top_face, IsHilightMat);

		DrawFaceGroupGeometry(mesh, fg, GL_TRIANGLE_FAN);
	}

	shader->EndShader();
}

GeometryVBO* GeometryRender::GetOrCreateVBO(lib_geo::BaseMesh& mesh, const FaceGroup& fg)
{
	std::map<int, GeometryVBO>::iterator it;
	it = m_FaceGroupVBO.find(fg.m_GroupID);
	if(it != m_FaceGroupVBO.end())
		return &it->second;

	GeometryVBO* vbo = &m_FaceGroupVBO[fg.m_GroupID];
	VBOBuilder builder;
	builder.CreateFromFaceGroup(vbo, mesh, fg);

	return vbo;
}

//! 指定したフェースグループの形状描画を実行
void GeometryRender::DrawFaceGroupGeometry(lib_geo::BaseMesh& mesh, const FaceGroup& fg, GLenum primitive_type)
{
	if(m_Config->m_EnableFaceVBO)
	{
		GetOrCreateVBO(mesh, fg)->DrawVBOAll(GL_TRIANGLES, true);
		return;
	}

	for (int fid : fg.m_Fids)
	{
		lib_geo::BaseFace& f = mesh.m_Faces[fid];
		glBegin(primitive_type);

		for(size_t j = 0; j < f.m_VertIds.size(); ++j)
		{
			if(f.HasUV())
				glTexCoord2fv( mesh.m_UVs[ f.m_UVIds[j] ].v() );

			if(f.HasNormal())
				glNormal3fv( mesh.m_Normals[ f.m_NormIds[j] ].v() );

			glVertex3fv( mesh.m_Verts[ f.m_VertIds[j] ].v() );
		}

		glEnd();
	}
}

void GeometryRender::AssignMaterialToFace(SceneMain& scene, GeomObject& obj, ShaderInterface* shader, const lib_geo::BaseFace& f, bool IsHilightMat) const
{
	AssinTextures(scene, obj, shader, f);
	AssignMatcap(scene, obj, shader, f);
	AssignShadowmap(scene, obj, shader, f);

	const lib_graph::Material* mat = GetFaceMaterial(scene, obj, f);

	if(IsHilightMat)
	{
		lib_graph::Material sel_mat = *mat;
		sel_mat.m_Emission.set(0.5f, 0.0f, 0.0f, 1.0f);
		lib_gl::MaterialSetter::SetGL( sel_mat );

		glColor4d(1,0.5,0.5,1);
	}
	else
	{
		lib_gl::MaterialSetter::SetGL( *mat );
	}
}

void GeometryRender::AssinTextures(SceneMain& scene, GeomObject& obj, ShaderInterface* shader, const lib_geo::BaseFace& f) const
{
	if (!m_Config->m_EnableTexture)
	{
		glDisable(GL_TEXTURE_2D);
		shader->SetTextureEnabled(false);
		return;
	}

	gl::GlTexture* tex = tex = GetFaceTexture(scene, obj, f);

	if(tex == NULL)
	{
		glDisable(GL_TEXTURE_2D);
		shader->SetTextureEnabled(false);
		return;
	}

	glEnable(GL_TEXTURE_2D);
	tex->BindGL();

	glColor4d(1,1,1,1);
	shader->SetTextureEnabled(true);
}

gl::GlTexture* GeometryRender::GetFaceTexture(SceneMain& scene, GeomObject& obj, const lib_geo::BaseFace& f) const
{
	if (obj.HasTexture())
	{
		geom::GeomTextureSet* texture = obj.GetTextureFromFace(f);
		if (texture == NULL)
			return NULL;

		gl::GlTexture* ctex = texture->TexColor;
		if (ctex == NULL)
			return NULL;

		if (!ctex->HasTextureObject())
			return NULL;

		return ctex;
	}
	else
	{
		if(!m_Config->m_UseFixTexture)
			return NULL;

		if(!scene.m_DefaultTexture.HasTextureObject())
			return NULL;

		return &scene.m_DefaultTexture;
	}
}

void GeometryRender::AssignMatcap(SceneMain& scene, GeomObject& obj, ShaderInterface* shader, const lib_geo::BaseFace& f) const
{
	geom::GeomTextureSet* texture = obj.GetTextureFromFace(f);
	if (texture != NULL)
	{
		if (texture->TexMatcap.HasImage())
		{
			shader->SetMatcap(&texture->TexMatcap);
			return;
		}
	}

	if (scene.m_MatcapImg.HasImage())
	{
		shader->SetMatcap(&scene.m_MatcapImg);
		return;
	}

	shader->SetMatcap(NULL);
}

void GeometryRender::AssignShadowmap(SceneMain& scene, GeomObject& obj, ShaderInterface* shader, const lib_geo::BaseFace& f) const
{
}

const lib_graph::Material* GeometryRender::GetFaceMaterial(SceneMain& scene, const GeomObject& geom, const lib_geo::BaseFace& f) const
{
	if(m_Config->m_IndexMaterial)
	{
		static bool mat_initialized = false;
		static lib_graph::Material index_mats[6];

		if (!mat_initialized)
		{
			for(int i = 0; i < 6; ++i)
			{
				index_mats[i] = lib_graph::MaterialSamples::GetSilver();
				index_mats[i].m_Diffuse = IndexColor::GetColor(i);
			}
			mat_initialized = true;
		}

		return &index_mats[geom.m_ObjectIndex % 6];
	}

	if(m_Config->m_UseFixMaterial)
		return &scene.m_DefaultMaterial;

	int mat_idx = f.m_MatIdx;
	if(0 <= mat_idx && mat_idx < (int)geom.m_Mesh.m_Materials.size())
	{
		return &geom.m_Mesh.m_Materials[mat_idx];
	}
	return &scene.m_DefaultMaterial;
}


void GeometryRender::DrawFaceIndexColor(GeomObject& obj, SceneMain& scene, unsigned int idx)
{
	if(obj.m_VertexOnlyMode)
		return;

	lib_geo::BaseMesh& mesh = obj.m_Mesh;
	if(mesh.m_Verts.empty())
		return;

	if(!m_Config->m_DrawFace && m_Config->m_DrawWire)
		return;

	glPushAttrib(GL_POLYGON_BIT | GL_ENABLE_BIT);
	glDisable(GL_LIGHTING);
	glDisable(GL_MULTISAMPLE);
	glDisable(GL_POLYGON_SMOOTH);
	glDisable(GL_LINE_SMOOTH);
	if(m_Config->m_DrawFace)
		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
	else
		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

	SetGlIndexColor(idx);

	std::map<int, FaceGroup>::iterator it;
	for(it = obj.m_FaceMatGroup.begin(); it != obj.m_FaceMatGroup.end(); ++it)
	{
		const FaceGroup& fg = it->second;
		if(fg.IsEmpty())
			continue;

		DrawFaceGroupGeometry(mesh, fg, GL_TRIANGLE_FAN);
	}

	glPopAttrib();
}

void GeometryRender::SetGlIndexColor(unsigned int idx) const
{
	glColor3ub( ((idx)&0xFF), (((idx)>>8)&0xFF), (((idx)>>16)&0xFF) );

	//// for test
	//switch(idx)
	//{
	//case 0 :return glColor3d(1,0,0);
	//case 1 :return glColor3d(0,1,0);
	//case 2 :return glColor3d(0,0,1);
	//default:
	//	return;
	//}
}


void GeometryRender::SetGLNormalColor(const lm::vec3f& n) const
{
	glColor3f( (n.x + 1.0f) * 0.5f, (n.y + 1.0f) * 0.5f, (n.z + 1.0f) * 0.5f);
}


void GeometryRender::DrawMeshExtAll(GeomObject& obj)
{
	lib_geo::BaseMesh& mesh = obj.m_Mesh;

	glPushAttrib(GL_ENABLE_BIT | GL_LINE_BIT | GL_POINT_BIT);
	glDisable(GL_LIGHTING);

	if(obj.m_VertexOnlyMode || m_Config->m_DrawVert)
	{
		glPointSize((float)m_Config->m_PointSize);

		glColor3d(0,1,1);

		DrawVertPoint(mesh);

		if(m_Config->m_DrawVid)
		{
			glPushAttrib(GL_ENABLE_BIT);

			if( m_Config->m_ShowVidTopMost )
				glDisable(GL_DEPTH_TEST);

			glLineWidth(1.0f);
			DrawVertVid(obj);

			glPopAttrib();
		}
	}

	if(m_Config->m_DrawVertNormal)
	{
		glLineWidth(1.0f);

		glColor3d(0, 0.5, 1);

		float LineLen = m_Config->m_NormalLength * obj.GetBoundingBox().max_length();
		DrawVertNormal(mesh, LineLen);
	}

	if(m_Config->m_DrawFid)
		DrawMeshFid(mesh);

	glPopAttrib();
}

void GeometryRender::DrawMeshFid(lib_geo::BaseMesh& mesh)
{
	glColor3d(1,1,0);
	glPointSize((float)m_Config->m_PointSize);

	if( m_Config->m_ShowVidTopMost )
		glDisable(GL_DEPTH_TEST);

	lib_gl::GlDisplayList& disp_list = m_DispList_Fid;
	if(disp_list.IsEnable())
	{
		disp_list.CallList();
	}
	else
	{
		disp_list.GenerateList();
		disp_list.BeginList(GL_COMPILE_AND_EXECUTE);

		for( size_t i = 0 ; i < mesh.m_Faces.size() ; ++i )
		{
			const lib_geo::BaseFace& f = mesh.m_Faces[i];
			lm::vec3f c = mesh.GetFaceCenter(f);
			lm::vec3f n = mesh.GetFaceNormal(f);

			lm::vec3f p = c + n * m_Config->m_IndexLineLen;

			glDrawSegment(c, p);
			glDrawPoint(c);
			glutBitmapStringVal3f(p, i);
		}

		disp_list.EndList();
	}
}

void GeometryRender::DrawVertPoint(const lib_geo::BaseMesh& mesh)
{
	LazyDisp ld(m_DispList_Vert);
	if(ld.CallOrBeginList() == LazyDisp::Called)
		return;

	glBegin(GL_POINTS);
	for (const lm::vec3f& v : mesh.m_Verts)
	{
		glVertex3fv(v.v());
	}
	glEnd();
}

void GeometryRender::DrawVertVid(GeomObject& obj)
{
	obj.CreateAdjBufOnce();
	lib_geo::BaseMesh& m = obj.m_Mesh;

	LazyDisp ld(m_DispList_Vid);
	if(ld.CallOrBeginList() == LazyDisp::Called)
		return;

	for(size_t i = 0; i < m.m_Verts.size(); ++i)
	{
		const lm::vec3f& v0 = m.m_Verts[i];
		const lm::vec3f& n = m.m_VertAdj[i].m_NormalAvg;
		const lm::vec3f& v1 = v0 + n * m_Config->m_IndexLineLen;

		std::ostringstream s;
		s << " " << i;

		glDrawSegment(v0, v1);
		glutBitmapString3f(v1, s.str().c_str());
	}
}

void GeometryRender::DrawVertNormal(const lib_geo::BaseMesh& mesh, float LineLen)
{
	for (const lib_geo::BaseFace& f : mesh.m_Faces)
	{
		for( size_t j = 0 ; j < f.m_NormIds.size() ; ++j )
		{
			int vi = f.m_VertIds[j];
			int ni = f.m_NormIds[j];

			const lm::vec3f& v = mesh.m_Verts[vi];
			const lm::vec3f& n = mesh.m_Normals[ni];
			glDrawSegment(v, v + n * LineLen);
		}
	}
}


bool GeometryRender::IsVisibleObject(const SceneMain& scene, const GeomObject& obj) const
{
	if(m_Config->m_ShowOnlySelect)
	{
		return (scene.GetPrimaryObject() == &obj);
	}
	else
	{
		return obj.m_Visible;
	}
}
