#ifndef WKT_GRAMMAR_H
#define WKT_GRAMMAR_H

#ifdef Gocad_utils_enter_scope_h
#error Please include all boost depending headers before GOCAD headers!
#endif

#include "GrammarRawStructs.h"
#include "IGeometryActions.h"
#include "Parsers/GrammarErrorHandler.hpp"

// #include <boost/config/warning_disable.hpp>
#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>

#include <iostream>

namespace spirit = boost::spirit;
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
namespace phoenix = boost::phoenix;

namespace GST
{
namespace Parsers
{

//
// the grammar
//
template<typename Iterator, typename Skipper>
class WKTGrammar3 : public qi::grammar<Iterator, Skipper>
{
public:
	WKTGrammar3(boost::shared_ptr<IGeometryActions> actions)
		: WKTGrammar3::base_type(
			  geometry_z_tagged_text_) // define here the base rule
		, _actions(actions)
	{
		using qi::double_;
		using qi::eps;
		using qi::lit;
		using qi::no_case;
		using qi::uint_;
		using qi::ulong_;

		// define here function pointers to Semantic actions (more beautiful
		// grammar definiton)
		typedef boost::function<void(
			const RawPoint3 &, qi::unused_type, qi::unused_type)>
			point_action;
		typedef boost::function<void(
			const IdxTriangle &, qi::unused_type, qi::unused_type)>
			triangle_action;
		typedef boost::function<void(
			const IdxTetrahedron &, qi::unused_type, qi::unused_type)>
			tetrahedron_action;
		typedef boost::function<void(
			qi::unused_type, qi::unused_type, qi::unused_type)>
			void_action;

		point_action addPoint(
			boost::bind(&IGeometryActions::addPoint, _actions, ::_1));
		void_action newSimplex(
			boost::bind(&IGeometryActions::newSimplex, _actions));
		void_action createPoint3(
			boost::bind(&IGeometryActions::CreateMultipoint3, _actions));
		void_action createMultiLine3(
			boost::bind(&IGeometryActions::CreateMultiline3, _actions));
		void_action createMultiPolygon3(
			boost::bind(&IGeometryActions::CreateMultiPolygon3, _actions));
		void_action createPolyhedralSurf3(
			boost::bind(&IGeometryActions::CreateMultiPolygon3, _actions));
		void_action createTIN3(
			boost::bind(&IGeometryActions::CreateTIN3, _actions));
		void_action createGeomCollection(
			boost::bind(&IGeometryActions::CreateCollection, _actions));
		void_action createTN3(
			boost::bind(&IGeometryActions::CreateTriangleNet3, _actions));
		void_action createTetrahedon3(
			boost::bind(&IGeometryActions::CreateTetrahedon3, _actions));
		triangle_action addTriangle(
			boost::bind(&IGeometryActions::addTriangle, _actions, ::_1));
		tetrahedron_action addTetrahedron(
			boost::bind(&IGeometryActions::addTetrahedron, _actions, ::_1));

		//
		// ###  the grammar definiton   ###

		point_z_ /*SFS conform: OGC 06-103r4*/
			= double_ /*x*/
			  > double_ /*y*/
			  > double_ /*z*/
			;

		triangle_point_index_ = ulong_ /*v1*/
								> ulong_ /*v2*/
								> ulong_ /*v3*/
			;

		tetrahedron_point_index_ = ulong_ /*v1*/
								   > ulong_ /*v2*/
								   > ulong_ /*v3*/
								   > ulong_ /*v4*/
			;

		triangle_point_index_list_
			= triangle_point_index_[addTriangle]
			  > *(lit(',') > triangle_point_index_[addTriangle]);

		tetrahedron_point_index_list_
			= tetrahedron_point_index_[addTetrahedron]
			  > *(lit(',') > tetrahedron_point_index_[addTetrahedron]);

		point_z_text_ /*SFS conform: OGC 06-103r4*/
			= lit('(') > point_z_[addPoint] /*create a point from rule*/
			  > lit(')');

		multipoint_z_text_ /*SFS conform: OGC 06-103r4*/
			= lit('(') > point_z_text_ > *(lit(',') > point_z_text_) > lit(')');

		linestring_z_text_ /*SFS conform: OGC 06-103r4*/
			= lit('(') > point_z_[addPoint]
			  > *(lit(',') > point_z_[addPoint]) /*create a point from rule*/
			  > lit(')');

		multilinestring_z_text_ /*SFS conform: OGC 06-103r4*/
			= lit('(') > eps[newSimplex] /*release newPart before addPoints*/
			  > linestring_z_text_
			  > *(lit(',') > eps[newSimplex]
				  > linestring_z_text_) /*release newPart before addPoints*/
			  > lit(')');

		polygon_z_text_ /*SFS conform: OGC 06-103r4*/
			= lit('(') > linestring_z_text_
			  > *(lit(',')
				  > linestring_z_text_) /*release newPart before addPoints*/
			  > lit(')');

		multipolygon_z_text_ /*SFS conform: OGC 06-103r4*/
			= lit('(') > eps[newSimplex] /*release newPart before addPoints*/
			  > polygon_z_text_
			  > *(lit(',') > eps[newSimplex]
				  > polygon_z_text_) /*release newPart before addPoints*/
			  > lit(')');

		trianglenet_z_ = lit('(')
						 > linestring_z_text_ /*is a point_coordinate_list*/
						 > lit(',') > triangle_point_index_list_ > lit(')');

		tetrahedronnet_z_
			= lit('(') > linestring_z_text_ /*is a point_coordinate_list*/
			  > lit(',') > tetrahedron_point_index_list_ > lit(')');

		geometrycollection_z_text_ /*SFS conform: OGC 06-103r4*/
			= lit('(')
			  > geometry_z_tagged_text_ /*create new geometries recursively*/
			  > *(lit(',') > geometry_z_tagged_text_) > lit(')');

		point_z_tagged_text_ /*SFS conform: OGC 06-103r4*/
			= no_case["point z"]
			  > eps[createPoint3] /*'point z' matched, so create an object*/
			  > point_z_text_;

		multipoint_z_tagged_text_ /*SFS conform: OGC 06-103r4*/
			= no_case["multipoint z"]
			  > eps[createPoint3] /*'multipoint z' matched, so create an
									 object*/
			  > multipoint_z_text_;

		linestring_z_tagged_text_ /*SFS conform: OGC 06-103r4*/
			= no_case["linestring z"]
			  > eps[createMultiLine3] /*'multilinestring z' matched, so create
										 an object*/
			  > eps[newSimplex] /*relase newSimplex() */
			  > linestring_z_text_;

		polygon_z_tagged_text_ /*SFS conform: OGC 06-103r4*/
			= no_case["polygon z"]
			  > eps[createMultiPolygon3] /*'polygon z' matched, so create an
											object*/
			  > eps[newSimplex] /*relase newSimplex() */
			  > polygon_z_text_;

		multipolygon_z_tagged_text_ /*SFS conform: OGC 06-103r4*/
			= no_case["multipolygon z"]
			  > eps[createMultiPolygon3] /*'polygon z' matched, so create an
											object*/
			  > multipolygon_z_text_;

		multilinestring_z_tagged_text_ /*SFS conform: OGC 06-103r4*/
			= no_case["multilinestring z"]
			  > eps[createMultiLine3] /*'multilinestring z' matched, so create
										 an object*/
			  > multilinestring_z_text_;

		polyhedralsurface_z_tagged_text_ /*SFS conform: OGC 06-103r4*/
			= no_case["polyhedralsurface z"]
			  > eps[createPolyhedralSurf3] /*'polyhedralsurface z' matched, so
											  create an object*/
			  > multipolygon_z_text_;

		triangle_tagged_text_ /*SFS conform: OGC 06-103r4*/
			= no_case["triangle z"]
			  > eps[createTIN3] /*'triangle z' matched, so create an object*/
			  > eps[newSimplex] /*relase newSimplex() */
			  > polygon_z_text_;

		tin_tagged_text_ /*SFS conform: OGC 06-103r4*/
			= no_case["tin z"]
			  > eps[createTIN3] /*'tin z' matched, so create an object*/
			  > multipolygon_z_text_
			//> polyhedralsurface_z_text_
			;

		trianglenet_tagged_text_
			= no_case["trianglenet z"]
			  > eps[createTN3] /*'trianglenet z' matched, so create an object*/
			  > trianglenet_z_;

		tetrahedronnet_tagged_text_
			= no_case["tetrahedronnet z"]
			  > eps[createTetrahedon3] /*'tetrahedronnet z' matched, so create
										  an object*/
			  > tetrahedronnet_z_;

		geometrycollection_z_tagged_text_ /*SFS conform: OGC 06-103r4*/
			= no_case["geometrycollection z"]
			  > eps[createGeomCollection] /*'geometrycollection z' matched, so
											 create an object*/
			  > geometrycollection_z_text_;

		geometry_z_tagged_text_
			= point_z_tagged_text_ /*SFS conform: OGC 06-103r4*/
			  | linestring_z_tagged_text_ /*SFS conform: OGC 06-103r4*/
			  | polygon_z_tagged_text_ /*SFS conform: OGC 06-103r4*/
			  | polyhedralsurface_z_tagged_text_ /*SFS conform: OGC 06-103r4*/
			  | triangle_tagged_text_ /*SFS conform: OGC 06-103r4*/
			  | tin_tagged_text_ /*SFS conform: OGC 06-103r4*/
			  | multipoint_z_tagged_text_ /*SFS conform: OGC 06-103r4*/
			  | multilinestring_z_tagged_text_ /*SFS conform: OGC 06-103r4*/
			  | multipolygon_z_tagged_text_ /*SFS conform: OGC 06-103r4*/
			  | geometrycollection_z_tagged_text_ /*SFS conform: OGC 06-103r4*/
			  | trianglenet_tagged_text_ /*this is our TIN; less memory
											consuming*/
			  | tetrahedronnet_tagged_text_ /*this is our multipolygon; less
											   memory consuming*/
			;
		// ### end of grammar definiton ###
		//

		//
		// ### error handling
		geometry_z_tagged_text_.name("geometry_z_tagged_text_");
		point_z_tagged_text_.name("point_z_tagged_text_");
		multipoint_z_tagged_text_.name("multipoint_z_tagged_text_");
		multipoint_z_text_.name("multipoint_z_text_");
		multilinestring_z_tagged_text_.name("multilinestring_z_tagged_text_");
		multilinestring_z_text_.name("multilinestring_z_text_");
		polygon_z_tagged_text_.name("polygon_z_tagged_text_");
		polygon_z_text_.name("polygon_z_text_");
		multipolygon_z_tagged_text_.name("multipolygon_z_tagged_text_");
		multipolygon_z_text_.name("multipolygon_z_text_");
		linestring_z_tagged_text_.name("linestring_z_tagged_text_");
		linestring_z_text_.name("linestring_z_text_");
		point_z_text_.name("point_z_text_");
		point_z_.name("point_z_");
		polyhedralsurface_z_tagged_text_.name(
			"polyhedralsurface_z_tagged_text_");
		polyhedralsurface_z_text_.name("polyhedralsurface_z_text_");
		triangle_tagged_text_.name("triangle_tagged_text_");
		tin_tagged_text_.name("tin_tagged_text_");
		geometrycollection_z_tagged_text_.name(
			"geometrycollection_z_tagged_text_");
		geometrycollection_z_text_.name("geometrycollection_z_text_");
		trianglenet_tagged_text_.name("trianglenet_tagged_text_");
		tetrahedronnet_tagged_text_.name("tetrahedronnet_tagged_text_");
		trianglenet_z_.name("trianglenet_z_");
		tetrahedronnet_z_.name("tetrahedronnet_z_");
		triangle_point_index_list_.name("triangle_point_index_list_");
		tetrahedron_point_index_list_.name("tetrahedron_point_index_list_");
		triangle_point_index_.name("triangle_point_index_");
		tetrahedron_point_index_.name("tetrahedron_point_index_");

		namespace qil = qi::labels;

		qi::on_error<qi::fail>(geometry_z_tagged_text_,
							   error_handler_func(error_handler())(
								   qil::_1, qil::_2, qil::_3, qil::_4));
		// ### end of error handling
		//

	} // end of c'tor

