#pragma once
#include <TopoDS.hxx>
#include "TopoEntity.h"
#include "domainGroup.h"
#include "boundaryGroup.h"
#include "base/base.h"
#include "nlohmann/json.hpp"
#include "mesh/MeshStats.hpp"
#include "ShapeStats.hpp"
using json = nlohmann::json;
class GeometryData
{
public:

	//pass raw geometry json and working directory
	//used to read incoming GD from FreeCAD and UI
	GeometryData(json input_json, std::string w);

	GeometryData(std::string w);

	GeometryData();

	GeometryData(GeometryData&);

	enum Mode {
		GROUPS_FROM_PREPROCESSOR,
		GROUPS_FROM_ALL_ENTITIES
	};

	void readInput(json);
	void readOperations(json);

	//used to return GD in format for UI
	json getJson() const;

	LengthUnit getLengthUnit();

	void setLengthUnit(LengthUnit u);

	void setMeshFile(std::string);

	std::vector<std::shared_ptr<DomainGroup>> getDomains() const;
	std::vector<std::shared_ptr<BoundaryGroup>> getBoundaries() const;
	std::vector<std::shared_ptr<TopoEntity>> getEntities() const;

	std::shared_ptr<DomainGroup> getDomainByName(std::string );
	std::shared_ptr<BoundaryGroup> getBoundaryByName(std::string);
	std::shared_ptr<TopoEntity> getEntityByName(std::string);

	// has to be called always if GD is created manually (e.g. in templates)
	// in GeometryData(json input_json, std::string w) it is called automatically
	void update();

	//write brep files for each entity
	void writeBreps() const;

	void addEntity(std::shared_ptr<TopoEntity>);
	void addDomainGroup(std::shared_ptr<DomainGroup>);
	void addBoundaryGroup(std::shared_ptr<BoundaryGroup>);

	void merge(std::vector<std::string>, std::string);
	void unmerge(std::vector<std::string>);

	void setGroupMode(Mode);

	void setMeshStats(MeshStats ms);

	void partition();

	void addAir(double padding = 0, std::vector<std::string> cut_faces = {});

	void readCADfiles(std::vector<std::string>);

	void scaleAll(double);

	void setAxisymmetric(bool);

	bool isAxisymmetric();

	ShapeStats getShapeStatistics();

	TopoDS_Shape getShape();

	std::map<std::string, std::vector<std::string>> getRelations();

	int getDimension();

	void recalculateShape();

private:
	std::string wdir; /// working directory

	std::vector<std::shared_ptr<TopoEntity>> entities; 
	std::vector<std::shared_ptr<DomainGroup>> domains;
	std::vector<std::shared_ptr<BoundaryGroup>> boundaries;

	//store entity relations in a map, so it will later be faster to check relations without using OCC booleans
	std::map<std::string, std::vector<std::string>> relations;

	LengthUnit unit;

	TopoDS_Shape total_shape;

	int dimension;

	json variables;

	std::string meshfile;

	void readGeometryData(json raw_json);

	int newUniqueGroupId();

	int newUniqueEntityId();

	void updateRelations();

	void updateDimension();

	void orientFaces();

	void validate() const;

	void checkIfMeshable() const;

	// flag is true if any topology changes are made (partition, addAir)
	bool changed_topology;

	//flag for 2D axisymmetric cases
	bool axisymmetric;

	MeshStats mesh_stats;

	Mode group_mode;

	std::vector<std::function<void()>> read_functions;
	std::vector<std::function<void()>> operate_functions;
	std::string editor_file; // relative path to editor (Salome or FreeCAD) case file

};

typedef std::shared_ptr<GeometryData> GeometryData_;