#include "EOFMagnetTemplate.h"
#include "EOFBlockTemplate.h"


#include <GC_MakeSegment.hxx>
#include <BRepBuilderAPI_MakeEdge.hxx>
#include <BRepBuilderAPI_MakeWire.hxx>
#include <BRepBuilderAPI_MakeFace.hxx>
#include <BRepPrimAPI_MakeBox.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Wire.hxx>
#include <TopoDS_Shell.hxx>
#include <TopoDS_Solid.hxx>
#include <TopoDS_Vertex.hxx>
#include <TopoDS_Face.hxx>
#include <TopTools_MapOfShape.hxx>
#include <BRepAlgoAPI_BuilderAlgo.hxx>
#include <BRepAlgoAPI_Cut.hxx>
#include <STEPControl_Writer.hxx>
#include <TopExp_Explorer.hxx>
#include <GProp_GProps.hxx>
#include <BRepGProp.hxx>
#include <BRepPrimAPI_MakeSphere.hxx>
#include <BRepPrimAPI_MakeCylinder.hxx>
#include <BRepBndLib.hxx>
#include <map>
#include <filesystem>
#include "easylogging++/easylogging++.h"
#include <TopTools_IndexedMapOfShape.hxx>
#include "topology/ShapeAlgorithms.h"
#include "vtkForCenos/topologyToVTK.h"
#include "misc/miscFunctions.hpp"
#include "mesh/MeshGenerator.h"
#include "mesh/AutoMeshSize.h"
#include <math.h> 


namespace nglib {
#include "nglib.h"
}


EOFMagnetTemplate::EOFMagnetTemplate(std::string path) : BasicTemplate(path)
{
    type = MAGNET;
}

void EOFMagnetTemplate::parseInputJSON(json* j)
{
    json& data = *j;
    name = data["name"].get<std::string>();
    half_length = data["properties"]["length"].get<double>()/2.0;
    magnet_radius = data["properties"]["radius"].get<double>()/2.0;
    d_l = data["properties"]["d_l"];
    d_w = data["properties"]["d_w"];
    d_h = data["properties"]["d_h"];
    angular_velocity = data["properties"]["w"];
    angle = data["properties"]["a"];   
}




TopoDS_Solid EOFMagnetTemplate::makeMagnet()
{

    gp_Pnt ptTop = gp_Pnt( d_w , d_l - sin(angle * 3.14159 / 180) * half_length, d_h - cos(angle * 3.14159 / 180) * half_length);
    gp_Dir vz = gp_Dir(0, 1 * sin(angle * 3.14159 / 180), 1 * cos(angle * 3.14159 / 180));
    gp_Dir vx = gp_Dir(1, 0, 0);

    gp_Ax2 axis_top = gp_Ax2(ptTop, vz, vx);
    TopoDS_Solid top_cylinder = BRepPrimAPI_MakeCylinder(axis_top, magnet_radius, half_length*2);
    return top_cylinder;
}


void EOFMagnetTemplate::generateGeometry()
{
    if (progress != nullptr)
        progress->sendLog("Generating magnet geometry ");

    TopoDS_Shape magnet = makeMagnet();
    TopoEntity_ magnet_ent = std::make_shared<TopoEntity>(magnet, "magnet_ent");
    magnet_ent->setEnabled(true);
    geom_data->addEntity(magnet_ent);

    DomainGroup_ magnet_group = std::make_shared<DomainGroup>();
    magnet_group->setName("magnet");
    magnet_group->setLabel("Magnet");
    magnet_group->setRole("magnet");
    magnet_group->addEntity(magnet_ent);
    magnet_group->update();
    geom_data->addDomainGroup(magnet_group);

}

void EOFMagnetTemplate::assignMeshSizes(MeshGenerator_ mg, GeometryData_ gd, double mesh_density_factor, double global_max)
{
    // Manual meshing part. Automatic meshing works inconsistently for dipole antenna template.
    // Will switch back to AMS when it is fixed
    // THIS PART IS EMPIRICAL! There is no theory behind element sizes

    double maxh_global = global_max;
    double minh_global = std::min(magnet_radius, magnet_radius);

    //refinement on faces
    std::shared_ptr<MeshParameters> mp1 = std::make_shared<MeshParameters>();
    double maxh_local = std::min(global_max / 2, magnet_radius);
    double minh_local = std::min(magnet_radius, magnet_radius) / 2;

    if (minh_local * 1.5 > maxh_local)
        minh_local = minh_local / 2;

    mp1->setMaxH(maxh_local * mesh_density_factor);
    mp1->setMinH(minh_local * mesh_density_factor);

    // set mp1 for all wire and feed entities

    auto magnet_group = gd->getDomainByName("magnet");

    for (auto ent : magnet_group->getEntities())
    {
        mg->setParameters(mp1, ent);
        for (auto subent : gd->getEntities())
        {
            if (subent->getType() != ShapeType::FACE)
                continue;
            if (subent->isSubShapeOf(ent))
                mg->setParameters(mp1, subent);
        }
    }

}


bool EOFMagnetTemplate::validate(std::string& msg)
{

    const int MAX_RATIO = 350;
    int ratio = (int)(((2 * half_length) / magnet_radius) / 2);

    if (ratio > MAX_RATIO)
    {
        msg = "The diameter to length ratio is too large(1:" + std::to_string(ratio) + ").Please do not exceed 1:" + std::to_string(MAX_RATIO);
        return false;
    }


    if (half_length <= 0 )
    {
        msg = "Magnet length should be positive number.";
        return false;
    }

   

    if (magnet_radius <= 0)
    {
        msg = "Magnet radius should be positive number.";
        return false;
    }

    

    return true;
}
