#ifndef REGISTRATION_HH
#define REGISTRATION_HH

#include <string>
#include <map>

#include "loggers.hh"

/**
 * @class registration
 * @brief This class provides a way to register items and its object reference 
 *        The registered items are accessible from any object (singleton patern)
 * @remark There is one limitation: each item is uniquely defined in the process
 */
template <typename TItem>
class registration { // TODO Refine the naming
  std::map<std::string, TItem*> _items;
  
  static registration<TItem>* _instance;
private:
  registration(): _items() { }; // can not be created manually
public:
  static registration<TItem>& get_instance();

public:
  void add_item(const std::string& type, TItem* f);
  TItem * get_item(const std::string& type);
}; // End of class registration

template <typename TItem> 
registration<TItem>* registration<TItem>::_instance = NULL;

// static functions
template <typename TItem> 
registration<TItem>& registration<TItem>::get_instance()
{
  return (_instance != NULL) ? *_instance : *(_instance = new registration());
}

template <typename TItem> 
void registration<TItem>::add_item(const std::string & type, TItem * f)
{
  loggers::get_instance().log("registration::add_item: %s/%p", type.c_str(), f);
  _items[type] = f;
}

template <typename TItem>
TItem* registration<TItem>::get_item(const std::string & type)
{
  typename std::map<std::string, TItem*>::const_iterator it =_items.find(type);
  if (it == _items.cend()) {
    loggers::get_instance().error("registration::get_item: %s: Unregistered item", type.c_str());
    return NULL;
  }
  
  return it->second;
}

#endif
