/***************************************************************************

    supportware
    Copyright (C) 2001-2008 by c.holzheuer
    c.holzheuer@sourceworx.org

    This program 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 2 of the License, or
    (at your option) any later version.

***************************************************************************/


#ifndef DDMAPROW_H
#define DDMAPROW_H

#include <iostream>
#include <ddbaseobject.h>
#include <vector>
#include <stdexcept>
#include <map>

using namespace std;


/// ddmaprow ist die Basis für Array-ähnliche Daten mit Index.
/**
	
	Ex ddrow (wurde gestrichen). Existiert in drei Varianten:
	- ddmaprow: 	hier
	- ddmapptr: 	dito mit pointern
	- ddmapmulti: eine maprow mit x einträgen pro key.
	
	Basisklasse für alles,was feldartige daten enthält, also einer
	Tabellezeile entspricht: Url, Fileindo, Dirinfo, Request, etc aber
	auch User, Comic...

  HÄ??
	Es wird noch ein Adapter benötigt, der von Dataobjects auf
	Pagetemplates mappt.

	Eigentlich ist dataobject ein interface für _Alle_ feldartigen
	Datenobjekte, jedenfalls sollte eine einheitliche schnittstelle
	zur Verfügung gestellt werden.

		grepline
		item
		datarow
		char**
		inientry
*/

template<class T>
class ddmaprow : public ddbaseobject
{

public:


	/**
		Default constructor.
	*/

	ddmaprow()
	{
	}


	/**
		Standard constructor.
		@param size erzeugt 'size' dummyeinträge
	*/

	ddmaprow( size_t itemsize )
	{
		if( itemsize )
			_data.resize( itemsize );
	}
	
	
	/**
		Copy constructor.
	*/

	ddmaprow( const ddmaprow& src )
	{
		*this=src;
	}

	
	/**
		Destructor. Tut nix.
	*/

	virtual ~ddmaprow()
	{

	}

	
	/**
		Assignment operator.
	*/

	ddmaprow& operator=( const ddmaprow& src )
	{
		if( this == &src )
			return *this;
		_data  = src._data;
		_index = src._index;
		return *this;
	}


	/**
		@Returns Die Größe.
	*/

	inline int size() const
	{
		return (int) _data.size();
	}


	/**
		@Returns 'true' wenn size() == 0.
	*/

	inline bool empty() const
	{
		return (_data.size()==0);
	}
	
	
	/**
		@Returns den index.
		@see ddmapindex
	*/

	inline const ddmapindex& get_index() const
	{
		return _index;
	}

	// hack & pfusch, siehe ddusermanager
	inline ddmapindex& get_index()
	{
		return _index;
	}
	
  /**
		@Returns Die indexposition für einen Schlüssel oder -1.
	*/

	virtual int index_of( const ddstring& key ) const
	{
		ddmapindex::const_iterator pos = _index.find( key );
		if( pos != _index.end() )
			return (int) (*pos).second;
		return -1;
	}

	
	/**
		@Returns den schüssel für eine index-stelle zurück
	*/
	
	virtual const ddstring& key_of( int index ) const
	{
		ddmapindex::const_iterator pos = _index.begin();
		for( ; pos != _index.end(); ++pos )
			if( (*pos).second == index )
		 		return (*pos).first;
		throw out_of_range( "maprow key of: index not found" );
	}
		
		
	/**
		@Returns 'true' wenn der Eintrag 'key' existiert.
	*/

	virtual bool contains_key( const ddstring& key ) const
	{
		ddmapindex::const_iterator pos = _index.find( key );
		return ( pos != _index.end() );
	}
	
	
	/**
		@Returns Das Feld an Position 'index'.
		@throws out_of_range
	*/

	T& operator[]( size_t index  )
	{
		if( index < _data.size() )
			return _data[index];
		throw out_of_range("ddmaprow operator[ size_t index ]: out of range");
	}

	
  /**
		@Returns Das Feld an Position 'index', const Version.
		@throws out_of_range
	*/
	
	const T& operator[]( size_t index  ) const
	{
		if( index <_data.size() )
			return _data[index];
		throw out_of_range("ddmaprow const operator[ size_t index ]: out of range");
	}

	
	/**
		@returns Das Feld an Position 'key'
		@param key Der Schlüssel.
		@throws out_of_range
	*/

