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

#ifndef toolx_hdf5_ntuple
#define toolx_hdf5_ntuple

#include "store"

#include <tools/vmanip>
#include <tools/ntuple_binding>
#include <tools/ntuple_booking>
#include <tools/sout>
#include <tools/scast>
#include <tools/forit>
#include <tools/stype>
#include <tools/mnmx>

namespace toolx {
namespace hdf5 {

class ntuple : public store {
public:
  class icol {
  public:
    virtual ~icol(){}
  public:
    virtual void* cast(tools::cid) const = 0;
    virtual tools::cid id_cls() const = 0;
  public:
    virtual bool set_basket_size(size_t a_size) = 0;
    virtual bool add() = 0;
    virtual bool fetch_entry() = 0;
    virtual const std::string& name() const = 0;
    virtual void reset() = 0;
  };
public:


  template <class T>
  class column_ref : public virtual icol {
  public:
    static tools::cid id_class() {
      static const T s_v = T(); //do that for T = std::string.
      return tools::_cid(s_v)+10000;
    }
    virtual void* cast(tools::cid a_class) const {
      if(void* p = tools::cmp_cast<column_ref>(this,a_class)) {return p;}
      else return 0;
    }
    virtual tools::cid id_cls() const {return id_class();}
  public: //icol
    virtual bool add() { //write.
      if(!m_write) return false;
      if(m_basket_pos>=m_basket_size) {
        if(!m_branch.write_page<T>(m_basket_size,m_basket)) {
          m_store.out() << "toolx::hdf5::ntuple::column_ref::add : write_page() failed." << std::endl;
          m_basket_pos = 0;
          return false;
	}
        m_basket_pos = 0;
	if(m_want_new_basket_size) {
	  delete [] m_basket;
	  m_basket = new T[m_want_new_basket_size];
	  m_basket_pos = 0;
	  m_basket_size = m_want_new_basket_size;
	  m_want_new_basket_size = 0;
	}
      }
      m_basket[m_basket_pos] = m_ref;
      m_basket_pos++;
      return true;
    }
    virtual bool fetch_entry() { //read.
      if(m_write) return false;
      if(m_basket_pos>=m_basket_end) { //we need more data.
        if(m_branch.pos()>=m_branch.entries()) {
          m_store.out() << "toolx::hdf5::ntuple::column_ref:fetch_entry : no more data." << std::endl;
          m_basket_pos = 0;
          m_basket_end = 0;
          return false;
	}
	if(m_want_new_basket_size) {
	  delete [] m_basket;
	  m_basket = new T[m_want_new_basket_size];
	  m_basket_pos = 0;
	  m_basket_size = m_want_new_basket_size;
	  m_want_new_basket_size = 0;
	}
        tools::uint64 remain = m_branch.entries()-m_branch.pos();
        size_t n = tools::min_of<size_t>((size_t)remain,m_basket_size);
        if(!m_branch.read_page<T>(n,m_basket)) {
          m_store.out() << "toolx::hdf5::ntuple::column_ref:fetch_entry : read_page() failed." << std::endl;
          m_basket_pos = 0;
          m_basket_end = 0;
          return false;
        }
        m_basket_pos = 0;
        m_basket_end = n;
      }
      m_ref = m_basket[m_basket_pos];
      m_basket_pos++;
      return true;
    }
    virtual void reset() {m_branch.reset_pos();}
    virtual const std::string& name() const {return m_name;}
    virtual bool set_basket_size(size_t a_size) {
      if(!a_size) return false;
      m_want_new_basket_size = a_size;
      return true;
    }
  public:
    column_ref(store& a_store,pages& a_pages,bool a_write,const std::string& a_name,size_t a_basket_size,T& a_ref)
    :m_store(a_store)
    ,m_branch(a_pages)
    ,m_write(a_write)
    ,m_name(a_name)
    ,m_ref(a_ref)
    ,m_basket_size(a_basket_size?a_basket_size:32000)
    ,m_basket_pos(0)
    ,m_basket_end(0) //read.
    ,m_basket(0)
    ,m_want_new_basket_size(0)
    {
      m_basket = new T[m_basket_size];
      if(m_write) {
      } else { //read.
        tools::uint64 _entries = m_branch.entries();
        size_t n = tools::min_of<size_t>((size_t)_entries,m_basket_size);
        if(_entries) {
          if(!m_branch.read_page<T>(n,m_basket)) {
            m_store.out() << "toolx::hdf5::ntuple::column_ref:column_ref : read_page() failed." << std::endl;
            m_basket_pos = 0;
            m_basket_end = 0;
            return;
          }
        }
  	m_basket_pos = 0;
	m_basket_end = n;
      }
    }
    virtual ~column_ref(){
      if(m_write && m_basket_pos) {
        if(!m_branch.write_page<T>(m_basket_pos,m_basket)) {
          m_store.out() << "toolx::hdf5::ntuple::column_ref::~column_ref : write_page() failed." << std::endl;
        }
      }
      delete [] m_basket;
    }
  protected:
    column_ref(const column_ref& a_from)
    :icol(a_from)
    ,m_store(a_from.m_store)
    ,m_branch(a_from.m_branch)
    ,m_write(a_from.m_write)
    ,m_name(a_from.m_name)
    ,m_ref(a_from.m_ref)
    ,m_basket_size(a_from.m_basket_size)
    ,m_basket_pos(0)
    ,m_basket_end(0)
    ,m_basket(0)
    ,m_want_new_basket_size(0)
    {}
    column_ref& operator=(const column_ref& a_from){
      if(&a_from==this) return *this;
      m_name = a_from.m_name;
      m_basket_size = a_from.m_basket_size;
      m_basket_pos = 0;
      m_basket_end = 0;
      m_want_new_basket_size = 0;
      return *this;
    }
  protected:
    store& m_store;
    pages& m_branch;
    bool m_write;
    std::string m_name;
    T& m_ref;
    size_t m_basket_size;
    size_t m_basket_pos;
    size_t m_basket_end;
    T* m_basket;
    size_t m_want_new_basket_size;
  };

