#ifndef CLUSTER_ABSTRACT_DEVICE_HH
# define CLUSTER_ABSTRACT_DEVICE_HH

# include <utility>
# include "utils/ArgList.hh"
# include "dummyUpdater.hh"


# include "DeviceIF.h"

/** @brief Abstract device interface.
 *
 * This class offers an abstract view of a device. It is used
 * as a new level of abstraction of AUV device interfaces to be
 * able to connect to devices and read their data in a
 * generic/transaprent way.
 *
 * @author Frederic Py <fpy@mbari.org>
 */
class AbstractDevice {
public:
  /** @brief Destructor
   */
  virtual ~AbstractDevice() {}

  bool updated() const {
    return is_updated;
  }

  void updateIt() {
    if( !is_updated ) {
      update();
      is_updated = true;
    }
  }

  void resetUpdate() {
    is_updated = false;
  }

  /** @brief Device name
   *
   * @param name A destination string
   *
   * This method writes the name of this device into @e name
   */
  virtual void name(DeviceIF::Name id) =0;
  /** @brief Chec if connected.
   *
   * This methods check if this interface is properly
   * connected to a device.
   *
   * @retval true if connected
   * @retval fasle else
   */
  virtual bool connected() =0;
  /** @brief Check for readyness.
   *
   * This method indicates if the device was correctly updated at
   * least one time. This indicates if the data provided by this
   * interface have a meaning 
   *
   * @retval true if connected and ready
   * @retval false else
   * @sa bool connected()
   */
  virtual bool ready() =0;

  /** @brief Check for attribute.
   *
   * @param name An identifier
   *
   * Looks if this device provide anny attribute named @e name
   *
   * @retval true if the attribute @e name exist
   * @retval false else
   */
  bool hasAttribute(Symbol const &name) const {
    return m_db.exists(name);
  }

  /** @brief get Attribute value.
   *
   * @param name An  identifier
   * @pre device is ready
   * @pre attribute @e name exist
   * @return The double value of @e name
   * @throw UnknownKey attribute @e name does not exist
   *
   * @sa bool ready() 
   * @sa bool hasAttribute(Symbol const &) const
   * @sa dbEntry *get(Symbol const &) const
   * @sa double dbEntry::getValue() const
   */
  double getAttribute(Symbol const &name) const /* throw(UnknownKey) */ {
    return m_db[name];
  }

  /** @brief get Attribute
   *
   * @param name An identifier
   * @pre device is ready
   * @pre attribute @e name exist
   * @return The entry for attribute @e name
   * @throw UnknownKey attribute @e name does not exist
   *
   * @sa bool ready() 
   * @sa bool hasAttribute(Symbol const &) const
   * @sa double getAttribute(Symbol const &) const
   */
  dbEntry *get(Symbol const &name) const /* throw(UnknownKey) */ {
    return m_db.getEntry(name);
  }
  
protected:
  /** @brief default constructor
   */
  AbstractDevice()
    :is_updated(false) {}

  /** @brief Update values.
   *
   * This method send an update request to the subjacent device to update
   * the value attached to it.
   */
  virtual void update() =0;

  /** @brief Attributes association map initialisation
   *
   * This method creates the association map between symbols
   * and attributes based on information provided by a dummyUpdater instance
   * @param updater subjacent updater for this class
   */
  void fillDB(dummyUpdater &updater) {
    updater.fillDB(m_db);
  }

private:
  /** @brief Internal attributes association map
   */
  ValDB m_db;
  bool is_updated;
}; // AbstractDevice

#endif // CLUSTER_ABSTRACT_DEVICE_HH
