#ifndef GST_CU_STABLE_NETWORK_INTERFACE_H__
#define GST_CU_STABLE_NETWORK_INTERFACE_H__

#include <buildspec.h>

#include "Client/ClientUtils/AreaLimiter.hpp"
#include "Client/ClientUtils/MoMaStructs.h"
#include "Client/ClientUtils/Network/NetworkInterfaceStructs.h"
#include "Client/ClientUtils/Network/TableAttributeDesc.h"
#include "Client/ClientUtils/Network/TableAttributeVal.h"
#include "Client/ClientUtils/Network/TableConstrainedAttributeDesc.h"
#include "Geometry/GeometryBuilder.h"
#include "Geometry/SRSItem.h"

#include <boost/shared_ptr.hpp>

#include <string>
#include <vector>

namespace GST
{
namespace ClientUtils
{
class StableNetworkInterface;
using StableNetworkPtr = boost::shared_ptr<StableNetworkInterface>;

class GST_API_EXPORT StableNetworkInterface
{
public:
	/**
	 * Creates a new stable network connection to the GST3 backend
	 */
	static StableNetworkPtr createGst3Connection(HostPtr host);

	/**
	 * virtual destructor.
	 */
	virtual ~StableNetworkInterface()
	{
	}

	/**
	 * Creates a new CommitKey with supplied commit message
	 *
	 *		@returns the newly created CommitKey
	 *		@parameter message				commit message
	 */
	virtual long BeginCommit(const std::string &message) const = 0;

	/**
	 * Starts a connection and check if the backend is working with this client
	 * version. This methods calls RetreiveAndCheckBackendVersion, so it will
	 * throw the same exceptions.
	 *
	 * @throws exceptions::ClientDepreciated if client is to old. (GST Desktop
	 * needs to be updated.)
	 * @throws exceptions::BackendDepreciated if backend is to old. (GST Storage
	 * needs to be updated.)
	 *
	 * @see \ref compatibility Module Compatibility Check
	 * @see RetreiveAndCheckBackendVersion()
	 */
	virtual void connect() = 0;

	/**
	 * Creates an element from the parameters.
	 * The id of the new Element will written into parameter Element.id.
	 * @param uniqueName if true, performs a pre check whether this name is in
	 * use
	 * @throws DuplicateName if uniqueName==true and name is in use
	 */
	virtual void CreateElement(ElementPtr Element,
							   bool uniqueName = false) const
		= 0;

	/**
	 * Creates a LinkAdjacency from the parameters (adding item to MoMa tree).
	 *
	 * @param parent is the link id of the parent in the MoMa tree
	 * @param refId can be an ElementId (Element::id) or a geometry id
	 * (FeatureDesc::geometryId) if the item you want to link (depending on
	 * refTarget)
	 * @param refTarget describing whether to link a Element or a Feature
	 * (meaning of refId)
	 * @param owner tells the owner of the item
	 *
	 * @returns The created LinkAdjacency
	 * @throws GSTRuntimeException if either parent or reference do not exist in
	 * the database
	 */
	virtual LinkAdjacencyPtr CreateLinkAdjacency(
		LinkAdjacency::LinkId parent,
		LinkAdjacency::ReferId refId,
		LinkAdjacency::TargetType refTarget,
		Owner::OwnerId owner) const
		= 0;
	/**
	 * Creates a LinkAdjacency with starting sibling rank.
	 *
	 * @param parent is the link id of the parent in the MoMa tree
	 * @param refId can be an ElementId (Element::id) or a geometry id
	 *        (FeatureDesc::geometryId) if the item you want to link
	 *        (depending on refTarget)
	 * @param refTarget describing whether to link a Element or a Feature
	 *        (meaning of refId)
	 * @param owner tells the owner of the item
	 * @param siblingRank the rank this node has between its siblings
	 *
	 * @returns The created LinkAdjacency
	 * @throws GSTRuntimeException if either parent or reference do not
	 *         exist in the database
	 */
	virtual LinkAdjacencyPtr CreateLinkAdjacency(
		LinkAdjacency::LinkId parent,
		LinkAdjacency::ReferId refId,
		LinkAdjacency::TargetType refTarget,
		Owner::OwnerId owner,
		int64_t siblingRank) const
		= 0;

