//------------------------------------------------------------------------------
//	Copyright (C) 2009, 2010 Humboldt-Universitaet zu Berlin
//
//	This library is free software; you can redistribute it and/or
//	modify it under the terms of the GNU Lesser General Public
//	License as published by the Free Software Foundation; either
//	version 2.1 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
//	Lesser General Public License for more details.
//
//	You should have received a copy of the GNU Lesser 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
//------------------------------------------------------------------------------

/**
 * @file ReportTable.h
 * @author Ronald Kluth
 * @date created at 2009/12/17
 * @brief Declaration of class odemx::data::ReportTable
 * @sa ReportTable.cpp
 * @since 3.0
 */

#ifndef ODEMX_DATA_REPORTTABLE_INCLUDED
#define ODEMX_DATA_REPORTTABLE_INCLUDED

#include <odemx/base/SimTime.h>
#include <odemx/data/Label.h>
#include <vector>

namespace odemx {
namespace data {

/**
 * @brief Container for accumulated data collected from ReportProducers
 * @ingroup data
 * @author Ronald Kluth
 * @since 3.0
 * @see Report ReportProducer
 *
 * All data are reported in tables. The structure of a table is defined
 * by a table definition. A table cannot be created manually. To get a table
 * object, the user has to request one from a Report object.
 */
class ReportTable
{
public:

	/**
	 * @brief Provides column label and type describing column data
	 *
	 * All columns in tables are associated to a data type like integer,
	 * real values or strings, and sometimes represent graphical bars.
	 */
	class Column {
	public:
		/// Destruction
		virtual ~Column() {}

		/// Enumeration of the allowed column types in tables
		enum Type {
			INTEGER, ///< integer numbers
			REAL, ///< real numbers
			STRING, ///< text
			BAR, ///< graphical representation
			SIMTIME, ///< used for SimTime
			INVALID ///< used for undefined columns
		};

		/// Each column must know its caption label
		const Label& getLabel() { return label_; }
		/// Each column must know its type
		Type getType() { return type_; }
		/// Get a string representation of the given column type
		static const std::string typeToString( Type type );

	protected:
		/// Construction
		Column( const Label& label, Type type ): label_( label ), type_( type ) {}

	private:
		/// Each column has a label for the table captions
		Label label_;
		/// Each column knows its type
		Type type_;
	};


	/**
	 * @brief Representation of a table column that stores data in a vector
	 *
	 * ColumnT is the class that stores all table data. It requires a
	 * label and a column type to be given during construction.
	 */
	template < typename DataType >
	class ColumnT: public Column {
	public:
		/// Destructor
		virtual ~ColumnT() {}

	protected:
		friend class ReportTable;

		friend ReportTable& operator<<( ReportTable& t, long d );
		friend ReportTable& operator<<( ReportTable& t, long long d );
		friend ReportTable& operator<<( ReportTable& t, double d );
		friend ReportTable& operator<<( ReportTable& t, std::string s );

		friend ReportTable& operator>>( ReportTable& t, long& to );
		friend ReportTable& operator>>( ReportTable& t, long long& to );
		friend ReportTable& operator>>( ReportTable& t, unsigned long long& to );
		friend ReportTable& operator>>( ReportTable& t, double& to );
		friend ReportTable& operator>>( ReportTable& t, std::string& to );

		/// Size type used for column indices
		typedef typename std::vector< DataType >::size_type SizeType;

		/// Constructor, to be called by Table class only
		ColumnT( const Label& label, Type type ): Column( label, type ) {}

		/// Append data to column, i.e. add content of next line in table
		void addData( DataType d ) { data.push_back( d ); }

		/// Read data from column, the given line is range-checked and may throw std::out_of_range
		const DataType& getData( SizeType line ) const
		{
			return data.at( line );
		}
	private:
		/// stores the column data, index represents lines in the Table
		std::vector< DataType > data;
	};

