#ifndef _DynamicColorScaleStructs_h__
#define _DynamicColorScaleStructs_h__

#include <Client/ClientUtils/Network/ObjectPropertyDesc.h>
#include <Client/ClientUtils/Network/TableAttributeVal.h>
#include <Client/ClientUtils/Network/constrained_color_value_desc.hpp>
#include <GSTenums.h>
#include <Utils/Color.h>
#include <buildspec.h>

#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/mem_fun.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index_container.hpp>
#include <boost/optional.hpp>
#include <boost/shared_ptr.hpp>

#include <set>
#include <string>
#include <vector>

namespace GST
{
namespace ClientUtils
{

// prototypes
class DynamicColorValue;
typedef boost::shared_ptr<DynamicColorValue> DynamicColorValuePtr;
class DynamicColorScale;

class GST_API_EXPORT DynamicColorValue
{
	friend class DynamicColorScale;

public:
	typedef long ID;

	DynamicColorValue();

	/**
	 * @returns true if this is a DCVNoData
	 */
	virtual bool isNoDataValue() const;

	///@name getter / setter
	//@{
	GST::ClientUtils::DynamicColorValue::ID getId() const
	{
		return m_id;
	}
	void setId(GST::ClientUtils::DynamicColorValue::ID val)
	{
		m_id = val;
	}
	std::string getLabel() const
	{
		return m_label;
	}
	void setLabel(std::string val)
	{
		m_label = val;
	}
	Utils::ColorRGBA getColor() const
	{
		return m_color;
	}
	void setColor(Utils::ColorRGBA val)
	{
		m_color = val;
	}
	static ID GetNewId();
	const DynamicColorScale *getScale() const;
	//@}

protected:
	ID m_id;
	std::string m_label;
	Utils::ColorRGBA m_color;
	DynamicColorScale *m_parent;
	static const ID NewId;
};
typedef std::vector<DynamicColorValuePtr> DynamicColorValueList;
typedef boost::shared_ptr<DynamicColorValueList> DynamicColorValueListPtr;

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

class GST_API_EXPORT DCVInterval : public DynamicColorValue
{
public:
	virtual bool isNoDataValue() const;

	///@name getter / setter
	//@{
	double getToValue() const
	{
		return m_toValue;
	}
	void setToValue(double val)
	{
		m_toValue = val;
	}
	//@}

	bool compare(const DCVInterval &other) const;

protected:
	double m_toValue;
};
typedef boost::shared_ptr<DCVInterval> DCVIntervalPtr;
typedef std::vector<DCVIntervalPtr> DCVIntervalList;
typedef boost::shared_ptr<DCVIntervalList> DCVIntervalListPtr;

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

struct GST_API_EXPORT IntervalCompare
{
	bool operator()(const DCVIntervalPtr &lhs, const DCVIntervalPtr &rhs) const;
};
typedef std::set<DCVIntervalPtr, IntervalCompare> DCVIntervalSet;
typedef boost::shared_ptr<DCVIntervalSet> DCVIntervalSetPtr;

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

class GST_API_EXPORT DCVQuantum : public DynamicColorValue
{
public:
	DCVQuantum();

	virtual bool compare(const DynamicColorValuePtr &other) const;
	virtual bool isNoDataValue() const;

	// helper to find this in set
	static bool CompareValue(DynamicColorValuePtr comparer,
							 const std::string &value);

	///@name getter / setter
	//@{
	std::string getValue() const
	{
		return m_value;
	}
	void setValue(std::string val)
	{
		m_value = val;
	}
	int getOrder() const
	{
		return m_order;
	}
	void setOrder(int val)
	{
		m_order = val;
	}
	//@}

	template<typename T>
	T getAs() const;

protected:
	std::string m_value;
	int m_order;
};
typedef boost::shared_ptr<DCVQuantum> DCVQuantumPtr;
typedef std::vector<DCVQuantumPtr> DCVQuantumList;
typedef boost::shared_ptr<DCVQuantumList> DCVQuantumListPtr;

template<typename T>
T DCVQuantum::getAs() const
{
	return boost::lexical_cast<T>(this->m_value);
}

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

/**
 * Dynamic Color Scale base class.
 *
 * Dynamic color scales are used to give TabeAttributeVal a color+label.
 * Similar to ColorScale and ColorValue structs of MoMa this are classes
 * to interpret features but here by value (not static).
 *
 * DynamicColorScale's constructed by default constructor are new ColorScales
 * (m_id == NewId). Retreive DynamicColorScales through the NetworkInterface
 * or create your own one, by inserting DCVQuantum or DCVInterval.
 *
 * <li> DCVQuantum are used for discrete value interpretation
 *      (DynamicDiscreteScale) </li>
 * <li> DCVInterval are used for continuous value interpretations
 *      (DynamicContinousScale)
 *
 * To connect DynamicColorScales to feature or feature classes the member
 * m_regex is used to match FeatureClass attributes by name. A "regex" is
 * a semicolon separated list of Property names (including the wildcards
 * '*' - Kleene star and '?' - any char).
 * Use the Match*() methods to validate if DynamicColorScales is applicable
 * to a feature.
 */
class GST_API_EXPORT DynamicColorScale
{
public:
	typedef long ID;
	typedef enum DynamicCSType
	{
		TypeContinuous,
		TypeDiscrete
	} DynamicCSType;

