#ifndef CLUSTER_ABSTRACT_CLASSIFIER_HH
# define CLUSTER_ABSTRACT_CLASSIFIER_HH

# include "Syslog.h"

# include "utils/ArgList.hh"
# include "utils/FactoryDeclare.hh"
# include "utils/ParseError.hh"
# include "ClusterOutput.hh"

/** @brief Abstract interface for classifiers
 *
 * This class provides an abstract interface to use and manipulate
 * classifiers.
 *
 * @author Frederic Py <fpy@mbari.org>
 */
class AbstractClassifier {
public:
  typedef ClusterOutput::cluster_type cluster_type;
  typedef ClusterOutput::dist_type dist_type;

  /** @brief Destructor
   */
  virtual ~AbstractClassifier() {}

  /** @brief Get classifier name
   *
   * @return The classifier identifier.
   */
  Symbol const &getId() const {
    return m_name;
  }

  /** @brief Get last classification result
   *
   * @pre the last classify method call was succesful
   *
   * @return The cluster identified during last classification
   *
   * @sa bool classify() const
   * @sa dist_type const &lastDistance() const
   */
  cluster_type const &lastWinner() const {
    return m_output.data.bestCluster;
  }
  /** @brief Get last classification quality
   *
   * @pre the last classify method call was succesful
   *
   * @return The quality of the last classification. This quality
   * is expressed in term of distance metric (i.e. a positive value
   * where 0 correspond to a perfect match and the qsuality degrades
   * as this value grows) preferably normalised between 0 and 1 
   *
   * @sa bool classify() const
   * @sa cluster_type const &lastWinner() const
   */
  dist_type const &lastDistance() const {
    return m_output.data.distance;
  }

  /** @brief Lauch classification
   *
   * This method lauch the classification for this classifier.
   *
   * @retval true if classifi!cation was successfull
   * @retval false failed to classify this is generally due
   * to a device needed for classification that is not ready.
   *
   * @sa bool classify(cluster_type &, dist_type &);
   */
  bool classify() {
    bool ret = classify(m_output.data.bestCluster, 
			m_output.data.distance);
    if( ret ) {
      m_output.write();
//       Syslog::write("cluster[%s] : result = %d %f.", 
// 		    getId().c_str(), m_output.data.bestCluster,
// 		    m_output.data.distance);
    } // else 
//       Syslog::write("cluster[%s] : sources not ready.", getId().c_str());
    return ret;
  }
  
protected:
  /** @brief Constructor.
   *
   * This constructor is used by ClusterLoader to create a
   * new classifier from a configuration file.
   *
   * @param args Configuration file informations
   *
   * This class just extract the field "name" from args to fix
   * the classifier name
   */
  AbstractClassifier(ArgList const &args)
    :m_name(*args.get("name")), m_output(*args.get("name"), SharedData::Write)
  {}
  
  /** brief classification function
   *
   * This method is called by classify() public method to
   * compute the classification
   */
  virtual bool classify(cluster_type &winner, dist_type &distance) =0;

private:
  /** @brief Classifier name
   */
  Symbol m_name;
  /** @brief Last classification result
   */
  ClusterOutput m_output;

}; // AbstractClassifier

template<class Ty>
class DeclareClassifier 
  :public FactoryDeclare<Ty, AbstractClassifier, ArgList> {
public:
  DeclareClassifier(Symbol const &type)
    :FactoryDeclare<Ty, AbstractClassifier, ArgList>(type) {}
  ~DeclareClassifier() {}
}; // DeclareClassifier<>

#endif // CLUSTER_ABSTRACT_CLASSIFIER_HH
