#ifndef _NETWORK_INTERFACE_EXCEPTIONS_H_
#define _NETWORK_INTERFACE_EXCEPTIONS_H_

#include <GSTenums.h>
#include <Utils/GSTVersion.h>
#include <boost/lexical_cast.hpp>
#include <buildspec.h>
#include <config.h>
#include <exceptions/GSTRuntimeException.h>

#include <string>

namespace GST
{

// prototypes:
namespace ClientUtils
{
class IDataTable;
class FeatureClassDetailedDesc;
class GeoColumnDesc;
struct FeatureDesc;
class FeatureClassDesc;
} // namespace ClientUtils
namespace Geometry
{
class ClientGeometry;
}

// exceptions:
namespace exceptions
{
/**
 * Throw whenever it is not possible to use a connection.
 */
class NoConnection : public GSTRuntimeException
{
public:
	NoConnection(const std::string &throwLocation, const std::string &msg)
		: GSTRuntimeException(throwLocation, msg)
	{
	}
};

class ServerCrowded : public NoConnection
{
public:
	ServerCrowded(const std::string &throwLocation, const std::string &msg)
		: NoConnection(throwLocation, msg)
	{
	}
};

class NoAdminConnection : public NoConnection
{
public:
	NoAdminConnection(const std::string &throwLocation, const std::string &msg)
		: NoConnection(throwLocation, msg)
	{
	}
};

/**
 * thrown on connect if the client is depreciated for the endpoint storage
 * version.
 */
class ClientDeprecated : public GSTRuntimeException
{
public:
	ClientDeprecated(const std::string &throwLocation, const std::string &msg)
		: GSTRuntimeException(throwLocation, msg)
	{
	}
};

/**
 * thrown on connect if the backend (=gst storage) is depreciated for this
 * version of client.
 */
class BackendDeprecated : public GSTRuntimeException
{
public:
	BackendDeprecated(const std::string &throwLocation, const std::string &msg)
		: GSTRuntimeException(throwLocation, msg)
	{
	}
};

class BackendAboveOrEqualRequired : public BackendDeprecated
{
public:
	Utils::GSTVersion minmalRequiredVersion;

	BackendAboveOrEqualRequired(const std::string &throwLocation,
								const std::string &msg,
								const Utils::GSTVersion &minmalRequiredVersion)
		: BackendDeprecated(throwLocation, msg)
		, minmalRequiredVersion(minmalRequiredVersion)
	{
	}

	virtual ~BackendAboveOrEqualRequired() throw()
	{
	}
};

class ServerInformationNotFound : public GSTRuntimeException
{
public:
	ServerInformationNotFound(const std::string &throwLocation)
		: GSTRuntimeException(
			  throwLocation,
			  "Relation server_information not found. Create one!")
	{
	}
};

class UpdateFailed : public GSTRuntimeException
{
public:
	UpdateFailed(const std::string &throwLocation, const std::string msg)
		: GSTRuntimeException(throwLocation, msg)
	{
	}
};

class InsertFailed : public GSTRuntimeException
{
public:
	InsertFailed(const std::string &throwLocation, const std::string msg)
		: GSTRuntimeException(throwLocation, msg)
	{
	}
};

class FeatureClassCreationFailed : public GSTRuntimeException
{
public:
	FeatureClassCreationFailed(const std::string &throwLocation,
							   const std::string msg)
		: GSTRuntimeException(throwLocation, msg)
	{
	}
};

class FeatureClassDoesNotExist : public GSTRuntimeException
{
public:
	FeatureClassDoesNotExist(const std::string &throwLocation,
							 const std::string &fclass)
		: GSTRuntimeException(throwLocation, "")
	{
		this->_msg = "FeatureDesc class " + fclass + " does not exist.";
	}
};

class LockKeyDoesNotExist : public GSTRuntimeException
{
public:
	std::string lockKey;

