#ifndef GST_MoMaStructs_h__
#define GST_MoMaStructs_h__

#include <Client/ClientUtils/Network/NetworkInterfaceStructs.h>
#include <Geometry/IGeometry.h>
#include <Utils/utils.h>
#include <boost/make_shared.hpp>
#include <boost/shared_ptr.hpp>
#include <buildspec.h>
#include <config.h>

#include <cstdint>
#include <string>
#include <vector>

namespace GST
{
namespace ClientUtils
{

class MoMaPropertyValue;

struct Owner;
typedef boost::shared_ptr<Owner> OwnerPtr;
typedef std::vector<OwnerPtr> OwnerList;
typedef boost::shared_ptr<OwnerList> OwnerListPtr;
struct Owner
{
	enum OwnerType
	{
		User,
		Group
	};
	typedef long OwnerId;

	OwnerId id;
	OwnerType type;
	std::string name;
	std::string createTime;
	bool operator==(const Owner &other) const
	{
		if(id != other.id)
			return false;
		if(type != other.type)
			return false;
		if(name != other.name)
			return false;
		if(createTime != other.createTime)
			return false;
		return true;
	};

	std::string nameWithoutPoolSuffix() const
	{
		if(this->type == User)
		{
			return this->name;
		}
		return Utils::removeSuffix(this->name, "_pool");
	}
};

/**
 * A GST User struct (a GST registered user).
 */
struct GST_API_EXPORT User : public Owner
{
	User(const Owner::OwnerId oid,
		 const std::string &name,
		 const std::string &createTime = std::string())
		: Owner()
	{
		this->type = Owner::User;
		this->id = oid;
		this->name = name;
		this->createTime = createTime;
	}

	User(const std::string &name, const std::string &createTime = std::string())
		: Owner()
	{
		this->type = Owner::User;
		this->id = -1;
		this->name = name;
		this->createTime = createTime;
	}
};
typedef boost::shared_ptr<User> UserPtr;
typedef boost::ptr_vector<User> UserList;
typedef boost::shared_ptr<UserList> UserListPtr;

/**
 * A GST Group struct (a GST registered group).
 */
struct GST_API_EXPORT Group : public Owner
{
	Group(const Owner::OwnerId oid,
		  const std::string &name,
		  const std::string &createTime = std::string())
		: Owner()
	{
		this->type = Owner::Group;
		this->id = oid;
		this->name = name;
		this->createTime = createTime;
	}

	Group(const std::string &name,
		  const std::string &createTime = std::string())
		: Owner()
	{
		this->type = Owner::Group;
		this->id = -1;
		this->name = name;
		this->createTime = createTime;
	}
};
typedef boost::ptr_vector<Group> GroupList;
typedef boost::shared_ptr<GroupList> GroupListPtr;

struct ColorScale
{
	typedef long ColorScaleId;

	ColorScaleId id;
	std::string label;
};
typedef boost::shared_ptr<ColorScale> ColorScalePtr;
typedef std::vector<ColorScalePtr> ColorScaleList;
typedef boost::shared_ptr<ColorScaleList> ColorScaleListPtr;

typedef std::vector<ColorScale::ColorScaleId> ColorScaleIdList;
typedef boost::shared_ptr<ColorScaleIdList> ColorScaleIdListPtr;

struct ColorValue
{
	typedef long ColorValueId;

	ColorValueId id;
	ColorScale::ColorScaleId refers;
	std::string label;
	Geometry::IGeometry::Color color;
	Geometry::IGeometry::TransparencyCode alpha;
};
typedef boost::shared_ptr<ColorValue> ColorValuePtr;
typedef std::vector<ColorValuePtr> ColorValueList;
typedef boost::shared_ptr<ColorValueList> ColorValueListPtr;

struct Element
{
	typedef long ElementId;

	ElementId id;
	Owner::OwnerId owner;
	std::string label;
};
typedef boost::shared_ptr<Element> ElementPtr;
typedef std::vector<ElementPtr> ElementList;
typedef boost::shared_ptr<ElementList> ElementListPtr;

struct LinkAdjacency
{
	enum TargetType
	{
		FeatureTarget,
		ElementTarget,
		UnknownTarget
	};
	typedef long LinkId;
	typedef long ReferId;

	LinkId id;
	ReferId targetId;
	TargetType targetType;
	LinkId parent;
	Owner::OwnerId owner;
	int64_t siblingRank;

	LinkAdjacency()
		: id(-1)
		, targetId(-1)
		, targetType()
		, parent(-1)
		, owner(-1)
		, siblingRank(0) {};

	LinkAdjacency(const LinkId &_id,
				  const ReferId &_targetId,
				  const TargetType &_targetType,
				  const LinkId &_parent,
				  const Owner::OwnerId &_owner,
				  int64_t _siblingRank)
		: id(_id)
		, targetId(_targetId)
		, targetType(_targetType)
		, parent(_parent)
		, owner(_owner)
		, siblingRank(_siblingRank) {};