  template <class T>
  class column : public column_ref<T> {
    typedef column_ref<T> parent;
  public:
    static tools::cid id_class() {
      static const T s_v = T(); //do that for T = std::string.
      return tools::_cid(s_v);
    }
    virtual void* cast(tools::cid a_class) const {
      if(void* p = tools::cmp_cast<column>(this,a_class)) {return p;}
      else return 0;
    }
    virtual tools::cid id_cls() const {return id_class();}
  public:
    column(store& a_store,pages& a_pages,bool a_write,const std::string& a_name,size_t a_basket_size)
    :parent(a_store,a_pages,a_write,a_name,a_basket_size,m_tmp)
    ,m_tmp(T())
    {
    }
    virtual ~column(){
    }
  protected:
    column(const column& a_from)
    :icol(a_from)
    ,parent(a_from)
    ,m_tmp(a_from.m_tmp)
    {}
    column& operator=(const column& a_from){
      if(&a_from==this) return *this;
      parent::operator=(a_from);
      m_tmp = a_from.m_tmp;
      return *this;
    }
  public:
    bool fill(const T& a_value) {m_tmp = a_value;return true;} //write.
    bool get_entry(T& a_v) const {a_v = m_tmp;return true;} //read.
  protected:
    T m_tmp;
  };

  TOOLS_CLASS_STRING_VALUE(vector_char,vector<char>)
  TOOLS_CLASS_STRING_VALUE(vector_short,vector<short>)
  TOOLS_CLASS_STRING_VALUE(vector_int,vector<int>)
  TOOLS_CLASS_STRING_VALUE(vector_float,vector<float>)
  TOOLS_CLASS_STRING_VALUE(vector_double,vector<double>)

  TOOLS_CLASS_STRING_VALUE(string,string)

  TOOLS_CLASS_STRING_VALUE(vector_int64,vector<tools::int64>)
  TOOLS_CLASS_STRING_VALUE(vector_uchar,vector<tools::uchar>)
  TOOLS_CLASS_STRING_VALUE(vector_ushort,vector<tools::ushort>)
  TOOLS_CLASS_STRING_VALUE(vector_uint32,vector<tools::uint32>)
  TOOLS_CLASS_STRING_VALUE(vector_uint64,vector<tools::uint64>)

  TOOLS_CLASS_STRING_VALUE(vector_string,vector<std::string>)

  template <class T>
  class std_vector_column_ref : public virtual icol {
  public:
    static tools::cid id_class() {return tools::_cid_std_vector<T>()+10000;}
    virtual void* cast(tools::cid a_class) const {
      if(void* p = tools::cmp_cast<std_vector_column_ref>(this,a_class)) {return p;}
      else return 0;
    }
    virtual tools::cid id_cls() const {return id_class();}
  public: //icol
    virtual bool add() { //write.
      if(!m_write) return false;
      const T* _data = m_ref.data();
      return m_branch.write_vlen<T>(m_ref.size(),_data);
    }
    virtual bool fetch_entry() { //read.
      if(m_write) return false;
      size_t n;
      T* _data;
      if(!m_branch.read_vlen<T>(n,_data)) {
        m_store.out() << "toolx::hdf5::ntuple::std_vector_column_ref:fetch_entry : read_page() failed." << std::endl;
        return false;
      }
      m_ref.resize(n);
      T* dpos = _data;
      T* pos = m_ref.data();
      for(size_t index=0;index<n;index++,pos++,dpos++) *pos = *dpos;
      delete [] _data;
      return true;
    }
    virtual void reset() {m_branch.reset_pos();}
    virtual const std::string& name() const {return m_name;}
    virtual bool set_basket_size(size_t) {return true;}
  public:
    std_vector_column_ref(store& a_store,pages& a_pages,bool a_write,const std::string& a_name,
                          size_t /*a_basket_size*/,
                          std::vector<T>& a_ref)
    :m_store(a_store)
    ,m_branch(a_pages)
    ,m_write(a_write)
    ,m_name(a_name)
    ,m_ref(a_ref)
    {
    }
    virtual ~std_vector_column_ref(){
    }
  protected:
    std_vector_column_ref(const std_vector_column_ref& a_from)
    :icol(a_from)
    ,m_store(a_from.m_store)
    ,m_branch(a_from.m_branch)
    ,m_write(a_from.m_write)
    ,m_name(a_from.m_name)
    ,m_ref(a_from.m_ref)
    {}
    std_vector_column_ref& operator=(const std_vector_column_ref& a_from){
      if(&a_from==this) return *this;
      m_name = a_from.m_name;
      return *this;
    }
  protected:
    store& m_store;
    pages& m_branch;
    bool m_write;
    std::string m_name;
    std::vector<T>& m_ref;
  };