	LockKeyDoesNotExist(const std::string &throwLocation,
						const std::string &msg,
						const std::string &lockKey)
		: GSTRuntimeException(throwLocation, msg)
		, lockKey(lockKey)
	{
	}
	virtual ~LockKeyDoesNotExist() throw()
	{
	}
};

class FeatureHasNoPropertyTable : public GSTRuntimeException
{
public:
	FeatureHasNoPropertyTable(const std::string &throwLocation,
							  const std::string &msg)
		: GSTRuntimeException(throwLocation, msg)
	{
	}
};

class ViolateConvention : public GSTRuntimeException
{
public:
	ViolateConvention(const std::string &throwLocation, const std::string &msg)
		: GSTRuntimeException(throwLocation, msg)
	{
	}
};

class NotAssigned : public GSTRuntimeException
{
public:
	NotAssigned(const std::string &throwLocation, const std::string &msg)
		: GSTRuntimeException(throwLocation, msg)
	{
	}
};

class DuplicateName : public GSTRuntimeException
{
public:
	DuplicateName(const std::string &throwLocation, const std::string &msg)
		: GSTRuntimeException(throwLocation, msg)
	{
	}
};

class NameAlreadyExists : public GSTRuntimeException
{
public:
	std::string suggestedName;

	NameAlreadyExists(const std::string &throwLocation,
					  const std::string &msg,
					  const std::string uniqueName)
		: GSTRuntimeException(throwLocation, msg)
		, suggestedName(uniqueName)
	{
	}

	virtual ~NameAlreadyExists() throw()
	{
	}
};

class NotFound : public GSTRuntimeException
{
public:
	NotFound(const std::string &throwLocation, const std::string &msg)
		: GSTRuntimeException(throwLocation, msg)
	{
	}
};

class GeometryTypeUnkown : public GSTRuntimeException
{
public:
	GeometryTypeUnkown(const std::string &throwLocation,
					   Geometry::GeometryTypes type)
		: GSTRuntimeException(throwLocation, "")
	{
		this->_msg = "Geometry type \"" + boost::lexical_cast<std::string>(type)
					 + "\" unknown.";
	}
};

class AlignmentTypeUnkown : public GSTRuntimeException
{
public:
	AlignmentTypeUnkown(const std::string &throwLocation,
						Geometry::PropertyAlignments type)
		: GSTRuntimeException(throwLocation, "")
	{
		this->_msg = "Alignment type \""
					 + boost::lexical_cast<std::string>(type) + "\" unknown.";
	}
};

/**
 * Thrown if result set is invalid.
 */
class BadResultsetException : public GSTRuntimeException
{
public:
	BadResultsetException(const std::string &throwLocation,
						  const std::string &msg)
		: GSTRuntimeException(throwLocation, msg)
	{
	}
};

/**
 * Thrown on accessing data from table if cell identifer is out of range.
 * Example: The given (row,col) pair does not exist in table.
 */
class InvalidCellKey : public GSTRuntimeException
{
public:
	InvalidCellKey(const std::string &throwLocation, const std::string &msg)
		: GSTRuntimeException(throwLocation, msg)
	{
	}
};

/**
 * Thrown on accessing data from table if cell is null value.
 * Prevent this by call isNull before get value.
 */
class IsNull : public GSTRuntimeException
{
public:
	std::string colName;
	int colNumber;

	IsNull(const std::string &throwLocation,
		   const std::string &colName,
		   int col)
		: GSTRuntimeException(throwLocation, "")
		, colName(colName)
		, colNumber(col)
	{
		_msg = "Value of " + colName + " (col num: "
			   + boost::lexical_cast<std::string>(col) + ") is null.";
	}
	~IsNull() throw()
	{
	}
};

/**
 * Thrown on accessing data from table if requested return type does not exist.
 * Try to use another type or add in derived class in getAs() the type you need!
 */
class UnregisteredType : public GSTRuntimeException
{
public:
	UnregisteredType(const std::string &throwLocation, const std::type_info &t)
		: GSTRuntimeException(throwLocation, "")
	{
		_msg = "type \"" + std::string(t.name()) + "\" not registered.";
	}
};

} // namespace exceptions

} // namespace GST

#endif // _NETWORK_INTERFACE_EXCEPTIONS_H_