	virtual ~LinkAdjacency()
	{
	}
};
typedef boost::shared_ptr<LinkAdjacency> LinkAdjacencyPtr;
typedef std::vector<LinkAdjacencyPtr> LinkAdjacencyList;
typedef boost::shared_ptr<LinkAdjacencyList> LinkAdjacencyListPtr;

class AssignableNode
{
protected:
	bool assigned;

public:
	AssignableNode(bool a) : assigned(a)
	{
	}
	virtual ~AssignableNode()
	{
	}
	virtual bool isAssigned() const
	{
		return assigned;
	}
	virtual void setAssigned(bool a)
	{
		assigned = a;
	}
};
typedef boost::shared_ptr<AssignableNode> AssignableNodePtr;
typedef std::vector<AssignableNodePtr> AssignableNodeList;
typedef boost::shared_ptr<AssignableNodeList> AssignableNodeListPtr;

class AssignableElement : public AssignableNode
{
	ElementPtr element;
	ColorScaleIdListPtr colorScaleIds;

public:
	AssignableElement(ElementPtr e, ColorScaleIdListPtr c, bool assigedFlag)
		: element(e)
		, colorScaleIds(c)
		, AssignableNode(assigedFlag)
	{
	}
	ElementPtr getElement() const
	{
		return element;
	}
	ColorScaleIdListPtr getColorScaleIds() const
	{
		return colorScaleIds;
	}
};
typedef boost::shared_ptr<AssignableElement> AssignableElementPtr;
typedef std::vector<AssignableElementPtr> AssignableElementList;
typedef boost::shared_ptr<AssignableElementList> AssignableElementListPtr;

class AssignableFeatureDesc : public AssignableNode
{
	FeatureDescPtr featureDesc;

public:
	AssignableFeatureDesc(FeatureDescPtr f, bool assigedFlag)
		: featureDesc(f)
		, AssignableNode(assigedFlag)
	{
	}
	FeatureDescPtr getFeatureDesc() const
	{
		return featureDesc;
	}
};
typedef boost::shared_ptr<AssignableFeatureDesc> AssignableFeatureDescPtr;
typedef std::vector<AssignableFeatureDescPtr> AssignableFeatureDescList;
typedef boost::shared_ptr<AssignableFeatureDescList>
	AssignableFeatureDescListPtr;

typedef boost::shared_ptr<MoMaPropertyValue> MoMaPropertyValuePtr;
typedef std::vector<MoMaPropertyValuePtr> MoMaPropertyValueList;
typedef boost::shared_ptr<MoMaPropertyValueList> MoMaPropertyValueListPtr;

struct LinkedElement : public LinkAdjacency
{
	AssignableElementPtr linkTarget;
	int64_t childCount;

private:
	GST::ClientUtils::MoMaPropertyValueListPtr assignedValues;

public:
	LinkedElement()
		: linkTarget()
		, childCount(0) {

		};
	LinkedElement(const LinkedElement &other)
		: LinkAdjacency(other)
		, linkTarget(other.linkTarget)
		, childCount(0) {};
	LinkedElement(const LinkAdjacency &other)
		: LinkAdjacency(other)
		, linkTarget()
		, childCount(0) {};
	void addMoMapropertyValue(MoMaPropertyValuePtr _propvalue)
	{
		if(!assignedValues)
			assignedValues = boost::make_shared<MoMaPropertyValueList>();
		assignedValues->push_back(_propvalue);
	};

	void setLinkTarget(const AssignableElementPtr lt)
	{
		linkTarget = lt;
	}

	Element::ElementId getLinkedElementId() const
	{
		return linkTarget->getElement()->id;
	}

	LinkAdjacency::LinkId getNodelinkId() const
	{
		return id;
	}
};

typedef boost::shared_ptr<LinkedElement> LinkedElementPtr;
typedef std::vector<LinkedElementPtr> LinkedElementList;
typedef boost::shared_ptr<LinkedElementList> LinkedElementListPtr;

struct LinkedFeatureDesc : public LinkAdjacency
{
	AssignableFeatureDescPtr linkTarget;
};
typedef boost::shared_ptr<LinkedFeatureDesc> LinkedFeatureDescPtr;
typedef std::vector<LinkedFeatureDescPtr> LinkedFeatureDescList;
typedef boost::shared_ptr<LinkedFeatureDescList> LinkedFeatureDescListPtr;

struct FeatureLinkIdGeometryIdPair
{
	ClientUtils::LinkAdjacency::LinkId featureLinkId;
	long geometryId;

