#include "ShapeRepair.h"
#include <TopExp_Explorer.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Edge.hxx>

#include <BRep_Tool.hxx>
#include <BRepTools.hxx>
#include <BRepTools_ReShape.hxx>
#include <BRepCheck_Analyzer.hxx>
#include <ShapeFix_Wireframe.hxx>
#include <ShapeFix_Shape.hxx>
#include "easylogging++/easylogging++.h"



bool ShapeRepair::is_valid(const TopoDS_Shape shape) {
	BRepCheck_Analyzer analyzer(shape, true);
	return analyzer.IsValid();
}

TopoDS_Shape ShapeRepair::fix_basic(const TopoDS_Shape shape) {
	Handle(ShapeFix_Shape) shapeFix = new ShapeFix_Shape(shape);
	shapeFix->Perform();
	TopoDS_Shape resultShape = shapeFix->Shape();
	return resultShape;
}

TopoDS_Shape ShapeRepair::fix_basic_wireframe(const TopoDS_Shape shape) {
	Handle(ShapeFix_Wireframe) shapeFixWireframe = new ShapeFix_Wireframe(shape);
	shapeFixWireframe->FixSmallEdges();
	shapeFixWireframe->FixWireGaps();
	TopoDS_Shape resultShape = shapeFixWireframe->Shape();
	return resultShape;
}

std::vector<TopoDS_Shape> ShapeRepair::get_degenerated_edges(const TopoDS_Shape shape) {
	std::vector<TopoDS_Shape> degenerate_edges;
	for (TopExp_Explorer edge_explorer(shape, TopAbs_ShapeEnum::TopAbs_EDGE); edge_explorer.More(); edge_explorer.Next())
	{
		TopoDS_Shape edge = edge_explorer.Current();
		if (BRep_Tool::Degenerated(TopoDS::Edge(edge))) {
			degenerate_edges.push_back(edge);
		}
	}
	return degenerate_edges;
}

TopoDS_Shape ShapeRepair::remove_subshapes(TopoDS_Shape shape, const std::vector<TopoDS_Shape> subshapes) {
	BRepTools_ReShape reshape;
	for (TopoDS_Shape subShape : subshapes) {
		reshape.Remove(subShape);
	}
	TopoDS_Shape resultShape = reshape.Apply(shape);
	return resultShape;
}

TopoDS_Shape ShapeRepair::pre_process_cad(TopoDS_Shape shape) {
	// Any modifications should be done only if nescessary

	if (is_valid(shape)) {
		LOG(INFO) << "pre_process_cad: Shape is valid, it will not be modified" << std::endl;
		return shape;
	}

	std::cout << "Some issues have been detected for the imported geometry. See log for details" << std::endl;
	LOG(INFO) << "pre_process_cad: Shape is invalid, beginning repair" << std::endl;
	shape = fix_basic(shape);
	if (ShapeRepair::is_valid(shape)) {
		LOG(INFO) << "pre_process_cad: Shape after fix_basic is valid" << std::endl;
		return shape;
	}
	LOG(INFO) << "pre_process_cad: resultShape after fix_basic is not valid" << std::endl;


	shape = fix_basic_wireframe(shape);
	if (ShapeRepair::is_valid(shape)) {
		LOG(INFO) << "pre_process_cad: Shape after fix_basic_wireframe is valid" << std::endl;
		return shape;
	}
	LOG(INFO) << "pre_process_cad: Shape after ShapeFix_Wireframe is not valid" << std::endl;



	std::vector<TopoDS_Shape> degenerated_edges = get_degenerated_edges(shape);
	if (!degenerated_edges.empty()) {
		// We will remove the degenerate edges
		remove_subshapes(shape, degenerated_edges);
	}
	if (ShapeRepair::is_valid(shape)) {
		LOG(INFO) << "pre_process_cad: Shape after removing degenerated_edges is valid" << std::endl;
		return shape;
	}

	LOG(INFO) << "pre_process_cad: Shape after removing degenerated_edges is invalid" << std::endl;
	shape = fix_basic(shape);
	if (ShapeRepair::is_valid(shape)) {
		LOG(INFO) << "pre_process_cad: Shape after fix_basic is valid" << std::endl;
		return shape;
	}
	LOG(INFO) << "pre_process_cad: resultShape after fix_basic is not valid, no further modifications will be done" << std::endl;
	return shape;
}
