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

#ifndef tools_rroot_named
#define tools_rroot_named

#include "buffer"
#include "../vdata"
#include "../vmanip"
#include "../forit"
#include "../scast" //for ObjArray

#include "cids"

namespace tools {
namespace rroot {

inline bool Object_stream(buffer& a_buffer,uint32& a_id,uint32& a_bits) {
  short v;
  if(!a_buffer.read_version(v)) return false;
  if(!a_buffer.read(a_id)) return false;
  if(!a_buffer.read(a_bits)) return false;
  return true;
}

inline bool Named_stream(buffer& a_buffer,
                         std::string& a_name,
                         std::string& a_title) {
  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;}
  if(!a_buffer.read(a_name)) return false;
  if(!a_buffer.read(a_title)) return false;
  if(!a_buffer.check_byte_count(s,c,"TNamed")) return false;
  return true;
}

inline bool AttLine_stream(buffer& a_buffer,
                           short& a_color,
                           short& a_style,
                           short& a_width){
  short v;
  unsigned int s, c;
  if(!a_buffer.read_version(v,s,c)) return false;
  if(!a_buffer.read(a_color)) return false;
  if(!a_buffer.read(a_style)) return false;
  if(!a_buffer.read(a_width)) return false;
  if(!a_buffer.check_byte_count(s,c,"TAttLine")) return false;
  return true;
}

inline bool AttFill_stream(buffer& a_buffer,
                           short& a_color,
                           short& a_style){
  short v;
  unsigned int s, c;
  if(!a_buffer.read_version(v,s,c)) return false;
  if(!a_buffer.read(a_color)) return false;
  if(!a_buffer.read(a_style)) return false;
  if(!a_buffer.check_byte_count(s,c,"TAttFill")) return false;
  return true;
}

inline bool AttMarker_stream(buffer& a_buffer) {
  short fMarkerColor;
  short fMarkerStyle;
  float fMarkerWidth;
  short v;
  unsigned int s, c;
  if(!a_buffer.read_version(v,s,c)) return false;
  if(!a_buffer.read(fMarkerColor)) return false;
  if(!a_buffer.read(fMarkerStyle)) return false;
  if(!a_buffer.read(fMarkerWidth)) return false;
  if(!a_buffer.check_byte_count(s,c,"TAttMarker")) return false;
  return true;
}

inline bool GeoAtt_stream(buffer& a_buffer) {
  unsigned int fGeoAtt;            // option flags
  short v;
  unsigned int s, c;
  if(!a_buffer.read_version(v,s,c)) return false;
  if(!a_buffer.read(fGeoAtt)) return false;
  if(!a_buffer.check_byte_count(s,c,"TGeoAtt")) return false;
  return true;
}

inline bool Att3D_stream(buffer& a_buffer) {
  short v;
  unsigned int s, c;
  if(!a_buffer.read_version(v,s,c)) return false;
  if(!a_buffer.check_byte_count(s,c,"TAtt3D")) return false;
  return true;
}

template <class T>
inline bool Array_stream(buffer& a_buffer,std::vector<T>& a_v) {
  a_v.clear();
  int sz;
  if(!a_buffer.read(sz)) return false;
  //check sz is not crazy :
  if(!a_buffer.check_eob(sz)) return false;
  a_v.resize(sz);
  if(!a_buffer.read_fast_array<T>(vec_data(a_v),sz)) return false;
  return true;
}

template <class T>
inline bool dummy_array_stream(buffer& a_buffer,int a_n){
  char is_array;
  if(!a_buffer.read(is_array)) return false;
  if(!is_array) return true;
  if(!a_n) return true;
  T* v = new T[a_n];
  bool status = a_buffer.read_fast_array<T>(v,a_n);
  delete [] v;
  return status;
}

template <class T>
inline bool fixed_array_stream(buffer& a_buffer,int a_n,T*& a_v){
  delete [] a_v;
  a_v = 0;
  char is_array;
  if(!a_buffer.read(is_array)) return false;
  if(!is_array) return true;
  if(!a_n) return true;
  a_v = new T[a_n];
  if(!a_buffer.read_fast_array<T>(a_v,a_n)) {
    delete [] a_v;
    a_v = 0;
    return false;
  }
  return true;
}

class iros : public virtual iro,public std::vector<iro*> {
  static const std::string& s_store_class() {
    static const std::string s_v("TObjArray");
    return s_v;
  }
public:
  static const std::string& s_class() {
    static const std::string s_v("tools::rroot::iros");
    return s_v;
  }
public: //iro
  virtual void* cast(const std::string& a_class) const {
    if(void* p = cmp_cast<iros>(this,a_class)) return p;
    return 0;
  }
  virtual const std::string& s_cls() const {return s_class();}
public:
  static cid id_class() {return List_cid();}
  virtual void* cast(cid a_class) const {
    if(void* p = cmp_cast<iros>(this,a_class)) {return p;}
    else return 0;
  }
public:
  virtual iro* copy() const {return new iros(*this);}
  virtual bool stream(buffer& a_buffer) {
    ifac::args args;
    bool accept_null = false;
    return stream(a_buffer,args,accept_null);
  }
public:
  iros(ifac& a_fac,bool a_owner)
  :m_fac(a_fac)
  ,m_owner(a_owner)
  {
#ifdef TOOLS_MEM
    mem::increment(s_class().c_str());
#endif
  }
  virtual ~iros(){
    _clear();
#ifdef TOOLS_MEM
    mem::decrement(s_class().c_str());
#endif
  }
public:
  iros(const iros& a_from)
  :iro(a_from)
  ,std::vector<iro*>()
  ,m_fac(a_from.m_fac)
  ,m_owner(a_from.m_owner)
  {
#ifdef TOOLS_MEM
    mem::increment(s_class().c_str());
#endif
    if(a_from.m_owner) {
      tools_vforcit(iro*,a_from,it) push_back((*it)->copy()); 
    } else {
      tools_vforcit(iro*,a_from,it) push_back((*it)); 
    }
  }
  iros& operator=(const iros& a_from){
    if(&a_from==this) return *this;

    _clear();

    m_owner = a_from.m_owner;

    if(a_from.m_owner) {
      tools_vforcit(iro*,a_from,it) push_back((*it)->copy()); 
    } else {
      tools_vforcit(iro*,a_from,it) push_back((*it)); 
    }

    return *this;
  }
public:
  void cleanup() {_clear();}
  void dump(std::ostream& a_out) {
    a_out << " iros : size " << size() << std::endl;
    tools_vforcit(iro*,*this,it) {
      a_out << " class " << (*it)->s_cls() << std::endl;
    }
  }
public:
  bool stream(buffer& a_buffer,
              const ifac::args& a_args,bool a_accept_null = false) {
    _clear();

    short v;
    unsigned int s, c;
    if(!a_buffer.read_version(v,s,c)) return false;

    //::printf("debug : iros::stream : version %d, byte count %d\n",v,c);

   {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;
    int lowerBound;
    if(!a_buffer.read(lowerBound)) return false;

    //::printf("debug : iros : name \"%s\", nobject %d, lowerBound %d\n",
    //  name.c_str(),nobjects,lowerBound);

    for (int i=0;i<nobjects;i++) {
      //::printf("debug : iros :    n=%d i=%d ...\n",nobjects,i);

      iro* obj;
      if(!a_buffer.read_object(m_fac,a_args,obj)){
        a_buffer.out() << "tools::rroot::iros::stream :"
                       << " can't read object."
                       << std::endl;
        return false;
      }
      //::printf("debug : iros :    n=%d i=%d : ok\n",nobjects,i);
      if(obj) {
        push_back(obj);
      } else {
        //a_accept_null for branch::stream m_baskets.
        if(a_accept_null) push_back(0);
      }
    }

    return a_buffer.check_byte_count(s,c,s_store_class());
  }
protected:
  void _clear() {
    if(m_owner) {
      tools::clear<iro>(*this);
    } else {
      std::vector<iro*>::clear();
    }
  }
protected:
  ifac& m_fac;
  bool m_owner;
};

template <class T>
class ObjArray : public virtual iro,public std::vector<T*> {
  static const std::string& s_store_class() {
    static const std::string s_v("TObjArray");
    return s_v;
  }
public:
  static const std::string& s_class() {
    static const std::string s_v("tools::rroot::ObjArray<"+T::s_class()+">");
    return s_v;
  }
public: //iro
  virtual void* cast(const std::string& a_class) const {
    if(void* p = cmp_cast< ObjArray<T> >(this,a_class)) return p;
    return 0;
  }
  virtual const std::string& s_cls() const {return s_class();}
public:
  static cid id_class() {return ObjArray_cid()+T::id_class();}
  virtual void* cast(cid a_class) const {
    if(void* p = cmp_cast<ObjArray>(this,a_class)) {return p;}
    return 0;
  }
  virtual iro* copy() const {return new ObjArray<T>(*this);}
  virtual bool stream(buffer& a_buffer) {
    ifac::args args;
    bool accept_null = false;
    return stream(a_buffer,args,accept_null);
  }
public:
  ObjArray(ifac& a_fac,bool a_owner)
  :m_fac(a_fac)
  ,m_owner(a_owner){
#ifdef TOOLS_MEM
    mem::increment(s_class().c_str());
#endif
  }
  virtual ~ObjArray(){
    _clear();
#ifdef TOOLS_MEM
    mem::decrement(s_class().c_str());
#endif
  }
public:
  ObjArray(const ObjArray& a_from)
  :iro(a_from)
  ,std::vector<T*>()
  ,m_fac(a_from.m_fac)
  ,m_owner(a_from.m_owner)
  {
#ifdef TOOLS_MEM
    mem::increment(s_class().c_str());
#endif
    if(a_from.m_owner) {
      typedef typename std::vector<T*>::const_iterator it_t;
      for(it_t it=a_from.begin();it!=a_from.end();++it) {
        if(!(*it)) {
          std::vector<T*>::push_back(0);
        } else {
          iro* _obj = (*it)->copy();
          T* obj = safe_cast<iro,T>(*_obj);
          if(!obj) {
            //a_buffer.out() << "tools::rroot::ObjArray::ObjArray :"
            //               << " inlib::cast failed."
            //               << std::endl;
            delete _obj;
          } else {
            std::vector<T*>::push_back(obj);
          }
        }
      }
    } else {
      std::vector<T*>::operator=(a_from); 
    }
  }
  ObjArray& operator=(const ObjArray& a_from){
    if(&a_from==this) return *this;

    _clear();

    m_owner = a_from.m_owner;

    if(a_from.m_owner) {
      typedef typename std::vector<T*>::const_iterator it_t;
      for(it_t it=a_from.begin();it!=a_from.end();++it) {
        if(!(*it)) {
          std::vector<T*>::push_back(0);
        } else {
          iro* _obj = (*it)->copy();
          T* obj = safe_cast<iro,T>(*_obj);
          if(!obj) {
            //a_buffer.out() << "tools::rroot::ObjArray::operator= :"
            //               << " inlib::cast failed."
            //               << std::endl;
            delete _obj;
          } else {
            std::vector<T*>::push_back(obj);
          }
        }
      }
    } else {
      std::vector<T*>::operator=(a_from); 
    }

    return *this;
  }
public:
  void cleanup() {_clear();}
public:
  bool stream(buffer& a_buffer,const ifac::args& a_args,bool a_accept_null = false) {
    _clear();

    short v;
    unsigned int sp, bc;
    if(!a_buffer.read_version(v,sp,bc)) return false;

    //::printf("debug : ObjArray::stream : version %d, byte count %d\n",v,c);

   {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;
    int lowerBound;
    if(!a_buffer.read(lowerBound)) return false;

    //::printf("debug : ObjArray : name \"%s\", nobject %d, lowerBound %d\n",
    //  name.c_str(),nobjects,lowerBound);

    for (int i=0;i<nobjects;i++) {
      //::printf("debug : ObjArray :    n=%d i=%d ...\n",nobjects,i);

      iro* obj;
      if(!a_buffer.read_object(m_fac,a_args,obj)){
        a_buffer.out() << "tools::rroot::ObjArray::stream :"
                       << " can't read object."
                       << std::endl;
        return false;
      }
      //::printf("debug : ObjArray :    n=%d i=%d : ok\n",nobjects,i);
      if(obj) {
        T* to = safe_cast<iro,T>(*obj);
        if(!to) {
          a_buffer.out() << "tools::rroot::ObjArray::stream :"
                         << " inlib::cast failed."
                         << " " << obj->s_cls() << " is not a " << T::s_class() << "."
                         << std::endl;
          delete obj;
        } else {
          std::vector<T*>::push_back(to);
        }
      } else {
        //a_accept_null for branch::stream m_baskets.
        if(a_accept_null) std::vector<T*>::push_back(0);
      }
    }

    return a_buffer.check_byte_count(sp,bc,s_store_class());
  }
protected:
  void _clear() {
    if(m_owner) {
      tools::clear<T>(*this);
    } else {
      std::vector<T*>::clear();
    }
  }
protected:
  ifac& m_fac;
  bool m_owner;
};

class List : public virtual iro, public std::vector<iro*> {
  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::List");
    return s_v;
  }
public: //iro
  virtual void* cast(const std::string& a_class) const {
    if(void* p = cmp_cast<List>(this,a_class)) return p;
    return 0;
  }
  virtual const std::string& s_cls() const {return s_class();}
public:
  static cid id_class() {return List_cid();}
  virtual void* cast(cid a_class) const {
    if(void* p = cmp_cast<List>(this,a_class)) {return p;}
    else return 0;
  }
  //virtual void* cast(cid) const {return List_cid();}
public:
  virtual iro* copy() const {return new List(*this);}
  virtual bool stream(buffer& a_buffer) {
    _clear();

    short v;
    unsigned int s, c;
    if(!a_buffer.read_version(v,s,c)) return false;

    //::printf("debug : List::stream : version %d, byte count %d\n",v,c);

   {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;

    //::printf("debug : List : name \"%s\", nobject %d\n",
    //    name.c_str(),nobjects);

    ifac::args args;
    for (int i=0;i<nobjects;i++) {
      //::printf("debug : List :    n=%d i=%d ...\n",nobjects,i);

      iro* obj;
      if(!a_buffer.read_object(m_fac,args,obj)){
        a_buffer.out() << "tools::rroot::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) push_back(obj);
    }

    return a_buffer.check_byte_count(s,c,s_store_class());
  }
public:
  List(ifac& a_fac,bool a_owner)
  :m_fac(a_fac)
  ,m_owner(a_owner)
  {
#ifdef TOOLS_MEM
    mem::increment(s_class().c_str());
#endif
  }
  virtual ~List(){
    _clear();
#ifdef TOOLS_MEM
    mem::decrement(s_class().c_str());
#endif
  }
protected:
  List(const List& a_from)
  :iro(a_from)
  ,std::vector<iro*>()
  ,m_fac(a_from.m_fac)
  ,m_owner(a_from.m_owner)
  {
#ifdef TOOLS_MEM
    mem::increment(s_class().c_str());
#endif
  }
  List& operator=(const List&){return *this;}
protected:
  void _clear() {
    if(m_owner) {
      tools::clear<iro>(*this);
    } else {
      std::vector<iro*>::clear();
    }
  }
protected:
  ifac& m_fac;
  bool m_owner;
};

class HashList : public List {
  static const std::string& s_store_class() {
    static const std::string s_v("THashList");
    return s_v;
  }
public:
  static const std::string& s_class() {
    static const std::string s_v("tools::rroot::HashList");
    return s_v;
  }
public: //iro
  virtual void* cast(const std::string& a_class) const {
    if(void* p = cmp_cast<HashList>(this,a_class)) return p;
    return List::cast(a_class);
  }
  virtual const std::string& s_cls() const {return s_class();}
public:
  static cid id_class() {return HashList_cid();}
  virtual void* cast(cid a_class) const {
    if(void* p = cmp_cast<HashList>(this,a_class)) {return p;}
    return List::cast(a_class);
  }
public:
  virtual bool stream(buffer& a_buffer) {
    uint32 startpos = a_buffer.length();
    unsigned int s, c;
    short v;
    if(!a_buffer.read_version(v,s,c)) return false;

    //if(!List::stream(a_buffer)) return false;

    a_buffer.set_offset(startpos+c+sizeof(unsigned int));
    if(!a_buffer.check_byte_count(s,c,s_store_class())) return false;
    return true;
  }

public:
  HashList(ifac& a_fac,bool a_owner):List(a_fac,a_owner){
#ifdef TOOLS_MEM
    mem::increment(s_class().c_str());
#endif
  }
  virtual ~HashList(){
#ifdef TOOLS_MEM
    mem::decrement(s_class().c_str());
#endif
  }
protected:
  HashList(const HashList& a_from):iro(a_from),List(a_from){}
  HashList& operator=(const HashList&){return *this;}
};

inline bool dummy_TXxx_pointer_stream(buffer& a_buffer,ifac& a_fac,bool a_del = true) {
  ifac::args args;
  iro* obj;
  bool status = a_buffer.read_object(a_fac,args,obj);
  if(a_del) delete obj;
  return status;
}

template <class T>
inline bool pointer_stream(buffer& a_buffer,
                           ifac& a_fac,ifac::args& a_args,
                           const std::string& a_T_class,
                           T*& a_obj){
  iro* obj;
  if(!a_buffer.read_object(a_fac,a_args,obj)) {
    a_buffer.out() << "tools::rroot::pointer_stream :"
                   << " read_object failed."
                   << std::endl;
    a_obj = 0;
    return false;
  }
  if(!obj) {
    a_obj = 0;
  } else {
    a_obj = (T*)obj->cast(a_T_class);
    if(!a_obj) {
      a_buffer.out() << "tools::rroot::pointer_stream : "
                     << " inlib::cast to " << a_T_class << " failed."
                     << ". Object is a " << obj->s_cls() << "."
                     << std::endl;
      return false;
    }
  }
  return true;
}

template <class T>
inline bool pointer_stream(buffer& a_buffer,
                           ifac& a_fac,ifac::args& a_args,
                           tools::cid a_T_class,
                           T*& a_obj){
  iro* obj;
  if(!a_buffer.read_object(a_fac,a_args,obj)) {
    a_buffer.out() << "tools::rroot::pointer_stream :"
                   << " read_object failed."
                   << std::endl;
    a_obj = 0;
    return false;
  }
  if(!obj) {
    a_obj = 0;
  } else {
    a_obj = (T*)obj->cast(a_T_class);
    if(!a_obj) {
      a_buffer.out() << "tools::rroot::pointer_stream : "
                     << " inlib::cast to " << a_T_class << " failed."
                     << ". Object is a " << obj->s_cls() << "."
                     << std::endl;
      return false;
    }
  }
  return true;
}

template <class T>
inline bool pointer_stream(buffer& a_buffer,
                           ifac& a_fac,ifac::args& a_args,T*& a_obj){
  //return pointer_stream(a_buffer,a_fac,a_args,T::s_class(),a_obj);
  return pointer_stream(a_buffer,a_fac,a_args,T::id_class(),a_obj);
}


inline bool dummy_TObjArray_pointer_stream(buffer& a_buffer,ifac& a_fac,bool a_owner = true) {
  iros oa(a_fac,a_owner);
  ifac::args args;
  return oa.stream(a_buffer,args);
}

inline bool dummy_TList_pointer_stream(buffer& a_buffer,ifac& a_fac,bool a_owner = true) {
  List l(a_fac,a_owner);
  return l.stream(a_buffer);
}
inline bool dummy_THashList_pointer_stream(buffer& a_buffer,ifac& a_fac,bool a_owner = true) {
  HashList l(a_fac,a_owner);
  return l.stream(a_buffer);
}

}}

#endif