  template <class T>
  class std_vector_column : public std_vector_column_ref<T> {
    typedef std_vector_column_ref<T> parent;
  public:
    static tools::cid id_class() {return tools::_cid_std_vector<T>();}
    virtual void* cast(tools::cid a_class) const {
      if(void* p = tools::cmp_cast<std_vector_column>(this,a_class)) {return p;}
      else return 0;
    }
    virtual tools::cid id_cls() const {return id_class();}
  public:
    std_vector_column(store& a_store,pages& a_pages,bool a_write,const std::string& a_name,size_t a_basket_size)
    :parent(a_store,a_pages,a_write,a_name,a_basket_size,m_tmp)
    {}
    virtual ~std_vector_column(){}
  protected:
    std_vector_column(const std_vector_column& a_from)
    :icol(a_from)
    ,parent(a_from)
    {}
    std_vector_column& operator=(const std_vector_column& a_from){
      parent::operator=(a_from);
      return *this;
    }
  public:
    const std::vector<T>& data() const {return m_tmp;}
  protected:
    std::vector<T> m_tmp;
  };

  class column_string_ref : public virtual icol {
  public:
    static tools::cid id_class() {
      static const std::string s_v;
      return tools::_cid(s_v)+10000;
    }
    virtual void* cast(tools::cid a_class) const {
      if(void* p = tools::cmp_cast<column_string_ref>(this,a_class)) {return p;}
      else return 0;
    }
    virtual tools::cid id_cls() const {return id_class();}
  public: //icol
    virtual bool add() { //write.
      if(!m_write) return false;
      return m_branch.write_string(m_ref);
    }
    virtual bool fetch_entry() { //read.
      if(m_write) return false;
      if(!m_branch.read_string(m_ref)) {
        m_store.out() << "toolx::hdf5::ntuple::column_string_ref:fetch_entry : read_page() failed." << std::endl;
        return false;
      }
      return true;
    }
    virtual void reset() {m_branch.reset_pos();}
    virtual const std::string& name() const {return m_name;}
    virtual bool set_basket_size(size_t) {return true;}
  public:
    column_string_ref(store& a_store,pages& a_pages,bool a_write,const std::string& a_name,
                      size_t /*a_basket_size*/,
                      std::string& a_ref)
    :m_store(a_store)
    ,m_branch(a_pages)
    ,m_write(a_write)
    ,m_name(a_name)
    ,m_ref(a_ref)
    {
    }
    virtual ~column_string_ref(){
    }
  protected:
    column_string_ref(const column_string_ref& a_from)
    :icol(a_from)
    ,m_store(a_from.m_store)
    ,m_branch(a_from.m_branch)
    ,m_write(a_from.m_write)
    ,m_name(a_from.m_name)
    ,m_ref(a_from.m_ref)
    {}
    column_string_ref& operator=(const column_string_ref& a_from){
      if(&a_from==this) return *this;
      m_name = a_from.m_name;
      return *this;
    }
  protected:
    store& m_store;
    pages& m_branch;
    bool m_write;
    std::string m_name;
    std::string& m_ref;
  };

  class column_string : public column_string_ref {
    typedef column_string_ref parent;
  public:
    static tools::cid id_class() {
      static const std::string s_v;
      return tools::_cid(s_v);
    }
    virtual void* cast(tools::cid a_class) const {
      if(void* p = tools::cmp_cast<column_string>(this,a_class)) {return p;}
      else return 0;
    }
    virtual tools::cid id_cls() const {return id_class();}
  public:
    column_string(store& a_store,pages& a_pages,bool a_write,const std::string& a_name,tools::uint32 a_basket_size)
    :parent(a_store,a_pages,a_write,a_name,a_basket_size,m_tmp)
    {
    }
    virtual ~column_string(){
    }
  protected:
    column_string(const column_string& a_from)
    :icol(a_from)
    ,parent(a_from)
    {}
    column_string& operator=(const column_string& a_from){
      if(&a_from==this) return *this;
      parent::operator=(a_from);
      return *this;
    }
  public:
    bool fill(const std::string& a_value) {m_tmp = a_value;return true;}
    bool get_entry(std::string& a_value) const {a_value = m_tmp;return true;}
  protected:
    std::string m_tmp;
  };



