/***************************************************************************
                          SIMPLY POWERFUL TOOLKIT (SPTK)
                          CFieldList.cpp  -  description
                             -------------------
    begin                : Wed Dec 15 1999
    copyright            : (C) 1999-2006 by Alexey Parshin
    email                : alexeyp@gmail.com
 ***************************************************************************/

/***************************************************************************
   This library is free software; you can redistribute it and/or modify it 
   under the terms of the GNU Library General Public License as published by
   the Free Software Foundation; either version 2 of the License, or (at 
   your option) any later version.

   This library 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 Library
   General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; if not, write to the Free Software Foundation, 
   Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.

   Please report all bugs and problems to "alexeyp@gmail.com"
 ***************************************************************************/

#ifndef __CFIELDLIST_H__
#define __CFIELDLIST_H__

#include <sptk3/istring.h>
#include <sptk3/CField.h>
#include <sptk3/cxml>
#include <map>
#include <vector>

namespace sptk {

   /// @addtogroup Database Database Support
   /// @{

   /// @brief The list of CField objects. 
   ///
   /// Is used in CDataSource.
   /// Allows to access data fields by the field name or field index.
   /// Provides the streaming output, and export to XML.
	class SP_EXPORT CFieldList {
		typedef std::map<istring,CField *> CFieldMap;
		typedef std::vector<CField *>      CFieldVector;

		void                   *m_userData;        ///< User data - any data you want to associate with that field list
		CFieldVector            m_list;            ///< The list of fields
		CFieldVector::iterator  m_fieldStreamItor; ///< The field iterator for the streamed fields reading
		CFieldMap              *m_index;           ///< The optional field index by name. 0L if field list isn't indexed.
		bool                    m_compactXmlMode;  ///< The compact XML mode flag

	public:

      /// @brief Default constructor
      ///
      /// @param indexed bool, if you want to have a field index by name added. Such index speeds up the search of the field by name, but increases the occupied memory.
      /// @param compactXmlMode bool, the compact XML export flag, @see xmlMode for details.
		CFieldList(bool indexed,bool compactXmlMode=true);

      /// Destructor
		~CFieldList();

      /// Clears the field list
		void clear();

      /// Returns the nummber of fields in the list
		unsigned size() const { return m_list.size(); }

      /// @brief Defines XML export mode
      ///
      /// The compact XML modes means that fields values are stored as attributes, w/o type information.
      /// Otherwise, fields are stored as subnodes, with the field information stored as attributes.
      /// @param compact bool, the compact XML export flag
		void xmlMode(bool compact) { m_compactXmlMode = compact; }

      /// @brief Adds a new field int the list
      ///
      /// Creates and returns a new field.
      /// @param fname const char *, field name
      /// @param checkDuplicates bool, if true check if the field already exists in the list
      /// @returns new field reference
		CField& push_back(const char *fname,bool checkDuplicates);

      /// @brief Adds a new field int the list without creating a new copy of the field.
      ///
      /// This method is useful if you create a new field with the new() operator.
      /// You shouldn't delete such fields manually - they would be maintained by CFieldList class.
      /// @param fld CField *, field name
      /// @returns new field reference
		CField& push_back(CField *fld);

      /// @brief Finds a field by the field name
      ///
      /// Fast field lookup using std::map.
      /// @param fname const char *, field name
      /// @returns CField pointer, or 0L if not found
		CField* fieldByName(const char * fname) const;

      /// @brief Field access by field index, non-const version
      ///
      /// @param index int, field index
      /// @returns field reference
		CField& operator [] (int index)          { return *(CField *)m_list[index]; }

      /// @brief Field access by field index, const version
      ///
      /// @param index int, field index
      /// @returns field reference
		const CField& operator [] (int index) const    { return *(CField *)m_list[index]; }

      /// @brief Field access by field name, non-const version
      ///
      /// @param fname const char *, field name
      /// @returns field reference
		CField& operator [] (const char *fname)  { return *fieldByName(fname); }

      /// Field access by field name, const version
      /// @param fname const char *, field name
      /// @returns field reference
		const CField& operator [] (const char *fname) const { return *fieldByName(fname); }