	/**
	 * Drops the feature with id id.
	 * @pre Feature must not be linked to anymore
	 * @throws NodeStillLinkedTo exception if the feature still is linked to
	 *
	 * The following exception might be thrown by GST Storage call:
	 * @include GSTAPI_exceptions_Delete_ByShapeID.dox
	 */
	virtual void DeleteFeature(long id) const = 0;

	/**
	 * Deletes the record referenced by the supplied attribute.
	 *
	 * This will execute something equivalent to `DELETE FROM attribute_table
	 * WHERE attribute_column = attribute_value`.
	 */
	virtual void DeleteRecord(
		const GST::ClientUtils::TableAttributeVal &attribute,
		bool quoteTablename = true) const
		= 0;

	/**
	 * Ends up a connection safe.
	 */
	virtual void disconnect() = 0;

	/**
	 * Drops the link with id linkId.
	 * @throws LinkHasChildren if the link to drop still has children.
	 */
	virtual void DropLinkAdjacency(LinkAdjacency::LinkId linkId) const = 0;

	/**
	 * Finalizes the commit with key commitKey
	 *
	 *		@returns error code from db if any. (0 for no error, error code < 0
	 *for error)
	 *		@parameter commitKey			commit to finish
	 */
	virtual int EndCommit(long commitKey) const = 0;

	/**
	 * Copies the actual connection. The copied one is independent of the forked
	 * one. The copied connection is not connected. Call fork()->connect() to
	 * start.
	 */
	virtual StableNetworkPtr fork() const = 0;

	/**
	 * @returns A list of all user and groups available in GST.
	 */
	virtual OwnerListPtr GetAllOwners() const = 0;

	/**
	 * @returns A list of all children from parent that are visible for the
	 * logged in user. Children of type element also inherit the colorScales
	 * from parent
	 */
	virtual LinkAdjacencyListPtr GetChilds(LinkAdjacency::LinkId parent) const
		= 0;

	/**
	 * @returns list of possible values of a column with corresponding values of
	 * primary key columns
	 */
	virtual ConstrainedTableAttributeValArrayPtr GetConstrainedPropertyValues(
		const TableAttributeDesc &cp) const
		= 0;

	/**
	 * @returns The GST User that is currently logged on. This pointer is null
	 * (bool operator of boost::shared_ptr), if the current user is not a
	 * registered GST User.
	 *
	 * If you want to get the Account name, of logged on user use getUserName().
	 */
	virtual UserPtr getCurrentGSTUser() const = 0;

	/**
	 * @returns A list of all (assignable) elements of user owner and level
	 * levelId
	 */
	virtual AssignableElementListPtr GetElements(Owner::OwnerId owner) const
		= 0;

	/**
	 * @returns a Feature instance, that can be parsed / converted or simply
	 *stored to a file.
	 *
	 * The format will be SFSP for mesh geometries or ISATIS for regular grids.
	 *
	 *		@param srs				Requesting SRS. If not a NULL pointer, do a
	 *real time coordinate transformation into specified SRS. (Pass SRSPtr() or
	 *a NoSRS object if you want to get the object in default SRS of the class.)
	 *		@param lock				If true no other user can modify the
	 *requested feature, until it is unlocked. In this case CrudeFeaturePtr will
	 *include a lock key needed to unlock the geometry again by UnlockByKey() or
	 *UpdateFeature()
	 *		@param areaLimiter			Specify an area, if you want to get a
	 *subset of a geometry. The returned CrudeFeaturePtr will be inside this
	 *area. If lock is true, areaLimiter's parameter safeAreaWidth will be used
	 *to determine the locking boundary around the area. In this case the
	 *returned Geometry will have extent of area+safeAreaWidth. For more
	 *information please refer to SeamlessTiles in the documentation!
	 *
	 * @see UnlockByKey
	 * @see UpdateFeature
	 *
	 * The following exception might be thrown by GST Storage call:
	 * If areaLimiter  unset (= QueryBoxPtr()):
	 *     @include GSTAPI_exceptions_Select_Geometry.dox
	 *
	 * If areaLimiter set and lock = false:
	 *     @include GSTAPI_exceptions_Select_Inside.dox
	 *
	 * If areaLimiter set and lock = true:
	 *     @include GSTAPI_exceptions_Select_InsideWithLock.dox
	 */
	virtual CrudeFeaturePtr GetFeature(
		const FeatureDesc &feature,
		Geometry::SRSPtr srs = Geometry::SRSPtr(),
		const boost::optional<AreaLimiter> &areaLimiter = boost::none,
		bool lock = false,
		long commitKey = -1) const
		= 0;