  class std_vector_column_string_ref : public virtual icol {
  public:
    static tools::cid id_class() {return tools::_cid_std_vector<std::string>()+10000;}
    virtual void* cast(tools::cid a_class) const {
      if(void* p = tools::cmp_cast<std_vector_column_string_ref>(this,a_class)) {return p;}
      else return 0;
    }
    virtual tools::cid id_cls() const {return id_class();}
  public: //icol
    virtual bool add() { //write.
      if(!m_write) return false;
      size_t _size = 0;
     {tools_vforcit(std::string,m_ref,it) {
        _size += (*it).size();
	_size++;
      }}
      char* _data = _size?new char[_size]:0;
     {char* pos = _data;
      tools_vforcit(std::string,m_ref,it) {
        ::memcpy(pos,(*it).c_str(),(*it).size());
	pos += (*it).size();
	*pos = 0;
	pos++;
      }}
      return m_branch.write_vlen<char>(_size,_data);
    }
    virtual bool fetch_entry() { //read.
      if(m_write) return false;
      size_t n;
      char* _data;
      if(!m_branch.read_vlen<char>(n,_data)) {
        m_store.out() << "toolx::hdf5::ntuple::std_vector_column_string_ref:fetch_entry : read_page() failed." << std::endl;
        return false;
      }
      m_ref.clear();
     {char* pos = _data;
      char* begin = _data;
      for(size_t ichar=0;ichar<n;ichar++,pos++) {
        if(*pos==0) {
	  m_ref.push_back(begin);
	  begin = pos+1;
        }
      }}
      delete [] _data;
      return true;
    }
    virtual void reset() {m_branch.reset_pos();}
    virtual const std::string& name() const {return m_name;}
    virtual bool set_basket_size(size_t) {return true;}
  public:
    std_vector_column_string_ref(store& a_store,pages& a_pages,bool a_write,const std::string& a_name,
                          size_t /*a_basket_size*/,
                          std::vector<std::string>& a_ref)
    :m_store(a_store)
    ,m_branch(a_pages)
    ,m_write(a_write)
    ,m_name(a_name)
    ,m_ref(a_ref)
    {
    }
    virtual ~std_vector_column_string_ref(){
    }
  protected:
    std_vector_column_string_ref(const std_vector_column_string_ref& a_from)
    :icol(a_from)
    ,m_store(a_from.m_store)
    ,m_branch(a_from.m_branch)
    ,m_write(a_from.m_write)
    ,m_name(a_from.m_name)
    ,m_ref(a_from.m_ref)
    {}
    std_vector_column_string_ref& operator=(const std_vector_column_string_ref& a_from){
      if(&a_from==this) return *this;
      m_name = a_from.m_name;
      return *this;
    }
  protected:
    store& m_store;
    pages& m_branch;
    bool m_write;
    std::string m_name;
    std::vector<std::string>& m_ref;
  };

  class std_vector_column_string : public std_vector_column_string_ref {
    typedef std_vector_column_string_ref parent;
  public:
    static tools::cid id_class() {return tools::_cid_std_vector<std::string>();}
    virtual void* cast(tools::cid a_class) const {
      if(void* p = tools::cmp_cast<std_vector_column_string>(this,a_class)) {return p;}
      else return 0;
    }
    virtual tools::cid id_cls() const {return id_class();}
  public:
    std_vector_column_string(store& a_store,pages& a_pages,bool a_write,const std::string& a_name,size_t a_basket_size)
    :parent(a_store,a_pages,a_write,a_name,a_basket_size,m_tmp)
    {}
    virtual ~std_vector_column_string(){}
  protected:
    std_vector_column_string(const std_vector_column_string& a_from)
    :icol(a_from)
    ,parent(a_from)
    {}
    std_vector_column_string& operator=(const std_vector_column_string& a_from){
      parent::operator=(a_from);
      return *this;
    }
  public:
    const std::vector<std::string>& data() const {return m_tmp;}
  protected:
    std::vector<std::string> m_tmp;
  };

public:
  ntuple(std::ostream& a_out,hid_t a_group,const std::string& a_name,
         unsigned int a_compress,size_t /*a_basket_size*//* = 32000*/)
  :store(a_out,a_group,a_name,true,a_compress)    //true=write
  {}

