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

#ifndef tools_sg_holder
#define tools_sg_holder

// class to manage an object.
// Only one holder owns the object. In case
// of holder copy(), the new holder receives a null object.

#include "node"

#include "sf_string"

namespace tools {
namespace sg {

class base_holder : public node {
  TOOLS_NODE(base_holder,tools::sg::base_holder,node)
public:
  base_holder(const std::string& a_name):parent(),m_name(a_name) {}
  virtual ~base_holder(){}
public:
  base_holder(const base_holder& a_from):parent(a_from),m_name(a_from.m_name) {}
  base_holder& operator=(const base_holder& a_from){
    parent::operator=(a_from);
    m_name = a_from.m_name;
    return *this;
  }
public:
  const std::string& name() const {return m_name;}
protected:
  std::string m_name;
};

template <class T>
class holder : public base_holder {
  typedef base_holder parent;
public:
  //WARNING : we do not put the T class name in the name of this class.
  //          We put it in the class_name field.
  //          We do that because of the bsg file format in order to be able
  //          to read back the object without having the T class around.
  TOOLS_T_SCLASS(T,tools::sg::holder)
public:
  virtual void* cast(const std::string& a_class) const {
    if(void* p = cmp_cast< holder<T> >(this,a_class)) {return p;}
    return parent::cast(a_class);
  }
  virtual node* copy() const {return new holder(*this);}
  virtual const std::string& s_cls() const {return s_class();}
public:
  sf_string class_name;
public:
  virtual const desc_fields& node_desc_fields() const {
    TOOLS_FIELD_DESC_NODE_CLASS(tools::sg::holder)
    static const desc_fields s_v(parent::node_desc_fields(),1, //WARNING : take care of count.
      TOOLS_ARG_FIELD_DESC(class_name)
    );
    return s_v;
  }
private:
  void add_fields(){
    add_field(&class_name);
  }
public:
  holder(T* a_obj = 0,const std::string& a_name = "",bool a_own=true)
  :parent(a_name)
  ,class_name(T::s_class())
  ,m_obj(a_obj) //it takes a_obj ownership.
  ,m_own(a_own)
  {
    add_fields();
  }

  virtual ~holder(){if(m_own) delete m_obj;}
public:
  //any copy of a holder does NOT own any object (and we empty the name).
  holder(const holder& a_from)
  :parent(a_from)
  ,class_name(std::string())
  ,m_obj(0)
  ,m_own(false)
  {
    parent::m_name.clear();
    add_fields();
  }
  holder& operator=(const holder& a_from){
    parent::operator=(a_from);
    class_name.value().clear();
    m_obj = 0;
    m_own = false;
    parent::m_name.clear();
    return *this;
  }
public:
  const T* object() const {return m_obj;}
  T* object() {return m_obj;}
protected:
  T* m_obj;
  bool m_own;
};

template <class T>
inline holder<T>* cast_holder(node& a_node) {
  typedef holder<T> h_t;
  h_t* _h = (h_t*)a_node.cast(h_t::s_class());
  if(!_h) return 0;
  return (_h->class_name.value()==T::s_class()?_h:0);
}

template <class T>
inline const holder<T>* cast_holder(const node& a_node) {
  typedef holder<T> h_t;
  h_t* _h = (h_t*)a_node.cast(h_t::s_class());
  if(!_h) return 0;
  return (_h->class_name.value()==T::s_class()?_h:0);
}

template <class T>
inline const T* cast_holder_object(const node& a_node) {
  typedef holder<T> h_t;
  h_t* _h = (h_t*)a_node.cast(h_t::s_class());
  if(!_h) return 0;
  return (_h->class_name.value()==T::s_class()?_h->object():0);
}
template <class T>
inline T* cast_holder_object(node& a_node) {
  typedef holder<T> h_t;
  h_t* _h = (h_t*)a_node.cast(h_t::s_class());
  if(!_h) return 0;
  return (_h->class_name.value()==T::s_class()?_h->object():0);
}

template <class T>
inline void remove_holders(std::vector<node*>& a_vec){
  typedef holder<T> h_t;

  std::vector<node*>::iterator it;
  for(it=a_vec.begin();it!=a_vec.end();) {
    if(h_t* h = cast_holder<T>(*(*it))){
      it = a_vec.erase(it);
      delete h;
      continue;
    }

    ++it;
  }
}

template <class T>
inline void remove_holders(std::vector<node*>& a_vec,const std::string& a_name){
  typedef holder<T> h_t;

  std::vector<node*>::iterator it;
  for(it=a_vec.begin();it!=a_vec.end();) {
    if(h_t* h = cast_holder<T>(*(*it))){
      if(h->name()==a_name) {
        it = a_vec.erase(it);
        delete h;
        continue;
      }
    }

    ++it;
  }
}

template <class T>
inline T* find_holder(std::vector<node*>& a_vec,const std::string& a_name) {
  //return the first named found.

  typedef holder<T> h_t;
  std::vector<node*>::iterator it;
  for(it=a_vec.begin();it!=a_vec.end();++it) {
    if(h_t* h = cast_holder<T>(*(*it))){
      if(h->name()==a_name) return h->object();
    }
  }
  return 0;
}

template <class T>
inline T* find_first_holder(std::vector<node*>& a_vec){
  //return the first T found.

  typedef holder<T> h_t;
  std::vector<node*>::iterator it;
  for(it=a_vec.begin();it!=a_vec.end();++it) {
    if(h_t* h = cast_holder<T>(*(*it))) return h->object();
  }


  return 0;
}

}}

#endif
