/* +---------------------------------------------------------------------------+
   |          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 CActionObservationSequence_H
#define CActionObservationSequence_H

#include <MRPT/MRML/CPose2D.h>
#include <MRPT/MRML/CSensoryFrame.h>
#include <MRPT/MRML/CActionCollection.h>

namespace MRML
{

	/** This class stores a sequence of actions and observations. There is a
	 *   sequence of objects, where each one can be of one type:
	 *		- An action:	Implemented as a CActionCollection object, the actuation of the robot (i.e. odometry increment).
	 *		- Observations: Implemented as a CSensoryFrame, refering to a set of robot observations from the same pose.
	 *
	 *	This class is used to store/load files in the ".rawlog" format. Two options exists:
	 *		- [PREFERRED] To load it from a file directly containing "CSensoryFrame" and "CActionCollection" objects.  This is achieved with the "loadFromRawLogFile" method, or
	 *		- To load/store a CActionObservationSequence object with the "<<" and ">>" operators. This method has the drawback of not allowing the incremental loading of the rawlog, which is a problem for large datasets.
	 *
	 *  Refer to the application <a href="../../index.php/Application:RawLogViewer" >RawLogViewer</a > for the GUI program that visualizes and manages rawlogs.
	 *
	 * \note Since MRPT version 0.5.5, this class also provides a STL container-like interface (see CActionObservationSequence::begin, CActionObservationSequence::iterator, ...).
	 *
	 * \sa CSensoryFrame, CPose2D
	 */
	class CActionObservationSequence : public UTILS::CSerializable
	{
		// This must be added to any CSerializable derived class:
		DEFINE_SERIALIZABLE( CActionObservationSequence )

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

		/** Copy operator:
		  * \sa moveFrom
		  */
		CActionObservationSequence&  operator =(const CActionObservationSequence &o);

		/** Destructor:
		  */
		virtual ~CActionObservationSequence();

		/** Clear the sequence of actions/observations, freeing the memory of all the objects in the list.
		  */
		void  clear();

		/** Clear the sequence of actions/observations, without deleting the objects themselves (USE ONLY IF YOU KNOW WHAT YOU DO, NORMALLY YOU'LL CALL "clear" INSTEAD).
		  */
		void  clearWithoutDelete();

		/** Add an action to the sequence: a collection of just one element is created.
		  *   The object is duplicated, so the original one can be free if desired.
		  */
		void  addAction( CAction &action );

		/** Add a set of actions to the sequence; the object is duplicated, so the original one can be free if desired.
		  * \sa addObservations, addActionsMemoryReference
		  */
		void  addActions( CActionCollection &action );

		/** Add a set of observations to the sequence; the object is duplicated, so the original one can be free if desired.
		  * \sa addActions, addObservationsMemoryReference
		  */
		void  addObservations( CSensoryFrame &observations );

		/** Add a set of actions to the sequence; the pointer to the object is copied, so the original copy CANNOT be deleted by the user (the memory will be managed by this class).
		  * \sa addActions, addObservationsMemoryReference
		  */
		void  addActionsMemoryReference( CActionCollection *action );

		/** Add a set of observations to the sequence; the pointer to the object is copied, so the original copy CANNOT be deleted by the user (the memory will be managed by this class).
		  * \sa addObservations, addActionsMemoryReference
		  */
		void  addObservationsMemoryReference( CSensoryFrame *observations );

		/** Load the contents from a file containing one of these possibilities:
		  *		- A "CActionObservationSequence" object.
		  *		- Directly "CSensoryFrame" and "CActionCollection" objects. In this case the method stops reading on EOF of an unrecogniced class name.
		  * \returns It returns false if the file does not exists.
		  */
		bool  loadFromRawLogFile( const std::string &fileName );

		/** Returns the number of actions / observations object in the sequence.
		  */
		size_t  size();

		/** Returns true if the given object is an action, or false if it is a set
		  *   of observations, where index=0 is the first object.
		  * \sa size, getAsAction, getAsObservations
		  * \exception std::exception If index is out of bounds
		  */
		bool  isAction( size_t index );

		/** Delete the action or observation stored in the given index.
		  * \exception std::exception If index is out of bounds
		  */
		void  remove( size_t index );

		/** Returns the i'th element in the sequence, as being actions, where index=0 is the first object.
		  *  If it is not an action, it returns NULL. Do neighter modify nor delete the returned pointer.
		  * \sa size, isAction, getAsObservations
		  * \exception std::exception If index is out of bounds
		  */
		CActionCollection*  getAsAction( size_t index );

		/** Returns the i'th element in the sequence, as being an action, where index=0 is the first object.
		  *  If it is not an action, it returns NULL. Do neighter modify nor delete the returned pointer.
		  * \sa size, isAction, getAsAction
		  * \exception std::exception If index is out of bounds
		  */
		CSensoryFrame*  getAsObservations( size_t index );

		/** Returns the i'th element in the sequence, being its class whatever.
		  * \sa size, isAction, getAsAction, getAsObservations
		  * \exception std::exception If index is out of bounds
		  */
		CSerializable*    getAsGeneric( size_t index );

		/** A normal iterator, plus the extra methods "isAction", "isObservation" to determine the type of each entry in the sequence.
		  */
		class iterator
		{
		protected:
			std::deque<CSerializable*>::iterator	m_it;

		public:
			iterator() : m_it() {  }
			iterator(const std::deque<CSerializable*>::iterator& it) : m_it(it)  {  }
			virtual ~iterator() { }

			iterator & operator = (const iterator& o) {  m_it = o.m_it; return *this; }

			bool operator == (const iterator& o) {  return m_it == o.m_it; }
			bool operator != (const iterator& o) {  return m_it != o.m_it; }

			CSerializable* operator *() { return *m_it; }

			iterator operator ++(int) {  m_it++; return *this; }
			iterator operator --(int) {  m_it--; return *this; }

			bool isAction() const { return (*m_it)->GetRuntimeClass()->derivedFrom( CLASS_ID(CActionCollection) ); }
			bool isObservation() const { return (*m_it)->GetRuntimeClass()->derivedFrom( CLASS_ID(CSensoryFrame) ); }

			static iterator erase( std::deque<CSerializable*>& lst, const iterator &it) { return lst.erase(it.m_it); }
		};

		/** A normal iterator, plus the extra methods "isAction", "isObservation" to determine the type of each entry in the sequence.
		  */
		class const_iterator
		{
		protected:
			std::deque<CSerializable*>::const_iterator	m_it;

		public:
			const_iterator() : m_it() {  }
			const_iterator(const std::deque<CSerializable*>::const_iterator& it) : m_it(it)  {  }
			virtual ~const_iterator() { }

			bool operator == (const const_iterator& o) {  return m_it == o.m_it; }
			bool operator != (const const_iterator& o) {  return m_it != o.m_it; }

			const CSerializable* operator *() const { return *m_it; }

			const_iterator operator ++(int) {  m_it++; return *this; }
			const_iterator operator --(int) {  m_it--; return *this; }

			bool isAction() const { return (*m_it)->GetRuntimeClass()->derivedFrom( CLASS_ID(CActionCollection) ); }
			bool isObservation() const { return (*m_it)->GetRuntimeClass()->derivedFrom( CLASS_ID(CSensoryFrame) ); }
		};


		const_iterator begin() const { return m_seqOfActObs.begin(); }
		iterator begin() { return m_seqOfActObs.begin(); }
		const_iterator end() const { return m_seqOfActObs.end(); }
		iterator end() { return m_seqOfActObs.end(); }

		iterator erase(const iterator &it) { return iterator::erase(m_seqOfActObs, it); }

		/** Efficiently copy the contents from other existing object, and remove the data from the origin (after calling this, the original object will have no actions/observations).
		  */
		void moveFrom( CActionObservationSequence &obj);

	private:
		std::deque<CSerializable*>		m_seqOfActObs;

	}; // End of class def.

	DEFINE_SERIALIZABLE_POST( CActionObservationSequence )


} // End of namespace

#endif

