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

//
// Helper functions to handle node(s) paths in a scene graph.
//

#ifndef tools_sg_path
#define tools_sg_path

#include "node"
#include "noderef"

namespace tools {
namespace sg {

typedef std::vector<node*> path_t;

inline bool parent_class(const path_t& a_path,std::string& a_class) {
  if(a_path.size()<2) {a_class.clear();return false;}
  node* parent = a_path[a_path.size()-2];
  a_class = parent->s_cls();
  return true;
}

template <class NODE>
inline NODE* rfind(const path_t& a_path) {
  path_t::const_reverse_iterator it;
  for(it=a_path.rbegin();it!=a_path.rend();++it){
    if(NODE* par = safe_cast<node,NODE>(*(*it))) return par;
  }
  return 0;
}

template <class NODE>
inline NODE* tail(const path_t& a_path) {
  if(a_path.empty()) return 0;
  node* _node = a_path[a_path.size()-1];
  return safe_cast<node,NODE>(*_node);
}

inline bool remove_tail(path_t& a_path) {
  if(a_path.empty()) return false;
  a_path.resize(a_path.size()-1);
  return true;
}

template <class CONTAINER>
inline CONTAINER* container(const path_t& a_path) {
  if(a_path.size()<2) return 0;
  node* parent = a_path[a_path.size()-2];
  return safe_cast<node,CONTAINER>(*parent);
}

template <class CONTAINER>
inline CONTAINER* container_container(const path_t& a_path) {
  if(a_path.size()<3) return 0;
  node* parent = a_path[a_path.size()-3];
  return safe_cast<node,CONTAINER>(*parent);
}

template <class WHAT,class CONTAINER>
inline bool rfind(const path_t& a_path,const node& a_from,
                  CONTAINER*& a_container,WHAT*& a_what,
                  int& a_container_index){
  node* from = (node*)&a_from;
  path_t::size_type sz = a_path.size();
  for(size_t index=1;index<sz;index++) {
    node* _node = a_path[sz-index-1];
    a_container = safe_cast<node,CONTAINER>(*_node);
    if(a_container) {
      //the below does not compile.
      //a_what = a_container->rsearch_from<WHAT>(from);
      //if(a_what) return true;

      void* p = a_container->rsearch_from(from,WHAT::s_class(),false);
      if(p) {
        a_what = (WHAT*)p;
        a_container_index = int(sz-index-1);
        return true;
      }

      from = a_container;
    } else if(noderef* _ref = safe_cast<node,noderef>(*_node)) {
      from = _ref;
    } else { // weird case :
      break;
    }
  }
  a_what = 0;
  a_container = 0;
  a_container_index = -1;
  return false;
}

template <class WHAT,class CONTAINER>
inline bool find_top(const path_t& a_path,const node& a_from,
                     CONTAINER*& a_container,WHAT*& a_what,
                     int& a_container_index){
  a_what = 0;
  a_container = 0;
  a_container_index = -1;
  path_t _path = a_path;
  node* from = (node*)&a_from;
  while(true) {
    CONTAINER* container;
    WHAT* what;
    int idx; //index in path of container.
    if(!sg::rfind<WHAT,CONTAINER>(_path,*from,container,what,idx)) {
      break;
    }
    a_container = container;
    a_what = what;
    a_container_index = idx;
    _path.resize(idx+1);
    _path.push_back(what);
    from = what;
  }
  return a_container?true:false;
}

typedef std::vector<path_t> paths_t;

}}

#endif