	/**
	 * @returns A Feature description by a geometry id.
	 *
	 * @throws GeometryIdUnknown
	 */
	virtual FeatureDescPtr GetFeatureDescByGeomId(const long &idgeo) const = 0;

	/**
	 * @returns A Feature description by it's lockid or throws.
	 *
	 * @throws GSTRuntimeException	if no geometry locked with the given key
	 */
	virtual FeatureDescPtr GetFeatureDescByLockId(
		const std::string &lockid) const
		= 0;

	/**
	 * Returns information about a lock.
	 *
	 * @see LockInfo
	 */
	virtual LockInfoListPtr GetLockInfo(long idgeo) const = 0;

	/**
	 * @returns A list of the object properties (whole geometry, like name) for
	 * a feature class. result can include both ObjectPropertyListDesc as well
	 * as ConstrainedObjectPropertyDesc
	 *
	 * @param filterMemberProperties if true, properties with the prefix "m_"
	 * are not returned
	 */
	virtual ObjectPropertyDescListPtr GetObjectPropertyList(
		const FeatureClassDesc &featureClass,
		bool filterMemberProperties = true) const
		= 0;

	/**
	 * @returns A list of object properties (including their values) of a given
	 * feature.
	 */
	virtual TableAttributeValListPtr GetObjectPropertyValues(
		const FeatureDesc &feature,
		long commitKey = -1) const
		= 0;

	virtual ObjectPropertyDescListBySubFeatureKind
	GetObjectPropertyListBySubfeatureKind(
		const FeatureClassDesc &featureClass) const
		= 0;

	/**
	 * @returns A list of all users and groups for which the logged in user has
	 * access rights to.
	 */
	virtual OwnerListPtr GetOwners() const = 0;

	/**
	 * @returns A list of the simplex properties (inside geometry) for a feature
	 * class.
	 *
	 * The returned list contains SimplexPropertyDescPtr (pointers to
	 * SimplexPropertyDesc). You can get the property alignment trough
	 * SimplexPropertyDesc::getAlignment()
	 *
	 * @note If the feature class does not have simplex properties (no property
	 * table defined), this method returns a list with size of 0.
	 */
	virtual SimplexPropertyDescListPtr GetSimplexPropertyList(
		const FeatureClassDesc &featureClass) const
		= 0;

	virtual SimplexPropertyDescListBySubFeatureKind
	GetSimplexPropertyListBySubfeatureKind(
		const FeatureClassDesc &featureClass) const
		= 0;

	/**
	 * @brief	Returns the srs definition (including the PROJ4 encoding)
	 *
	 * @throws GST::exceptions::NotFound, if there is no entry found with
	 * srsKey
	 */
	virtual Geometry::SRSItemValPtr GetSRSDefinition(long srsKey) const = 0;

	/**
	 * @returns Values of a table.
	 */
	virtual DataTablePtr GetTableValues(const std::string &tablename,
										const TableAttributeDescList &selection
										= TableAttributeDescList(),
										const std::string &filter
										= std::string()) const
		= 0;

	/**
	 * @returns The account name that are used to connect.
	 */
	virtual std::string getUserName() const = 0;

	/**
	 * Inserts new data into table.
	 */
	virtual void InsertRecord(
		const GST::ClientUtils::TableAttributeValList &attributes,
		bool quoteTablename = true) const
		= 0;

