#ifndef _FEATURE_CLASS_DESC_H_
#define _FEATURE_CLASS_DESC_H_

#include <Client/ClientUtils/Network/NetworkInterfaceStructs.h>
#include <Client/ClientUtils/Network/ObjectPropertyDesc.h>
#include <Client/ClientUtils/Network/SimplexPropertyDesc.h>
#include <Client/ClientUtils/Network/TableAttributeDesc.h>
#include <Client/ClientUtils/Network/TableConstrainedAttributeDesc.h>
#include <GSTenums.h>
#include <Geometry/IGeometry.h>
#include <Geometry/SRSItem.h>
#include <buildspec.h>
#include <config.h>
#include <exceptions/GSTRuntimeException.h>

using GST::Geometry::IGeometry;
using GST::Geometry::PropertyAlignments;

#include <Geometry/boost_serialize_includes.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/optional.hpp>
#include <boost/shared_ptr.hpp>
#include <string>

namespace GST
{

// exceptions:
// see below the class

namespace ClientUtils
{

/**
 * A special TableAttributeDesc
 */
class GST_API_EXPORT GeoColumnDesc : public TableAttributeDesc
{
	friend class boost::serialization::access;

public:
	typedef PropertyAlignments PropertyAlignment;

	GeoColumnDesc(const std::string &name,
				  const std::string &fullFeatureClassName,
				  const IGeometry::GeometryType &geometryType,
				  Geometry::SRSPtr srs = Geometry::SRSPtr(),
				  const PropertyAlignment &alignment = Geometry::VERTEX_ALIGNED,
				  const std::string propertyTable = "");

	///@name getters
	//@{
	/// returns (unquoted) the table name
	std::string getPropertyTableName() const;
	/// returns (unquoted) schema.tablename
	std::string getFullPropertyTableName() const;
	void setPropertyTableName(const std::string &tableName);
	std::string getGeometryTypeAsDDL() const;
	std::string getPropertyAlignmentAsDDL() const;

	GST::ClientUtils::GeoColumnDesc::PropertyAlignment getAlignment() const;
	void setAlignment(GST::ClientUtils::GeoColumnDesc::PropertyAlignment val);
	IGeometry::GeometryType getGeometryType() const;
	void setGeometryType(IGeometry::GeometryType val);
	GST::Geometry::SRSPtr getSrs() const;
	void setSrs(GST::Geometry::SRSPtr val);
	//@}

protected:
	IGeometry::GeometryType geomtype;
	PropertyAlignment alignment;
	std::string propertyTableName;
	Geometry::SRSPtr srs;

	GeoColumnDesc() : TableAttributeDesc()
	{
	}

private:
	template<typename Archive>
	void serialize(Archive &ar, const unsigned int version)
	{
		ar &boost::serialization::make_nvp(
			"columnDesc",
			boost::serialization::base_object<TableAttributeDesc>(*this));
		// If you change members here do not forget to increment object version
		// (see bottom of this file)
		ar &boost::serialization::make_nvp("geomtype", this->geomtype);
		ar &boost::serialization::make_nvp("alignment", this->alignment);
		ar &boost::serialization::make_nvp("propertyTableName",
										   this->propertyTableName);
		ar &boost::serialization::make_nvp("srs", this->srs);
	}
};
typedef boost::shared_ptr<GeoColumnDesc> GeoColumnDescPtr;

//-------------------------- class of headerfile
//--------------------------------

/**
 * A detailed description of a feature class
 */
class GST_API_EXPORT FeatureClassDetailedDesc
{
	friend class boost::serialization::access;

protected:
	std::string name;
	GeoColumnDescPtr geometryColumn;
	std::string owner;
	ObjectPropertyDescListPtr objectProperties;
	PropertyAliasMapPtr objectPropertyAliases;
	SimplexPropertyDescListPtr simplexProperties;
	PropertyAliasMapPtr simplexPropertyAliases;
	int numberOfFeatures;
	long long id;
	ZAxisDomain zAxisDomain;
	std::set<FeatureClassTag> tags;
	bool protected_;
	std::vector<SubfeatureKind> subfeatureKinds;
	ObjectPropertyDescListBySubFeatureKind subFeatureAttributesByKind;
	SimplexPropertyDescListBySubFeatureKind subFeaturePropertiesByKind;
	PropertyAliasMapBySubFeatureKind subFeatureAttributeAliasesByKind;
	PropertyAliasMapBySubFeatureKind subFeaturePropertyAliasesByKind;

public:
	FeatureClassDetailedDesc();

	FeatureClassDescPtr getSimpleDesc() const;
	bool isPublic() const;
	void setGroup(const std::string &group);
	std::string getGroup() const;
	std::string getFullName() const;

