#include "group.h"
#include "ShapeAlgorithms.h"
#include "vtkForCenos/topologyToVTK.h"
#include "ShapePartition.h"
#include <BRepBuilderAPI_Copy.hxx>
#include "cenos_exception.h"

// constructor accepts json with basic data as argument
// input json also has data on belonging entity names
// second argument is passed entities - the constructor picks entities from the name list and assigns to own entity list
Group::Group(json inp_json, std::vector<std::shared_ptr<TopoEntity>> p_entities)
{
	//name is assumed to be always there!
	name = inp_json.at("name").get<std::string>();

	if (inp_json.find("label") != inp_json.end())
		label = inp_json.at("label").get<std::string>();
	else
		label = name;

	if (inp_json.find("id") != inp_json.end())
		id = inp_json.at("id").get<int>();
	else
		id = 0;

	std::vector<std::string > entity_names;
	if (!inp_json.at("entities").get<std::vector<std::string>>().empty())
		entity_names = inp_json.at("entities").get<std::vector<std::string>>();

	if (inp_json.find("mergedFrom") != inp_json.end())
	{
		json merged_json = inp_json.at("mergedFrom");
		if (!merged_json.is_null())
		{
			for (const auto& item : merged_json.items())
			{
				std::shared_ptr<Group> ge = std::make_shared<Group>(item.value(), p_entities);
				merged_from.push_back(ge);
			}
		}
	}

	for (auto merged_group : merged_from)
	{
		for (auto ent : merged_group->getEntities())
		{
			if (std::find(entity_names.begin(), entity_names.end(), ent->getName()) == entity_names.end())
				entity_names.push_back(ent->getName());
		}
	}

	for (auto ent : p_entities)
	{
		if (std::find(entity_names.begin(), entity_names.end(), ent->getName()) != entity_names.end())
			entities.push_back(ent);
	}

	update();

	if (inp_json.find("role") != inp_json.end())
	{
		if (!inp_json.at("role").is_null())
			role = inp_json.at("role").get<std::string>();
		else
			role = "";
	}
	else
		role = "";


	needs_preview = true;
}

Group::Group()
{
	id = 0;
	name = "";
	label = "";
	role = "";
	needs_preview = true;
}

void Group::setName(const std::string n)
{
	name = n;
}

void Group::setRole(const std::string r)
{
	role = r;
}

void Group::setLabel(const std::string l)
{
	label = l;
}

int Group::getId()
{
	return id;
}

std::string Group::getName()
{
	return name;
}

std::string Group::getLabel()
{
	return label;
}

std::string Group::getRole()
{
	return role;
}

json Group::getJson()
{
	json outjson;
	outjson["name"] = name ;
	outjson["label"] = label;
	outjson["id"] = id;
	std::vector<std::string> entity_names;
	for (auto ent : entities)
		entity_names.push_back(ent->getName());

	outjson["entities"] = entity_names;
	outjson["role"] = role;
	outjson["previewPath"] = "vtk/" + name + ".vtk";
	json merged_from_json = json::array();
	for (auto mf : merged_from)
		merged_from_json.push_back(mf->getJson());
	outjson["mergedFrom"] = merged_from_json;
	return outjson;
}

std::vector<std::shared_ptr<TopoEntity>> Group::getEntities()
{
	return entities;
}

void Group::addEntity(std::shared_ptr<TopoEntity> ent)
{
	entities.push_back(ent);
}

void Group::setId(int i)
{
	id = i;
}

void Group::clearEntities()
{
	entities.clear();
}

void Group::writePreviewVTK(std::string wdir)
{
	if (needs_preview)
	{
		// make deep copy to prevent crash CP-853
		BRepBuilderAPI_Copy shape_copy(shape);
		TopoDS_Shape myResultShape = shape_copy.Shape();
		topologyToVTK::saveShapeAsVtk(myResultShape, wdir + "/vtk/" + name + ".vtk");
	}
}

void Group::update()
{
	if (entities.empty())
		throw(cenos_exception("There are no entities assigned to group " + name + ". This is internal error. Please contact support!"));

	ShapePartition part;
	for (auto ent : entities)
		part.addShape(ent->getShape());
	shape = part.getPartition();

	if (id == 0)
		id = entities[0]->getId();
}

ShapeStats Group::getShapeStatistics()
{
	ShapeStats stat;
	ShapeAlgorithms::getShapeStatistics(&shape, stat);
	return stat;
}

TopoDS_Shape Group::getShape()
{
	return shape;
}

void Group::setPreviewWriting(bool flag)
{
	needs_preview = flag;
}

std::vector<std::shared_ptr<Group>> Group::getMergedFrom()
{
	return merged_from;
}