// Filename: findApproxLevelEntry.cxx
// Created by:  drose (13Mar02)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University.  All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license.  You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////

#include "findApproxLevelEntry.h"
#include "nodePathCollection.h"
#include "pandaNode.h"
#include "indent.h"

TypeHandle FindApproxLevelEntry::_type_handle;

////////////////////////////////////////////////////////////////////
//     Function: FindApproxLevelEntry::output
//       Access: Public
//  Description: Formats the entry for meaningful output.  For
//               debugging only.
////////////////////////////////////////////////////////////////////
void FindApproxLevelEntry::
output(ostream &out) const {
  out << "(" << _node_path << "):";
  if (is_solution(0)) {
    out << " solution!";
  } else {
    out << "(";
    _approx_path.output_component(out, _i);
    out << ")," << _i;
  }
}

////////////////////////////////////////////////////////////////////
//     Function: FindApproxLevelEntry::write_level
//       Access: Public
//  Description: Writes the entire level (a linked list of entries
//               beginning at this entry).  For debugging only.
////////////////////////////////////////////////////////////////////
void FindApproxLevelEntry::
write_level(ostream &out, int indent_level) const {
  for (const FindApproxLevelEntry *entry = this;
       entry != (const FindApproxLevelEntry *)NULL;
       entry = entry->_next) {
    indent(out, indent_level);
    out << *entry << "\n";
  }
}

////////////////////////////////////////////////////////////////////
//     Function: FindApproxLevelEntry::consider_node
//       Access: Public
//  Description: Considers the node represented by the entry for
//               matching the find path.  If a solution is found, it
//               is added to result; if the children of this node
//               should be considered, the appropriate entries are
//               added to next_level.
//
//               The return value is true if result now contains
//               max_matches solutions, or false if we should keep
//               looking.
////////////////////////////////////////////////////////////////////
bool FindApproxLevelEntry::
consider_node(NodePathCollection &result, FindApproxLevelEntry *&next_level,
              int max_matches, int increment) const {
  if (is_solution(increment)) {
    // If the entry represents a solution, save it and we're done with
    // the entry.
    result.add_path(_node_path.get_node_path());
    if (max_matches > 0 && result.get_num_paths() >= max_matches) { 
      return true;
    }

    return false;
  }

  // If the entry is not itself a solution, consider its children.

  if (_approx_path.is_component_match_many(_i + increment)) {
    // Match any number, zero or more, levels of nodes.  This is the
    // tricky case that requires this whole nutty breadth-first thing.

    // This means we must reconsider our own entry with the next path
    // entry, before we consider the next entry--this supports
    // matching zero levels of nodes.

    // We used to make a temporary copy of our own record, and then
    // increment _i on that copy, but we can't do that nowadays
    // because the WorkingNodePath object stores a pointer to each
    // previous generation, which means we can't use any temporary
    // FindApproxLevelEntry objects.  Instead, we pass around the
    // increment parameter, which increments _i on the fly.

    if (consider_node(result, next_level, max_matches, increment + 1)) {
      return true;
    }
  }

  PandaNode *this_node = _node_path.node();
  nassertr(this_node != (PandaNode *)NULL, false);

  bool stashed_only = next_is_stashed(increment);

  if (!stashed_only) {
    // Check the normal list of children.
    PandaNode::Children children = this_node->get_children();
    int num_children = children.get_num_children();
    for (int i = 0; i < num_children; i++) {
      PandaNode *child_node = children.get_child(i);
      
      consider_next_step(child_node, next_level, increment);
    }
  }

  if (_approx_path.return_stashed() || stashed_only) {
    // Also check the stashed list.
    int num_stashed = this_node->get_num_stashed();
    for (int i = 0; i < num_stashed; i++) {
      PandaNode *stashed_node = this_node->get_stashed(i);
      
      consider_next_step(stashed_node, next_level, increment);
    }
  }

  return false;
}

////////////////////////////////////////////////////////////////////
//     Function: FindApproxLevelEntry::consider_next_step
//       Access: Public
//  Description: Compares the indicated child node (which is assumed
//               to be a child of _node_path) with the next component
//               of the path.  If it matches, generates whatever
//               additional entries are appropriate and stores them in
//               next_level.
////////////////////////////////////////////////////////////////////
void FindApproxLevelEntry::
consider_next_step(PandaNode *child_node, FindApproxLevelEntry *&next_level, 
                   int increment) const {
  nassertv(child_node != _node_path.node());

  if (!_approx_path.return_hidden() && child_node->is_overall_hidden()) {
    // If the approx path does not allow us to return hidden nodes,
    // and this node has indeed been completely hidden, then stop
    // here.
    return;
  }

  nassertv(_i + increment < _approx_path.get_num_components());

  if (_approx_path.is_component_match_many(_i + increment)) {
    // Match any number, zero or more, levels of nodes.  This is the
    // tricky case that requires this whole nutty breadth-first thing.

    // And now we just add the next entry without incrementing its
    // path entry.

    next_level = new FindApproxLevelEntry
      (*this, child_node, _i + increment, next_level);

  } else {
    if (_approx_path.matches_component(_i + increment, child_node)) {
      // That matched, and it consumes one path entry.
      next_level = new FindApproxLevelEntry
        (*this, child_node, _i + increment + 1, next_level);
    }
  }
}
