/* +---------------------------------------------------------------------------+
   |          The Mobile Robot Programming Toolkit (MRPT) C++ library          |
   |                                                                           |
   |                   http://mrpt.sourceforge.net/                            |
   |                                                                           |
   |   Copyright (C) 2005-2008  University of Malaga                           |
   |                                                                           |
   |    This software was written by the Perception and Robotics               |
   |      research group, University of Malaga (Spain).                        |
   |    Contact: Jose-Luis Blanco  <jlblanco@ctima.uma.es>                     |
   |                                                                           |
   |  This file is part of the MRPT project.                                   |
   |                                                                           |
   |     MRPT is free software: you can redistribute it and/or modify          |
   |     it under the terms of the GNU General Public License as published by  |
   |     the Free Software Foundation, either version 3 of the License, or     |
   |     (at your option) any later version.                                   |
   |                                                                           |
   |   MRPT is distributed in the hope that it will be useful,                 |
   |     but WITHOUT ANY WARRANTY; without even the implied warranty of        |
   |     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         |
   |     GNU General Public License for more details.                          |
   |                                                                           |
   |     You should have received a copy of the GNU General Public License     |
   |     along with MRPT.  If not, see <http://www.gnu.org/licenses/>.         |
   |                                                                           |
   +---------------------------------------------------------------------------+ */
#ifndef  CMHPropertiesValuesList_H
#define  CMHPropertiesValuesList_H

#include <MRPT/UTILS/CSerializable.h>
#include <MRPT/UTILS/CMemoryChunk.h>

/*---------------------------------------------------------------
	Class
  ---------------------------------------------------------------*/
namespace UTILS
{
	/** Internal triplet for each property in UTILS::CMHPropertiesValuesList */
	struct TPropertyValueIDTriplet
	{
		TPropertyValueIDTriplet() : name(), value(NULL),ID(0)
		{}

		std::string		name;
		CSerializable	*value;
		int64_t         ID;
	};

	/** An arbitrary list of "annotations", or named attributes, each being an instance of any CSerializable object (Multi-hypotheses version).
	 *   For each named annotation (or attribute), several values may exist, each associated to a given hypothesis ID.
	 * A non multi-hypotheses version exists in CPropertiesValuesList.
	 * \sa CSerializable, CPropertiesValuesList
	 */
	class CMHPropertiesValuesList : public UTILS::CSerializable , public std::vector<TPropertyValueIDTriplet>
	{
		// This must be added to any CSerializable derived class:
		DEFINE_SERIALIZABLE( CMHPropertiesValuesList )

	public:
		/** Default constructor
		  */
		CMHPropertiesValuesList();

		/** Destructor
		  */
		virtual ~CMHPropertiesValuesList();

		/** Clears the list and frees all object's memory.
		  */
		void  clear();

		/** Returns the value of the property (case insensitive) for some given hypothesis ID, or NULL if it does not exist.
		  */
		CSerializable*  get(const char *propertyName, const int64_t & hypothesis_ID ) const;

		/** Returns the value of the property (case insensitive) for the first hypothesis ID found, or NULL if it does not exist.
		  */
		CSerializable*  getAnyHypothesis(const char *propertyName) const;

		/** Sets/change the value of the property (case insensitive) for the given hypothesis ID, making a copy of the object (or setting it to NULL if it is the passed value)
		  * \sa setMemoryReference
		  */
		void  set(const char *propertyName, CSerializable* obj, const int64_t & hypothesis_ID);

		/** Sets/change the value of the property (case insensitive) for the given hypothesis ID, directly replacing the pointer instead of making a copy of the object.
		  * \sa set
		  */
		void  setMemoryReference(const char *propertyName, CSerializable* obj, const int64_t & hypothesis_ID);

		/** Remove a given property, if it exists.
		  */
		void  remove(const char *propertyName, const int64_t & hypothesis_ID);

		/** Remove all the properties for the given hypothesis.
		  */
		void  removeAll(const int64_t & hypothesis_ID);

		/** Sets/change the value of a property (case insensitive) for the given hypothesis ID, from an elemental data type.
		  */
		template <class T>
		void  setElemental(const char *propertyName, const T &data, const int64_t & hypothesis_ID)
		{
			MRPT_TRY_START;

			CMemoryChunk *memChunk ( new CMemoryChunk() );
			(*memChunk) << data;

			for (std::vector<TPropertyValueIDTriplet>::iterator it=begin();it!=end();it++)
			{
				if ( it->ID == hypothesis_ID && !MRPT_OS::_strcmpi(propertyName,it->name.c_str()) )
				{
					// Delete current contents:
					if (it->value)
						delete it->value;

					// Copy new value:
					it->value = memChunk;
					return;
				}
			}

			// Insert as new:
			TPropertyValueIDTriplet	newPair;
			newPair.name = std::string(propertyName);
			newPair.value = memChunk;
			newPair.ID    = hypothesis_ID;
			push_back(newPair);

			MRPT_TRY_END_WITH_CLEAN_UP( \
				printf("Exception while setting annotation '%s'",propertyName); \
				);
		}

		/** Gets the value of a property (case insensitive) for the given hypothesis ID, retrieves it as an elemental data type (types must coincide, basic size check is performed).
		  * \return false if the property does not exist for the given hypothesis.
		  */
		template <class T>
		bool getElemental(const char *propertyName, T &out_data, const int64_t & hypothesis_ID, bool raiseExceptionIfNotFound = false) const
		{
			MRPT_TRY_START
			for (std::vector<TPropertyValueIDTriplet>::const_iterator it=begin();it!=end();it++)
			{
				if (!MRPT_OS::_strcmpi(propertyName,it->name.c_str()) && it->ID == hypothesis_ID )
				{
					CMemoryChunk* memChunk = static_cast<CMemoryChunk*>(it->value);
					ASSERT_(memChunk)
					if (memChunk->getTotalBytesCount()!=sizeof(out_data)) THROW_EXCEPTION("Data sizes do not match.");
					out_data = *static_cast<T*>( memChunk->getRawBufferData() );
					return true;
				}
			}
			// Not found:
			if (raiseExceptionIfNotFound)
				THROW_EXCEPTION_CUSTOM_MSG1("Property '%s' not found", propertyName );
			return false;
			MRPT_TRY_END
		}

		/** Returns the name of all properties in the list
		  */
		std::vector<std::string>  getPropertyNames() const;

	}; // End of class def.

	// This must be added to any CSerializable derived class:
	DEFINE_SERIALIZABLE_POST( CMHPropertiesValuesList )

} // End of namespace

#endif
