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

#ifndef tools_wroot_tree
#define tools_wroot_tree

#include "itree"
#include "iobject"
#include "idir"

#include "branch_element"
#include "branch_object"

namespace tools {
namespace wroot {

class tree : public virtual iobject, public virtual itree {
public: //iobject
  virtual const std::string& name() const {return m_name;}
  virtual const std::string& title() const {return m_title;}
  virtual const std::string& store_class_name() const {
    static const std::string s_v("TTree");
    return s_v;
  }
  virtual bool stream(buffer& a_buffer) const {
    unsigned int c;
    if(!a_buffer.write_version(5,c)) return false;

    if(!Named_stream(a_buffer,m_name,m_title)) return false;

    // Beurk.
    if(!AttLine_stream(a_buffer)) return false;
    if(!AttFill_stream(a_buffer)) return false;
    if(!AttMarker_stream(a_buffer)) return false;

    double fEntries = (double)m_entries;
    if(!a_buffer.write(fEntries)) return false;

    double fTotBytes = (double)m_tot_bytes;
    double fZipBytes = (double)m_zip_bytes;
    if(!a_buffer.write(fTotBytes)) return false;
    if(!a_buffer.write(fZipBytes)) return false;
    if(!a_buffer.write((double)0)) return false; //fSavedBytes
    if(!a_buffer.write((int)0)) return false;    //fTimerInterval
    if(!a_buffer.write((int)25)) return false;   //fScanField (25)
    if(!a_buffer.write((int)0)) return false;    //fUpdate
    if(!a_buffer.write((int)1000000000)) return false; //fMaxEntryLoop
    int fMaxVirtualSize = 0;
    int fAutoSave = 100000000;
    if(!a_buffer.write(fMaxVirtualSize)) return false;
    if(!a_buffer.write(fAutoSave)) return false;
    if(!a_buffer.write((int)1000000)) return false;    //fEstimate;

    if(!m_branches.stream(a_buffer)) return false;

   {obj_array<base_leaf> m_leaves;
    tools_vforcit(branch*,m_branches,itb) {
      const std::vector<base_leaf*>& leaves = (*itb)->leaves();
      tools_vforcit(base_leaf*,leaves,itl) {
        m_leaves.push_back(*itl); //WARNING : ownership touchy.
      }
    }
    if(!m_leaves.stream(a_buffer)) return false;
    m_leaves.clear();} //WARNING : important.

    // fIndexValues (TArrayD).
    if(!a_buffer.write_array(std::vector<double>())) return false; //TArrayD
    // fIndex (TArrayI).
    if(!a_buffer.write_array(std::vector<int>())) return false; //TArrayI

    if(!a_buffer.set_byte_count(c)) return false;
    return true;
  }
public: //itree
  virtual idir& dir() {return m_dir;}
  virtual const idir& dir() const {return m_dir;}
public:
  tree(idir& a_dir,const std::string& a_name,const std::string& a_title,bool a_managed = true)
  :m_dir(a_dir)
  ,m_out(a_dir.file().out())
  ,m_name(a_name)
  ,m_title(a_title)
  ,m_entries(0)
  ,m_tot_bytes(0)
  ,m_zip_bytes(0)
  {
    if(a_managed) a_dir.append_object(this); //a_dir takes ownership of tree.
  }
  virtual ~tree(){
  }
protected:
  tree(const tree& a_from)
  :iobject(a_from),itree(a_from)
  ,m_dir(a_from.m_dir)
  ,m_out(a_from.m_out)
  {}
  tree& operator=(const tree&){return *this;}
public:
  std::ostream& out() const {return m_out;}
  const std::vector<branch*>& branches() const {return m_branches;}
  uint64 entries() const {return m_entries;}

  branch* create_branch(const std::string& a_name){
    const ifile& _file = m_dir.file();
    branch* br = new branch(m_out,_file.byte_swap(),_file.compression(),
                            m_dir.seek_directory(),a_name,m_name,_file.verbose());
    if(!br) return 0;
    m_branches.push_back(br);
    return br;
  }

  ////////////////////////////////////////////////
  /// ref : //////////////////////////////////////
  ////////////////////////////////////////////////
  template <class TYPE>
  leaf_ref<TYPE>* create_leaf_ref(const std::string& a_name,const TYPE& a_ref){
    branch* br = create_branch(a_name);
    if(!br) return 0;
    return br->create_leaf_ref<TYPE>(a_name,a_ref);
  }

