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

#ifndef tools_rroot_obj_list
#define tools_rroot_obj_list

#include "iro"

#include "object"
#include "cids"

#include "../forit"
#include "../scast"

namespace tools {
namespace rroot {

class obj_list : public virtual iro, protected std::vector<iro*> {  //proteced to avoid using std::vector::clear().
  typedef std::vector<iro*> parent;
public:
  static const std::string& s_store_class() {
    static const std::string s_v("TList");
    return s_v;
  }
public:
  static const std::string& s_class() {
    static const std::string s_v("tools::rroot::obj_list");
    return s_v;
  }
public: //iro
  virtual void* cast(const std::string& a_class) const {
    if(void* p = cmp_cast<obj_list>(this,a_class)) return p;
    return 0;
  }
  virtual const std::string& s_cls() const {return s_class();}
public:
  static cid id_class() {return obj_list_cid();}
  virtual void* cast(cid a_class) const {
    if(void* p = cmp_cast<obj_list>(this,a_class)) {return p;}
    else return 0;
  }
public:
  virtual iro* copy() const {return new obj_list(*this);}
  virtual bool stream(buffer& a_buffer) {
    safe_clear();

    short v;
    unsigned int _s,_c;
    if(!a_buffer.read_version(v,_s,_c)) return false;

   {uint32 id,bits;
    if(!Object_stream(a_buffer,id,bits)) return false;}

    std::string name;
    if(!a_buffer.read(name)) return false;
    int nobjects;
    if(!a_buffer.read(nobjects)) return false;

    ifac::args args;
    for (int i=0;i<nobjects;i++) {
      iro* obj;
      bool created;
      if(!a_buffer.read_object(m_fac,args,obj,created)){
        a_buffer.out() << "tools::rroot::obj_list::stream : can't read object." << std::endl;
        return false;
      }

      unsigned char nch;
      if(!a_buffer.read(nch)) return false;
      if(nch) {
        char readOption[256];
        if(!a_buffer.read_fast_array(readOption,nch)) return false;
        readOption[nch] = 0;
      }
      if(obj) {
        if(created) {
          parent::push_back(obj);
          m_owns.push_back(true);
	} else { //someone else may manage this object.
          parent::push_back(obj);
          m_owns.push_back(false);
	}
      }
    }

    return a_buffer.check_byte_count(_s,_c,s_store_class());
  }
public:
  obj_list(ifac& a_fac)
  :m_fac(a_fac)
  {
  }
  virtual ~obj_list(){
    safe_clear();
  }
public:
  obj_list(const obj_list& a_from)
  :iro(a_from)
  ,parent()
  ,m_fac(a_from.m_fac)
  {
    tools_vforcit(iro*,a_from,it) {
      parent::push_back((*it)->copy());
      m_owns.push_back(true);
    }
  }
  obj_list& operator=(const obj_list& a_from) {
    if(&a_from==this) return *this;

    safe_clear();

    tools_vforcit(iro*,a_from,it) {
      parent::push_back((*it)->copy());
      m_owns.push_back(true);
    }

    return *this;
  }
public:
  parent::const_iterator begin() const {return parent::begin();}
  parent::const_iterator cbegin() const {return parent::cbegin();}
  parent::iterator begin() {return parent::begin();}
  parent::const_iterator end() const {return parent::end();}
  parent::const_iterator cend() const {return parent::cend();}
  parent::iterator end() {return parent::end();}
  parent::size_type size() const {return parent::size();}
  bool empty() const {return parent::empty();}
public:
  void add_object(iro* a_obj) { //take ownership.
    parent::push_back(a_obj);
    m_owns.push_back(true);
  }

  template <class T>
  T* get_entry(std::ostream& a_out,size_t a_index) const {
    if(a_index>=size()) return 0;
    iro* _obj = parent::operator[](a_index);
    if(!_obj) return 0;
    T* od = id_cast<iro,T>(*_obj);
    if(!od) {
      a_out << "tools::rroot::obj_list::get_entry :"
            << " object of class " << sout(_obj->s_cls()) << " not a " << T::s_class() << std::endl;
      return 0;
    }
    return od;
  }

  void safe_clear() {
    typedef parent::iterator it_t;
    typedef std::vector<bool>::iterator itb_t;
    while(!parent::empty()) {
      it_t it = parent::begin();
      itb_t itb = m_owns.begin();
      iro* entry  = (*it);
      bool own = (*itb);
      parent::erase(it);
      m_owns.erase(itb);
      if(own) delete entry;
    }
  }

  void cleanup() {safe_clear();}
protected:
  ifac& m_fac;
  std::vector<bool> m_owns;
};

}}

#endif