	/**
	 * @brief This class is used to build table definitions at runtime
	 */
	class Definition
	{
	public:
		/**
		 * @brief Control codes used for operator << input into tables
		 */
		enum ControlCode
		{
			ENDL, ///< end of table line
			TAB ///< skip column
		};

		/// Size type used for column indices
		typedef std::vector< std::string >::size_type SizeType;

		/// Get the number of table columns
		SizeType getNumberOfColumns() const;
		/// Get the label of column @c index
		const std::string& getLabelOfColumn( SizeType index ) const;
		/// Get the type of column @c index
		Column::Type getTypeOfColumn( SizeType index ) const;

		/**
			\brief Add columns to the table definition
			\param label column label
			\param type column type
			Use addColumn() to build a TableDefinition.
		*/
		void addColumn( const std::string& label, Column::Type type );

	private:
		std::vector< std::string > columnLabels_; ///< column labels
		std::vector< Column::Type > columnTypes_; ///< column types
	};

	/// Size type used for table indices
	typedef Definition::SizeType SizeType;

	/// Stores a Table input or output position when streaming data
	struct Position
	{
		Position(): col( 0 ), line( 0 ) {}
		SizeType col;
		SizeType line;
	};

	/// Construction is managed by Report
	ReportTable( const Label& label, const Definition& def );

	/// Destruction is managed by Report
	~ReportTable();

	/// \name Table attributes
	//@{
	/// Get the name of the table
	const Label& getLabel() const { return label_; }
	/// Get the table definition
	const Definition& getDefinition() const { return def_; }

	/**
	 * @brief Get number of lines in the table
	 *
	 * @note This function returns the number of completely filled lines.
	 * To finish a line, use Definition::ControlCode.
	 */
	SizeType getNumberOfLines() const { return lineCount_; }

	/// Get the number of columns in a table
	SizeType getNumberOfColumns() const { return columns_.size(); }

	/// Get a specific column label by index
	const std::string& getLabelOfColumn( SizeType index ) const
	{
		return columns_.at( index )->getLabel();
	}

	/// Get a specific column type by index
	Column::Type getTypeOfColumn( SizeType index ) const
	{
		return columns_.at( index )->getType();
	}
	//@}

	/**
		\name Input streaming

		The data can be entered into a table in a streaming like fashion.
		The data type has to match the column type. A ControlCode can be
		used to skip columns or the rest of a line. Skipped columns are
		filled with default	data <tt>(0, 0.0, "")</tt>.

		@{
	*/
	friend ReportTable& operator<<( ReportTable& t, int d );
	friend ReportTable& operator<<( ReportTable& t, unsigned int d );
	friend ReportTable& operator<<( ReportTable& t, long d );
	friend ReportTable& operator<<( ReportTable& t, long long d );
	friend ReportTable& operator<<( ReportTable& t, unsigned long d );
	friend ReportTable& operator<<( ReportTable& t, unsigned long long d );
	friend ReportTable& operator<<( ReportTable& t, float d );
	friend ReportTable& operator<<( ReportTable& t, double d );
	friend ReportTable& operator<<( ReportTable& t, std::string d );
	friend ReportTable& operator<<( ReportTable& t, const char* d );
	friend ReportTable& operator<<( ReportTable& t, Definition::ControlCode d );
	//@}

	/**
		\name Output streaming

		The data of a table can be read in a streaming like fashion.
		The data type has to match the column type.
		@{
	*/
	friend ReportTable& operator>>( ReportTable& t, long& to );
	friend ReportTable& operator>>( ReportTable& t, long long& to );
	friend ReportTable& operator>>( ReportTable& t, unsigned long long& to );
	friend ReportTable& operator>>( ReportTable& t, double& to );
	friend ReportTable& operator>>( ReportTable& t, std::string& to );
	//@}

