#include "StdAfx.h"
#include "CrossSection.h"

#include "Algorithm/Geom3d.h"
#include "Algorithm/ConvexHullGen.h"



namespace lib_geo
{


void CrossSection::Clear(void)
{
	m_Sections.clear();
}

void CrossSection::CreateCrossSection(const BaseMesh& mesh, const Plane& cut_plane, CrossSectionMode mode)
{
	Clear();
	m_CutPlane = cut_plane;

	std::vector<bool> IsVisited( mesh.m_Faces.size() , false );
	for (size_t i = 0; i < mesh.m_Faces.size(); ++i)
	{
		if(mode == CS_WHOLE)
		{
			if (m_Sections.empty())
				m_Sections.resize(m_Sections.size() + 1);
		}
		else
		{
			if (m_Sections.empty() || !m_Sections.back().m_Segments.empty())
				m_Sections.resize(m_Sections.size() + 1);
		}

		VisitFace(mesh, IsVisited, (int)i);
	}

	if (m_Sections.empty())
		return;

	if (m_Sections.back().m_Segments.empty())
		m_Sections.pop_back();

	for (CrossSectionUnit& s : m_Sections)
	{
		s.UpdateParams(cut_plane);
	}
}

void CrossSection::CreateCrossSectionWhole(const BaseMesh& mesh, const Plane& cut_plane)
{
	CreateCrossSection(mesh, cut_plane, CS_WHOLE);
}

//! 連続する区間ごとに断面を求める
void CrossSection::CreateCrossSectionEachGroup(const BaseMesh& mesh, const Plane& cut_plane)
{
	CreateCrossSection(mesh, cut_plane, CS_EACH_GROUP);
}

void CrossSection::VisitFace(const BaseMesh& mesh, std::vector<bool>& IsVisited, int fid)
{
	if (IsVisited[fid])
		return;

	IsVisited[fid] = true;

	const BaseFace& f = mesh.m_Faces[fid];
	int top_vid = f.m_VertIds[0];

	int top_eids[2] = {-1, -1};
	int top_ev[2] = {-1, -1};
	{
		int top_ev_count = 0;
		for (size_t i = 0; i < f.m_AdjEids.size(); ++i)
		{
			int adj_eid = f.m_AdjEids[i];
			const BaseEdge& e = mesh.m_Edges[adj_eid];

			int ev0 = e.m_EdgeVids[0];
			int ev1 = e.m_EdgeVids[1];
			if (ev0 == top_vid)
				top_ev[top_ev_count] = ev1;
			else if (ev1 == top_vid)
				top_ev[top_ev_count] = ev0;
			else
				continue;

			top_eids[top_ev_count] = adj_eid;
			top_ev_count++;
		}
	}

	for (size_t i = 0; i < f.m_AdjEids.size(); ++i)
	{
		CSFace cf;
		{
			int adj_eid = f.m_AdjEids[i];
			const BaseEdge& e = mesh.m_Edges[adj_eid];

			int ev0 = e.m_EdgeVids[0];
			int ev1 = e.m_EdgeVids[1];
			if (ev0 == top_vid || ev1 == top_vid)
				continue;

			cf.edges[0].Set(top_vid, ev0, -1);
			cf.edges[1].Set(ev0, ev1, adj_eid);
			cf.edges[2].Set(ev1, top_vid, -1);

			for (int j = 0; j < 2; ++j)
			{
				if (ev0 == top_ev[j])
					cf.edges[0].eid = top_eids[j];
				if (ev1 == top_ev[j])
					cf.edges[2].eid = top_eids[j];
			}

			cf.parent_fid = fid;
		}

		CSCache cc;
		cc.CalcFace(mesh, cf, m_CutPlane);

		// ポリゴンの三辺が断面に乗っている
		if (cc.NumEdgeOnface >= 2)
			return;

		if (cc.NumEdgeOnface == 1)
		{
			// 1辺が断面に乗っている
			VistNextOnTouchToEdge(cc, mesh, cf, IsVisited);
		}
		else if (cc.NumEdgeInter == 3)
		{
			// 1点が断面上にあり(2辺が断面とちょうど接触している),
			// 残りの辺が断面と交差している
			VistNextOnTouchToVert(cc, mesh, cf, IsVisited);
		}
		else if (cc.NumEdgeInter == 2)
		{
			// 2辺が断面と交差している
			VistNextOnIntersectStd(cc, mesh, cf, IsVisited);
		}
	}
}

void CrossSection::VistNextOnTouchToEdge(CSCache& cc, const BaseMesh& mesh, CSFace& cf, std::vector<bool>& IsVisited)
{
	int lid;
	cc.GetLid(lid);

	const CSEdge& ce = cf.edges[lid];
	if (ce.eid != -1)
	{
		const BaseEdge& be = mesh.m_Edges[ce.eid];
		for (size_t i = 0; i < be.m_AdjFids.size(); ++i)
			IsVisited[be.m_AdjFids[i]] = true;
	}

	SectionSegment s;
	s.p0 = mesh.m_Verts[ce.vid0];
	s.p1 = mesh.m_Verts[ce.vid1];
	s.fid = cf.parent_fid;
	m_Sections.back().m_Segments.push_back( s );

	VisitVert(mesh, IsVisited, ce.vid0);
	VisitVert(mesh, IsVisited, ce.vid1);
}

void CrossSection::VistNextOnTouchToVert(CSCache& cc, const BaseMesh& mesh, CSFace& cf, std::vector<bool>& IsVisited)
{
	int base_leid;
	int sub_leid0, sub_leid1;
	cc.GetCloseDist(base_leid, sub_leid0, sub_leid1);

	SectionSegment s;
	s.p0 = cc.InterPos[base_leid];
	s.p1 = cc.InterPos[sub_leid0];
	s.fid = cf.parent_fid;
	m_Sections.back().m_Segments.push_back( s );

	VisitEdge(mesh, IsVisited, cf.edges[base_leid].eid);
	const CSEdge& e0 = cf.edges[sub_leid0];
	const CSEdge& e1 = cf.edges[sub_leid1];

	if (false) {}
	else if (e0.vid0 == e1.vid0) VisitVert(mesh, IsVisited, e0.vid0);
	else if (e0.vid0 == e1.vid1) VisitVert(mesh, IsVisited, e0.vid0);
	else if (e0.vid1 == e1.vid0) VisitVert(mesh, IsVisited, e0.vid1);
	else if (e0.vid1 == e1.vid1) VisitVert(mesh, IsVisited, e0.vid1);
}

void CrossSection::VistNextOnIntersectStd(CSCache& cc, const BaseMesh& mesh, CSFace& cf, std::vector<bool>& IsVisited)
{
	int leid0, leid1;
	cc.GetTwoEdgeCS(leid0, leid1);

	SectionSegment s;
	s.p0 = cc.InterPos[leid0];
	s.p1 = cc.InterPos[leid1];
	s.fid = cf.parent_fid;
	m_Sections.back().m_Segments.push_back( s );

	VisitEdge(mesh, IsVisited, cf.edges[leid0].eid);
	VisitEdge(mesh, IsVisited, cf.edges[leid1].eid);
}

void CrossSection::VisitEdge(const BaseMesh& mesh, std::vector<bool>& IsVisited, int eid)
{
	if (eid == -1)
		return;

	const BaseEdge& e = mesh.m_Edges[eid];
	for (size_t i = 0; i < e.m_AdjFids.size(); ++i)
	{
		VisitFace(mesh, IsVisited, e.m_AdjFids[i]);
	}
}

void CrossSection::VisitVert(const BaseMesh& mesh, std::vector<bool>& IsVisited, int vid)
{
	const VertAdj& v = mesh.m_VertAdj[vid];
	for (size_t i = 0; i < v.m_AdjFids.size(); ++i)
	{
		VisitFace(mesh, IsVisited, v.m_AdjFids[i]);
	}
}


}
