 /**
  *  \brief     Element class.
  *  \details   Used to store properties of the element
  *  \author    vadims
  *  \date      May 9, 2017
  *  \bug       Not all memory is freed when deleting an object of this class.
  *  \warning   Improper use can crash your application
  *  \copyright CENOS LLC
  */

#pragma once

#include <vector>
#include "nlohmann/json.hpp"
#include "Node.hpp"
#include "Face.h"
#include "ElementTypesEnum.hpp"

class MeshEntity;
using json = nlohmann::json;

class Element
{
public: 
	enum Dim { ONEDIM = 1, TWODIM = 2, THREEDIM = 3 }; ///< Enum for dimension of element

protected:
	int id; ///< Element Id
	std::vector<std::weak_ptr<Node>> nodes; ///< Nodes belonging to this element

	ElementTypes elType;
	unsigned int vtkType;
	Dim dimension;
	unsigned int typeNodeCount;
	std::string stringType;

	std::weak_ptr<MeshEntity> ownerEntity; ///< Entity this element belongs to

	friend class GmshMesh;

	int mark;

public:
	Element();
	~Element();
	/// @brief Constructor of Element.
	/// @param owner Entity which owns this element
	/// @param nodelist which belongs to this element.
	Element(std::shared_ptr<MeshEntity> owner, std::vector<std::shared_ptr<Node>> nodelist);

	static std::shared_ptr<Element> createElement(ElementTypes);

	/// @brief Element copy constructor
	Element(const Element& that);

	/// @brief equal to operator
	bool operator==(const Element& rhs) const;
	/// @brief not equal to operator
	bool operator!=(const Element& rhs) const;

	/// Get Element Id
	int getId();

	/// Get Element nodes
	std::vector<std::shared_ptr<Node>> getNodes();

	/// Get Element type according to getdp (and Cenos) format
	virtual int getElementType();

	/// Get reference to Entity this element belongs to
	std::shared_ptr<MeshEntity> getOwnerEntity();

	/// Check if all nodes of el are present in this
	bool Element::hasAllNodesOf(std::shared_ptr<Element> el);

	/// Get Element type according to VTK format
	/// https://vtk.org/wp-content/uploads/2015/04/file-formats.pdf (page 9)
	virtual int getElementVTKType();

	/// Get Element descriptive type
	virtual std::string getElementStringType();

	/// Get Element dimension (1 for 1D, 2 for 2D, 3 for 3D)
	virtual Dim getDimension();

	/// Get node count for this type of elements
	virtual int getTypeNodeCount();

	int getMark();

	virtual std::vector<std::shared_ptr<Node>> getConnectedNodes(std::shared_ptr<Node>) = 0;
	void setNodes(std::vector<std::shared_ptr<Node>>);
	void setId(int);
	void setEntity(std::shared_ptr<MeshEntity>);
	void setMark(int);

	std::vector<double> getCentroid() ;
	virtual double getLength() = 0;
	virtual double getArea() = 0;
	virtual double getVolume() = 0;

	virtual void setMidsideNodes() = 0;

	virtual std::vector<Face> getFaces() = 0;
};

typedef std::shared_ptr<Element> Element_;

