#ifndef CLUSTER_DATA_TRANSFORMER_HH
# define CLUSTER_DATA_TRANSFORMER_HH

# include <string.h>

# include "DevicesManager.hh"
# include "utils/FactoryDeclare.hh"
# include "utils/ParseError.hh"

template<unsigned ArgC>
class UpdateFunctor :public dummyUpdater {
public:
  virtual ~UpdateFunctor() {}

  void name(DeviceIF::Name id) {
    strcpy(id, m_name.c_str());
  }
  
  bool ready() const {
    return m_ready;
  }
  void update() {
    unsigned i;
    AbstractDevice **j = m_arg_sources;

    m_ready = false;
    for( i=0; i<ArgC; ++i, ++j ) 
      if( !(*j)->ready() )
	return;
    m_value = compute(m_args);
    m_ready = true;
  }

protected:
  UpdateFunctor(std::pair<Symbol, ArgList const *> const &args)
    :m_name(args.first), m_ready(false) {
    Argument arg = args.second->get(m_name);
    unsigned i = 0;
    AbstractDevice **j = m_arg_sources;
    dbEntry **k = m_args;

    for( ; i<ArgC; ++i, ++j, ++k) {
      *j = m_devs->useDevice(arg[1+2*i], *(args.second));
      if( NULL==*j )
	throw ParseError(arg[1+2*i]+" is an unknown device");
      else if( !(*j)->hasAttribute(arg[2*(i+1)]) )
	throw ParseError(arg[1+2*i]+" does not hacve attribute "+arg[2*(i+1)]);
      *k = (*j)->get(arg[2*(i+1)]);
    }
  }

  virtual double compute(dbEntry **args) =0;

private:
  void fillDB(ValDB &db) {
    addEntry(db, "value", &m_value);
  }

  AbstractDevice *m_arg_sources[ArgC];
  dbEntry        *m_args[ArgC];
  SingletonUse<DevicesManager> m_devs;

  Symbol m_name;
  double m_value;
  bool m_ready;
};


template<class Updater>
class DataTransformer :public AbstractDevice {
public:
  DataTransformer(std::pair<Symbol, ArgList const *> const &args)
    :m_updater(args) {
    fillDB(m_updater);
  }
  virtual ~DataTransformer() {}
  
private:  
  void name(DeviceIF::Name id) {
    m_updater.name(id);
  }
  bool connected() {
    return true;
  }
  void update() {
    m_updater.update();
  }
  bool ready() {
    return m_updater.ready();
  }

  Updater m_updater;
}; // DataTransformer

template<class Updater>
class DevOpDeclare
  :public FactoryDeclare< DataTransformer<Updater>, AbstractDevice, 
			  std::pair<Symbol, ArgList const *> > {
public:
  DevOpDeclare(Symbol const &type)
    :FactoryDeclare< DataTransformer<Updater>, AbstractDevice, 
		     std::pair<Symbol, ArgList const *> >(type) {}
  ~DevOpDeclare() {}
}; // DevOpDeclare<>

#endif // CLUSTER_DATA_TRANSFORMER_HH