	static std::string TypeToString(DynamicCSType type);
	static DynamicCSType StringToType(const std::string &type);

	DynamicColorScale();
	DynamicColorScale(const DynamicColorScale &other);
	DynamicColorScale &operator=(const DynamicColorScale &rhs);
	virtual DynamicColorScale *clone() = 0;

	/**	@returns a human readable string to identify the color scale */
	std::string ToIdentityString() const;

	///@name getter / setter
	//@{
	GST::ClientUtils::DynamicColorScale::ID getId() const
	{
		return m_id;
	}
	void setId(GST::ClientUtils::DynamicColorScale::ID val)
	{
		m_id = val;
	}
	std::string getLabel() const
	{
		return m_label;
	}
	void setLabel(std::string val)
	{
		m_label = val;
	}
	Utils::ColorRGBA getNdvColor() const;
	void setNdvColor(Utils::ColorRGBA val);
	std::string getNdvLabel() const;
	void setNdvLabel(std::string val);
	std::string getRegEx() const;
	void setRegEx(std::string val);
	static ID GetNewId();
	bool isNdvDominant() const
	{
		return m_ndvDominantes;
	}
	void setNdvDominantes(bool val)
	{
		m_ndvDominantes = val;
	}
	long getAccessLevelId() const
	{
		return m_accessLevelId;
	}
	void setAccessLevelId(long accessLevelId)
	{
		m_accessLevelId = accessLevelId;
	}
	//@}

	/**
	 * @return the color scale type
	 */
	virtual DynamicCSType getType() const = 0;

	/**
	 * @returns the number of classes
	 */
	virtual int size() const = 0;
	/**
	 * @return true if the class has no color values
	 */
	virtual bool empty() const;

	/**
	 * Removes all color value from scale. (Afterwards empty() will return true)
	 */
	virtual void clear() = 0;

	///@name iteration interface (ordered ascendant)
	//@{
	/**
	 * Set the intern iterator to end. (next incrIterator will move to first
	 * color value)
	 */
	virtual void resetIterator() = 0;

	/**
	 * Increment intern iterator. @returns false if this is not possible (end of
	 * container).
	 */
	virtual bool incrIterator() = 0;

	/**
	 * @returns element at iterators position
	 *
	 * @throw GSTRuntimeException if iterator points to the end (avoid this
	 *        by checking incrIterator() that returns false)
	 */
	virtual DynamicColorValuePtr getIteratorRef() const = 0;
	//@}

	///@name DynamicColorValue getter/setter interface
	//@{
	/**
	 * Appends a ColorValue to the color scale.
	 *
	 * @note Take care to insert the color value type to the scale DCVQuantum
	 *       or DCVNoData to a DynamicDiscreteScale and DCVInterval or
	 *       DCVNoData to a DynamicContinousScale.
	 *
	 * @throws exceptions::GSTRuntimeException Adding a wrong type will throw.
	 */
	virtual void insert(DynamicColorValuePtr dcv) = 0;

	/**
	 * Tries to drop the value dcv from scale.
	 *
	 * In case dcv is not a value of this scale or being the null data value
	 * an ouput to debug console will be send.
	 * @see Utils::Console::SendDebug()
	 */
	virtual void drop(const DynamicColorValue &dcv) = 0;
	virtual void drop(DynamicColorValuePtr dcv);

	/**
	 * General getter. Depending in this Color Scale type
	 * <ul>
	 * <li> Return interval color value (DCVInterval) for the given value
	 *      or DCVNoDataPtr if value is greater as the biggest inserted
	 *      DCVInterval or smaller/equal the smallest inserted DCVInterval</li>
	 * <li> Return discrete color value (DCVQuantum) for the given value
	 *      or DCVNoDataPtr if value is not inserted before.</li>
	 * </ul>
	 */
	virtual DynamicColorValuePtr GetColorValue(
		const TableAttributeVal &tval) const
		= 0;

	virtual DynamicColorValuePtr GetColorValue(
		const std::string &objectValue) const;
	/**
	 * Returns the no data color value
	 */
	virtual DynamicColorValuePtr GetNoDataColorValue() const;
	//@}

	///@name Match methods
	//@{
	/**
	 * Returns true if desc is matched by the colorscales regex. A "regex" is
	 * a semicolon separated list of Property names (including the wildcards
	 * '*' - Kleene star and '?' - any char).
	 */
	bool MatchByName(const std::string &attributeName) const;

	/**
	 * Return true for compatible dtype. For Descrete Scale the
	 * TableAttributeVal::ToString() resp for Continous Scale the
	 * TableAttributeVal::ToDouble() is called on
	 * DynamicColorScale::GetColorValue(). This methods returns true if the
	 * DType is castable to the scale getter.
	 */
	bool MatchByDType(const TableAttributeDesc::DType &desc) const;

