/**
 * @file
 * $Id$
 * $Revision$
 * $Author$
 * $Date$
 *
 * This file is part of The iWear Framework.
 * In particular this file is part of the Framework Core Library
 *
 * The iWear Framework 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 as in version 2 of the License.
 * 
 * The iWear Framework 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
 * The iWear Framework; if not, write to the Free Software Foundation, Inc., 59
 * Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifndef __IWEAR_MANAGER_H
#define __IWEAR_MANAGER_H


#include <iwear/uid.h>
#include <iwear/exceptions.h>
#include <iwear/debugstream.h>
#include <iwear/debug.h>
#include <iwear/i18n.h>

#include <map>
using std::map;

namespace iwear {
class Configuration;
/**
 * This is a base set for managers of Sensors, I/O devices and possibly others.
 * Every Manager should implement only the functions not mentioned here, and
 * should be derived from this class, rather than creating an object. Therefore
 * some of the Functions are declared to be abstract.
 */
template <class Types, class ObjBase>
class Manager /*{{{*/
{
protected:
   /**
    * Every Object should have an ID and can be identified by this. 
    */
    typedef map<uid, ObjBase* > IDtoPointerMap;
    IDtoPointerMap IDtoPointer;

   /**
    * We keep also track of a list sorted by type, e.g. Location, Volume etc.
    */
    map<Types, set<ObjBase* > > TypetoPointer;

    Configuration& conf;
    Manager( Configuration& cf ) : conf( cf ) { }

public:
    Configuration& get_config( void ) { return conf; }
    const Configuration& get_config( void ) const { return conf; }

    virtual ~Manager();
    bool register_obj( ObjBase* );
    bool deregister_obj( const uid& );
    bool deregister_obj( ObjBase* );

   /**
    * @name Get Functions
    *
    * @{
    * 
    */

   /**
    * This gets the managed object by the uid. If it was not found, this
    * returns NULL
    */
    ObjBase* get_by_id( const uid& );
   /**
    * This gets the managed object by the uid. If it was not found, this
    * returns NULL.
    * @note This function returns a const pointer.
    */
    const ObjBase* get_by_id( const uid& ) const;

   /**
    * This returns a reference to the set of managed objects which are of the
    * specified type. The set is const, and cannot be changed, while the
    * objects it contains can be.
    * If there are no objects of the specified type, this either returns an
    * empty set or throws a mapped_error exception
    */
    const set<ObjBase* >& get_by_type( Types ) const;
   /**
    * @}
    */

   /**
    * @name Check Contents functions
    * @{
    */
   /**
    * @return true if we know of the object with the specified uid
    */
    bool has_id( const uid& ) const;

   /**
    * @return true if we have objects of the specified type.
    */
    bool has_type( Types ) const;

   /**
    * @}
    */

   /**
    * To Manage, we need to access all types through the same interface. But
    * since we may want to have multiple type levels, all those modules will
    * probably implement different type getting functions. therefore we need
    * this function, which will in a derived manager be overriden and call the
    * correct function to get a Types type. When inlining there, the compiler
    * should be able to optimize pretty much away.
    */
    virtual Types get_type( const ObjBase& ) const = 0;
}; /*}}}*/

template <class Types, class ObjBase>
inline Manager<Types,ObjBase>::~Manager()/*{{{*/
{
} /*}}}*/

template <class Types, class ObjBase>
inline bool Manager<Types,ObjBase>::has_type( Types t ) const/*{{{*/
{
    return ( TypetoPointer.find(t) != TypetoPointer.end() );
} /*}}}*/

template <class Types, class ObjBase>
inline bool Manager<Types,ObjBase>::has_id( const uid& u) const/*{{{*/
{
    return ( IDtoPointer.find(u) != IDtoPointer.end() );
}/*}}}*/

template <class Types, class ObjBase>
inline const set<ObjBase*>& Manager<Types,ObjBase>::get_by_type( Types t) const/*{{{*/
{
    typename map<Types, set<ObjBase*> >::const_iterator ci(TypetoPointer.find(t));
    if ( ci == TypetoPointer.end() )
    {
	THROW( mapping_error,("No element of such type"));
    }
    return ci->second;
}/*}}}*/

template <class Types, class ObjBase>
inline ObjBase* Manager<Types,ObjBase>::get_by_id( const uid& u )/*{{{*/
{
    typename map<uid, ObjBase* >::const_iterator ci(IDtoPointer.find(u));
    if ( ci == IDtoPointer.end() )
    {
	THROW( mapping_error,("No element of such type"));
    }
    return ci->second;
}/*}}}*/

template <class Types, class ObjBase>
inline const ObjBase* Manager<Types,ObjBase>::get_by_id( const uid& u ) const/*{{{*/
{
    typename map<uid, ObjBase*>::const_iterator ci(IDtoPointer.find(u));
    if ( ci == IDtoPointer.end() )
    {
	THROW( mapping_error,("No element of such type"));
    }
    return ci->second;
}/*}}}*/

template <class Types, class ObjBase>
inline bool Manager<Types,ObjBase>::deregister_obj( ObjBase* obj )/*{{{*/
{
    return deregister_obj(obj->get_uid());
}/*}}}*/

template <class Types, class ObjBase>
inline bool Manager<Types,ObjBase>::deregister_obj( const uid& u)/*{{{*/
{
    ObjBase* ptr;
    typename map<uid, ObjBase*>::iterator uit(IDtoPointer.find(u));
    if ( uit == IDtoPointer.end() )
    {
	THROW( mapping_error,("Object not found"));
    }
    ptr = uit->second;
    d_inf << demangle_cpp_name(typeid(*this).name()) << i18n::trans(" deregistered Object ") 
	<< reinterpret_cast<void*>( ptr) << i18n::trans(" of Type ") << get_type(*ptr) << std::endl;
    IDtoPointer.erase(uit);
    typename map<Types, set<ObjBase*> >::iterator tit(TypetoPointer.find((get_type(*ptr))));
    tit->second.erase(ptr);
    return true;
}/*}}}*/

template <class Types, class ObjBase>
inline bool Manager<Types,ObjBase>::register_obj( ObjBase* obj )/*{{{*/
{

    ObjBase*& ptr = IDtoPointer[obj->get_uid()];
    if ( ptr != NULL )
    {
	// We throw an exception since it is a programming error if an object
	// wants to register itself multiple times
	THROW(iwlogic_error,("Already registered this object at this manager"));
	return false;
    }
    ptr = obj;
    d_inf << demangle_cpp_name(typeid(*this).name()) << i18n::trans(" registered Object ") 
	<< static_cast<void*>(obj) << i18n::trans(" of Type ") << get_type(*obj) << std::endl;

    TypetoPointer[get_type(*obj)].insert(obj);
    return true;
} /*}}}*/

} // namespace iwear
#endif

