/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2014 Raytrix GmbH. All rights reserved.
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma once

#include <vector>
#include "Rx.Core/RxString.h"
#include "Rx.Core/RxExceptions.h"

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// namespace: Rx
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
namespace Rx
{
	/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// namespace: Constraint
	/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	namespace Constraint
	{
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Any.
		/// </summary>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		class CAny
		{
		public:

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			CAny()
			{
			}

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			virtual ~CAny()
			{
			}

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			virtual bool IsValid(const void* ptValue)
			{
				return true;
			}

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			virtual CRxString GetInfoString(const void* ptValue)
			{
				return "All values meet this constraint";
			}
		};

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	Range.
		/// </summary>
		///
		/// <typeparam name="T">	Generic type parameter. </typeparam>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		template<class T>
		class CRange : public CAny
		{
		public:

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			CRange()
			{
				Set(T(0), T(0));
			}

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			CRange(T tMin, T tMax)
			{
				Set(tMin, tMax);
			}

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			virtual ~CRange()
			{
			}

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			void Set(T tMin, T tMax)
			{
				if (tMin > tMax)
				{
					RX_THROW(CRxException, "Minimum value is larger than maximum value");
				}

				m_tMin = tMin;
				m_tMax = tMax;
			}

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			void Get(T& tMin, T& tMax) const
			{
				tMin = m_tMin;
				tMax = m_tMax;
			}

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			virtual bool IsValid(const void* ptValue)
			{
				T& tValue = *((T*) ptValue);
				return tValue >= m_tMin && tValue <= m_tMax;
			}

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			virtual CRxString GetInfoString(const void* ptValue)
			{
				if (IsValid(ptValue))
				{
					return RX_S "Given value meets this constraint";
				}
				else
				{
					T& tValue = *((T*) ptValue);
					return RX_S "Given value '" << tValue << "' is not in range of [" << m_tMin << ", " << m_tMax << "]";
				}
			}

		protected:

			T m_tMin, m_tMax;
		};

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	An array range.
		/// </summary>
		///
		/// <typeparam name="TArray"> ARRAY. </typeparam>
		/// <typeparam name="TValue"> VALUE. </typeparam>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		template<class TArray, class TValue>
		class CArrayRange : public CAny
		{
		public:

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			CArrayRange()
			{
				Set(0, 0, TValue(1), TValue(0));
			}

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			CArrayRange(size_t nArrayLenMin, size_t nArrayLenMax, TValue tMin, TValue tMax)
			{
				Set(nArrayLenMin, nArrayLenMax, tMin, tMax);
			}

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			virtual ~CArrayRange()
			{
			}

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			bool Set(size_t nArrayLenMin, size_t nArrayLenMax, TValue tMin, TValue tMax)
			{
				m_nArrayLenMin = nArrayLenMin;
				m_nArrayLenMax = nArrayLenMax;
				m_tMin         = tMin;
				m_tMax         = tMax;
				return true;
			}

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			void Get(size_t& nArrayLenMin, size_t& nArrayLenMax, TValue& tMin, TValue& tMax)
			{
				nArrayLenMin = m_nArrayLenMin;
				nArrayLenMax = m_nArrayLenMax;
				tMin         = m_tMin;
				tMax         = m_tMax;
			}

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			virtual bool IsValid(const void* ptValue)
			{
				const TArray& tArray = *static_cast<const TArray*>(ptValue);

				size_t nSize = tArray.Length();

				// See whether we need to check the array length
				if (m_nArrayLenMax >= m_nArrayLenMin)
				{
					if ((nSize < m_nArrayLenMin) || (nSize > m_nArrayLenMax))
					{
						return false;
					}
				}

				// Now see whether we need to check the range of all array elements
				if (m_tMin <= m_tMax)
				{
					for (size_t nIdx = 0; nIdx < nSize; ++nIdx)
					{
						if ((tArray[nIdx] < m_tMin) || (tArray[nIdx] > m_tMax))
						{
							return false;
						}
					}
				}

				return true;
			}

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			virtual CRxString GetInfoString(const void* ptValue)
			{
				if (IsValid(ptValue))
				{
					return RX_S "Given value meets this constraint";
				}
				else
				{
					return RX_S "Given array has invalid size of values";
				}
			}

		protected:

			size_t m_nArrayLenMin, m_nArrayLenMax;
			TValue m_tMin, m_tMax;
		};

		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 	An array size.
		/// </summary>
		///
		/// <typeparam name="TArray"> ARRAY. </typeparam>
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		template<class TArray>
		class CArraySize : public CAny
		{
		public:

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			CArraySize()
			{
				Set(0, 0);
			}

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			CArraySize(size_t nArrayLenMin, size_t nArrayLenMax)
			{
				Set(nArrayLenMin, nArrayLenMax);
			}

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			virtual ~CArraySize()
			{
			}

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			bool Set(size_t nArrayLenMin, size_t nArrayLenMax)
			{
				m_nArrayLenMin = nArrayLenMin;
				m_nArrayLenMax = nArrayLenMax;
				return true;
			}

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			void Get(size_t& nArrayLenMin, size_t& nArrayLenMax)
			{
				nArrayLenMin = m_nArrayLenMin;
				nArrayLenMax = m_nArrayLenMax;
			}

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			virtual bool IsValid(const void* ptValue)
			{
				size_t nSize = static_cast<const TArray*>(ptValue)->Length();

				// See whether we need to check the array length
				if (m_nArrayLenMax >= m_nArrayLenMin)
				{
					if ((nSize < m_nArrayLenMin) || (nSize > m_nArrayLenMax))
					{
						return false;
					}
				}

				return true;
			}

			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			virtual CRxString GetInfoString(const void* ptValue)
			{
				if (IsValid(ptValue))
				{
					return RX_S "Given value meets this constraint";
				}
				else
				{
					size_t nSize = static_cast<const TArray*>(ptValue)->Length();
					return RX_S "Given array's size '" << nSize << "' is not in range of [" << m_nArrayLenMin << ", " << m_nArrayLenMax << "]";
				}
			}

		protected:

			size_t m_nArrayLenMin, m_nArrayLenMax;
		};
	}
}
