#include "TopoEntity.h"
#include <TopExp_Explorer.hxx>
#include <BRep_Builder.hxx>
#include <BRepTools.hxx>
#include "easylogging++/easylogging++.h"
#include "ShapeAlgorithms.h"
#include "shapePartition.h"
#include "misc/miscFunctions.hpp"
#include "vtkForCenos/topologyToVTK.h"
#include "cenos_exception.h"

void TopoEntity::unsetIsindomain()
{
	isindomain = false;
}

void TopoEntity::setIsindomain()
{
	isindomain = true;
}

bool TopoEntity::getIsindomain()
{
	return isindomain;
}

TopoEntity::TopoEntity(TopoDS_Shape sh, std::string n, int i) : shape(sh), name(n), id(i)
{
	if (shape.IsNull())
	{
		std::string message = "TopoEntity::TopoEntity - empty shape passed as argument";
		throw cenos_exception(message);
	}
	ent_type = getShapeType(shape);
	brep_path = "";
	enabled = true;
	isindomain = false;
}

TopoEntity::TopoEntity(json inp, std::string wdir)
{
	TopoDS_Shape sh;
	BRep_Builder builder;
	name = inp.at("name").get<std::string>();
	if (inp.find("id") != inp.end())
		id = inp.at("id").get<int>();
	else
		id = 0;

	if (inp.at("path").get<std::string>() == "")
	{
		std::string message = "No shape path specified for entity " + name;
		throw cenos_exception(message);
	}

	std::string filename = wdir + "/" + inp.at("path").get<std::string>();
	if (!misc::fileExist(filename))
	{
		std::string message = "Brep file." + filename + "does not exist";
		throw cenos_exception(message);
	}

	BRepTools::Read(sh, filename.c_str(), builder);

	brep_path = inp.at("path").get<std::string>();
	ent_type = getShapeType(sh);
	shape = sh;
	enabled = true;
	isindomain = false;
}


TopoEntity::TopoEntity(const TopoEntity& that)
{
	id = that.id;
	name = that.name;
	shape = that.shape;
	brep_path = that.brep_path;
	ent_type = that.ent_type;
	enabled = that.enabled;
	isindomain = false;
}

TopoEntity::TopoEntity(const std::shared_ptr<TopoEntity> that) 
{
	id = that->id;
	name = that->name;
	shape = that->shape;
	brep_path = that->brep_path;
	ent_type = that->ent_type;
	enabled = that->enabled;
	isindomain = false;
}


ShapeType TopoEntity::getType()
{
	return ent_type;
}

std::string TopoEntity::getName()
{
	return name;
}

TopoDS_Shape TopoEntity::getShape()
{
	return shape;
}

int TopoEntity::getId()
{
	return id;
}

void TopoEntity::setId(int i)
{
	id = i;
}

void TopoEntity::setShape(TopoDS_Shape sh)
{
	shape = sh;
	ent_type = getShapeType(shape);
}

bool TopoEntity::isSubShapeOf(TopoDS_Shape sh)
{
	std::shared_ptr<TopoEntity> te = std::make_shared<TopoEntity>(sh, "");
	return isSubShapeOf(te);
}

// checks whether this entity is a subshape of passed entity
bool TopoEntity::isSubShapeOf(std::shared_ptr<TopoEntity> ent)
{
	if ((ent->getType() == ShapeType::SOLID))
	{
		if ((this->ent_type == ShapeType::FACE))
		{
			for (TopExp_Explorer explorer(ent->getShape(), TopAbs_ShapeEnum::TopAbs_FACE); explorer.More(); explorer.Next())
			{
				if (ShapeAlgorithms::have_equal_shapes(explorer.Current(), this->shape))
				{
					return true;
				}
			}
		}
		else if ((this->ent_type == ShapeType::EDGE))
		{
			for (TopExp_Explorer explorer(ent->getShape(), TopAbs_ShapeEnum::TopAbs_EDGE); explorer.More(); explorer.Next())
			{
				if (ShapeAlgorithms::have_equal_shapes(explorer.Current(), this->shape))
				{
					return true;
				}
			}
		}

	}
	else if ((ent->getType() == ShapeType::FACE)
		&& (this->ent_type == ShapeType::EDGE))
	{
		for (TopExp_Explorer explorer(ent->getShape(), TopAbs_ShapeEnum::TopAbs_EDGE); explorer.More(); explorer.Next())
		{
			if (ShapeAlgorithms::have_equal_shapes(explorer.Current(), this->shape))
			{
				return true;
			}
		}
	}
	return false;
}