	FeatureLinkIdGeometryIdPair(
		ClientUtils::LinkAdjacency::LinkId featureLinkId,
		long geometryId)
		: featureLinkId(featureLinkId)
		, geometryId(geometryId)
	{
	}
};
typedef std::vector<FeatureLinkIdGeometryIdPair>
	FeatureLinkIdGeometryIdPairList;
typedef boost::shared_ptr<FeatureLinkIdGeometryIdPairList>
	FeatureLinkIdGeometryIdPairListPtr;

/**
 * Commit info
 */
struct GST_API_EXPORT Commit
{
	typedef long CommitKey;

	CommitKey key;
	User::OwnerId userid;
	std::string message;
	/// format YYYY-MM-DD"T"HH24:MI:SS, e.g. 2013-12-06T18:31:25
	std::string timestamp;

	Commit(const long &key,
		   const User::OwnerId &userid,
		   const std::string &message,
		   const std::string &timestamp)
		: key(key)
		, userid(userid)
		, message(message)
		, timestamp(timestamp)
	{
	}
};
typedef boost::shared_ptr<Commit> CommitPtr;
typedef std::vector<CommitPtr> CommitList;
typedef boost::shared_ptr<CommitList> CommitListPtr;

/**
 * Detailed Commit info
 */
struct GST_API_EXPORT CommitVersion
{
	typedef long VersionNo;

	VersionNo versionNo;
	Commit::CommitKey commitKey;
	long idgeo;

	CommitVersion(const VersionNo &versionNo,
				  const Commit::CommitKey &commitKey,
				  const long &idgeo)
		: versionNo(versionNo)
		, commitKey(commitKey)
		, idgeo(idgeo)
	{
	}
};
typedef boost::shared_ptr<CommitVersion> CommitVersionPtr;
typedef std::vector<CommitVersionPtr> CommitVersionList;
typedef boost::shared_ptr<CommitVersionList> CommitVersionListPtr;

struct GST_API_EXPORT AssignedAccessRight
{
	Owner::OwnerId assigneeId = -1;
	Owner::OwnerType assigneeType = Owner::User;
	AccessRight accessRight = AccessRight::Read;
};

} // namespace ClientUtils

// constants

// sentinel to handle quirk in oracle (empty strings are interpreted as NULL
// (wtf))
const std::string EMPTY_STRING_SENTINEL = "@@@empty_string@@@";
// id for new colorvalue
const ClientUtils::ColorValue::ColorValueId NEWCOLORVALUEID = -1;
// id for new colorscale
const ClientUtils::ColorScale::ColorScaleId NEWCOLORSCALEID = -1;
// id of the root node link
const ClientUtils::LinkAdjacency::LinkId ROOT_NODE_LINKID = 0;
// auto sorting moma property name
static const std::string S_M_AUTO_SORTING_MOMA_PROP_NAME = "M_AUTO_SORTING";

namespace ClientUtils
{

struct GST_API_EXPORT MoMaKey
{
	LinkAdjacency::LinkId subtreeRootElement;
	LinkAdjacency::LinkId csRootElement;
	ColorScale::ColorScaleId csRootScale;

	MoMaKey(LinkAdjacency::LinkId subtreeRootElement = ROOT_NODE_LINKID,
			LinkAdjacency::LinkId csRootElement = ROOT_NODE_LINKID,
			ColorScale::ColorScaleId csRootScale = -1)
		: subtreeRootElement(subtreeRootElement)
		, csRootElement(csRootElement)
		, csRootScale(csRootScale)
	{
	}
};
typedef boost::shared_ptr<MoMaKey> MoMaKeyPtr;

} // namespace ClientUtils

namespace exceptions
{
class LinkHasChildren : public GSTRuntimeException
{
public:
	ClientUtils::LinkAdjacencyListPtr childrenList;

	LinkHasChildren(const std::string &throwLocation,
					std::string msg,
					ClientUtils::LinkAdjacencyListPtr childs)
		: GSTRuntimeException(throwLocation, msg)
		, childrenList(childs)
	{
	}
	// http://stackoverflow.com/questions/8618060/c-exception-subclass-string-member-variable
	virtual ~LinkHasChildren() throw()
	{
	}
};

class NodeStillLinkedTo : public GSTRuntimeException
{
public:
	ClientUtils::LinkAdjacencyListPtr nodeLinks;

	NodeStillLinkedTo(const std::string &throwLocation,
					  std::string msg,
					  ClientUtils::LinkAdjacencyListPtr links)
		: GSTRuntimeException(throwLocation, msg)
		, nodeLinks(links)
	{
	}
	// http://stackoverflow.com/questions/8618060/c-exception-subclass-string-member-variable
	virtual ~NodeStillLinkedTo() throw()
	{
	}
};
} // namespace exceptions
} // namespace GST
#endif // GST_MoMaStructs_h__
