#ifndef _H_GEOMETRY_BUILDER_
#define _H_GEOMETRY_BUILDER_

#include <Geometry/SRS.h>
#include <Geometry/SimplexProperty.h>
#include <Geometry/StreamableGeometry.h>
#include <Parsers/IParsingActions.h>
#include <Parsers/image_data.hpp>
#include <boost/optional.hpp>
#include <ostream>
#include <vector>

namespace GST
{

namespace Geometry
{

// this file contains a basic geometry
// which inherits from IParsingActions in order to
// parse a SFS/SFSP string into the geometry

/**
 *	We need a basic vertex which has x,y,z
 */
struct Vertex
{
	double x;
	double y;
	double z;

	/**
	 *	construct a vertex by setting x,y,z. If one of them is omitted it is set
	 *to 0.0.
	 *	@param a double coordinate for x, defaults to 0.0
	 *	@param b double coordinate for y, defaults to 0.0
	 *	@param c double coordinate for z, defaults to 0.0
	 */
	Vertex(const double &a = 0.0, const double &b = 0.0, const double &c = 0.0);
};

//-----------------------------------------------------------------------------------------------------------

/**
 *	A simple line which is made of two vertices
 */
struct LineSegment
{
	size_t v[2];

	/**
	 *	construct a line by passing two vertex indices and store them in the
	 *member
	 *	@param v1 the index of the first vertex, defaults to 0
	 *	@param v2 the index of the second vertex, defaults to 0
	 */
	LineSegment(const size_t &v1 = 0, const size_t &v2 = 0);
};

//-----------------------------------------------------------------------------------------------------------

/**
 *	A simple triangle which is made of three vertices
 */
struct Triangle
{
	size_t v[3];

	/**
	 *	construct a triangle by passing three vertex indices and store them in
	 *the member
	 *	@param v1 the index of the first vertex, defaults to 0
	 *	@param v2 the index of the second vertex, defaults to 0
	 *	@param v3 the index of the third vertex, defaults to 0
	 */
	Triangle(const size_t &v1 = 0, const size_t &v2 = 0, const size_t &v3 = 0);
};

//-----------------------------------------------------------------------------------------------------------

/**
 *	A simple tetrahedroon which is made of four vertices
 */
struct Tetra
{
	size_t v[4];

	/**
	 *	construct a triangle by passing three vertex indices and store them in
	 *the member
	 *	@param v1 the index of the first vertex, defaults to 0
	 *	@param v2 the index of the second vertex, defaults to 0
	 *	@param v3 the index of the third vertex, defaults to 0
	 *	@param v4 the index of the fourth vertex, defaults to 0
	 */
	Tetra(const size_t &v1 = 0,
		  const size_t &v2 = 0,
		  const size_t &v3 = 0,
		  const size_t &v4 = 0);
};

//-----------------------------------------------------------------------------------------------------------

/**
 * Handles meta data about a simplex property (property inside a geometry).
 */
class SimplexPropertyDescription : public SimplexProperty
{
public:
	SimplexPropertyDescription(const std::string name,
							   PropertyType dataType,
							   unsigned int dimension,
							   PropertyAlignment alignment)
		: name(name)
		, dataType(dataType)
		, dimension(dimension)
		, alignment(alignment)
	{
	}