  ntuple(std::ostream& a_out,hid_t a_group,const std::string& a_name)
  :store(a_out,a_group,a_name,false,0)  //false=read
  {
    tools_vforcit(pages*,m_pagess,it){

#define TOOLX_HDF5_NTUPLE_READ_CREATE_COL(a__type) \
      if((*it)->form()==tools::stype(a__type())) {\
        column<a__type>* col = new column<a__type>(*this,*(*it),false,(*it)->name(),0);\
        if(!col) {tools::safe_clear<icol>(m_cols);return;}\
        m_cols.push_back(col);\
      }

#define TOOLX_HDF5_NTUPLE_READ_CREATE_VEC_COL(a__vec_type,a__type) \
      if((*it)->form()==s_vector_##a__vec_type()) {\
        std_vector_column<a__type>* col = new std_vector_column<a__type>(*this,*(*it),false,(*it)->name(),0);\
        if(!col) {tools::safe_clear<icol>(m_cols);return;}\
        m_cols.push_back(col);\
      }

#define TOOLX_HDF5_NTUPLE_READ_CREATE_VEC_COL_STRING \
      if((*it)->form()==s_vector_string()) {\
        std_vector_column_string* col = new std_vector_column_string(*this,*(*it),false,(*it)->name(),0);\
        if(!col) {tools::safe_clear<icol>(m_cols);return;}\
        m_cols.push_back(col);\
      }

           TOOLX_HDF5_NTUPLE_READ_CREATE_COL(char)
      else TOOLX_HDF5_NTUPLE_READ_CREATE_COL(short)
      else TOOLX_HDF5_NTUPLE_READ_CREATE_COL(int)
      else TOOLX_HDF5_NTUPLE_READ_CREATE_COL(tools::int64)

      else TOOLX_HDF5_NTUPLE_READ_CREATE_COL(float)
      else TOOLX_HDF5_NTUPLE_READ_CREATE_COL(double)

      else TOOLX_HDF5_NTUPLE_READ_CREATE_COL(tools::byte)
      else TOOLX_HDF5_NTUPLE_READ_CREATE_COL(tools::ushort)
      else TOOLX_HDF5_NTUPLE_READ_CREATE_COL(tools::uint32)
      else TOOLX_HDF5_NTUPLE_READ_CREATE_COL(tools::uint64)

      else
      if((*it)->form()==s_string()) {
        column_string* col = new column_string(*this,*(*it),false,(*it)->name(),0);
        if(!col) {tools::safe_clear<icol>(m_cols);return;}
        m_cols.push_back(col);
      }

      else TOOLX_HDF5_NTUPLE_READ_CREATE_VEC_COL(char,char)
      else TOOLX_HDF5_NTUPLE_READ_CREATE_VEC_COL(short,short)
      else TOOLX_HDF5_NTUPLE_READ_CREATE_VEC_COL(int,int)
      else TOOLX_HDF5_NTUPLE_READ_CREATE_VEC_COL(int64,tools::int64)

      else TOOLX_HDF5_NTUPLE_READ_CREATE_VEC_COL(float,float)
      else TOOLX_HDF5_NTUPLE_READ_CREATE_VEC_COL(double,double)

      else TOOLX_HDF5_NTUPLE_READ_CREATE_VEC_COL(uchar,tools::uchar)
      else TOOLX_HDF5_NTUPLE_READ_CREATE_VEC_COL(ushort,tools::ushort)
      else TOOLX_HDF5_NTUPLE_READ_CREATE_VEC_COL(uint32,tools::uint32)
      else TOOLX_HDF5_NTUPLE_READ_CREATE_VEC_COL(uint64,tools::uint64)

      else TOOLX_HDF5_NTUPLE_READ_CREATE_VEC_COL_STRING

      else {
        m_out << "toolx::hdf5::ntuple::ntuple(read) :"
              << " for column " << tools::sout((*it)->name())
              << ", type " << tools::sout((*it)->form()) << " not yet handled."
              << std::endl;
        //throw
        tools::safe_clear<icol>(m_cols);
        return;
      }
    }
#undef TOOLX_HDF5_NTUPLE_READ_CREATE_VEC_COL
#undef TOOLX_HDF5_NTUPLE_READ_CREATE_COL
  }

  ntuple(std::ostream& a_out,hid_t a_group,const std::string& a_name,const tools::ntuple_binding& a_bd)
  :store(a_out,a_group,a_name,false,0)  //false=read
  {
    if(!initialize(a_out,a_bd)) {}
  }