	/**
	  * Inserts a new SRS into GST and returns a ref to it. With it you can
	  create an SRS from various encodings. *		@param label		Name of
	  the created SRS *		@param code_type	A key type identifier *
	  @param code_value	A value for the key (should be a valid value for the
	  specified code_type).
	  *
		\code
		//This sample code creates a wgs reference system in GST, specifying a
	  label and the correct EPGS code. std::string wkt = "GEOGCS["WGS
	  84",DATUM["WGS_1984",SPHEROID["WGS
	  84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]]";
		network->InsertSRS(*GST::Geometry::SRS::FromWKT(wkt), "WGS84", "EPSG",
	  "4326" ); \endcode
	  *
	  * More info about SRS Handling see Module \ref refSRS "Spatial Reference
	  Handling".
	  *
	  * @return The id of the new created SRS.
	  *
	  * @throws GST::exceptions::GSTRuntimeException If new_srs is of type NoSRS
	  or GSTSRSRef (not allowed, because they just refers to SRS's).
	  * @throws GST::exceptions::ViolateConvention If optional parameter
	  code_type is set, but code_value is empty.
	  * @throws DuplicateName if the pair code_type:code is already existent
	  *
	  * The following exception might be thrown by GST Storage call:
	  * @include GSTAPI_exceptions_create_srs.dox
	  */
	virtual Geometry::GSTSRSRefPtr InsertSRS(const Geometry::SRS &new_srs,
											 const std::string &label,
											 const std::string &code_type
											 = std::string(),
											 const std::string &code_value
											 = std::string()) const
		= 0;

	/**
	 * @returns true if the connection is established.
	 */
	virtual bool isConnected() const = 0;

	/**
	 * Verifies if the current connection is a connection with "admin"
	 * privileges (that are required for User Handling see below, e.g.
	 * CreateUser() ).
	 */
	virtual bool isSuperUserConnection() const = 0;

	/**
	 * @returns A list of all (assignable) features of the feature class
	 * featureClass that are visible for the logged in user.
	 */
	virtual AssignableFeatureDescListPtr ListAllFeatures(
		const FeatureClassDesc &featureClass) const
		= 0;

	/**
	 * @returns A list of all SRS. This may take some time until the list can
	 * become large. List entries can be accessed by multi_index_map see
	 * SRSDescList.
	 */
	virtual Geometry::SRSItemDescListPtr ListAllSRS() const = 0;

	/**
	 * @returns A list of all tables of all databases which consist of
	 * references permission on the table or at least on PK column in the table
	 * for connected user
	 */
	virtual NamesListPtr ListAllTables() const = 0;
	virtual std::vector<TableDescription> ListAllTablesEx() const = 0;

	/**
	 * Retrieves a list of the current commits
	 *
	 *		@returns pointer to the commit list
	 */
	virtual CommitListPtr ListCommits() const = 0;

	/**
	 *  Retrieves a list of the commits that changed the current feature.
	 */
	virtual CommitListPtr ListCommitsByFeature(long geometryId) const = 0;

	/**
	 * @returns values of primary keys for a value of given column
	 */
	// virtual KeyValueList GetKeyValuesForColumnValue(std::string columnname,
	// std::string value) const=0;
	virtual FeatureClassDescListPtr ListFeatureClasses() const = 0;

	/**
	 *
	 * @returns a list of MoMaPropertyValues for a linked element inclusive its
	 * parent elements (model and/or unit)
	 * @throws GSTRuntimeException if linked element or MoMaProperty description
	 * doesn't exist
	 *
	 */
	virtual MoMaPropertyValueListPtr ListMoMaPropertyValues(
		const LinkedElement &element) const
		= 0;

	/**
	 * locks the geometry identified by idgeo
	 *
	 * The following exception might be thrown by GST Storage call:
	 * @include GSTAPI_exceptions_lock_geometry.dox
	 */
	virtual std::string LockGeometry(long idgeo) const = 0;

	/**
	 * @returns The desired feature class of a given geometry id.
	 */
	virtual FeatureClassDescPtr ResolveFeatureClassFromGeometryId(
		const long &geometryId) const
		= 0;