	std::string getName() const;
	void setName(std::string val);
	GST::ClientUtils::GeoColumnDescPtr getGeometryColumn() const;
	void setGeometryColumn(GST::ClientUtils::GeoColumnDescPtr val);
	std::string getOwner() const;
	void setOwner(std::string val);
	ObjectPropertyDescListPtr getObjectProperties() const;
	void setObjectProperties(ObjectPropertyDescListPtr val);
	SimplexPropertyDescListPtr getSimplexProperties() const;
	void setSimplexProperties(SimplexPropertyDescListPtr val);
	PropertyAliasMapPtr getObjectPropertyAliases() const;
	void setObjectPropertyAliases(PropertyAliasMapPtr val);
	PropertyAliasMapPtr getSimplexPropertyAliases() const;
	void setSimplexPropertyAliases(PropertyAliasMapPtr val);
	int getNumberOfFeatures() const;
	void setNumberOfFeatures(int val);
	long long getID() const;
	void setID(const long long val);
	ZAxisDomain getZAxisDomain() const;
	void setZAxisDomain(ZAxisDomain zAxisDomain);
	const std::set<FeatureClassTag> &getTags() const;
	void setTags(std::set<FeatureClassTag> tags);
	bool isProtected() const;
	void setProtected(bool value);
	const std::vector<SubfeatureKind> &getSubfeatureKinds() const;
	void setSubfeatureKinds(std::vector<SubfeatureKind> subfeatureKinds);
	void setSubFeatureAttributesByKindMap(
		ObjectPropertyDescListBySubFeatureKind value);
	const ObjectPropertyDescListBySubFeatureKind &
	getSubFeatureAttributesByKindMap() const;
	const PropertyAliasMapBySubFeatureKind &
	getSubFeatureAttributesAliasesByKindMap() const;
	void setSubFeatureAttributesAliasesByKindMap(
		PropertyAliasMapBySubFeatureKind val);
	void setSubFeaturePropertiesByKindMap(
		SimplexPropertyDescListBySubFeatureKind value);
	const SimplexPropertyDescListBySubFeatureKind &
	getSubFeaturePropertiesByKindMap() const;
	const PropertyAliasMapBySubFeatureKind &
	getSubFeaturePropertyAliasesByKindMap() const;
	void setSubFeaturePropertyAliasesByKindMap(
		PropertyAliasMapBySubFeatureKind val);

	std::map<int, PropertyAliasPtr> getAttributeAliasFromKind(
		const std::string &sub_kind,
		const ObjectPropertyDesc &objProp);

	std::map<int, PropertyAliasPtr> getPropertyAlias(
		const ObjectPropertyDesc &objProp);
	std::map<int, PropertyAliasPtr> getPropertyAlias(
		const SimplexPropertyDesc &simplexProp);

private:
	template<typename Archive>
	void serialize(Archive &ar, const unsigned int version)
	{
		if(version < 2)
		{
			throw exceptions::GSTRuntimeException(
				__METHOD_NAME__,
				"Can not read class FeatureClassDetailedDesc below version 2.");
		}

		// If you change members here do not forget to increment object version
		// (see bottom of this file)
		ar &boost::serialization::make_nvp("name", this->name);
		ar &boost::serialization::make_nvp("geometryColumn",
										   this->geometryColumn);
		ar &boost::serialization::make_nvp("owner", this->owner);
		// removing m_shape before serializing object properties and readding it
		// afterwards
		ObjectPropertyDescList::iterator it
			= this->objectProperties->find("m_shape");
		const bool found = (it != this->objectProperties->end());
		ObjectPropertyDescPtr m_shape;
		if(found)
		{
			m_shape = it->second;
			this->objectProperties->erase(it);
		}
		ar &boost::serialization::make_nvp("objectProperties",
										   this->objectProperties);
		// readding m_shape to object properties again
		if(found)
		{
			(*this->objectProperties)["m_shape"] = m_shape;
		}
		ar &boost::serialization::make_nvp("simplexProperties",
										   this->simplexProperties);

		if(version >= 3)
		{
			ar &boost::serialization::make_nvp("objectPropertyAliases",
											   this->objectPropertyAliases);
			ar &boost::serialization::make_nvp("simplexPropertyAliases",
											   this->simplexPropertyAliases);
		}
		if(version >= 4)
		{
			ar &boost::serialization::make_nvp("id", this->id);
		}
		if(version >= 5)
		{
			ar &boost::serialization::make_nvp("zAxisDomain",
											   this->zAxisDomain);
		}
		if(version >= 6)
		{
			// `class std::set<GST::ClientUtils::FeatureClassTag>` has no member
			// named `serialize`
			// ar &boost::serialization::make_nvp("tag", this->tag);
			ar &boost::serialization::make_nvp("protected", this->protected_);
		}
		if(version >= 7)
		{
			ar &boost::serialization::make_nvp(
				"subFeatureAttributesByKind", this->subFeatureAttributesByKind);
		}
		if(version >= 8)
		{
			ar &boost::serialization::make_nvp("subfeatureKinds",
											   this->subfeatureKinds);
			ar &boost::serialization::make_nvp(
				"subFeaturePropertiesByKind", this->subFeaturePropertiesByKind);
			ar &boost::serialization::make_nvp(
				"subFeatureAttributeAliasesByKind",
				this->subFeatureAttributeAliasesByKind);
			ar &boost::serialization::make_nvp(
				"subFeaturePropertyAliasesByKind",
				this->subFeaturePropertyAliasesByKind);
		}
	}
};
typedef boost::shared_ptr<FeatureClassDetailedDesc> FeatureClassDetailedDescPtr;
typedef std::vector<FeatureClassDetailedDescPtr> FeatureClassDetailedDescList;
typedef boost::shared_ptr<FeatureClassDetailedDescList>
	FeatureClassDetailedDescListPtr;

} // namespace ClientUtils
} // namespace GST

BOOST_CLASS_EXPORT_KEY(GST::ClientUtils::GeoColumnDesc);
BOOST_CLASS_EXPORT_KEY(GST::ClientUtils::FeatureClassDetailedDesc);

BOOST_CLASS_VERSION(GST::ClientUtils::GeoColumnDesc, 1)
BOOST_CLASS_VERSION(GST::ClientUtils::FeatureClassDetailedDesc, 8)

#endif //_FEATURE_CLASS_DESC_H_
