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

#ifndef tools_wroot_branch_element
#define tools_wroot_branch_element

#include "branch"

namespace tools {
namespace wroot {

inline const std::string& branch_element_store_class() {
  static const std::string s_v("TBranchElement");
  return s_v;
}

class branch_element : public branch {
  typedef branch parent;
public: //ibo
  virtual const std::string& store_cls() const {return branch_element_store_class();}
  virtual bool stream(buffer& a_buffer) const {
    unsigned int c;
    if(!a_buffer.write_version(1,c)) return false;
    if(!parent::stream(a_buffer)) return false;

    if(!a_buffer.write(fClassName)) return false;
    if(!a_buffer.write(fClassVersion)) return false;
    if(!a_buffer.write(fID)) return false;
    if(!a_buffer.write(fType)) return false;
    if(!a_buffer.write(fStreamerType)) return false;

    if(!a_buffer.set_byte_count(c)) return false;
    return true;
  }

public:
  branch_element(std::ostream& a_out,bool a_byte_swap,uint32 a_compression,
                 seek a_seek_directory,const std::string& a_name,const std::string& a_title,bool a_verbose)
  :parent(a_out,a_byte_swap,a_compression,a_seek_directory,a_name,a_title,a_verbose)
  ,fClassVersion(0)
  ,fID(0)
  ,fType(0)
  ,fStreamerType(-1)
  {
  }
  virtual ~branch_element(){
  }
protected:
  branch_element(const branch_element& a_from):ibo(a_from),parent(a_from) {}
  branch_element& operator=(const branch_element& a_from){parent::operator=(a_from);return *this;}
public:
  leaf_element* create_leaf_element(const std::string& a_name){
    leaf_element* lf = new leaf_element(m_out,a_name,fID,fType);
    m_leaves.push_back(lf);
    return lf;
  }
protected:
  virtual bool fill_leaves(buffer&) {return false;} //must be derived.
protected:
  std::string fClassName; //Class name of referenced object
  int fClassVersion;      //Version number of class
  int fID;                //element serial number in fInfo
  int fType;              //branch type
  int fStreamerType;      //branch streamer type
};

template <class T>
class std_vector_be_ref : public branch_element {
  typedef branch_element parent;
public:
  std_vector_be_ref(std::ostream& a_out,bool a_byte_swap,uint32 a_compression,
                    seek a_seek_directory,
                    const std::string& a_name,const std::string& a_title,const std::vector<T>& a_ref,bool a_verbose)
  :parent(a_out,a_byte_swap,a_compression,a_seek_directory,a_name,a_title,a_verbose)
  ,m_ref(a_ref)
  {
    fClassName = "vector<"+stype(T())+">";
    fClassVersion = 0;
    fID = -1;
    fType = 0;
    fStreamerType  = -1; // TStreamerInfo::kSTLp;
  }
  virtual ~std_vector_be_ref(){
  }
protected:
  std_vector_be_ref(const std_vector_be_ref& a_from)
  :ibo(a_from)
  ,parent(a_from)
  ,m_ref(a_from.m_ref)
  {}
  std_vector_be_ref& operator=(const std_vector_be_ref& a_from){
    parent::operator=(a_from);
    return *this;
  }
protected:
  virtual bool fill_leaves(buffer& a_buffer) {
    unsigned int c;
    if(!a_buffer.write_version(4,c)) return false;
    if(!a_buffer.write((int)m_ref.size())) return false;
    if(m_ref.size()) {
      // The awfull below is to pass T=bool :
      const T& vr = m_ref[0];
      if(!a_buffer.write_fast_array(&vr,(int)m_ref.size())) return false;
    }
    if(!a_buffer.set_byte_count(c)) return false;
    return true;
  }
public:
  const std::vector<T>& variable() const {return m_ref;}
  std::vector<T>& variable() {return const_cast< std::vector<T>& >(m_ref);}
protected:
  const std::vector<T>& m_ref;
};

template <class T>
class std_vector_be : public std_vector_be_ref<T> {
  typedef std_vector_be_ref<T> parent;
public:
  std_vector_be(std::ostream& a_out,bool a_byte_swap,uint32 a_compression,
                seek a_seek_directory,
                const std::string& a_name,const std::string& a_title,const std::vector<T>& a_def,bool a_verbose)
  :parent(a_out,a_byte_swap,a_compression,a_seek_directory,a_name,a_title,m_value,a_verbose)
  ,m_def(a_def),m_value(a_def)
  {
  }
  virtual ~std_vector_be(){
  }
protected:
  std_vector_be(const std_vector_be& a_from)
  :ibo(a_from)
  ,parent(a_from)
  ,m_def(a_from.m_def)
  ,m_value(a_from.m_value)
  {}
  std_vector_be& operator=(const std_vector_be& a_from){
    parent::operator=(a_from);
    m_def = a_from.m_def;
    m_value = a_from.m_value;
    return *this;
  }
protected:
  std::vector<T> m_def;
  std::vector<T> m_value;
};

template <class T>
class std_vector_be_pointer : public branch_element {
  typedef branch_element parent;
public:
  std_vector_be_pointer(std::ostream& a_out,bool a_byte_swap,uint32 a_compression,
                    seek a_seek_directory,
                    const std::string& a_name,const std::string& a_title,std::vector<T>* a_pointer,bool a_verbose)
  :parent(a_out,a_byte_swap,a_compression,a_seek_directory,a_name,a_title,a_verbose)
  ,m_pointer(a_pointer)
  {
    fClassName = "vector<"+stype(T())+">";
    fClassVersion = 0;
    fID = -1;
    fType = 0;
    fStreamerType  = -1; // TStreamerInfo::kSTLp;
  }
  virtual ~std_vector_be_pointer(){
  }
protected:
  std_vector_be_pointer(const std_vector_be_pointer& a_from)
  :ibo(a_from)
  ,parent(a_from)
  ,m_pointer(a_from.m_pointer)
  {}
  std_vector_be_pointer& operator=(const std_vector_be_pointer& a_from){
    parent::operator=(a_from);
    return *this;
  }
protected:
  virtual bool fill_leaves(buffer& a_buffer) {
    if(!m_pointer) return false;
    unsigned int c;
    if(!a_buffer.write_version(4,c)) return false;
    if(!a_buffer.write((int)m_pointer->size())) return false;
    if(m_pointer->size()) {
      // The awfull below is to pass T=bool :
      T& vr = (*m_pointer)[0];
      if(!a_buffer.write_fast_array(&vr,(int)m_pointer->size())) return false;
    }
    if(!a_buffer.set_byte_count(c)) return false;
    return true;
  }
public:
  void set_pointer(std::vector<T>* a_pointer) {m_pointer = a_pointer;}
  const std::vector<T>* get_pointer() const {return m_pointer;}
  std::vector<T>* get_pointer() {return m_pointer;}
protected:
  std::vector<T>* m_pointer;
};

}}

#endif