	/**
	 * Uses the actual connection to reconnect with different account details.
	 * The connection endpoint keeps the same.
	 */
	virtual StableNetworkPtr reconnect(const std::string &user,
									   const std::string &pwd,
									   const std::string &db
									   = std::string()) const
		= 0;

	/**
	 * To unlock a geometry without updating, use this function. It will release
	 * the lock of Feature by a given lockid (=key).
	 *
	 * The following exception might be thrown by GST Storage call:
	 * @include GSTAPI_exceptions_releaseLock.dox
	 */
	virtual void UnlockByKey(const std::string &key) const = 0;

	/**
	 * Inserts a new geometry into a given class.
	 *
	 * @return A description to the new created feature.
	 *
	 * Setting srs (GST::GeometryBuilder::setSrs) to a valid SRS is required.
	 *
	 * If commitKey is default (-1) then a single commit is automatic created.
	 * This is not recommended, because it ends up in many auto created commits.
	 * Better group your objects into one commit by bracket this methods by
	 * GST::ClienUtils::NetworkInterface::BeginCommit() and
	 * GST::ClienUtils::NetworkInterface::EndCommit()
	 *
	 * @see GST::ClienUtils::NetworkInterface::BeginCommit()
	 * @see GST::ClienUtils::NetworkInterface::EndCommit()
	 *
	 * The following exception might be thrown by GST Storage call:
	 * @include GSTAPI_exceptions_InserGeometry.dox
	 */
	virtual FeatureDescPtr UploadNewFeature(
		const Geometry::GeometryBuilder &feature,
		const FeatureClassDesc &target,
		long commitKey = -1) const
		= 0;

	/**
	 * Inserts a new geometry into a given class, as an update of an existing
	 * object in this class. This is only possible if the geometry is locked and
	 * the given lockid (=key) is valid for this lock. The Geometry will be
	 * updated and the lock will be released (unless keepLock = true). If the
	 * object was selected as SeamlessTile the database takes care for model
	 * consistency (=border check). For more information please refer to
	 * SeamlessTiles in the documentation!
	 *
	 * @throws GST::exceptions::InsertFailed If update failed. The message tells
	 * what happens.
	 *
	 * Setting srs (GST::GeometryBuilder::setSrs) to a valid SRS is required.
	 *
	 * If commitKey is default (-1) then a single commit is automatic created.
	 * This is not recommended, because it ends up in many auto created commits.
	 * Better group your objects into one commit by bracket this methods by
	 * GST::ClienUtils::NetworkInterface::BeginCommit() and
	 * GST::ClienUtils::NetworkInterface::EndCommit()
	 *
	 * @see GST::ClienUtils::NetworkInterface::BeginCommit()
	 * @see GST::ClienUtils::NetworkInterface::EndCommit()
	 *
	 * The following exception might be thrown by GST Storage call:
	 * @include GSTAPI_exceptions_UpdateGeometry.dox
	 */
	virtual void UpdateFeature(const Geometry::GeometryBuilder &feature,
							   const std::string &lockid,
							   const FeatureClassDesc &target,
							   long commitKey = -1,
							   bool keepLock = false) const
		= 0;

	/**
	 * @see UpdateObjectPropertyValue()
	 */
	virtual void UpdateObjectPropertyValues(
		const FeatureDesc &feature,
		const TableAttributeValList &values) const
		= 0;

	/**
	 * Updates one record of a table. (ecord::m_data::description::tablename
	 * tells which table.) Use this method to update a record which you get from
	 * GetTableValues().
	 *
	 * Only Attribute values where record->isModified(column) is true are
	 * getting updated. The primary key columns are used to generate the where
	 * clause in the update statement. So only records with with columns where
	 * record->isPrimaryKey() is true for at least one column. Otherwise this
	 * method throws an exception.
	 */
	virtual void UpdateTableValues(RecordPtr record) const = 0;

	// INSERT UploadNewFeature here
};
} // namespace ClientUtils
} // namespace GST
#endif // GST_CU_STABLE_NETWORK_INTERFACE_H__