	/**
		\name Output random access

		Get the data of a specific table cell

		@{
	*/
	/**
		\brief Get SIMTIME value from field (\p col, \p line).
		\note Column col must be a SIMTIME column.
	*/
	base::SimTime getSIMTIME( SizeType col, SizeType line );

	/**
		\brief Get graphical BAR value from field (\p col, \p line).
		\note Column col must be a BAR column.
	*/
	long getBAR(SizeType col, SizeType line );

	/**
		\brief Get INTEGER value from field (\p col, \p line).
		\note Column col must be an INTEGER column.
	*/
	long getINTEGER(SizeType col, SizeType line );

	/**
		\brief Get REAL value from field (\p col, \p line).
		\note Column col must be an INTEGER or a REAL column.
	*/
	double getREAL(SizeType col, SizeType line );

	/**
		\brief Get a STRING value from field (\p col, \p line).
		\note Type conversion from BAR, INTEGER and REAL to STRING
		is done if required.
	*/
	std::string getSTRING( SizeType col, SizeType line );
	//@}

	/// Get the current input column to add data
	Column* getCurrentInputColumn()
	{
		return columns_.at( inputPosition_.col );
	}
	/// Get the current output column to read data from
	Column* getCurrentOutputColumn()
	{
		return columns_.at( outputPosition_.col );
	}
	/// Get the index of the current output streaming line
	SizeType getOutputLine() { return outputPosition_.line; }

	/// Add a default value (0, 0.0, "", 0) to the column
	void addDefaultValue();

private:
	/// Increment input pointer for streaming, change line if necessary
	void advanceInputPosition();
	/// Increment output pointer for streaming, change line if necessary
	void advanceOutputPosition();

	friend class Report;
	/// Vector type for storing pointers to table columns
	typedef std::vector< Column* > ColumnVec;

	Label label_; ///< table label
	Definition def_; ///< table definition
	SizeType lineCount_; /// size of table
	ColumnVec columns_; ///< vector containing pointers to columns
	Position inputPosition_; ///< input streaming position pointer
	Position outputPosition_; ///< output streaming position pointer
};

extern ReportTable& operator<<( ReportTable& t, int d ); ///< enter int value
extern ReportTable& operator<<( ReportTable& t, unsigned int d ); ///< enter unsigned int value
extern ReportTable& operator<<( ReportTable& t, long d ); ///< enter long value
extern ReportTable& operator<<( ReportTable& t, long long d ); ///< enter long long value
extern ReportTable& operator<<( ReportTable& t, unsigned long d ); ///< enter unsigned long value
extern ReportTable& operator<<( ReportTable& t, unsigned long long d ); ///< enter unsigned long long value
extern ReportTable& operator<<( ReportTable& t, float d ); ///< enter float value
extern ReportTable& operator<<( ReportTable& t, double d ); ///< enter double value
extern ReportTable& operator<<( ReportTable& t, std::string d ); ///< enter std::string value
extern ReportTable& operator<<( ReportTable& t, const char* d ); ///< enter const char* value
extern ReportTable& operator<<( ReportTable& t, ReportTable::Definition::ControlCode d ); ///< enter control codes

extern ReportTable& operator>>( ReportTable& t, long& to ); ///< read long value
extern ReportTable& operator>>( ReportTable& t, long long& to ); ///< read long long value
extern ReportTable& operator>>( ReportTable& t, unsigned long long& to ); ///< read unsigned long long value
extern ReportTable& operator>>( ReportTable& t, double& to ); ///< read double value
extern ReportTable& operator>>( ReportTable& t, std::string& to ); ///< read std::string value

/**
 * @brief Compare two table definitions, used for grouping ReportProducer results in the same table
 * @return @c true if number of columns, all labels and all types are equal
 */
extern bool operator==( const ReportTable::Definition& lhs, const ReportTable::Definition& rhs );

} } // namespace odemx::data

#endif /* ODEMX_DATA_REPORTTABLE_INCLUDED */