	/**
	 * @returns true if MatchByName() and MatchByDType() matches for the
	 * TableAttributeDesc
	 */
	bool Match(const TableAttributeDesc &desc) const;

	/**
	 * Match by ObjectPropertyDescList (each by name). A ObjectPropertyDescList
	 * is renturned by NetworkInterface::GetObjectPropertyList().
	 *
	 * @returns the first matching property or return an empty pointer if no
	 * ObjectPropertyDesc is matched.
	 */
	ObjectPropertyDescPtr GetMatched(const ObjectPropertyDescList &list) const;

	/**
	 * Use this to detect multiple matches.
	 * @returns All ObjectPropertyDesc from list that are matching the regEx.
	 */
	ObjectPropertyDescList GetMatchedAll(
		const ObjectPropertyDescList &list) const;
	//@}

	bool isConstrained() const;
	const ConstrainedColorValueDesc &constrainedDesc() const;
	void setConstrainedDesc(ConstrainedColorValueDesc desc);

protected:
	ID m_id;
	std::string m_label;
	DynamicColorValuePtr m_ndvValue;
	std::vector<std::string> m_attributeMatches;
	static const ID NewId;
	long m_accessLevelId;
	bool m_ndvDominantes;
	void setThisAsParent(DynamicColorValue *value);
	boost::optional<ConstrainedColorValueDesc> m_ConstrainedInfo;
};
typedef boost::shared_ptr<DynamicColorScale> DynamicColorScalePtr;
typedef std::vector<DynamicColorScalePtr> DynamicColorScaleList;
typedef boost::shared_ptr<DynamicColorScaleList> DynamicColorScaleListPtr;

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

class GST_API_EXPORT DynamicContinousScale : public DynamicColorScale
{
public:
	DynamicContinousScale();
	DynamicContinousScale(const DynamicContinousScale &other);
	DynamicContinousScale &operator=(const DynamicContinousScale &rhs);
	virtual DynamicColorScale *clone();

	virtual int size() const;
	virtual void clear();
	virtual DynamicColorScale::DynamicCSType getType() const;

	///@name DynamicColorValue getter/setter interface
	//@{
	virtual DynamicColorValuePtr GetColorValue(
		const TableAttributeVal &tval) const;
	virtual DynamicColorValuePtr GetColorValue(
		const std::string &objectValue) const;
	virtual void insert(DynamicColorValuePtr dcv);
	virtual void drop(const DynamicColorValue &dcv);
	//@}

	///@name iteration interface
	//@{
	virtual void resetIterator();
	virtual bool incrIterator();
	virtual DynamicColorValuePtr getIteratorRef() const;
	//@}

protected:
	DCVIntervalSet m_intervals;
	DCVIntervalSet::iterator m_intervalItr;
};
typedef boost::shared_ptr<DynamicContinousScale> DynamicContinousScalePtr;

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

class GST_API_EXPORT DynamicDiscreteScale : public DynamicColorScale
{
public:
	DynamicDiscreteScale();
	DynamicDiscreteScale(const DynamicDiscreteScale &other);
	DynamicDiscreteScale &operator=(const DynamicDiscreteScale &rhs);
	virtual DynamicColorScale *clone();

	virtual int size() const;
	virtual void clear();
	virtual DynamicColorScale::DynamicCSType getType() const;

	///@name DynamicColorValue getter/setter interface
	//@{
	virtual DynamicColorValuePtr GetColorValue(
		const TableAttributeVal &tval) const;
	virtual void insert(DynamicColorValuePtr dcv);
	virtual void drop(const DynamicColorValue &dcv);
	//@}

	///@name iteration interface
	//@{
	virtual void resetIterator();
	virtual bool incrIterator();
	virtual DynamicColorValuePtr getIteratorRef() const;
	//@}

protected:
	typedef boost::multi_index::multi_index_container<
		DCVQuantumPtr,
		boost::multi_index::indexed_by<
			// 0-th index: ordered by DCVQuantum::getOrder()
			boost::multi_index::ordered_unique<
				boost::multi_index::
					const_mem_fun<DCVQuantum, int, &DCVQuantum::getOrder>>,
			// 1-st index: hashed_unique by ordered by DCVQuantum::getValue()
			boost::multi_index::hashed_unique<
				boost::multi_index::const_mem_fun<DCVQuantum,
												  std::string,
												  &DCVQuantum::getValue>>>>
		DCVQuantumMultiContainer;

	static const int OrderIndex = 0;
	static const int ValueIndex = 1;
	typedef DCVQuantumMultiContainer::nth_index<0>::type OrderIndexType;
	typedef DCVQuantumMultiContainer::nth_index<1>::type ValueIndexType;

	DCVQuantumMultiContainer m_quantums;
	OrderIndexType::const_iterator m_quantumOrderItr;
};
typedef boost::shared_ptr<DynamicDiscreteScale> DynamicDiscreteScalePtr;

const DynamicColorScale::ID NEW_COLOSCALE_ID = -1;

} // namespace ClientUtils
} // namespace GST

#endif // _DynamicColorScaleStructs_h__