  ntuple(std::ostream& a_out,hid_t a_group,const tools::ntuple_booking& a_bkg,
         unsigned int a_compress,size_t a_basket_size/* = 32000*/)
  :store(a_out,a_group,a_bkg.name(),true,a_compress) //true=write
  ,m_title(a_bkg.title())
  {
    const std::vector<tools::column_booking>& cols = a_bkg.columns();
    tools_vforcit(tools::column_booking,cols,it){

#define TOOLX_HDF5_NTUPLE_CREATE_COL(a__type) \
      if((*it).cls_id()==tools::_cid(a__type())) {\
        a__type* user = (a__type*)(*it).user_obj();\
        if(user) {\
          if(!create_column_ref<a__type>((*it).name(),a_basket_size,*user)) {\
            m_out << "toolx::hdf5::ntuple :"\
                  << " can't create column_ref " << tools::sout((*it).name()) << "."\
                  << std::endl;\
            tools::safe_clear<icol>(m_cols);\
            return;\
          }\
        } else {\
          if(!create_column<a__type>((*it).name(),a_basket_size)) {\
            m_out << "toolx::hdf5::ntuple :"\
                  << " can't create column " << tools::sout((*it).name()) << "."\
                  << std::endl;\
            tools::safe_clear<icol>(m_cols);\
            return;\
          }\
	}\
      }

#define TOOLX_HDF5_NTUPLE_CREATE_VEC_COL(a__type) \
      if((*it).cls_id()==tools::_cid_std_vector<a__type>()) {\
        std::vector<a__type>* vec = (std::vector<a__type>*)(*it).user_obj();\
        if(!vec) {\
          m_out << "toolx::hdf5::ntuple :"\
                << " for std::vector column " << tools::sout((*it).name())\
                << ", the user vector pointer is null."\
                << std::endl;\
          tools::safe_clear<icol>(m_cols);\
          return;\
        }\
        if(!create_std_column_ref<a__type>((*it).name(),a_basket_size,*vec)) {\
          m_out << "toolx::hdf5::ntuple :"\
                << " can't create std::vector column " << tools::sout((*it).name()) << "."\
                << std::endl;\
          tools::safe_clear<icol>(m_cols);\
          return;\
	}\
      }

#define TOOLX_HDF5_NTUPLE_CREATE_VEC_COL_STRING \
      if((*it).cls_id()==tools::_cid_std_vector<std::string>()) {\
        std::vector<std::string>* vec = (std::vector<std::string>*)(*it).user_obj();\
        if(!vec) {\
          m_out << "toolx::hdf5::ntuple :"\
                << " for std::vector column " << tools::sout((*it).name())\
                << ", the user vector pointer is null."\
                << std::endl;\
          tools::safe_clear<icol>(m_cols);\
          return;\
        }\
        if(!create_std_column_string_ref((*it).name(),a_basket_size,*vec)) {\
          m_out << "toolx::hdf5::ntuple :"\
                << " can't create std::vector column " << tools::sout((*it).name()) << "."\
                << std::endl;\
          tools::safe_clear<icol>(m_cols);\
          return;\
	}\
      }

           TOOLX_HDF5_NTUPLE_CREATE_COL(char)
      else TOOLX_HDF5_NTUPLE_CREATE_COL(short)
      else TOOLX_HDF5_NTUPLE_CREATE_COL(int)
      else TOOLX_HDF5_NTUPLE_CREATE_COL(tools::int64)

      else TOOLX_HDF5_NTUPLE_CREATE_COL(float)
      else TOOLX_HDF5_NTUPLE_CREATE_COL(double)

      else TOOLX_HDF5_NTUPLE_CREATE_COL(tools::byte)
      else TOOLX_HDF5_NTUPLE_CREATE_COL(tools::ushort)
      else TOOLX_HDF5_NTUPLE_CREATE_COL(tools::uint32)
      else TOOLX_HDF5_NTUPLE_CREATE_COL(tools::uint64)

      else if((*it).cls_id()==tools::_cid(std::string())) {
        if(!create_column_string((*it).name(),a_basket_size)) {
          m_out << "toolx::hdf5::ntuple :"
                << " can't create string column " << tools::sout((*it).name()) << "."
                << std::endl;
          tools::safe_clear<icol>(m_cols);
          return;
	}

      } else TOOLX_HDF5_NTUPLE_CREATE_VEC_COL(char)
      else TOOLX_HDF5_NTUPLE_CREATE_VEC_COL(short)
      else TOOLX_HDF5_NTUPLE_CREATE_VEC_COL(int)
      else TOOLX_HDF5_NTUPLE_CREATE_VEC_COL(tools::int64)

      else TOOLX_HDF5_NTUPLE_CREATE_VEC_COL(float)
      else TOOLX_HDF5_NTUPLE_CREATE_VEC_COL(double)

      else TOOLX_HDF5_NTUPLE_CREATE_VEC_COL(tools::uchar)
      else TOOLX_HDF5_NTUPLE_CREATE_VEC_COL(tools::ushort)
      else TOOLX_HDF5_NTUPLE_CREATE_VEC_COL(tools::uint32)
      else TOOLX_HDF5_NTUPLE_CREATE_VEC_COL(tools::uint64)

      else TOOLX_HDF5_NTUPLE_CREATE_VEC_COL_STRING

      else {
        m_out << "toolx::hdf5::ntuple :"
              << " for column " << tools::sout((*it).name())
              << ", type with cid " << (*it).cls_id() << " not yet handled."
              << std::endl;
        tools::safe_clear<icol>(m_cols);
        return;
      }
    }
#undef TOOLX_HDF5_NTUPLE_CREATE_COL
#undef TOOLX_HDF5_NTUPLE_CREATE_VEC_COL

  }

