#ifndef _CLIENTGEOMETRY_H_
#define _CLIENTGEOMETRY_H_

#include <buildspec.h>

#include <boost/cstdint.hpp>
#include <boost/optional.hpp>
#include <boost/ptr_container/ptr_list.hpp>
#include <boost/ptr_container/ptr_vector.hpp>
#include <boost/shared_ptr.hpp>

#include <Client/ClientUtils/Network/TableAttributeDesc.h>
#include <Client/ClientUtils/writer_options.hpp>
#include <Geometry/ClientSimplexProperty.h>
#include <Geometry/IFileSerialisable.h>
#include <Geometry/ObjectArchive.h>
#include <Geometry/StreamableGeometry.h>
#include <Geometry/geometry_structs.hpp>

namespace GST
{

// forward declaration (may restructure target)
namespace ClientGUI
{
struct UploadPackage;
}

namespace Geometry
{

/**
 * this class handles the forward declared pointer
 */
struct UploadPackageRef
{
	ClientGUI::UploadPackage *ref;
	int refCount;

	UploadPackageRef(ClientGUI::UploadPackage *ref) : refCount(0), ref(ref)
	{
	}
};

/**
 * Class for the geometry on clientside.
 */
class GST_API_EXPORT ClientGeometry
	: public StreamableGeometry
	, public IFileSerialisable
{
protected:
	ClientGeometry();
	ClientGeometry(IGeometry::GeometryType type);

public:
	///@name typedefs
	//@{
	typedef boost::ptr_vector<ClientSimplexProperty> SmplxPropertyList;
	typedef boost::shared_ptr<SmplxPropertyList> SmplxPropertyListPtr;
	typedef SmplxPropertyList::iterator SmplxPropertyItr;
	/**
	 * The Simplex Property Matching list is for geometry sending formats that
	 *aren't separated in geometry stream and property stream (e.g. Gocad ASCII
	 *format). Then a Simplex Property Matching list represents
	 *		#- which property in geometry stream is equivalent with which
	 *property in database (matching by name)
	 *		#- only the properties which are listed in the matching list are
	 *stored. All the other values will be ignored and became NULL in database
	 *
	 * The matching list is simple a key value List. The encoding of the
	 *matching list is the following:
	 * - key: local property name
	 * - value: db property name
	 */
	typedef std::map<std::string, std::string> SimplexPropMatchingList;
	typedef boost::shared_ptr<SimplexPropMatchingList>
		SimplexPropMatchingListPtr;
	//@}

	///@name Client called members (lib independend)
	//@{
	virtual unsigned int addSimplexProperty(
		const std::string &name,
		ClientSimplexProperty::PropertyType PropertyType,
		unsigned int dims,
		ClientSimplexProperty::PropertyAlignment alignment
		= Geometry::VERTEX_ALIGNED)
		= 0;
	virtual unsigned int addSimplexProperty(const ClientSimplexProperty &p) = 0;
	virtual unsigned int addSimplexProperty(
		const ClientUtils::TableAttributeDesc &p);
	virtual SmplxPropertyListPtr getProperties() const = 0;
	virtual void setProperties(const SmplxPropertyListPtr &newlist) = 0;

	virtual std::string constructGeometryFileName(const FileType &filetype,
												  const std::string &prefix
												  = std::string(),
												  const std::string suffix
												  = std::string()) const;
	virtual std::string constructPropertyFileName(const FileType &filetype,
												  const std::string &prefix
												  = std::string(),
												  const std::string suffix
												  = std::string()) const;
	virtual std::string constructArchiveFileName(const FileType &filetype,
												 const std::string &prefix
												 = std::string(),
												 const std::string suffix
												 = std::string()) const;
	virtual std::string constructFileStamp(const FileType &filetype,
										   const std::string &prefix
										   = std::string(),
										   const std::string suffix
										   = std::string()) const;
	virtual void SaveGeometry(const std::string &filename,
							  const FileType &filetype)
		= 0;
	virtual void SaveProperty(const std::string &filename,
							  const FileType &filetype)
		= 0;
	virtual void SaveArchive(const std::string &filename,
							 const FileType &filetype);

	virtual void setSimplexPropertyMatchList(
		SimplexPropMatchingListPtr matchList);
	virtual SimplexPropMatchingListPtr getSimplexPropertyMatchList() const;

	virtual boost::optional<boost::uint64_t> GetGeometryFileStart() const;
	virtual boost::optional<boost::uint64_t> GetGeometryFileEnd() const;
	virtual GeologicalProfileSettingsPtr GetGeologicalProfileSettings() const;
	virtual void SetGeologicalProfileSettings(
		GeologicalProfileSettingsPtr geologicalProfileSettings);
	const boost::optional<std::vector<std::pair<std::string, int64_t>>> &
	GetShapefileCollectionAttributeMapping() const;
	void SetShapefileCollectionAttributeMapping(
		boost::optional<std::vector<std::pair<std::string, int64_t>>>
			shapefileCollectionAttributeMapping);
	ClientUtils::TableAttributeDescListPtr GetShapefileCollectionAttributes()
		const;
	void SetShapefileCollectionAttributes(
		ClientUtils::TableAttributeDescListPtr shapefileCollectionAttributes);
	const std::string &GetUploadTexturePath() const;
	void SetUploadTexturePath(std::string uploadTexture);
	ClientUtils::CoordinateSystemUpAxis GetUpAxis() const;
	void SetUpAxis(ClientUtils::CoordinateSystemUpAxis upAxis);
	ClientUtils::CoordinateSystemHandedness GetHandedness() const;
	void SetHandedness(ClientUtils::CoordinateSystemHandedness handedness);

	//@}

	///@name getters/setters for archive
	//@{
	virtual const GeometryTypes &getType() const;
	virtual void setType(const IGeometry::GeometryType &geomtype);
	virtual void setName(const std::string &name);
	virtual void setOriginalName(const std::string &name);
	virtual void setColor(const IGeometry::Color &color);
	virtual void setTransparency(const IGeometry::TransparencyCode &code);
	virtual void setLockid(const std::string &lockid);
	virtual std::string getLockid() const;
	virtual bool isLocked() const;
	virtual void setFileType(const IFileSerialisable::FileType &ftype);
	virtual IFileSerialisable::FileType GetFileType() const;
	virtual void setGeometryFile(const std::string &fileName);
	virtual std::string GetGeometryFile() const;
	virtual void setPropertyFile(const std::string &fileName);
	virtual std::string GetPropertyFile() const;
	virtual void addObjectPropertyValue(const std::string name,
										const std::string &value);
	virtual void dropObjectProperty(const std::string name);
	virtual std::string getObjectPropertyValue(
		const std::string &propertyName) const;
	virtual ObjectArchive::VariousPropertyMap GetObjProperties() const;
	virtual void clearObjectProperties();
	virtual void setObjProperties(
		const ObjectArchive::VariousPropertyMap &kvmap);
	bool RenameObjProperty(const std::string &oldName,
						   const std::string &newName);
	void setSrs(GST::Geometry::SRSPtr val);

	virtual void setArchiveFileName(const std::string &fileName);
	virtual std::string GetArchiveFileName() const;
	//@}

	ObjArchivePtr GetArchive() const;
	ObjArchivePtr GetOrCreateArchive();
	void setArchive(boost::shared_ptr<ObjectArchive> a);
	void setArchiveLowPriority(boost::shared_ptr<ObjectArchive> a);

	///@name StreamableGeometry members
	//@{
	virtual Geometry::SRSPtr GetSRS() const;
	virtual IGeometry::TransparencyCode GetTransparency() const;
	virtual IGeometry::Color GetColor() const;
	virtual std::string GetName() const;
	virtual std::string GetOriginalName() const;
	virtual KeyValueListPtr GetVariousProperties() const;
	//@}

protected:
	///@name helpers
	//@{
	std::string ExtraktFileNameFromPath(const std::string &filename) const;
	//@}

	///@name members
	//@{
	ObjArchivePtr archive;
	std::string archive_filename;
	SimplexPropMatchingListPtr simplexPropertyMatchingList;
	GeologicalProfileSettingsPtr geologicalProfileSettings;
	GeometryTypes geomtype;
	boost::optional<std::vector<std::pair<std::string, int64_t>>>
		shapefileCollectionAttributeMapping;
	ClientUtils::TableAttributeDescListPtr shapefileCollectionAttributes;
	std::string uploadTexturePath;
	// Axis settings for the geometry.
	//
	// Currently this is only relevant for OBJ, but since it is a general
	// property we put it as general fields.
	ClientUtils::CoordinateSystemUpAxis upAxis
		= ClientUtils::CoordinateSystemUpAxis::Z;
	ClientUtils::CoordinateSystemHandedness handedness
		= ClientUtils::CoordinateSystemHandedness::Right;
	//@}
};
typedef boost::shared_ptr<ClientGeometry> ClientGeometryPtr;
typedef std::vector<ClientGeometryPtr> ClientGeometryList;
typedef boost::shared_ptr<ClientGeometryList> ClientGeometryListPtr;

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

class GST_API_EXPORT NullGeometry : public ClientGeometry
{
public:
	NullGeometry();

	NullGeometry(IGeometry::GeometryType type);

	unsigned int addSimplexProperty(
		const std::string &name,
		ClientSimplexProperty::PropertyType PropertyType,
		unsigned int dims,
		ClientSimplexProperty::PropertyAlignment alignment
		= Geometry::VERTEX_ALIGNED) override;
	unsigned int addSimplexProperty(const ClientSimplexProperty &p) override;

	SmplxPropertyListPtr getProperties() const override;
	void setProperties(const SmplxPropertyListPtr &newlist) override;

	void SaveGeometry(const std::string &filename,
					  const FileType &filetype) override;
	void SaveProperty(const std::string &filename,
					  const FileType &filetype) override;

	bool serializeGeometry(std::ostream &os) const override;
	bool serializeSimplexPropertyValues(std::ostream &os) const override;
	bool serializeSimplexPropertyDescription(std::ostream &os) const override;

	void saveToFile(const std::string &filename,
					const FileType &filetype) override;
	void loadFromFile(const std::string &filename,
					  const FileType &filetype) override;
};

} // namespace Geometry
} // namespace GST

#endif //_CLIENTGEOMETRY_H_