bool TopoEntity::hasSharedEdges(std::shared_ptr<TopoEntity> ent)
{
	TopTools_ListOfShape edge_list;
	for (TopExp_Explorer edgeExplorer(ent->getShape(), TopAbs_ShapeEnum::TopAbs_EDGE); edgeExplorer.More(); edgeExplorer.Next())
	{
		TopoDS_Shape current = edgeExplorer.Current();
		edge_list.Append(current);
	}

	for (TopExp_Explorer explorer(this->shape, TopAbs_ShapeEnum::TopAbs_EDGE); explorer.More(); explorer.Next())
	{
		if (ShapeAlgorithms::have_equal_shapes(explorer.Current(), edge_list))
		{
			return true;
		}
	}
	return false;
}

bool TopoEntity::hasSharedFaces(std::shared_ptr<TopoEntity> ent)
{
	TopTools_ListOfShape face_list;
	for (TopExp_Explorer faceExplorer(ent->getShape(), TopAbs_ShapeEnum::TopAbs_FACE); faceExplorer.More(); faceExplorer.Next())
	{
		TopoDS_Shape current = faceExplorer.Current();
		face_list.Append(current);
	}

	for (TopExp_Explorer explorer(this->shape, TopAbs_ShapeEnum::TopAbs_FACE); explorer.More(); explorer.Next())
	{
		if (ShapeAlgorithms::have_equal_shapes(explorer.Current(), face_list))
		{
			return true;
		}
	}
	return false;
}



ShapeType TopoEntity::getShapeType(TopoDS_Shape sh)
{
	if (sh.ShapeType() == TopAbs_EDGE)
		return ShapeType::EDGE;
	else if (sh.ShapeType() == TopAbs_WIRE)
		return ShapeType::EDGE;
	else if (sh.ShapeType() == TopAbs_FACE)
		return ShapeType::FACE;
	else if (sh.ShapeType() == TopAbs_SOLID)
		return ShapeType::SOLID;
	else if (sh.ShapeType() == TopAbs_COMPOUND)
	{
		for (TopExp_Explorer explorer(sh, TopAbs_ShapeEnum::TopAbs_SOLID); explorer.More(); explorer.Next())
		{
			return ShapeType::SOLID;
		}

		for (TopExp_Explorer explorer(sh, TopAbs_ShapeEnum::TopAbs_FACE); explorer.More(); explorer.Next())
		{
			return ShapeType::FACE;
		}

		for (TopExp_Explorer explorer(sh, TopAbs_ShapeEnum::TopAbs_EDGE); explorer.More(); explorer.Next())
		{
			return ShapeType::EDGE;
		}

		return VERTEX;
	}

	return VERTEX;
}

int TopoEntity::getDimension()
{
	if (ent_type == EDGE)
		return 1;
	else if (ent_type == FACE)
		return 2;
	else if (ent_type == SOLID)
		return 3;
	else
		return 0;
}

json TopoEntity::getJson()
{
	json outjson;
	outjson["name"] = name;
	outjson["id"] = id;
	outjson["path"] = brep_path;
	return outjson;
}

//Ensure that folder exists!
void TopoEntity::writeBrep(std::string p)
{
	brep_path = "/geometry/raw/" + name + ".brep";
	std::string path = p + brep_path;
	BRepTools::Write(shape, path.c_str());
}


void TopoEntity::addLinkedEntity(std::shared_ptr<TopoEntity> te)
{
	linked_entities.push_back(te);
}


std::vector<std::shared_ptr<TopoEntity>> TopoEntity::getLinkedEntities()
{
	return linked_entities;
}

void TopoEntity::setEnabled(bool b)
{
	enabled = b;
}

bool TopoEntity::isEnabled()
{
	return enabled;
}
