/***************************************************************************
*                                                                          *
*  Copyright (C) Golaem S.A.  All Rights Reserved.                         *
*                                                                          *
***************************************************************************/

#pragma once

#include "glmCoreAPI.h"
#include "glmAssert.h"

#ifdef _MSC_VER  
	#pragma warning ( push )  
	#pragma warning ( disable : 4701 )  
#endif 

namespace glm
{
	/**
	* \brief Pointer to any type of data loaded into the memory, useful for binary (de)serialisation
	* The data passed to this class must be preallocated in a contiguous memory block. 
	* Memory must be managed outside this class.
	*
	* \ingroup memory
	* \author Golaem <dev@golaem.com>
	*/
	class DataPtr
	{
	private:
		/**
		* @brief Memory data block
		*/
		void* _data;
		/**
		* @brief Size of the data block, in bytes
		*/
		unsigned long _size;
		/**
		* @brief Current position inside the data block, in bytes
		* The value has no sense if _size equals 0.
		*/
		unsigned long _pos;
	public:
		/**
		* @brief Default constructor
		*/
		DataPtr();
		/**
		* @brief Initializes the data pointer
		* The memory must be managed outside this class.
		*/
		void init(void* data, unsigned long size);
		/**
		* @brief Resets the data pointer
		*/
		void reset();
		/**
		* @brief Returns the pointer to the data block
		*/
		const void* getData() const;
		/**
		* @brief Returns the data size
		*/
		unsigned long getSize() const;
		/**
		* @brief Returns the current position inside the data block, in bytes
		* The value has no sense if _size equals 0.
		*/
		unsigned long getPos() const;
		/**
		* @brief Sets the current position inside the data block, in bytes
		* This value must be lower than the size of the data block.
		*/
		void setPos(unsigned long pos);
		/**
		* @brief Returns a value of a given type from the data block.
		* @param[in] pos position of the value in the data block, in bytes.
		* @param[out] success Boolean indicating if the operation was successful.
		*/
		template <class T>
		T getValue(unsigned long pos, bool& success) const;
		/**
		* @brief Returns a value of a given type from the data block.
		* Data is interpreted as an array of characters, in little-endian format.
		* @param[in] pos position of the value in the data block, in bytes.
		* @param[out] success Boolean indicating if the operation was successful.
		*/
		template <class T>
		T getValueC(unsigned long pos, bool& success) const;
		/**
		* @brief Returns the next value of a given type from the data block, starting from the current position.
		* This also increments the current position by the size of the requested value
		* @param[out] success Boolean indicating if the operation was successful.
		*/
		template <class T>
		T getNextValue(bool& success);
		/**
		* @brief Returns the next value of a given type from the data block, starting from the current position.
		* Data is interpreted as an array of characters, in little-endian format.
		* This also increments the current position by the size of the requested value
		* @param[out] success Boolean indicating if the operation was successful.
		*/
		template <class T>
		T getNextValueC(bool& success);
		/**
		* @brief Returns an array of values of a given type from the data block, starting from the current position.
		* This also increments the current position by the total size of the requested values
		* @param[in] nb Number of values to extract.
		* @param[out] values the requested array of values. This array must be preallocated.
		* @param[out] success Boolean indicating if the operation was successful.
		*/
		template <class T>
		void getNextValues(unsigned long nb, T* values, bool& success);
		/**
		* @brief Returns an array of values of a given type from the data block, starting from the current position.
		* Data is interpreted as an array of characters, in little-endian format.
		* This also increments the current position by the total size of the requested values
		* @param[in] nb Number of values to extract.
		* @param[out] values the requested array of values. This array must be preallocated.
		* @param[out] success Boolean indicating if the operation was successful.
		*/
		template <class T>
		void getNextValuesC(unsigned long nb, T* values, bool& success);
		/**
		* @brief Sets a value of a given type inside the data block.
		* @param[in] pos position of the value in the data block, in bytes.
		* @param[in] val value to set.
		* @param[out] success Boolean indicating if the operation was successful.
		*/
		template <class T>
		void setValue(unsigned long pos, const T& val, bool& success);
		/**
		* @brief Sets a value of a given type inside the data block.
		* Data is interpreted as an array of characters, in little-endian format.
		* @param[in] pos position of the value in the data block, in bytes.
		* @param[in] val value to set.
		* @param[out] success Boolean indicating if the operation was successful.
		*/
		template <class T>
		void setValueC(unsigned long pos, const T& val, bool& success);
	};

	template <class T>
	T DataPtr::getValue(unsigned long pos, bool& success) const
	{
		T result;
		if(pos + sizeof(T) <= _size)
		{
			T* valPtr = static_cast<T*>(_data) + pos;
			result = *valPtr;
			success = true;
		}
		else
		{
			success = false;
		}
		return result;
	}

	template <class T>
	T DataPtr::getValueC(unsigned long pos, bool& success) const
	{
		T result;
		if(pos + sizeof(T) <= _size)
		{
			char* valPtr = (char*)_data + pos;
			char* tmp = (char*)&result;
			for(int i = 0; i < sizeof(T); ++i)
			{
				tmp[i] = valPtr[i];
			}
			success = true;
		}
		else
		{
			success = false;
		}
		return result;
	}

	template <class T>
	T DataPtr::getNextValue(bool& success)
	{
		T result;
		if(_pos + sizeof(T) <= _size)
		{
			T* valPtr = static_cast<T*>(_data) + _pos;
			result = *valPtr;
			_pos += sizeof(T);
			success = true;
		}
		else
		{
			success = false;
		}
		return result;
	}

	template <class T>
	T DataPtr::getNextValueC(bool& success)
	{
		T result;
		if(_pos + sizeof(T) <= _size)
		{
			char* valPtr = (char*)_data + _pos;
			char* tmp = (char*)&result;
			for(unsigned int i = 0; i < sizeof(T); ++i)
			{
				tmp[i] = valPtr[i];
			}
			_pos += sizeof(T);
			success = true;
		}
		else
		{
			success = false;
		}
		GLM_DEBUG_ASSERT( success ) ;
		return result;
	}

	template <class T>
	void DataPtr::getNextValues(unsigned long nb, T* values, bool& success)
	{
		if(_pos + nb * sizeof(T) <= _size)
		{
			for(unsigned long iVal = 0; iVal < nb; ++iVal)
			{
				values[iVal] = getNextValue<T>(success);
			}
			success = true;
		}
		else
		{
			success = false;
		}
	}

	template <class T>
	void DataPtr::getNextValuesC(unsigned long nb, T* values, bool& success)
	{
		if(_pos + nb * sizeof(T) <= _size)
		{
			for(unsigned long iVal = 0; iVal < nb; ++iVal)
			{
				values[iVal] = getNextValueC<T>(success);
			}
			success = true;
		}
		else
		{
			success = false;
		}
	}

	template <class T>
	void DataPtr::setValue(unsigned long pos, const T& val, bool& success)
	{
		if(pos + sizeof(T) <= _size)
		{
			T* valPtr = static_cast<T*>(_data) + pos;
			*valPtr  = val;
			success = true;
		}
		else
		{
			success = false;
		}
	}

	template <class T>
	void DataPtr::setValueC(unsigned long pos, const T& val, bool& success)
	{
		if(pos + sizeof(T) <= _size)
		{
			const char* charVal = (const char*)&val;
			char* valPtr = (char*)_data + pos;
			for(int i = 0; i < sizeof(T); ++i)
			{
				valPtr[i] = charVal[i];
			}
			success = true;
		}
		else
		{
			success = false;
		}
	}
}

#ifdef _MSC_VER  
	#pragma warning ( pop )  
#endif  
