/* -*- c++ -*- */
#ifndef AKAXISO_ELEMENT_PTR_H__
#define AKAXISO_ELEMENT_PTR_H__

#include <akaxiso/classes/node.h>
#include <assert.h>

namespace aka2 {

  struct refstate {
    refstate(int count) : count_(count), owner_(true) {}
    int count_;
    bool owner_;
  };

  class node_holder {
    friend class node_ptr;
    friend class const_node_ptr;
    node_holder() : refstate_(0){}

    void attach(const node &nd) {
      assert(refstate_ == 0);
      assert(node_.empty() || (&node_.op() == &nd.op()));
      refstate_ = new refstate(1);
      node_ = nd;
    }

    void attach(const node_holder &rhs) {
      assert(refstate_ == 0);
      assert(node_.empty() || (&node_.op() == &rhs.node_.op()));

      if (!rhs.refstate_->owner_) {
	node_ = node();
	return;
      }
      node_ = rhs.node_;
      refstate_ = rhs.refstate_;
      ++(refstate_->count_);
    }

    void detach() {
      if (refstate_ == 0)
	return;
      if (--(refstate_->count_) == 0) {
	if (refstate_->owner_)
	  node_.destroy();
	delete refstate_;
      }
      refstate_ = 0;
      node_ = node();
    }

    node adopt() {
      assert(refstate_ != 0);
      refstate_->owner_ = false;
      node ret = node_;
      node_ = node();
      return ret;
    }
    node get_node() const {
      if ((refstate_ == 0) || (!refstate_->owner_))
	return node();
      return node_;
    }

    node node_;
    refstate *refstate_;
  };


  class node_ptr {
  public:
    node_ptr(){}
    node_ptr(const node &nd) {
      holder_.attach(nd);
    }
    node_ptr(const node_ptr &ptr) {
      holder_.attach(ptr.holder_);
    }
    ~node_ptr() {
      holder_.detach();
    }

    node_ptr& operator=(const node_ptr& rhs) {
      if ((this != &rhs) && (rhs.holder_.node_.ptr() != holder_.node_.ptr())) {
	holder_.detach();
	holder_.attach(rhs.holder_);
      }
      return *this;
    }

    node adopt() { return holder_.adopt(); }
    node get_node() const { return holder_.node_; }

  private:
    node_holder holder_;
  };

} // namespace aka2

#endif /* AKAXISO_ELEMENT_PTR_H__ */
