#ifndef _TABLE_ATTRIBUTE_VAL_H_
#define _TABLE_ATTRIBUTE_VAL_H_

#include <Client/ClientUtils/Network/NetworkInterfaceExceptions.hpp>
#include <Client/ClientUtils/Network/TableAttributeDesc.h>
#include <buildspec.h>
#include <exceptions/GSTRuntimeException.h>

#include <boost/any.hpp>
#include <boost/ptr_container/ptr_map.hpp>
#include <boost/shared_ptr.hpp>

#include <string>
#include <vector>

namespace GST
{

// exceptions:
namespace exceptions
{
class BadCast : public GSTRuntimeException
{
public:
	BadCast(const std::string &throwLocation, const std::string &msg)
		: GSTRuntimeException(throwLocation, msg)
	{
	}
};

class KeyIsNull : public GSTRuntimeException
{
public:
	ClientUtils::TableAttributeValPtr key;

	KeyIsNull(const std::string &throwLocation,
			  ClientUtils::TableAttributeValPtr key)
		: GSTRuntimeException(throwLocation, "KeyIsNull")
		, key(key)
	{
	}
	~KeyIsNull() throw()
	{
	}
};
} // namespace exceptions

namespace ClientUtils
{

// prototype:
class AbstractRDBAccess;
class GRPCAccess;
class StableGRPCAccess;

/**
 * A value with a table description attached to it.
 */
class GST_API_EXPORT TableAttributeVal
{
public:
	/// Construct with value ctor
	TableAttributeVal(TableAttributeDescPtr desc, const boost::any &value);
	TableAttributeVal(
		TableAttributeDescPtr desc,
		const std::vector<std::tuple<boost::any, TableAttributeDesc::DType>>
			&values);
	/// Construct with value as string ctor
	TableAttributeVal(TableAttributeDescPtr desc, const std::string &value);
	/// Construct without value (NULL) ctor
	TableAttributeVal(TableAttributeDescPtr desc);
	TableAttributeVal(const TableAttributeVal &other);

	TableAttributeVal();

	virtual ~TableAttributeVal()
	{ ///@todo: implement me!
	}

	void assign(const boost::any &value);
	void assigns(
		const std::vector<std::tuple<boost::any, TableAttributeDesc::DType>>
			&values);
	void assignNull();
	TableAttributeVal &operator=(const TableAttributeVal &other);
	bool operator==(const TableAttributeVal &other) const;
	/**
	 * compares the internal values (dtype and dimension) and return true if
	 * they are equal
	 * @parameter compareDesc optionally compares AttributeName and tablename
	 * from description
	 *
	 */
	bool compareValue(const TableAttributeVal &other,
					  bool compareDesc = false) const;

	bool operator!=(const TableAttributeVal &other) const
	{
		return !(*this == other);
	};
	///@name getters
	//@{
	virtual TableAttributeDescPtr getDesc() const;
	bool isNullValue() const;
	bool isNullValue(const int &row) const;
	/// alias for getValue()
	std::string ToString() const;
	std::string ToString(const int &row) const;
	static bool CanCastToString(TableAttributeDesc::DType dtype);
	double ToDouble() const;
	double ToDouble(const int &row) const;
	static bool CanCastToDouble(TableAttributeDesc::DType dtype);
	boost::any getBoostAny() const;
	boost::any getBoostAny(const int &row) const;
	std::vector<std::tuple<boost::any, TableAttributeDesc::DType>>
	getBoostAnys() const;

	void setDescription(TableAttributeDescPtr d)
	{
		this->description = d;
	}

