#ifndef CLUSTER_VAL_DB_HH
# define CLUSTER_VAL_DB_HH

# include "utils/KeyMap.hh"

class ValDB;

/** @brief Abstract ValDB entry
 *
 * This class is an abstract interface to a ValDB entry. It provides
 * the ability to read the entry value as a double.
 * @relates ValDB
 *
 * @author Frederidc Py <fpy@mbari.org>
 */
class dbEntry {
public:
  /** @brief Desturctor
   */
  virtual ~dbEntry() {}
  /** @brief Entry value.
   *
   * @return the entry value has a double.
   */
  virtual double getValue() const =0;

protected:
  /** @brief Default Constructor 
   */
  dbEntry() {}
}; // dbEntry

/** @brief Real entry
 * @relates ValDB
 *
 * This class encapsulates a reference to a variable that can
 * then be stored into a ValDB and be read as a double value.
 *
 * @param Ty real type of the entry.
 * @pre Ty has to be castable to a double.
 */
template<class Ty>
class ConcreteEntry :public dbEntry {
public:
  /** @brief Constructor.
   *
   * @param ref A pointer to the variable we want to store.
   *
   * Create a new instance pointing to @e ref
   */
  ConcreteEntry(Ty const *ref)
    :m_ref(ref) {}
  /** @brief Destructor 
   */
  ~ConcreteEntry() {}

  /** @brief Get entry value.
   *
   * This method convert the pointed variable by this entry into a
   * double value.
   *
   * @return double value of *m_ref
   */
  double getValue() const {
    double res = (double)*m_ref;
    return res;
  }

private:
  /** @brief Pointed variable.
   *
   * This pointer refers to one particular variable
   * which will store the value of this entry.
   */
  Ty const *m_ref;
  
}; // ConcreteEntry<>

/** @brief Varaiables data base
 *
 * This class is an association map between symbolic names and varaiables.
 * User can then use it to have a double value of one variable dynamically.
 */
class ValDB {
public:
  /** @brief default Constructor.
   */
  ValDB() {}
  /** @brief Destructor
   */
  ~ValDB() {
    clear();
  }

  /** @brief Insert a new entry
   *
   * @param id entry key
   * @param ref corresponding entry
   *
   * This method adds a new entry @e ref attached to the symbolic name @e id.
   *
   * @retval true if the erntry was succesfully created
   * @retval false if there's already an entry attached to @e id
   *
   * @note It is strongly recommended to use the template addEntry function
   * instead which is much more robust than thios one.
   *
   * @sa bool addEntry<Ty>(ValDB &, Symbol const &, Ty const &ref)
   */
  bool addEntry(Symbol const &id, dbEntry *ref);

  /** @brief Check for entry existence.
   *
   * @param name An identifier
   * @retval true if there's an entry attached to @e name
   * @retval false else
   */
  bool exists(Symbol const &name) const {
    return m_entries.exists(name);
  }
  
  /** @brief Get entry
   *
   * @param name and identifier
   *
   * @pre There exists an entry attached to @e name
   * @return The entry associated to @e name
   * @throw UnknownKey No entry attached to @e name
   *
   * @sa bool exists(Symbol const &) const
   * @sa double get(Symbol const &) const
   * @sa double operator[](Symbol const &) const
   */
  dbEntry *getEntry(Symbol const &name) const /* throw(UnknownKey) */;
  /** @brief Get entry value
   *
   * @param name and identifier
   *
   * @pre There exists an entry attached to @e name
   * @return The double value of entry associated to @e name
   * @throw UnknownKey No entry attached to @e name
   *
   * @sa bool exists(Symbol const &) const
   * @sa dbEntry *getEntry(Symbol const &) const
   * @sa double operator[](Symbol const &) const
   * @sa double dbEntry::getValue() const
   */
  double get(Symbol const &name) const /* throw(UnknownKey) */ {
    return getEntry(name)->getValue();
  }
  /** @brief Get entry value
   *
   * @param name and identifier
   *
   * @pre There exists an entry attached to @e name
   * @return The double value of entry associated to @e name
   * @throw UnknownKey No entry attached to @e name
   *
   * @sa bool exists(Symbol const &) const
   * @sa dbEntry *getEntry(Symbol const &) const
   * @sa double get(Symbol const &) const
   * @sa double dbEntry::getValue() const
   */
  double operator[](Symbol const &name) const /* throw(UnknownKey) */ {
    return get(name);
  }

  /** @brief Check for emptyness
   *
   * @retval true if this instance has no entry
   * @retavl false else
   */
  bool empty() const {
    return m_entries.empty();
  }
  /** @brief Clear the DB
   *
   * This method removes all the entries from this DB
   *
   * @post This DB is empty
   */
  void clear();

private:
  /** @brief Entries
   *
   * This is the associative map that stores the entries provided by
   * this instance
   */
  KeyMap<dbEntry *> m_entries;

}; // ValDB

/** @brief Add a new entry
 * @relates ValDB
 *
 * @param db A ValDB
 * @param name An identifier
 * @param ref A pointer to a variable
 *
 * This function adds a new entry to @e db that associate @e ref to
 * the identifier @e name
 *
 * @retval true If the entry was successfully created
 * @retval false if @e db had already an entry attached to @e name
 */
template<class Ty>
bool addEntry(ValDB &db, char const *name, Ty const *ref) {
  dbEntry *to_ins = new ConcreteEntry<Ty>(ref);
  Symbol id(name, true);

  bool ret = db.addEntry(id, to_ins);
  if( !ret )
    delete to_ins;
  return ret;
}

#endif // CLUSTER_VAL_DB_HH