	qi::rule<Iterator, Skipper> geometry_z_tagged_text_;
	qi::rule<Iterator, Skipper> point_z_tagged_text_;
	qi::rule<Iterator, Skipper> point_z_text_;
	qi::rule<Iterator, RawPoint3(), Skipper> point_z_;
	qi::rule<Iterator, Skipper> multipoint_z_tagged_text_;
	qi::rule<Iterator, Skipper> multipoint_z_text_;
	qi::rule<Iterator, Skipper> multilinestring_z_tagged_text_;
	qi::rule<Iterator, Skipper> multilinestring_z_text_;
	qi::rule<Iterator, Skipper> polygon_z_tagged_text_;
	qi::rule<Iterator, Skipper> polygon_z_text_;
	qi::rule<Iterator, Skipper> multipolygon_z_tagged_text_;
	qi::rule<Iterator, Skipper> multipolygon_z_text_;
	qi::rule<Iterator, Skipper> linestring_z_tagged_text_;
	qi::rule<Iterator, Skipper> linestring_z_text_;
	qi::rule<Iterator, Skipper> polyhedralsurface_z_tagged_text_;
	qi::rule<Iterator, Skipper> polyhedralsurface_z_text_;
	qi::rule<Iterator, Skipper> triangle_tagged_text_;
	qi::rule<Iterator, Skipper> tin_tagged_text_;
	qi::rule<Iterator, Skipper> geometrycollection_z_tagged_text_;
	qi::rule<Iterator, Skipper> geometrycollection_z_text_;
	qi::rule<Iterator, Skipper> trianglenet_tagged_text_;
	qi::rule<Iterator, Skipper> trianglenet_z_;
	qi::rule<Iterator, Skipper> tetrahedronnet_tagged_text_;
	qi::rule<Iterator, Skipper> tetrahedronnet_z_;
	qi::rule<Iterator, Skipper> triangle_point_index_list_;
	qi::rule<Iterator, Skipper> tetrahedron_point_index_list_;
	qi::rule<Iterator, IdxTriangle(), Skipper> triangle_point_index_;
	qi::rule<Iterator, IdxTetrahedron(), Skipper> tetrahedron_point_index_;

private:
	boost::shared_ptr<IGeometryActions> _actions;
};

} // namespace Parsers
} // namespace GST

#endif // WKT_GRAMMAR_H
