#ifndef _ColorMap_h__
#define _ColorMap_h__

#include <buildspec.h>
#include <config.h>

// use of color
#include <Geometry/IGeometry.h>

#include <map>

namespace GST
{
namespace Utils
{
// prototypes:
class Colormap;

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

// some helpers for a map with floating point keys:

struct SafeDoubleLess
{
	bool operator()(double left, double right) const
	{
		bool leftNaN = (left != left);
		bool rightNaN = (right != right);
		if(leftNaN != rightNaN)
			return leftNaN < rightNaN;
		return left < right;
	}
};

/*
template<typename Container>
auto my_equal_range( Container&& container, double target, double epsilon =
0.00001 ) -> decltype( container.equal_range(target) )
{
	auto lower = container.lower_bound( target-epsilon );
	auto upper = container.upper_bound( target+epsilon );
	return std::make_pair(lower, upper);
}

template<typename Container>
bool key_exists( Container&& container, double target, double epsilon = 0.00001
)
{
	auto range = my_equal_range(container, target, epsilon);
	return range.first == range.second;
}
*/

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

/**
 * Represents one segment in the color path trough the color cube. The segment
 * is represented by it's endpoint (toValue with toColor). Getting the lower
 * bound use the segement before by preSegment() method. The first segment in
 * path is a virtual segment with a theoretical value range from -inf to first
 * value of path. The path is stored in member Colormap::m_path
 *
 * @see Colormap
 */
class ColormapSegment
{
	friend class Colormap;

public:
	ColormapSegment(Colormap *parent,
					const float &segmentUpperValue,
					const Geometry::IGeometry::Color &segementUpperColor);
	ColormapSegment *preSegment() const;
	ColormapSegment *postSegment() const;
	float getUpperValue() const;
	Geometry::IGeometry::Color getUpperColor() const;

protected:
	Colormap *m_parent;
	float m_toValue;
	Geometry::IGeometry::Color m_toColor;
};
typedef boost::shared_ptr<ColormapSegment> ColormapSegmentPtr;
typedef std::map<float, ColormapSegmentPtr, SafeDoubleLess> ColorSegmentMap;

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

typedef boost::shared_ptr<Colormap> ColormapPtr;

class Colormap
{
	friend class ColormapSegment;

public:
	typedef ColorSegmentMap::const_iterator const_iterator;

	///@name constructors
	//@{
	Colormap();
	//@}

	enum GetColor_Result
	{
		VALUE_IS_SMALLER_THAN_FIRST_VALUE
		= -1, ///< color was set to the lower bound color of the colormap
		VALUE_IS_INTERPOLATED, ///< color was interpolated due to it's position
							   ///< in colormap
		VALUE_IS_GREATER_THAN_LAST_VALUE, ///< color was set to upper bound
										  ///< color of the colormap
		VALUE_IS_NO_DATA_VALUE ///< color was set to no data value
	};

	const_iterator begin() const;
	const_iterator end() const;

	std::size_t size() const;

	/**
	 * Calculate the color for the given value and write the color to parameter
	 * out_color.
	 */
	GetColor_Result getColor(Geometry::IGeometry::Color &out_color,
							 const float &value);

	/**
	 * Inserts a ColormapSegment in the Colormap path at the end.
	 *
	 * @pre segment's upperValue has to be greater than the upperValue of the
	 * last segment in m_path (last inserted segment).
	 * @throws exceptions::GSTRuntimeException on precondition fail
	 */
	void push_back(float toValue, const Geometry::IGeometry::Color &toColor);

	/**
	 * Inserts a ColormapSegment in the Colormap path.
	 */
	void push(float toValue, const Geometry::IGeometry::Color &toColor);

	void setNodataColor(const Geometry::IGeometry::Color &nodataColor);

	Geometry::IGeometry::Color getNodataColor() const;

	/**
	 * @returns The segment the value is related to.
	 */
	ColormapSegmentPtr findSegment(const float &value) const;

	static void Interpolate(Geometry::IGeometry::Color &out_color,
							const float &interpretationValue,
							const Geometry::IGeometry::Color &lowerColor,
							const float &lowerValue,
							const Geometry::IGeometry::Color &upperColor,
							const float &upperValue);

protected:
	/**
	 * key: upper bound of the (segment) interval, value: segment
	 *
	 * The first segment in path is a virtual segment with a theoretical value
	 * range from -inf to first value of path
	 */
	ColorSegmentMap m_path;

	Geometry::IGeometry::Color m_nodataValueColor;
};

} // namespace Utils
} // namespace GST

#endif // _ColorMap_h__
