// Copyright (C) 2010, Guy Barrand. All rights reserved.
// See the file tools.license for terms.

#ifndef tools_wcsv_ntuple
#define tools_wcsv_ntuple

// A simple ntuple class to write at the csv format.
// (csv = comma separated value).
// Each add_row() write a row at the csv format.

#include "vfind"
#include "vmanip"
#include <ostream>

namespace tools {
namespace wcsv {

class ntuple {
protected:
  class icol {
  public:
    virtual ~icol(){}
  public:
    virtual void add() = 0;
    virtual const std::string& name() const = 0;
  };
  
public:
  template <class T>
  class column : public virtual icol {
  public: //icol
    virtual void add() {
      m_writer << m_tmp;
      m_tmp = m_def;
    }
    virtual const std::string& name() const {return m_name;}
  public:
    inline column(std::ostream& a_writer,
                  const std::string& a_name,
                  const T& a_def)
    :m_writer(a_writer)
    ,m_name(a_name),m_def(a_def),m_tmp(a_def)
    {}
    inline virtual ~column(){}
  protected:
    inline column(const column& a_from)
    :icol(a_from)
    ,m_writer(a_from.m_writer)
    ,m_name(a_from.m_name) 
    ,m_def(a_from.m_def)
    ,m_tmp(a_from.m_tmp)
    {}
    inline column& operator=(const column& a_from){
      m_name = a_from.m_name;
      m_def = a_from.m_def;
      m_tmp = a_from.m_tmp;
      return *this;
    }
  public:
    inline bool fill(const T& a_value) {m_tmp = a_value;return true;}
  protected:
    std::ostream& m_writer;
    std::string m_name;
    T m_def;
    T m_tmp;
  };


public:
  inline ntuple(std::ostream& a_writer,char a_sep = ',')
  :m_writer(a_writer)
  ,m_sep(a_sep)
  {}
  inline virtual ~ntuple() {
    tools::clear<icol>(m_cols);
  }
protected:
  inline ntuple(const ntuple& a_from)
  :m_writer(a_from.m_writer)
  ,m_sep(a_from.m_sep)
  {}
  inline ntuple& operator=(const ntuple& a_from){
    m_sep = a_from.m_sep;
    return *this;
  }
public:
  template <class T>
  inline column<T>* create_column(const std::string& a_name,
                                  const T& a_def = T()) {
    if(find_named<icol>(m_cols,a_name)) return 0;
    column<T>* col = new column<T>(m_writer,a_name,a_def);
    if(!col) return 0;
    m_cols.push_back(col);
    return col;
  }

  inline bool add_row() {
    if(m_cols.empty()) return false;
    std::vector<icol*>::iterator it;
    it=m_cols.begin();
    (*it)->add();
    it++;
    for(;it!=m_cols.end();++it) {
      m_writer << m_sep;
      (*it)->add();
    }
    m_writer << std::endl;
    return true;
  }

  inline const std::vector<icol*>& columns() const {return m_cols;}
protected:
  std::ostream& m_writer;
  char m_sep;
  std::vector<icol*> m_cols;
};

}}

#endif