	template<typename T>
	T getAs() const
	{
		if(this->isNullValue())
		{
			throw GST::exceptions::IsNull(
				__METHOD_NAME__, this->getDesc()->getName(), -1);
		}

		try
		{
			return boost::any_cast<T>(std::get<0>(this->values[0]));
		}
		catch(const boost::bad_any_cast &e)
		{
			throw exceptions::BadCast(
				__METHOD_NAME__,
				"Cast problem. Value was set in incompatible type. "
				"boost_any_cast message: "
					+ std::string(e.what()));
		}
	}
	template<typename T>
	T getAs(const int &row) const
	{
		if(this->isNullValue(row))
		{
			throw GST::exceptions::IsNull(
				__METHOD_NAME__, this->getDesc()->getName(), -1);
		}

		try
		{
			return boost::any_cast<T>(std::get<0>(this->values[row]));
		}
		catch(const boost::bad_any_cast &e)
		{
			throw exceptions::BadCast(
				__METHOD_NAME__,
				"Cast problem. Value was set in incompatible type. "
				"boost_any_cast message: "
					+ std::string(e.what()));
		}
	}
	//@}
	std::vector<std::tuple<boost::any, TableAttributeDesc::DType>> values;

protected:
	TableAttributeDescPtr description;
};
typedef boost::shared_ptr<TableAttributeVal> TableAttributeValPtr;

// single record handle
typedef std::map<std::string, TableAttributeValPtr, Utils::UpperCaseCompare>
	TableAttributeValList;
typedef boost::shared_ptr<TableAttributeValList> TableAttributeValListPtr;
typedef std::vector<TableAttributeValPtr> TableAttributeValueArray;
typedef boost::shared_ptr<TableAttributeValueArray> TableAttributeValueArrayPtr;
/**
 * Maps a feature_id to its attribute values.
 */
typedef std::map<long, TableAttributeValListPtr> ObjectPropertiesByIdgeoMap;
typedef boost::shared_ptr<ObjectPropertiesByIdgeoMap>
	ObjectPropertiesByIdgeoMapPtr;

typedef std::vector<std::string> NamesList;
typedef boost::shared_ptr<NamesList> NamesListPtr;

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

/**
 * Table description with value and with key values of constrained property
 * attached to it.
 *
 * This class represents a constrained property value (one value of the mapped
 * column from the referenced table). The corresponding key values are stored in
 * keysval and can be accessed trough getKeyValues() -- which can be a list of
 * values if the foreign key is consisting of multiple columns.
 *
 * @see TableConstrainedAttributeDesc
 */
class GST_API_EXPORT ConstrainedTableAttributeVal : public TableAttributeVal
{
public:
	/// Construct an empty value (NULL in mapped column)
	ConstrainedTableAttributeVal(TableAttributeDescPtr desc);
	/// Construct with existing val and keys values
	ConstrainedTableAttributeVal(TableAttributeVal val,
								 TableAttributeValueArray keys);
	ConstrainedTableAttributeVal();
	ConstrainedTableAttributeVal(TableAttributeVal val);
	virtual ~ConstrainedTableAttributeVal();

	/**
	 * comparison operator
	 * @returns true if key values, key description and mapped value are equal
	 */
	bool operator==(const ConstrainedTableAttributeVal &op2) const;
	/**
	 * @returns true if the pkValues have equal values like keysval of this
	 * object. pkValues.getDesc() no need to be equal (so values can be in
	 * different tables)
	 * @throws KeyIsNull if one of the pkValues contains a NULL value
	 */
	bool compareKeyValues(const TableAttributeValueArray &pkValues) const;

	///@name getters
	//@{
	virtual NamesList getKeyValuesNameList() const;
	virtual TableAttributeValueArray getKeyValues() const;
	virtual void addKeyValue(TableAttributeValPtr kv);
	//	virtual TableAttributeDescPtr getDesc() const;

	//@}

protected:
	TableAttributeValueArray keysval;
};
typedef boost::shared_ptr<ConstrainedTableAttributeVal>
	ConstrainedTableAttributeValPtr;
typedef std::vector<ConstrainedTableAttributeValPtr>
	ConstrainedTableAttributeValArray;
typedef boost::shared_ptr<ConstrainedTableAttributeValArray>
	ConstrainedTableAttributeValArrayPtr;

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

// table handle
class GST_API_EXPORT Record
{
	friend class AbstractRDBAccess;
	friend class GRPCAccess;
	friend class StableGRPCAccess;

public:
	Record(int columns);
	Record(const TableAttributeValList &values);

	const TableAttributeVal *getValue(int column) const;
	const TableAttributeVal *getValue(const std::string &columnName) const;
	void setValue(int column, const boost::any &changedValue);
	void setValue(const std::string &columnName,
				  const boost::any &changedValue);
	void setNull(int column);
	void setNull(const std::string &columnName);
	bool isNull(int column) const;
	bool isNull(const std::string &columnName) const;
	size_t size() const;

	bool isPrimaryKey(int column) const;
	bool isPrimaryKey(const std::string &columnName) const;
	bool isModified(int column) const;
	bool isModified(const std::string &columnName) const;
	int resolveColumnByName(const std::string &columnName) const;
	void clearModifiedFlags();

protected:
	std::vector<TableAttributeValPtr> m_data;
	std::vector<bool> m_modified;
	std::vector<boost::any> m_originalvalue;
	typedef std::map<std::string, int, Utils::UpperCaseCompare> NameColMap;
	NameColMap m_NameToCol;

	boost::any getOriginalValue(int column) const;
	void initValue(int column, TableAttributeValPtr val);
};
// typedef std::vector< TableAttributeValPtr > Record;
typedef boost::shared_ptr<Record> RecordPtr;
typedef std::vector<RecordPtr> DataTable;
typedef boost::shared_ptr<DataTable> DataTablePtr;

} // namespace ClientUtils
} // namespace GST

#endif //_TABLE_ATTRIBUTE_VAL_H_