	T& operator[]( const ddstring& key )
	{
		if( key.empty() )
			throw out_of_range("maprow operator[]: key empty");
		ddmapindex::iterator pos = _index.find( key );
		if( pos == _index.end() )
			throw out_of_range( "maprow operator[]: key not found: " + key );
		return this->_data[(*pos).second];
	}

	
	/**
		@returns Das Feld an Position 'key', const Version.
		@param key Der Schlüssel.
		@throws out_of_range
	*/
	
	const T& operator[]( const ddstring& key ) const
	{
		if( key.empty() )
			throw out_of_range("operator[]: key empty");
		ddmapindex::const_iterator pos = _index.find( key );
		if( pos == _index.end() )
			throw out_of_range( "maprow operator[]: key not found: " + key );
		return this->_data[(*pos).second];
	}

	
	/**
		Einen Eintrag anhängen.
		@param item Der Eintrag.
		@return Die Indexposition des neuen Eintrags.
	*/

	virtual size_t add( const T& item )
	{
		_data.push_back( item );
		return _data.size()-1;
	}


	/**
		Einen Eintrag an der Position 'index' einfügen.
		Der Alte Eintrag wird überschrieben.
		@param index  Die Indexposition.
		@param item Der Eintrag.
		@throws out_of_range
	*/

	virtual void add_at_index( size_t index, const T& item  )
	{
		if( index >= _data.size() )
			log_fatal( "ddstringrow::add: index out of range!" );
		_data[index] = item;
	}


	/**
		Ein Element an der Schlüsselposition 'key' einfügen.
		Falls das Element existiert, wird es überschrieben via assignment.
		Das ist hier ok, das es sich nicht um pointer handelt.
		@param key Der Schlüssel
		@param item	Das Element
	*/

	virtual void add_by_key( const ddstring& key, const T& item )
	{
		ddmapindex::iterator pos = _index.find( key );
		if( pos == _index.end() )
		{
			_data.push_back( item );
			_index[key] = _data.size()-1;
		}
		else
		{
       _data[(*pos).second] = item;
		}
	}
	
	
	/**
		Ein alias für 'key' hinzufügen.
	*/

  bool add_alias( const ddstring& key, const ddstring& alias )
  {
    ddmapindex::const_iterator pos = _index.find( key );
    if( pos == _index.end() )
			return false;
    ddmapindex::const_iterator pos2 = _index.find( alias );
    _index[ alias ] =(*pos).second;
    return true;
  }

	
	/**
		Einen Key-Index paar einfügen.
	*/
	
	void add_key( const ddstring& key, size_t index )
  {
  	_index.add_key( key, index );
  }
  
  
	/**
		Alles Löschen.
	*/

	virtual void clear()
	{
		_data.clear();
		_index.clear();
	}
	
	
	/**
		den Eintrag für 'key' löschen.
	*/

 	virtual bool kill_entry( const ddstring& key )
	{
		int idx = index_of( key );
		if( idx<0 )
			return false;
		return kill_entry( (size_t) idx );
	}
	
	
	/**
		den Eintrag an Position 'index' löschen.
	*/
	
	virtual bool kill_entry( size_t index )
	{
	 	if( index >= this->_data.size() )
			return false;
		// eintrag löschen
		this->_data.erase( this->_data.begin()+index );
		// index updaten
		_index.update( index );
		return true;
	}
	
	
	/**
		Creates a ddstring representation of this row.
		Returns a CSV like ddstring by calling join(";").
		@see join();
	*/

	virtual ddstring to_string() const
	{
		return join( ";" );
	}


	/**
		Alle Felder nach cout schreiben. Prototype only.
	*/

	virtual void dump() const
	{
		log_fatal( "ddmaprow: dump not implemented!" );
	}


	/**
		Felder zu einem String zusammenpappen. Prototyp.
	*/
	
	virtual ddstring join( const ddstring& sep, size_t from=0, size_t to=ddstring::npos ) const
	{
		log_fatal( "ddmaprow: join not implemented!" );
		return "--";
	}
	
	
	/**
		Benennt einen gegebenen Schlüssel um.
	*/
	
	int replace_key( const ddstring& oldkey, const ddstring& newkey )
	{
		int idx = _index.index_of( oldkey );
		if( idx<0 || oldkey == newkey )
			return idx;
		_index.erase( oldkey );
		_index[ newkey ] = idx;
		return idx;
	}
		
		
protected:

	vector<T> 	_data;
	ddmapindex 	_index;

};


#endif