  leaf_string_ref* create_leaf_string_ref(const std::string& a_name,const std::string& a_ref){
    branch* br = create_branch(a_name);
    if(!br) return 0;
    return br->create_leaf_string_ref(a_name,a_ref);
  }

  template <class T>
  leaf_element* create_std_vector_leaf_ref(const std::string& a_name,const std::vector<T>& a_ref){
    const ifile& _file = m_dir.file();
    std_vector_be_ref<T>* br = new std_vector_be_ref<T>(m_out,_file.byte_swap(),_file.compression(),
                                                        m_dir.seek_directory(),a_name,m_name,a_ref,_file.verbose());
    leaf_element* le = br->create_leaf_element(a_name);
    m_branches.push_back(br);
    return le;
  }
  ////////////////////////////////////////////////
  ////////////////////////////////////////////////
  ////////////////////////////////////////////////
  template <class T>
  std_vector_be_ref<T>* create_std_vector_be_ref(const std::string& a_name,const std::vector<T>& a_ref){
    const ifile& _file = m_dir.file();
    std_vector_be_ref<T>* br = new std_vector_be_ref<T>(m_out,_file.byte_swap(),_file.compression(),
                                                        m_dir.seek_directory(),a_name,m_name,a_ref,_file.verbose());
    m_branches.push_back(br);
    return br;
  }

  template <class T>
  std_vector_be<T>* create_std_vector_be(const std::string& a_name,const std::vector<T>& a_def = std::vector<T>()){
    const ifile& _file = m_dir.file();
    std_vector_be<T>* br = new std_vector_be<T>(m_out,_file.byte_swap(),_file.compression(),
                                                m_dir.seek_directory(),a_name,m_name,a_def,_file.verbose());
    m_branches.push_back(br);
    return br;
  }

  template <class T>
  std_vector_be_pointer<T>* create_std_vector_be_pointer(const std::string& a_name,std::vector<T>* a_pointer){
    const ifile& _file = m_dir.file();
    std_vector_be_pointer<T>* br = new std_vector_be_pointer<T>(m_out,_file.byte_swap(),_file.compression(),
                                                                m_dir.seek_directory(),a_name,m_name,a_pointer,_file.verbose());
    m_branches.push_back(br);
    return br;
  }

  template <class TYPE>
  leaf<TYPE>* create_leaf(const std::string& a_name){
    branch* br = create_branch(a_name);
    if(!br) return 0;
    return br->create_leaf<TYPE>(a_name);
  }

  leaf_object* create_leaf(const std::string& a_name,const iobject& a_obj){
    const ifile& _file = m_dir.file();
    branch_object* br = new branch_object(m_out,_file.byte_swap(),_file.compression(),
                                          m_dir.seek_directory(),a_name,m_name,_file.verbose());
    m_branches.push_back(br);
    return br->create_leaf(a_name,a_obj);
  }

  bool fill(uint32& a_nbytes) {
    // Fill all branches of a Tree :
    //   This function loops on all the branches of this tree.
    //   For each branch, it copies to the branch buffer (basket) the current
    //   values of the leaves data types.
    //   If a leaf is a simple data type, a simple conversion to a machine
    //   independent format has to be done.
    a_nbytes = 0;
    tools_vforcit(branch*,m_branches,it) {
      //FIXME if ((*it)->testBit(kDoNotProcess)) continue;
      uint32 n,add_bytes,nout;
      if(!(*it)->fill(m_dir.file(),n,add_bytes,nout)) {a_nbytes = 0;return false;}
      a_nbytes += n;
      m_tot_bytes += add_bytes;
      m_zip_bytes += nout;
    }

    m_entries++;

    return true;
  }

  void reset() {
    // Reset buffers and entries count in all branches/leaves
    m_entries       = 0;
    m_tot_bytes     = 0;
    m_zip_bytes     = 0;
   {tools_vforcit(branch*,m_branches,it) (*it)->reset();}
  }
protected:
  idir& m_dir;
  std::ostream& m_out;
  //Named
  std::string m_name;
  std::string m_title;

  obj_array<branch> m_branches;
  uint64 m_entries;   // Number of entries
  uint64 m_tot_bytes; // Total number of bytes in branches before compression
  uint64 m_zip_bytes; // Total number of bytes in branches after compression
};

}}

#endif
