// Filename: pgTop.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 "pgTop.h"
#include "pgMouseWatcherGroup.h"
#include "pgCullTraverser.h"
#include "cullBinAttrib.h"

#include "omniBoundingVolume.h"

TypeHandle PGTop::_type_handle;

////////////////////////////////////////////////////////////////////
//     Function: PGTop::Constructor
//       Access: Published
//  Description: 
////////////////////////////////////////////////////////////////////
PGTop::
PGTop(const string &name) : 
  PandaNode(name)
{
  set_cull_callback();

  _start_sort = 0;

  // A PGTop node normally has an infinite bounding volume.  Screw
  // culling.
  set_internal_bounds(new OmniBoundingVolume());
  set_final(true);

  // Also, screw state sorting.  By default, everything under PGTop
  // will be unsorted: rendered in scene graph order.  This is closer
  // to what the user wants anyway in a 2-d scene graph.

  set_attrib(CullBinAttrib::make("unsorted", 0));
}

////////////////////////////////////////////////////////////////////
//     Function: PGTop::Destructor
//       Access: Public, Virtual
//  Description: 
////////////////////////////////////////////////////////////////////
PGTop::
~PGTop() {
  set_mouse_watcher((MouseWatcher *)NULL);
}

////////////////////////////////////////////////////////////////////
//     Function: PGTop::make_copy
//       Access: Protected, Virtual
//  Description: Returns a newly-allocated Node that is a shallow copy
//               of this one.  It will be a different Node pointer,
//               but its internal data may or may not be shared with
//               that of the original Node.
////////////////////////////////////////////////////////////////////
PandaNode *PGTop::
make_copy() const {
  return new PGTop(*this);
}

////////////////////////////////////////////////////////////////////
//     Function: PGTop::cull_callback
//       Access: Protected, Virtual
//  Description: This function will be called during the cull
//               traversal to perform any additional operations that
//               should be performed at cull time.  This may include
//               additional manipulation of render state or additional
//               visible/invisible decisions, or any other arbitrary
//               operation.
//
//               Note that this function will *not* be called unless
//               set_cull_callback() is called in the constructor of
//               the derived class.  It is necessary to call
//               set_cull_callback() to indicated that we require
//               cull_callback() to be called.
//
//               By the time this function is called, the node has
//               already passed the bounding-volume test for the
//               viewing frustum, and the node's transform and state
//               have already been applied to the indicated
//               CullTraverserData object.
//
//               The return value is true if this node should be
//               visible, or false if it should be culled.
////////////////////////////////////////////////////////////////////
bool PGTop::
cull_callback(CullTraverser *trav, CullTraverserData &data) {
  // We create a new MouseWatcherGroup for the purposes of collecting
  // a new set of regions visible onscreen.
  PT(PGMouseWatcherGroup) old_watcher_group;
  if (_watcher_group != (PGMouseWatcherGroup *)NULL) {
    _watcher_group->clear_top(this);
    old_watcher_group = _watcher_group;
    _watcher_group = new PGMouseWatcherGroup(this);
  }

  // Now subsitute for the normal CullTraverser a special one of our
  // own choosing.  This just carries around a pointer back to the
  // PGTop node, for the convenience of PGItems to register themselves
  // as they are drawn.
  PGCullTraverser pg_trav(this, trav);
  pg_trav.local_object();
  pg_trav._sort_index = _start_sort;
  pg_trav.traverse_below(data);
  pg_trav.end_traverse();

  // Now tell the watcher about the new set of regions.  Strictly
  // speaking, we shouldn't do this until the frame that we're about
  // to render has been presented; otherwise, we may make regions
  // active before they are actually visible.  But no one has
  // complained about this so far.
  if (_watcher_group != (PGMouseWatcherGroup *)NULL) {
    nassertr(_watcher != (MouseWatcher *)NULL, false);
    _watcher->replace_group(old_watcher_group, _watcher_group);
  }

  // We've taken care of the traversal, thank you.
  return false;
}

////////////////////////////////////////////////////////////////////
//     Function: PGTop::is_renderable
//       Access: Public, Virtual
//  Description: Returns true if there is some value to visiting this
//               particular node during the cull traversal for any
//               camera, false otherwise.  This will be used to
//               optimize the result of get_net_draw_show_mask(), so
//               that any subtrees that contain only nodes for which
//               is_renderable() is false need not be visited.
////////////////////////////////////////////////////////////////////
bool PGTop::
is_renderable() const {
  // We flag the PGTop as renderable, even though it technically
  // doesn't have anything to render, but we do need the traverser to
  // visit it every frame.
  return true;
}

////////////////////////////////////////////////////////////////////
//     Function: PGTop::set_mouse_watcher
//       Access: Published
//  Description: Sets the MouseWatcher pointer that the PGTop object
//               registers its PG items with.  This must be set before
//               the PG items are active.
////////////////////////////////////////////////////////////////////
void PGTop::
set_mouse_watcher(MouseWatcher *watcher) {
  if (_watcher_group != (PGMouseWatcherGroup *)NULL) {
    _watcher_group->clear_top(this);
  }
  if (_watcher != (MouseWatcher *)NULL) {
    _watcher->remove_group(_watcher_group);
  }

  _watcher = watcher;
  _watcher_group = (PGMouseWatcherGroup *)NULL;

  if (_watcher != (MouseWatcher *)NULL) {
    _watcher_group = new PGMouseWatcherGroup(this);
    _watcher->add_group(_watcher_group);
  }
}
