#include "shapePartition.h"
#include <BRepAlgoAPI_BuilderAlgo.hxx>
#include <BRepTools.hxx>
#include <BRep_Builder.hxx>
#include <gp_Trsf.hxx>
#include <BRepBuilderAPI_Transform.hxx>
#include <STEPControl_Writer.hxx>
#include <TopExp_Explorer.hxx>
#include <GEOMAlgo_Gluer2.hxx>
#include "misc/miscFunctions.hpp"

ShapePartition::ShapePartition()
{
}

/// <summary>
/// ctor for initialization with entity list from GeometryData or MeshGenerator
/// </summary>
/// <param name="">entities - vector from GD or MG </param>
ShapePartition::ShapePartition(std::vector<TopoEntity_> entities)
{
    std::string CENOS_APP = misc::getCenosApp();

    int dim = 0;
    //get dimension
    for (auto ent : entities)
    {
        if (ent->getDimension() > dim)
            dim = ent->getDimension();
    }

    for (auto ent : entities)
    {
        if (!ent->isEnabled())
            continue;

        //antennas has surfaces that are quasi-domains
        // therefore in antennas entities have to be input for partition
        // in induction currently such scenario is not possible,
        // so we omit these shapes from partition.
        // it makes partition more stable.
        // TODO - discuss what to do in antennas to prevent partition failures.
        if (CENOS_APP == "ANTENNAS")
        {
            addShape(ent->getShape());
        }
        else
        {
            if (ent->getDimension() == dim)
                addShape(ent->getShape());
        }
    }
}



ShapePartition::~ShapePartition()
{
}

void ShapePartition::addShape(TopoDS_Shape shape)
{
    partition_shapes.Append(shape);
}

void ShapePartition::addShapeFromFile(std::string filename)
{
    TopoDS_Shape shape;
    BRep_Builder builder;
    BRepTools::Read(shape, filename.c_str(), builder);
    if (shape.IsNull())
        return;

    partition_shapes.Append(shape);

}

TopoDS_Shape ShapePartition::getPartition()
{
    if (partition_shapes.Size() == 1)
        return partition_shapes.First();

    reorderShapes();
    BRepAlgoAPI_BuilderAlgo builder;
    builder.SetRunParallel(true);
    builder.SetArguments(partition_shapes);
    builder.SetNonDestructive(true);
    builder.Build();

    return builder.Shape();
}


TopoDS_Shape ShapePartition::getGlue()
{
    if (partition_shapes.Size() == 1)
        return partition_shapes.First();

    BRep_Builder compbuilder;
    TopoDS_Compound comp;
    compbuilder.MakeCompound(comp);


    TopAbs_ShapeEnum highestShape = TopAbs_ShapeEnum::TopAbs_SHAPE;

    for (auto sh : partition_shapes)
    {
        if (sh.ShapeType() < highestShape)
            highestShape = sh.ShapeType();
    }

    for (auto sh : partition_shapes)
    {
        if (sh.ShapeType() == highestShape)
            compbuilder.Add(comp, sh);
    }

    GEOMAlgo_Gluer2 gluer;
    gluer.SetArgument(comp);
    gluer.SetTolerance(1e-7);
    gluer.SetKeepNonSolids(true);
    gluer.Detect();
    gluer.Perform();
    return gluer.Shape();
}

void ShapePartition::savePartitionAsStep(std::string filename)
{
    reorderShapes();
    BRepAlgoAPI_BuilderAlgo builder;
    builder.SetArguments(partition_shapes);
    builder.SetNonDestructive(true);
    builder.Build();
    if (builder.Shape().IsNull())
    {
        std::cout << "shape is null" << std::endl;
        return;
    }

    //writing for testing purposes
    STEPControl_Writer writer;
    writer.Transfer(builder.Shape(), STEPControl_AsIs);
    writer.Write(filename.c_str());

}

// OCC sometimes creates wrong partition result when edges are passed first
void ShapePartition::reorderShapes()
{
    TopTools_ListOfShape partition_shapes_copy;

    for (auto sh : partition_shapes)
    {
        if (sh.ShapeType() == TopAbs_SOLID)
            partition_shapes_copy.Append(sh);
    }

    for (auto sh : partition_shapes)
    {
        if (sh.ShapeType() == TopAbs_FACE)
            partition_shapes_copy.Append(sh);
    }

    for (auto sh : partition_shapes)
    {
        if (sh.ShapeType() == TopAbs_EDGE)
            partition_shapes_copy.Append(sh);
    }

    for (auto sh : partition_shapes)
    {
        if (sh.ShapeType() == TopAbs_VERTEX)
            partition_shapes_copy.Append(sh);
    }

    partition_shapes.Assign(partition_shapes_copy);
}