  virtual ~ntuple() {
    tools::safe_clear<icol>(m_cols);
  }
protected:
  ntuple(const ntuple& a_from):store(a_from),m_title(a_from.m_title){}
  ntuple& operator=(const ntuple&){return *this;}
public:
  bool initialize(std::ostream& a_out,const tools::ntuple_binding& a_bd = tools::ntuple_binding()) {
    tools::safe_clear<icol>(m_cols);
   {tools_vforcit(pages*,m_pagess,it) (*it)->reset_pos();}

    tools_vforcit(pages*,m_pagess,it) {

#define TOOLX_HDF5_NTUPLE_READ_BINDING_CREATE_COL(a__type) \
      if((*it)->form()==tools::stype(a__type())) {\
        a__type* user_var = a_bd.find_variable<a__type>((*it)->name());\
        if(user_var) {\
          column_ref<a__type>* col = new column_ref<a__type>(*this,*(*it),false,(*it)->name(),0,*user_var);\
          if(!col) {tools::safe_clear<icol>(m_cols);return false;}\
          m_cols.push_back(col);\
	} else {\
          column<a__type>* col = new column<a__type>(*this,*(*it),false,(*it)->name(),0);\
          if(!col) {tools::safe_clear<icol>(m_cols);return false;}\
          m_cols.push_back(col);\
	}\
      }

#define TOOLX_HDF5_NTUPLE_READ_BINDING_CREATE_VEC_COL(a__vec_type,a__type) \
      if((*it)->form()==s_vector_##a__vec_type()) {\
        std::vector<a__type>* user_var = a_bd.find_variable< std::vector<a__type> >((*it)->name());\
	if(user_var) {\
          std_vector_column_ref<a__type>* col = \
	    new std_vector_column_ref<a__type>(*this,*(*it),false,(*it)->name(),0,*user_var);\
          if(!col) {tools::safe_clear<icol>(m_cols);return false;}\
          m_cols.push_back(col);\
	} else {\
          std_vector_column<a__type>* col = new std_vector_column<a__type>(*this,*(*it),false,(*it)->name(),0);\
          if(!col) {tools::safe_clear<icol>(m_cols);return false;}\
          m_cols.push_back(col);\
	}\
      }

#define TOOLX_HDF5_NTUPLE_READ_BINDING_CREATE_VEC_COL_STRING \
      if((*it)->form()==s_vector_string()) {\
        std::vector<std::string>* user_var = a_bd.find_variable< std::vector<std::string> >((*it)->name());\
	if(user_var) {\
          std_vector_column_string_ref* col = \
	    new std_vector_column_string_ref(*this,*(*it),false,(*it)->name(),0,*user_var);\
          if(!col) {tools::safe_clear<icol>(m_cols);return false;}\
          m_cols.push_back(col);\
	} else {\
          std_vector_column_string* col = new std_vector_column_string(*this,*(*it),false,(*it)->name(),0);\
          if(!col) {tools::safe_clear<icol>(m_cols);return false;}\
          m_cols.push_back(col);\
	}\
      }

           TOOLX_HDF5_NTUPLE_READ_BINDING_CREATE_COL(char)
      else TOOLX_HDF5_NTUPLE_READ_BINDING_CREATE_COL(short)
      else TOOLX_HDF5_NTUPLE_READ_BINDING_CREATE_COL(int)
      else TOOLX_HDF5_NTUPLE_READ_BINDING_CREATE_COL(tools::int64)

      else TOOLX_HDF5_NTUPLE_READ_BINDING_CREATE_COL(float)
      else TOOLX_HDF5_NTUPLE_READ_BINDING_CREATE_COL(double)

      else TOOLX_HDF5_NTUPLE_READ_BINDING_CREATE_COL(tools::byte)
      else TOOLX_HDF5_NTUPLE_READ_BINDING_CREATE_COL(tools::ushort)
      else TOOLX_HDF5_NTUPLE_READ_BINDING_CREATE_COL(tools::uint32)
      else TOOLX_HDF5_NTUPLE_READ_BINDING_CREATE_COL(tools::uint64)

      else
      if((*it)->form()==s_string()) {
        std::string* user_var = a_bd.find_variable<std::string>((*it)->name());
	if(user_var) {
          column_string_ref* col = new column_string_ref(*this,*(*it),false,(*it)->name(),0,*user_var);
          if(!col) {tools::safe_clear<icol>(m_cols);return false;}
          m_cols.push_back(col);
	} else {
          column_string* col = new column_string(*this,*(*it),false,(*it)->name(),0);
          if(!col) {tools::safe_clear<icol>(m_cols);return false;}
          m_cols.push_back(col);
        }
      }

      else TOOLX_HDF5_NTUPLE_READ_BINDING_CREATE_VEC_COL(char,char)
      else TOOLX_HDF5_NTUPLE_READ_BINDING_CREATE_VEC_COL(short,short)
      else TOOLX_HDF5_NTUPLE_READ_BINDING_CREATE_VEC_COL(int,int)
      else TOOLX_HDF5_NTUPLE_READ_BINDING_CREATE_VEC_COL(int64,tools::int64)

      else TOOLX_HDF5_NTUPLE_READ_BINDING_CREATE_VEC_COL(float,float)
      else TOOLX_HDF5_NTUPLE_READ_BINDING_CREATE_VEC_COL(double,double)

      else TOOLX_HDF5_NTUPLE_READ_BINDING_CREATE_VEC_COL(uchar,tools::uchar)
      else TOOLX_HDF5_NTUPLE_READ_BINDING_CREATE_VEC_COL(ushort,tools::ushort)
      else TOOLX_HDF5_NTUPLE_READ_BINDING_CREATE_VEC_COL(uint32,tools::uint32)
      else TOOLX_HDF5_NTUPLE_READ_BINDING_CREATE_VEC_COL(uint64,tools::uint64)

      else TOOLX_HDF5_NTUPLE_READ_BINDING_CREATE_VEC_COL_STRING

      else {
        a_out << "toolx::hdf5::ntuple::ntuple(read with binding) :"
              << " for column " << tools::sout((*it)->name())
              << ", type " << tools::sout((*it)->form()) << " not yet handled."
              << std::endl;
        tools::safe_clear<icol>(m_cols);
        return false;
      }
    }
#undef TOOLX_HDF5_NTUPLE_READ_BINDING_CREATE_COL
#undef TOOLX_HDF5_NTUPLE_READ_BINDING_CREATE_VEC_COL

    size_t num = m_cols.size();
    if(!num) {
      a_out << "toolx::hdf5::ntuple::ntuple(read with binding) :"
            << " zero columns."
            << std::endl;
      return false;
    }

   {tools_vforcit(tools::column_binding,a_bd.columns(),it) {
      if(!tools::find_named<icol>(m_cols,(*it).name())) {
        a_out << "toolx::hdf5::ntuple::ntuple(read with binding) :"
              << " error : for column binding with name " << tools::sout((*it).name()) << ", no ntuple column found."
              << std::endl;
        tools::safe_clear<icol>(m_cols);
        return false;
      }
    }}

    return true;
  }

  const std::string& title() const {return m_title;}