      /// @brief Field access by field name, non-const version
      ///
      /// @param fname const std::string&, field name
      /// @returns field reference
		CField& operator [] (const std::string& fname)   { return *fieldByName(fname.c_str()); }

      /// @brief Field access by field name, const version
      ///
      /// @param fname const std::string&, field name
      /// @returns field reference
		const CField& operator [] (const std::string& fname) const { return *fieldByName(fname.c_str()); }

      /// @brief Sets user data
      ///
      /// User data is usually a pointer to some outside memory object,
      /// or an index (id) of some object. CFieldList doesn't maintain this pointer, just keeps it
      /// as a tag.
      /// @param data void *, a user-defined data
		void user_data(void *data) { m_userData = data; } 

      /// @brief Returns user data
		void* user_data() const { return m_userData; } 

      /// @brief Sets the field stream iterator to the first field
      ///
      /// This method is useful if you're using stream access to fields
		void rewind() { m_fieldStreamItor = m_list.begin(); }

      /// @brief Sets the field stream iterator to the next field.
      ///
      /// After the last field is reached, the iterator is switched to the first field
      /// @returns current field
		CField& next();

      /// @brief Exports data into XML node
      ///
      /// @see setXmlMode() for details.
      /// @param xml const CXmlNode&, an XML node to store fields into
		void toXML(CXmlNode& xml) const;
	};
}

/// @brief Streamed field output
///
/// The data is read from the current field,
/// and then next field becomes current. The rewind() method is called
/// automatically to reset the field iterator to the first field upon
/// query open() or fetch() method calls.
/// @param fieldList CFieldList&, a list of fields to assign
/// @param data const bool&, a variable to read current field to
SP_EXPORT sptk::CFieldList& operator >> (sptk::CFieldList& fieldList, bool& data);

/// @brief Streamed field output
///
/// The data is read from the current field,
/// and then next field becomes current. The rewind() method is called
/// automatically to reset the field iterator to the first field upon
/// query open() or fetch() method calls.
/// @param fieldList CFieldList&, a list of fields to assign
/// @param data const std::string&, a variable to read current field to
SP_EXPORT sptk::CFieldList& operator >> (sptk::CFieldList& fieldList, std::string& data);

/// @brief Streamed field output
///
/// The data is read from the current field,
/// and then next field becomes current. The rewind() method is called
/// automatically to reset the field iterator to the first field upon
/// query open() or fetch() method calls.
/// @param fieldList CFieldList&, a list of fields to assign
/// @param data int&, a variable to read current field to
SP_EXPORT sptk::CFieldList& operator >> (sptk::CFieldList& fieldList, int& data);

/// @brief Streamed field output
///
/// The data is read from the current field,
/// and then next field becomes current. The rewind() method is called
/// automatically to reset the field iterator to the first field upon
/// query open() or fetch() method calls.
/// @param fieldList CFieldList&, a list of fields to assign
/// @param data double&, a variable to read current field to
SP_EXPORT sptk::CFieldList& operator >> (sptk::CFieldList& fieldList, double& data);

/// @brief Streamed field output
///
/// The data is read from the current field,
/// and then next field becomes current. The rewind() method is called
/// automatically to reset the field iterator to the first field upon
/// query open() or fetch() method calls.
/// @param fieldList CFieldList&, a list of fields to assign
/// @param data CDateTime, a data to assign to current parameter
SP_EXPORT sptk::CFieldList& operator >> (sptk::CFieldList& fieldList, sptk::CDateTime& data);

/// @brief Streamed field output
///
/// The data is read from the current field,
/// and then next field becomes current. The rewind() method is called
/// automatically to reset the field iterator to the first field upon
/// query open() or fetch() method calls.
/// @param fieldList CFieldList&, a list of fields to assign
/// @param data const CBuffer&, a variable to read current field to
SP_EXPORT sptk::CFieldList& operator >> (sptk::CFieldList& fieldList, sptk::CBuffer& data);

/// @brief Streamed fields output
///
/// Exports the data from fields into XML node
/// @param fieldList CFieldList&, a list of fields to assign
/// @param fields const CXmlNode&, an XML node variable to read fields into
SP_EXPORT sptk::CFieldList& operator >> (sptk::CFieldList& fieldList, sptk::CXmlNode& fields);

/// @}

#endif
