/// QasTools: Desktop toolset for the Linux sound system ALSA.
/// \copyright See COPYING file.

#include "static_tree.hpp"

Static_Tree::Static_Tree ()
{
  reset ();
}

Static_Tree::~Static_Tree () = default;

void
Static_Tree::reset ()
{
  clear ();

  // Add root node
  Node nn_root;
  nn_root.parent_idx = 0;
  nn_root.self_idx = 0;
  nn_root.children_idx = 0;
  push_back ( nn_root );
}

const Static_Tree::Node *
Static_Tree::parent_node ( const Node * node_n ) const
{
  if ( node_n != nullptr ) {
    if ( !is_root ( node_n ) ) {
      return node ( node_n->parent_idx );
    }
  }
  return nullptr;
}

Static_Tree::Node *
Static_Tree::parent_node ( const Node * node_n )
{
  if ( node_n != nullptr ) {
    if ( !is_root ( node_n ) ) {
      return node ( node_n->parent_idx );
    }
  }
  return nullptr;
}

const Static_Tree::Node *
Static_Tree::child_node ( const Node * node_n, std::size_t child_n ) const
{
  if ( node_n != nullptr ) {
    if ( node_n->children_idx > 0 ) {
      return node ( node_n->children_idx + child_n );
    }
  }
  return nullptr;
}

Static_Tree::Node *
Static_Tree::child_node ( const Node * node_n, std::size_t child_n )
{
  if ( node_n != nullptr ) {
    if ( node_n->children_idx > 0 ) {
      return node ( node_n->children_idx + child_n );
    }
  }
  return nullptr;
}

std::size_t
Static_Tree::depth ( const Node * node_n ) const
{
  std::size_t res = 0;
  if ( node_n != nullptr ) {
    while ( !is_root ( node_n ) ) {
      node_n = parent_node ( node_n );
      ++res;
    }
  }
  return res;
}

std::size_t
Static_Tree::row ( const Node * node_n ) const
{
  std::size_t idx = 0;
  if ( node_n != nullptr ) {
    idx = node_n->self_idx;
    while ( idx > 0 ) {
      const Node * nn = node ( idx );
      if ( nn->parent_idx != node_n->parent_idx ) {
        break;
      }
      --idx;
    }
    return ( node_n->self_idx - idx ) - 1;
  }
  return idx;
}

std::size_t
Static_Tree::num_children ( const Node * node_n ) const
{
  std::size_t res = 0;
  if ( node_n != nullptr ) {
    if ( node_n->children_idx > 0 ) {
      const std::size_t idx_lim = size ();
      std::size_t idx = node_n->children_idx;
      while ( idx < idx_lim ) {
        const Node * node_c = node ( idx );
        if ( node_c->parent_idx != node_n->self_idx ) {
          break;
        }
        ++res;
        ++idx;
      }
    }
  }
  return res;
}

void
Static_Tree::rootline ( const Node * node_n,
                        std::vector< std::size_t > & rows_n ) const
{
  rows_n.clear ();
  if ( node_n != nullptr ) {
    while ( !is_root ( node_n ) ) {
      rows_n.push_back ( row ( node_n ) );
      node_n = parent_node ( node_n );
    }
  }
}

std::array< std::size_t, 2 >
Static_Tree::append_children ( std::size_t node_idx_n, std::size_t num_n )
{
  std::array< std::size_t, 2 > child_range = { { 0, 0 } };
  if ( ( node_idx_n >= size () ) || ( num_n == 0 ) ) {
    return child_range;
  }

  // Reserve space in advance. This invalidates all node pointers
  const std::size_t size_new = size () + num_n;
  if ( capacity () < size_new ) {
    reserve ( size_new );
  }

  child_range[ 0 ] = size ();
  child_range[ 1 ] = size () + num_n;

  node ( node_idx_n )->children_idx = child_range[ 0 ];
  for ( std::size_t ii = 0; ii < num_n; ++ii ) {
    Node nn;
    nn.parent_idx = node_idx_n;
    nn.self_idx = size ();
    push_back ( nn );
  }

  return child_range;
}