  const std::vector<icol*>& columns() const {return m_cols;}
  std::vector<icol*>& columns() {return m_cols;}

  template <class T>
  column_ref<T>* create_column_ref(const std::string& a_name,size_t a_basket_size,T& a_ref) {
    if(tools::find_named<icol>(m_cols,a_name)) return 0;
    pages* _pages = create_pages(a_name,tools::stype(T()));
    if(!_pages) return 0;
    column_ref<T>* col = new column_ref<T>(*this,*_pages,true,a_name,a_basket_size,a_ref);
    if(!col) return 0;
    m_cols.push_back(col);
    return col;
  }

  template <class T>
  column<T>* create_column(const std::string& a_name,size_t a_basket_size) {
    if(tools::find_named<icol>(m_cols,a_name)) return 0;
    pages* _pages = create_pages(a_name,tools::stype(T()));
    if(!_pages) return 0;
    column<T>* col = new column<T>(*this,*_pages,true,a_name,a_basket_size);
    if(!col) return 0;
    m_cols.push_back(col);
    return col;
  }

  column_string* create_column_string(const std::string& a_name,size_t a_basket_size) {
    if(tools::find_named<icol>(m_cols,a_name)) return 0;
    pages* _pages = create_pages(a_name,s_string());
    if(!_pages) return 0;
    column_string* col = new column_string(*this,*_pages,true,a_name,a_basket_size);
    if(!col) return 0;
    m_cols.push_back(col);
    return col;
  }

  template <class T>
  std_vector_column_ref<T>* create_std_column_ref(const std::string& a_name,size_t a_basket_size,std::vector<T>& a_ref) {
    //NOTE : to optimize, we do not handle a default std::vector value logic.
    if(tools::find_named<icol>(m_cols,a_name)) return 0;
    pages* _pages = create_pages(a_name,"vector<"+tools::stype(T())+">");
    if(!_pages) return 0;
    std_vector_column_ref<T>* col = new std_vector_column_ref<T>(*this,*_pages,true,a_name,a_basket_size,a_ref);
    if(!col) return 0;
    m_cols.push_back(col);
    return col;
  }

  std_vector_column_string_ref* create_std_column_string_ref(const std::string& a_name,size_t a_basket_size,std::vector<std::string>& a_ref) {
    //NOTE : to optimize, we do not handle a default std::vector value logic.
    if(tools::find_named<icol>(m_cols,a_name)) return 0;
    pages* _pages = create_pages(a_name,"vector<std::string>");
    if(!_pages) return 0;
    std_vector_column_string_ref* col = new std_vector_column_string_ref(*this,*_pages,true,a_name,a_basket_size,a_ref);
    if(!col) return 0;
    m_cols.push_back(col);
    return col;
  }

  template <class T>
  column_ref<T>* find_column_ref(const std::string& a_name) {
    icol* col = tools::find_named<icol>(m_cols,a_name);
    if(!col) return 0;
    return tools::id_cast<icol, column_ref<T> >(*col);
  }
  template <class T>
  column<T>* find_column(const std::string& a_name) {
    icol* col = tools::find_named<icol>(m_cols,a_name);
    if(!col) return 0;
    return tools::id_cast<icol, column<T> >(*col);
  }

  template <class T>
  std_vector_column_ref<T>* find_std_vector_column_ref(const std::string& a_name) {
    icol* col = tools::find_named<icol>(m_cols,a_name);
    if(!col) return 0;
    return tools::id_cast<icol, std_vector_column_ref<T> >(*col);
  }
  template <class T>
  std_vector_column<T>* find_std_vector_column(const std::string& a_name) {
    icol* col = tools::find_named<icol>(m_cols,a_name);
    if(!col) return 0;
    return tools::id_cast<icol, std_vector_column<T> >(*col);
  }

  std_vector_column_string_ref* find_std_vector_column_string_ref(const std::string& a_name) {
    icol* col = tools::find_named<icol>(m_cols,a_name);
    if(!col) return 0;
    return tools::id_cast<icol, std_vector_column_string_ref >(*col);
  }
  std_vector_column_string* find_std_vector_column_string(const std::string& a_name) {
    icol* col = tools::find_named<icol>(m_cols,a_name);
    if(!col) return 0;
    return tools::id_cast<icol, std_vector_column_string >(*col);
  }

  column_string* find_column_string(const std::string& a_name) {
    icol* col = tools::find_named<icol>(m_cols,a_name);
    if(!col) return 0;
    return tools::id_cast<icol, column_string >(*col);
  }

  bool add_row() { //write.
    if(m_cols.empty()) return false;
    bool status = true;
    tools_vforit(icol*,m_cols,it) {if(!(*it)->add()) status = false;}
    return status;
  }

  bool get_row() { //read.
    bool status = true;
    tools_vforcit(icol*,m_cols,it) {
      if(!(*it)->fetch_entry()) status = false;
    }
    return status;
  }

  bool set_basket_size(size_t a_size) {
    tools_vforit(icol*,m_cols,it) {if(!(*it)->set_basket_size(a_size)) return false;}
    return true;
  }

  void reset() { //read.
    tools_vforit(icol*,m_cols,it) (*it)->reset();
  }

protected:
  std::string m_title;
  std::vector<icol*> m_cols;
};

}}


#endif