	///@name SimplexProperty members
	//@{
	virtual std::string getName() const
	{
		return this->name;
	}
	virtual void setName(const std::string &name)
	{
		this->name = name;
	}
	virtual PropertyType getType() const
	{
		return this->dataType;
	}
	virtual int getDimension() const
	{
		return this->dimension;
	}
	virtual PropertyAlignment getAlignment() const
	{
		return this->alignment;
	}
	//@}

private:
	std::string name;
	PropertyType dataType;
	unsigned int dimension;
	PropertyAlignment alignment;
};

//-----------------------------------------------------------------------------------------------------------

// typedef some vectors
typedef std::vector<Vertex> Vertices;
typedef std::vector<LineSegment> LineSegments;
typedef std::vector<Triangle> Triangles;
typedef std::vector<Tetra> Tetrahedrons;

//-----------------------------------------------------------------------------------------------------------

/**
	*	\defgroup refGeometryBuilder The Class GeometryBuilder
	*	The class GeometryBuilder makes it easy to construct a description of
   the geometry for the network communication. It *	inherits from
   StreamableGeometry and thus it can be used by ClientUtils::NetworkInterface
   to send geometries.
	*
	*
	*	This class also inherits from Parsers::IParsingActions and thus is also
   can be used from WKT Parser. This makes *	the understanding of calling
   convention more easy. If you know in which order IParsingActions are called
   by the *	Parser, you know how to construct a geometry description with the
   help of this class.
	*
	*	A sample creation of a Multipoint geometry with two properties stored on
   vertex level can look following. Property1 *	always have one-digit values,
   property2 has two-digit integer values. Both storing 1 dimensional values:
			\code
			GeometryBuilder gb;
			gb.CreateMultipoint3();
			gb.CreateVertexProperty("property1",
   SimplexProperty::PropertyType::TypeInt, 1);
			gb.CreateVertexProperty("property2",
   SimplexProperty::PropertyType::TypeInt, 1);

			gb.addPoint( RawPoint3(0,0,0) ); gb.PropAddValue("1");
   gb.PropAddValue("10"); gb.addPoint( RawPoint3(0,1,0) ); gb.PropAddValue("2");
   gb.PropAddValue("20"); gb.addPoint( RawPoint3(0,0,1) ); gb.PropAddValue("3");
   gb.PropAddValue("30"); gb.addPoint( RawPoint3(1,1,1) ); gb.PropAddValue("4");
   gb.PropAddValue("40");

			std::stringstream output_geometry;
			std::stringstream output_property;
			gb.serializeGeometry(output_geometry);
			gb.serializeSimplexPropertyValues(output_property);
			\endcode

	*	As you see the sequence of creating a property (line 3 and 4) is the
   same when calling PropAddValue(). (The values 1,2,3,4 belongs to \e property1
   and 10,20,30,40 to \e property2 .)
	*
	*	You also can add Property Values in one call with a vector. In meta code
   it will look like this: \code gb.addPoint( RawPoint3(0,0,0) ); gb.PropVector(
   {"1";"10"} ); gb.addPoint( RawPoint3(0,1,0) ); gb.PropVector( {"2";"20"} );
		gb.addPoint( RawPoint3(0,0,1) ); gb.PropVector( {"3";"30"} );
		gb.addPoint( RawPoint3(1,1,1) ); gb.PropVector( {"4";"40"} );
		\endcode

	*	If you have a multidimensional property it will be filled in the same
   way. If you have a multidimensional property (let's say a dimension of 3)
   call PropAddVector() *	1 times with a vector with 3 elements (or
   PropAddValue() 3 times) *	to refer to the next property in sequence.
	*
	*	The Spatial Reference System can be defined by setSRS(). For examples
   how to construct Spatial Reference systems please consult the class
   Geometry::SRS. *	If no SRS is set, the geometry will be assumed to have no
   SRS (NoSRS). You can upload this geometry into a class with a SRS. In this
   case it's assumed that the *	geometry has the same SRS (see
   NetworkInterface::UploadFeature()). *	It is recommended to define the SRS
   of an object.
	*
	*
	*/

/**
 *\copydoc refGeometryBuilder
 */
class GST_API_EXPORT GeometryBuilder
	: public GST::Parsers::IParsingActions
	, public GST::Geometry::StreamableGeometry
{
public:
	/**
	 *	basic constructor which only set the geometry type to GeomType_unkown.
	 */
	GeometryBuilder();

	//@name Geometry creation
	//@{
	/**
		*	implementation of IParsingActions::CreateMultipoint3.
		*	This sets the type to geoType::GeomType_Point. If the geometry is a
	   GeomType_Collection *	a new geometry is created and CreateMultipoint3
	   is invoked for it. *	The new created geometry will be pushed to the
	   collection_container vector.
		*
		*	Sample Creation
				\code
				GeometryBuilder gb;
				gb.CreateMultipoint3();
				gb.CreateVertexProperty("property1",
	   SimplexProperty::PropertyType::TypeInt, 1);
				gb.CreateVertexProperty("property2",
	   SimplexProperty::PropertyType::TypeInt, 1);

				gb.addPoint( RawPoint3(0,0,0) ); gb.PropAddValue("1");
	   gb.PropAddValue("10"); gb.addPoint( RawPoint3(0,1,0) );
	   gb.PropAddValue("2"); gb.PropAddValue("20"); gb.addPoint(
	   RawPoint3(0,0,1) ); gb.PropAddValue("3"); gb.PropAddValue("40");
				gb.addPoint( RawPoint3(1,1,1) ); gb.PropAddValue("4");
	   gb.PropAddValue("50");

				std::stringstream output_geometry;
				std::stringstream output_property;
				gb.serializeGeometry(output_geometry);
				gb.serializeSimplexPropertyValues(output_property);
				\endcode
		*	@note Mixable call: It's up to you to call the PropAddValue() or
	   PropAddVector() direct after each addPoint(), *	or at the end. The only
	   rule is, that order of Add methods refers to the same simplices.
		*
		*	@see IParsingActions::CreateMultipoint3()
		*/
	void CreateMultipoint3() override;

	/**
		*	implementation of IParsingActions::CreateMultiline3.
		*	This sets the type to geoType::GeomType_SegmentLine. If the geometry
	   is a GeomType_Collection *	a new geometry is created and
	   CreateMultiline3 is invoked for it. *	The new created geometry will be
	   pushed to the collection_container vector.
		*
		*	Sample Creation
				\code
				GeometryBuilder gb;
				gb.CreateMultiline3();
				gb.CreateVertexProperty("property1",
	   SimplexProperty::PropertyType::TypeInt, 1);
				gb.CreateVertexProperty("property2",
	   SimplexProperty::PropertyType::TypeInt, 1);

				gb.newSimplex();
				gb.addPoint( RawPoint3(0,0,0) ); gb.PropAddValue("1");
	   gb.PropAddValue("10"); gb.addPoint( RawPoint3(0,1,0) );
	   gb.PropAddValue("2"); gb.PropAddValue("20"); gb.addPoint(
	   RawPoint3(0,0,1) ); gb.PropAddValue("3"); gb.PropAddValue("30");
				gb.addPoint( RawPoint3(1,1,1) ); gb.PropAddValue("4");
	   gb.PropAddValue("40");

				gb.newSimplex();
				gb.addPoint( RawPoint3(0,0,0) ); gb.PropAddValue("5");
	   gb.PropAddValue("50"); gb.addPoint( RawPoint3(0,2,0) );
	   gb.PropAddValue("6"); gb.PropAddValue("60"); gb.addPoint(
	   RawPoint3(0,0,2) ); gb.PropAddValue("7"); gb.PropAddValue("70");
				gb.addPoint( RawPoint3(2,2,2) ); gb.PropAddValue("8");
	   gb.PropAddValue("80");

				std::stringstream output_geometry;
				std::stringstream output_property;
				gb.serializeGeometry(output_geometry);
				gb.serializeSimplexPropertyValues(output_property);
				\endcode
		*	@note Mixable call: It's up to you to call the PropAddValue() or
	   PropAddVector() direct after each addPoint(), *	or at the end. The only
	   rule is, that order of Add methods refers to the same simplices.
		*
		*	@see IParsingActions::CreateMultiline3()
		*/
	void CreateMultiline3() override;

	/**
		*	2nd style creation of a Multiline object. The serializeGeometry()
	   method will output the same, *	but the process of creation differs.
		*	This sets the type to geoType::GeomType_SegmentLine. If the geometry
	   is a GeomType_Collection *	a new geometry is created and
	   CreateMultiline3 is invoked for it. *	The new created geometry will be
	   pushed to the collection_container vector.
		*
		*	Sample creation:
				\code
				GeometryBuilder gb;
				gb.CreateSegmentLine3();
				gb.CreateVertexProperty("property1",
	   SimplexProperty::PropertyType::TypeInt, 1);
				gb.CreateVertexProperty("property2",
	   SimplexProperty::PropertyType::TypeInt, 1);

				gb.addPoint( RawPoint3(0,0,0) ); gb.PropAddValue("1");
	   gb.PropAddValue("10"); gb.addPoint( RawPoint3(0,1,0) );
	   gb.PropAddValue("2"); gb.PropAddValue("20"); gb.newSimplex(); //Part here
	   has the meaning of a segement (v1-v2) gb.addPoint( RawPoint3(0,0,1) );
	   gb.PropAddValue("3"); gb.PropAddValue("30"); gb.newSimplex();
				gb.addPoint( RawPoint3(1,1,1) ); gb.PropAddValue("4");
	   gb.PropAddValue("40"); gb.newSimplex();

				//two time addPoint indicates a new part
				gb.addPoint( RawPoint3(0,0,0) ); gb.PropAddValue("5");
	   gb.PropAddValue("50"); gb.addPoint( RawPoint3(0,2,0) );
	   gb.PropAddValue("6"); gb.PropAddValue("60"); gb.newSimplex();
				gb.addPoint( RawPoint3(0,0,2) ); gb.PropAddValue("7");
	   gb.PropAddValue("70"); gb.newSimplex(); gb.addPoint( RawPoint3(2,2,2) );
	   gb.PropAddValue("8"); gb.PropAddValue("80"); gb.newSimplex();

				std::stringstream output_geometry;
				std::stringstream output_property;
				gb.serializeGeometry(output_geometry);
				gb.serializeSimplexPropertyValues(output_property);
				\endcode
		*	@note Mixable call: It's up to you to call the PropAddValue() or
	   PropAddVector() direct after each addPoint(), *	or at the end. The only
	   rule is, that order of Add methods refers to the same simplices.
		*/
	virtual void CreateSegmentLine3();

	/**
		*	implementation of IParsingActions::CreateTIN3. 2nd style creation of
	   a TriangleNet3 object. The serializeGeometry() method will output the
	   same, *	but the process of creation differs. *	This sets the type to
	   geoType::GeomType_TriangleNet. If the geometry is a GeomType_Collection
		*	a new geometry is created and CreateTIN3 is invoked for it.
		*	The new created geometry will be pushed to the collection_container
	   vector.
		*
		*	Sample creation:
				\code
				GeometryBuilder gb;
				gb.CreateTIN3();
				gb.CreateVertexProperty("property1",
	   SimplexProperty::PropertyType::TypeInt, 1);
				gb.CreateVertexProperty("property2",
	   SimplexProperty::PropertyType::TypeInt, 1);

				gb.newSimplex();
				gb.addPoint( RawPoint3( 0.5 ,   0 , 1) ); //index: 0
				gb.PropAddValue("1"); gb.PropAddValue("10");
				gb.addPoint( RawPoint3(   0 ,   1 , 0) ); //index: 1
				gb.PropAddValue("2"); gb.PropAddValue("20");
				gb.addPoint( RawPoint3(   1 ,   1 , 0) ); //index: 2
				gb.PropAddValue("3"); gb.PropAddValue("30");
				gb.newSimplex();
				gb.addPoint( RawPoint3(   0 ,   1 , 0) ); //index: 1
				gb.addPoint( RawPoint3(   1 ,   1 , 0) ); //index: 2
				gb.addPoint( RawPoint3(   0 ,   2 , 0) ); //index: 3
				gb.PropAddValue("4"); gb.PropAddValue("40");
				gb.newSimplex();
				gb.addPoint( RawPoint3(   1 ,   1 , 0) ); //index: 2
				gb.addPoint( RawPoint3(   0 ,   2 , 0) ); //index: 3
				gb.addPoint( RawPoint3(   2 ,   2 , 0) ); //index: 4
				gb.PropAddValue("5"); gb.PropAddValue("50");
				\endcode
		*	@note Mixable call: It's up to you to call the PropAddValue() or
	   PropAddVector() direct after each addPoint(), *	or at the end. The only
	   rule is, that order of Add methods refers to the same simplices.
		*
		*	@see IParsingActions::CreateTIN3()
		*/
	void CreateTIN3() override;

	/**
		*	implementation of IParsingActions::CreateTriangleNet3.
		*	This sets the type to geoType::GeomType_TriangleNet. If the geometry
	   is a GeomType_Collection *	a new geometry is created and
	   CreateTriangleNet3 is invoked for it. *	The new created geometry will be
	   pushed to the collection_container vector.
		*
		*	Sample creation:
				\code
				GeometryBuilder gb;
				gb.CreateTriangleNet3();
				gb.CreateVertexProperty("property1",
	   SimplexProperty::PropertyType::TypeInt, 1);
				gb.CreateVertexProperty("property2",
	   SimplexProperty::PropertyType::TypeInt, 1);

				gb.addPoint( RawPoint3( 0.5 ,   0 , 1) ); //index: 0
				gb.PropAddValue("1"); gb.PropAddValue("10");
				gb.addPoint( RawPoint3(   0 ,   1 , 0) ); //index: 1
				gb.PropAddValue("2"); gb.PropAddValue("20");
				gb.addPoint( RawPoint3(   1 ,   1 , 0) ); //index: 2
				gb.PropAddValue("3"); gb.PropAddValue("30");
				gb.addPoint( RawPoint3(   0 ,   2 , 0) ); //index: 3
				gb.PropAddValue("4"); gb.PropAddValue("40");
				gb.addPoint( RawPoint3(   2 ,   2 , 0) ); //index: 4
				gb.PropAddValue("5"); gb.PropAddValue("50");
				//...
				gb.addTriangle( IdxTriangle(0 ,1 ,2) );
				gb.addTriangle( IdxTriangle(1 ,2 ,3) );
				gb.addTriangle( IdxTriangle(2 ,3, 4) );


				std::stringstream output_geometry;
				std::stringstream output_property;
				gb.serializeGeometry(output_geometry);
				gb.serializeSimplexPropertyValues(output_property);
				\endcode
		*	@note Mixable call: It's up to you to call the PropAddValue() or
	   PropAddVector() direct after each addPoint(), *	or at the end. The only
	   rule is, that order of Add methods refers to the same simplices.
		*
		*	@see IParsingActions::CreateTriangleNet3()
		*/
	void CreateTriangleNet3() override;

	/**
		*	implementation of IParsingActions::CreateMultiPolygon3.
		*	2nd style creation of a Tetrahedon3 object. The serializeGeometry()
	   method will output the same, *	but the process of creation differs.
		*	This sets the type to geoType::GeomType_Tetrahedronnet. If the
	   geometry is a GeomType_Collection *	a new geometry is created and
	   CreateMultiPolygon3 is invoked for it. *	The new created geometry will be
	   pushed to the collection_container vector.
		*
		*	Sample creation:
				\code
				GeometryBuilder gb;
				gb.CreateMultiPolygon3();
				gb.CreateVertexProperty("property1",
	   SimplexProperty::PropertyType::TypeInt, 1);
				gb.CreateVertexProperty("property2",
	   SimplexProperty::PropertyType::TypeInt, 1);

				gb.newSimplex();
				gb.addPoint( RawPoint3(   0 ,   0 , 0) ); //index: 0
				gb.PropAddValue("1"); gb.PropAddValue("10");
				gb.addPoint( RawPoint3(   1 ,   0 , 0) ); //index: 1
				gb.PropAddValue("2"); gb.PropAddValue("20");
				gb.addPoint( RawPoint3(   0 ,   1 , 0) ); //index: 2
				gb.PropAddValue("3"); gb.PropAddValue("30");
				gb.addPoint( RawPoint3( 0.5 , 0.5 , 1) ); //index: 4
				gb.PropAddValue("4"); gb.PropAddValue("40");
				gb.newSimplex();
				gb.addPoint( RawPoint3(   1 ,   0 , 0) ); //index: 1
				gb.PropAddValue("5"); gb.PropAddValue("50");
				gb.addPoint( RawPoint3(   0 ,   1 , 0) ); //index: 2
				gb.PropAddValue("6"); gb.PropAddValue("60");
				gb.addPoint( RawPoint3(   1 ,   1 , 0) ); //index: 3
				gb.PropAddValue("7"); gb.PropAddValue("70");
				gb.addPoint( RawPoint3( 0.5 , 0.5 , 1) ); //index: 4
				gb.PropAddValue("8"); gb.PropAddValue("80");
				\endcode
		*	@note Mixable call: It's up to you to call the PropAddValue() or
	   PropAddVector() direct after each addPoint(), *	or at the end. The only
	   rule is, that order of Add methods refers to the same simplices.
		*
		*	@see IParsingActions::CreateMultiPolygon3()
		*/
	void CreateMultiPolygon3() override;

	/**
		*	implementation of IParsingActions::CreateTetrahedon3.
		*	This sets the type to geoType::GeomType_Tetrahedronnet. If the
	   geometry is a GeomType_Collection *	a new geometry is created and
	   CreateTetrahedon3 is invoked for it. *	The new created geometry will be
	   pushed to the collection_container vector.
		*
		*	Sample creation:
				\code
				GeometryBuilder gb;
				gb.CreateTetrahedon3();
				gb.CreateVertexProperty("property1",
	   SimplexProperty::PropertyType::TypeInt, 1);
				gb.CreateVertexProperty("property2",
	   SimplexProperty::PropertyType::TypeInt, 1);

				gb.addPoint( RawPoint3(   0 ,   0 , 0) ); //index: 0
				gb.PropAddValue("1"); gb.PropAddValue("10");
				gb.addPoint( RawPoint3(   1 ,   0 , 0) ); //index: 1
				gb.PropAddValue("2"); gb.PropAddValue("20");
				gb.addPoint( RawPoint3(   0 ,   1 , 0) ); //index: 2
				gb.PropAddValue("3"); gb.PropAddValue("30");
				gb.addPoint( RawPoint3(   1 ,   1 , 0) ); //index: 3
				gb.PropAddValue("4"); gb.PropAddValue("40");
				gb.addPoint( RawPoint3( 0.5 , 0.5 , 1) ); //index: 4
				gb.PropAddValue("5"); gb.PropAddValue("50");
				//...
				gb.addTetrahedron( IdxTetrahedron(0 ,1 ,2, 4) );
				gb.addTetrahedron( IdxTetrahedron(1 ,2, 3, 4) );

				std::stringstream output_geometry;
				std::stringstream output_property;
				gb.serializeGeometry(output_geometry);
				gb.serializeSimplexPropertyValues(output_property);
				\endcode
		*	@note Mixable call: It's up to you to call the PropAddValue() or
	   PropAddVector() direct after each addPoint(), *	or at the end. The only
	   rule is, that order of Add methods refers to the same simplices.
		*
		*	@see IParsingActions::CreateTetrahedon3()
		*/
	void CreateTetrahedon3() override;

	/**
	*	implementation of IParsingActions::CreateGrid3.
	*	This sets the type to geoType::GeomType_Grid. If the geometry is a
	GeomType_Collection *	a new geometry is created and CreateGrid3 is invoked
	for it. *	The new created geometry will be pushed to the
	collection_container vector.
	*
	*	Sample creation:
			\code
			GeometryBuilder gb;

			GridDescription grid_desc;
			grid_desc.setPosition(RawPoint3(4400, 120.55, 312.73));
			grid_desc.setSize(10,10,10);
			grid_desc.setOrientation(RawPoint3(20,0,0), RawPoint3(0,13,0),
	RawPoint3(0,0,10));

			gb.CreateGrid3(grid_desc);
			gb.set_srs(srs)
			gb.set_name(new_feature_name)

			gb.CreateVertexProperty("property1",
	SimplexProperty::PropertyType::TypeInt, 1);
			gb.CreateVertexProperty("property2",
	SimplexProperty::PropertyType::TypeText, 1);

			// add value to property1, then add property to property2
			gb.PropAddValue("1"); gb.PropAddValue("10");
			// repeat this until all values (in this case: 10*10*10 = 1000, see
	"setSize()") are
			// added for both properties
			gb.PropAddValue("2"); gb.PropAddValue("20");
			gb.PropAddValue("3"); gb.PropAddValue("30");
			gb.PropAddValue("4"); gb.PropAddValue("40");
			gb.PropAddValue("5"); gb.PropAddValue("50");
			gb.PropAddValue("6"); gb.PropAddValue("60");
			gb.PropAddValue("7"); gb.PropAddValue("70");
			gb.PropAddValue("8"); gb.PropAddValue("80");
			//...
			//...
			gb.PropAddValue("998"); gb.PropAddValue("9980");
			gb.PropAddValue("999"); gb.PropAddValue("9990");
			gb.PropAddValue("1000"); gb.PropAddValue("10000");

			std::stringstream output_geometry;
			gb.serializeGeometry(output_geometry);
	\endcode
	*/
	void CreateGrid3(
		const GST::Parsers::GridDescription &grid_description) override;
	void CreateSGrid3(
		const GST::Parsers::GridDescription &grid_description) override;

	/**
		*	implementation of IParsingActions::CreateCollection.
		*	This sets the type to geoType::GeomType_Collection. If the geometry
	   is a GeomType_Collection *	a new geometry is created and
	   CreateCollection is invoked for it. *	The new created geometry will be
	   pushed to the collection_container vector.
		*
		*	Sample creation:
				\code
				GeometryBuilder gb;
				gb.CreateCollection();
				gb.CreateTriangleNet3();
				//...
				gb.CreateTriangleNet3();
				//...
				gb.CreateMultiline3();
				\endcode
		*	@note Mixable call: It's up to you to call the PropAddValue() or
	   PropAddVector() direct after each addPoint(), *	or at the end. The only
	   rule is, that order of Add methods refers to the same simplices.
		*
		*	@see IParsingActions::CreateCollection()
		*/
	void CreateCollection() override;
	/**
	Creates a geological profile

	TODO: more info

	\code
		//example
		CreateGeologicalProfile();

		CreateMultiline3();	//1st member
		subfeatureKind(1); // specify subfeature kind
		addPoint(p);
		addPoint(p);
		//...

		CreateMultiline3();		//2nd member
		subfeatureKind(2); // specifiy subfeature kind
		//...
		//and so on
	\endcode
*/
	void CreateGeologicalProfile() override;
	/**
		This function sets the subfeature kind of the geological profile part.

		This should best be called right after one of the Create* frunctions
		like `CreateMultiline3`. Omitting this for geological profiles will
		result in an error on.
	*/
	void setSubfeatureKind(std::string subfeatureKind) override;

	/**
	 *	This function is invoked by the parse everytime a part has been
	 *finished. Thus depending on geoType a line, triangle or tretrahedron is
	 *created with the last 2,3,4 indices of pushed back vertices. For a point
	 *set nothing will be done here. For a collection of geometries the newPart
	 *method will be invoked for the last geometry in the vector
	 *collection_container.
	 *	@see IParsingActions::newSimplex()
	 */
	void newSimplex() override;

	/**
	 *	This function is invoked by the parse everytime a part has been
	 *finished. Thus depending on geoType a line, triangle or tretrahedron is
	 *created with the last 2,3,4 indices of pushed back vertices. For a point
	 *set nothing will be done here. For a collection of geometries the newPart
	 *method will be invoked for the last geometry in the vector
	 *collection_container.
	 *	@see IParsingActions::newSimplex()
	 */
	void newPart(bool oldStyle = true) override
	{
		if(oldStyle)
			this->newSimplex();
	}

	/**
	 *	Adds a raw point to the geomtetry. For a collection the addPoint method
	 *is invoked for the last element in vector collection_container.
	 *	@param d is a structure containing x,y,z
	 *	@see IParsingActions::addPoint()
	 */
	void addPoint(const GST::Parsers::RawPoint3 &d) override;

	/**
	 *	Adds a triangle to the geometry. For a collection the addTriangle method
	 *is invoked for the last element in vector collection_container.
	 *	@param t is a structure containing v1,v2,v3
	 *	@see IParsingActions::addTriangle()
	 */
	void addTriangle(const GST::Parsers::IdxTriangle &t) override;

	/**
	 *	Adds a tetrahedron to the geometry. For a collection the addTetrahedron
	 *method is invoked for the last element in vector collection_container.
	 *	@param t is a structure containing v1,v2,v3,v4
	 *	@see IParsingActions::addTetrahedron()
	 */
	void addTetrahedron(const GST::Parsers::IdxTetrahedron &t) override;

	/**
	 *
	 */
	void attachImage(const GST::Parsers::AttachedImageMetadata &imageMetadata,
					 const std::vector<uint8_t> &imageBinData) override;
	//@}

	///@name Property creation
	//@{
	/**
	 *		Invokes a new property aligned to the cell of the object. What a
	 *cell is depends of the type of of the object. E.g. the cells for a
	 *Multiline are the segment, for a TIN the triangles and for a
	 *Tetrahedronnetwork the tetrahedron. Just for Multipoint objects cell are
	 *the vertices too.
	 *
	 *		The sequence of creating a property is the same sequence when
	 *calling PropAddValue() or PropAddVector().
	 */
	int CreateCellProperty(
		const std::string &name,
		const Geometry::SimplexProperty::PropertyType &PropertyType,
		const unsigned int &dims) override;

	/**
	 *		Invoces a new property aligned to the vertices of the object.
	 *
	 *		The sequence of creating a property is the same sequence when
	 *calling PropAddValue() or PropAddVector().
	 */
	int CreateVertexProperty(
		const std::string &name,
		const GST::Geometry::SimplexProperty::PropertyType &PropertyType,
		const unsigned int &dims) override;

	/**
	 *		Add a property value to the simplex (cell or vertex, depends on the
	 *type of property creation).
	 *
	 *		The sequence a value/vector refers to which property depends on the
	 *creation sequence of the properties. (See samples of creation above.) If
	 *you have a multidimensional property (let's say a dimsion of 3) call
	 *PropAddValue() 3 times (or PropAddVector() 1 time with a vector with 3
	 *elements) to refer to the next property in sequence.
	 *
	 *		@see CreateMultipoint3()
	 */
	void PropAddValue(const std::string &value) override;

	/**
	 *		Add a set of property values to the simplex (cell or vertex, depends
	 *on the type of property creation).
	 *
	 *		The sequence a value/vector refers to which property depends on the
	 *creation sequence of the properties. (See samples of creation above.) If
	 *you have a multidimensional property (let's say a dimsion of 3) call
	 *PropAddVector() 1 times with a vector with 3 elements (or PropAddValue() 3
	 *times) to refer to the next property in sequence.
	 *
	 *		@see CreateMultipoint3()
	 */
	void PropAddVector(const std::vector<std::string> &vector) override;
	//@}

	///@name Meta creation
	//@{
	/**
	 *		Set the Spatial Refence System for the added coordinates by the
	 *AddPoint() method.
	 */
	void setSRS(Geometry::SRSPtr srs);

	/**
	 *		Set object transparency. 0..1 -> solid..transparent
	 */
	void SetTransparency(const IGeometry::TransparencyCode &alpha) override;

	/**
	 *		Set object color. Each channel: 0..1 -> zero..full (e.g. {0.8 0.8
	 *0.8} is light grey)
	 */
	void SetColor(const IGeometry::Color &color) override;

	/**
	 *		Sets the object name
	 */
	void SetName(const std::string &name) override;

	void SetOriginalName(const std::string &name) override;

	/**
	 *		Sets a custom object property
	 */
	void SetCustomProperty(const std::string &name,
						   const std::string &value) override;

	void SetCRS(const std::string &crs) override;

	virtual std::vector<SimplexPropertyDescription> getPropertyDescriptions()
		const;
	//@}

	///@name StreamableGeometry members
	//@{
	/**
	 *		Implementation of StreamableGeoemtry
	 */
	bool serializeGeometry(std::ostream &os) const override;

	/**
	 *		Implementation of StreamableGeoemtry
	 */
	bool serializeSimplexPropertyDescription(std::ostream &os) const override;

	/**
	 *		Implementation of StreamableGeoemtry
	 */
	bool serializeSimplexPropertyValues(std::ostream &os) const override;

	/**
	 *		Implementation of StreamableGeoemtry
	 */
	Geometry::SRSPtr GetSRS() const override;

	/**
	 *		Implementation of StreamableGeoemtry
	 */
	IGeometry::TransparencyCode GetTransparency() const override;

	/**
	 *		Implementation of StreamableGeoemtry
	 */
	IGeometry::Color GetColor() const override;

	/**
	 *		Implementation of StreamableGeoemtry
	 */
	std::string GetName() const override;

	/**
	 *		Implementation of StreamableGeoemtry
	 */
	KeyValueListPtr GetVariousProperties() const override;
	//@}

	/**
	 *	The following function are implemented without any function in order
	 *	to be able to instantiate an object of the Geometry class. For more
	 *	information refer to IParsingActions.
	 *	@see IParsingActions
	 */
	///@name not implemented with function
	//@{
	void GeometryParsingFinished() override;
	void PropertyParsingFinished() override;
	void PropNextObject() override;
	boost::ptr_list<IGeometry> *getGeometries() override;
	void clearGeometries() override;
	virtual void clear();
	void NewObject(const std::streampos &, const std::streampos &) override
	{
	}
	//@}
	const Vertices &vertexContainer() const;
	Geometry::GeometryTypes geomtype() const;
	size_t numParts() const;
	const GeometryBuilder &part(size_t i) const;
	const std::string &getSubfeatureKind() const;
	const boost::optional<GST::Parsers::ImageData> &getImageData() const;

protected:
	typedef std::vector<SimplexPropertyDescription>
		SimplexPropertyDescriptionContainer;
	typedef SimplexPropertyDescriptionContainer::iterator
		SimplexPropertyDescriptionContainerItr;
	typedef SimplexPropertyDescriptionContainer::const_iterator
		SimplexPropertyDescriptionContainerCItr;

	std::string object_name;
	std::string original_name;
	IGeometry::Color object_color;
	IGeometry::TransparencyCode object_transparency;
	Geometry::SRSPtr object_srs;
	KeyValueList various_properties;
	GST::Parsers::GridDescription grid_description;
	std::string subfeatureKind;

	/**
	 *	A bunch of vertices. They are needed for every geometry type.
	 */
	Vertices vertex_container;
	/**
	 *	Some lines. This vector contains only elements if the geometry type
	 *	has been set to GeomType_SegmentLine.
	 */
	LineSegments segment_container;
	/**
	 *	Some triangles. This vector contains only elements if the geometry type
	 *	has been set to GeomType_TriangleNet.
	 */
	Triangles triangle_container;
	/**
	 *	Some tetrahedra. This vector contains only elements if the geometry type
	 *	has been set to GeomType_Tetrahedronnet.
	 */
	Tetrahedrons tetra_container;
	/**
	 *	Some geometries. This vector contains only elements if the geometry type
	 *	has been set to GeomType_Collection or GeomType_GeologicalProfile.
	 */
	std::vector<GeometryBuilder> collection_container;

	/**
	 * stores the property values that are aligned with the vertices.
	 */
	std::vector<std::string> vertex_property_val_container;
	/**
	 * stores the property values that are aligned with the cells (eg. segment,
	 * triangle or tetrahedron).
	 */
	std::vector<std::string> cell_property_val_container;
	/**
	 * stores the property description in the same order they are created.
	 */
	SimplexPropertyDescriptionContainer property_desc_container;
	/// loop helper for addValue and addVector
	struct PropertyLooper
	{
		SimplexPropertyDescriptionContainerItr property_desc_iterator;
		int property_desc_dim_count;
	} property_looper;
	/**
	 *	This enum lists all possible geometry types. The names
	 *	are partly borrowed from gOcad.
	 */
	enum geoType
	{
		GeomType_Point, ///< A Multipoint object
		GeomType_SegmentLine, ///< A Multiline object created segment wise
		GeomType_MultiLine, ///< A Multiline object created linestring wise
		GeomType_TIN, ///< A Triangulated object create triangle wise
		GeomType_TriangleNet, ///< A Triangulated object created by point and
							  ///< index list
		GeomType_Tetrahedronnet, ///< A Tetrahedron object created tretrahedron
								 ///< wise
		GeomType_TetraMultipolygon, ///< A Tetrahedron object created by point
									///< and index list
		GeomType_Collection, ///< A Collection of objects above
		GeomType_Grid, ///< A Grid object
		GeomType_SGrid, ///< A Structured Grid object
		GeomType_GeologicalProfile, ///< A Geological Profile object
		GeomType_unkown ///< A default value. This is not presenting a valid
						///< type.
	};
	geoType type;
	boost::optional<GST::Parsers::ImageData> imageData;

private:
	///@name private helpers
	//@{
	bool serializeGeometrySFSP(std::ostream &os) const;
	bool serializeGeometryISATIS(std::ostream &os) const;
	void writeCoordinateMultipoint(std::ostream &os) const;
	void writeCoordinateLineString(std::ostream &os) const;
	void writeVertex(const size_t &pos, std::ostream &os) const;
	void writeTriangleList(std::ostream &os) const;
	void writeTetraList(std::ostream &os) const;
	//@}
};

} // namespace Geometry
} // namespace GST

#endif //_H_GEOMETRY_BUILDER_
