// Filename: pStatCollector.I
// Created by:  drose (10Jul00)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license.  You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////


#ifdef DO_PSTATS

////////////////////////////////////////////////////////////////////
//     Function: PStatCollector::Constructor
//       Access: Private
//  Description: Normally, this constructor is called only from
//               PStatClient.  Use one of the constructors below to
//               create your own Collector.
////////////////////////////////////////////////////////////////////
INLINE PStatCollector::
PStatCollector(PStatClient *client, int index) :
  _client(client),
  _index(index),
  _level(0.0f)
{
}

////////////////////////////////////////////////////////////////////
//     Function: PStatCollector::Default Constructor
//       Access: Public
//  Description: Creates an invalid PStatCollector.  Any attempt to
//               use this collector will crash messily.
//
//               You can reassign it to a different, valid one later.
////////////////////////////////////////////////////////////////////
INLINE PStatCollector::
PStatCollector() : 
  _client(NULL),
  _index(0),
  _level(0.0f)
{
}

////////////////////////////////////////////////////////////////////
//     Function: PStatCollector::Constructor
//       Access: Published
//  Description: Creates a new PStatCollector, ready to start
//               accumulating data.  The name of the collector
//               uniquely identifies it among the other collectors; if
//               two collectors share the same name then they are
//               really the same collector.
//
//               The name may also be a compound name, something like
//               "Cull:Sort", which indicates that this is a collector
//               named "Sort", a child of the collector named "Cull".
//               The parent may also be named explicitly by reference
//               in the other flavor of the constructor; see further
//               comments on this for that constructor.
//
//               If the client pointer is non-null, it specifies a
//               particular client to register the collector with;
//               otherwise, the global client is used.
////////////////////////////////////////////////////////////////////
INLINE PStatCollector::
PStatCollector(const string &name, PStatClient *client) :
  _level(0.0f)
{
  if (client == (PStatClient *)NULL) {
    client = PStatClient::get_global_pstats();
  }
  (*this) = client->make_collector_with_relname(0, name);
}

////////////////////////////////////////////////////////////////////
//     Function: PStatCollector::Constructor
//       Access: Published
//  Description: Creates a new PStatCollector, ready to start
//               accumulating data.  The name of the collector
//               uniquely identifies it among the other collectors; if
//               two collectors share the same name then they are
//               really the same collector.
//
//               The parent is the collector that conceptually
//               includes all of the time measured for this collector.
//               For instance, a particular character's animation time
//               is owned by the "Animation" collector, which is in
//               turn owned by the "Frame" collector.  It is not
//               strictly necessary that all of the time spent in a
//               particular collector is completely nested within time
//               spent in its parent's collector.  If parent is the
//               empty string, the collector is owned by "Frame".
//
//               This constructor does not take a client pointer; it
//               always creates the new collector on the same client
//               as its parent.
////////////////////////////////////////////////////////////////////
INLINE PStatCollector::
PStatCollector(const PStatCollector &parent, const string &name) :
  _level(0.0f)
{
  nassertv(parent._client != (PStatClient *)NULL);
  (*this) =
    parent._client->make_collector_with_relname(parent._index, name);
}

////////////////////////////////////////////////////////////////////
//     Function: PStatCollector::Copy Constructor
//       Access: Published
//  Description:
////////////////////////////////////////////////////////////////////
INLINE PStatCollector::
PStatCollector(const PStatCollector &copy) :
  _client(copy._client),
  _index(copy._index),
  _level(0.0f)
{
}

////////////////////////////////////////////////////////////////////
//     Function: PStatCollector::Copy Assignment Operator
//       Access: Published
//  Description:
////////////////////////////////////////////////////////////////////
INLINE void PStatCollector::
operator = (const PStatCollector &copy) {
  _client = copy._client;
  _index = copy._index;
}

////////////////////////////////////////////////////////////////////
//     Function: PStatCollector::is_valid
//       Access: Published
//  Description: Returns true if collector is valid and may be used,
//               or false if it was constructed with the default
//               constructor (in which case any attempt to use it will
//               crash).
////////////////////////////////////////////////////////////////////
INLINE bool PStatCollector::
is_valid() const {
  return (_client != (PStatClient *)NULL);
}

////////////////////////////////////////////////////////////////////
//     Function: PStatCollector::get_name
//       Access: Published
//  Description: Returns the local name of this collector.  This is
//               the rightmost part of the fullname, after the
//               rightmost colon.
////////////////////////////////////////////////////////////////////
INLINE string PStatCollector::
get_name() const {
  if (_client != (PStatClient *)NULL) {
    return _client->get_collector_name(_index);
  }
  return string();
}

////////////////////////////////////////////////////////////////////
//     Function: PStatCollector::get_fullname
//       Access: Published
//  Description: Returns the full name of this collector.  This
//               includes the names of all the collector's parents,
//               concatenated together with colons.
////////////////////////////////////////////////////////////////////
INLINE string PStatCollector::
get_fullname() const {
  if (_client != (PStatClient *)NULL) {
    return _client->get_collector_fullname(_index);
  }
  return string();
}

////////////////////////////////////////////////////////////////////
//     Function: PStatCollector::output
//       Access: Published
//  Description: 
////////////////////////////////////////////////////////////////////
INLINE void PStatCollector::
output(ostream &out) const {
  out << "PStatCollector(\"" << get_fullname() << "\")";
}

////////////////////////////////////////////////////////////////////
//     Function: PStatCollector::is_active
//       Access: Published
//  Description: Returns true if this particular collector is active
//               on the default thread, and we are currently
//               transmitting PStats data.
////////////////////////////////////////////////////////////////////
INLINE bool PStatCollector::
is_active() {
#ifndef HAVE_THREADS
  return _client->is_active(_index, 0);
#else  // HAVE_THREADS
  return is_active(_client->get_current_thread());
#endif  // HAVE_THREADS
}

////////////////////////////////////////////////////////////////////
//     Function: PStatCollector::is_started
//       Access: Published
//  Description: Returns true if this particular collector has been
//               started on the default thread, or false otherwise.
////////////////////////////////////////////////////////////////////
INLINE bool PStatCollector::
is_started() {
#ifndef HAVE_THREADS
  return _client->is_started(_index, 0);
#else  // HAVE_THREADS
  return is_started(_client->get_current_thread());
#endif  // HAVE_THREADS
}

////////////////////////////////////////////////////////////////////
//     Function: PStatCollector::start
//       Access: Published
//  Description: Starts this particular timer ticking.  This should be
//               called before the code you want to measure.
////////////////////////////////////////////////////////////////////
INLINE void PStatCollector::
start() {
#ifndef HAVE_THREADS
  _client->start(_index, 0);
#else  // HAVE_THREADS
  start(_client->get_current_thread());
#endif  // HAVE_THREADS
}

////////////////////////////////////////////////////////////////////
//     Function: PStatCollector::stop
//       Access: Published
//  Description: Stops this timer.  This should be called after the
//               code you want to measure.
////////////////////////////////////////////////////////////////////
INLINE void PStatCollector::
stop() {
#ifndef HAVE_THREADS
  _client->stop(_index, 0);
#else  // HAVE_THREADS
  stop(_client->get_current_thread());
#endif  // HAVE_THREADS
}

////////////////////////////////////////////////////////////////////
//     Function: PStatCollector::clear_level
//       Access: Published
//  Description: Removes the level setting associated with this
//               collector for the main thread.  The collector
//               will no longer show up on any level graphs in the
//               main thread.  This implicitly calls flush_level().
////////////////////////////////////////////////////////////////////
INLINE void PStatCollector::
clear_level() {
  _client->clear_level(_index, 0);
  _level = 0.0f;
}

////////////////////////////////////////////////////////////////////
//     Function: PStatCollector::set_level
//       Access: Published
//  Description: Sets the level setting associated with this
//               collector for the main thread to the indicated
//               value.  This implicitly calls flush_level().
////////////////////////////////////////////////////////////////////
INLINE void PStatCollector::
set_level(double level) {
  _client->set_level(_index, 0, level);
  _level = 0.0f;
}

////////////////////////////////////////////////////////////////////
//     Function: PStatCollector::add_level
//       Access: Published
//  Description: Adds the indicated increment (which may be negative)
//               to the level setting associated with this collector
//               for the main thread.  If the collector did not
//               already have a level setting for the main thread, it
//               is initialized to 0.
//
//               As an optimization, the data is not immediately set
//               to the PStatClient.  It will be sent the next time
//               flush_level() is called.
////////////////////////////////////////////////////////////////////
INLINE void PStatCollector::
add_level(double increment) {
  _level += increment;
}

////////////////////////////////////////////////////////////////////
//     Function: PStatCollector::sub_level
//       Access: Published
//  Description: Subtracts the indicated decrement (which may be
//               negative) to the level setting associated with this
//               collector for the main thread.  If the collector did
//               not already have a level setting for the main thread,
//               it is initialized to 0.
//
//               As an optimization, the data is not immediately set
//               to the PStatClient.  It will be sent the next time
//               flush_level() is called.
////////////////////////////////////////////////////////////////////
INLINE void PStatCollector::
sub_level(double decrement) {
  _level -= decrement;
}

////////////////////////////////////////////////////////////////////
//     Function: PStatCollector::add_level_now
//       Access: Published
//  Description: Calls add_level() and immediately calls flush_level().
////////////////////////////////////////////////////////////////////
INLINE void PStatCollector::
add_level_now(double increment) {
  add_level(increment);
  flush_level();
}

////////////////////////////////////////////////////////////////////
//     Function: PStatCollector::sub_level_now
//       Access: Published
//  Description: Calls sub_level() and immediately calls flush_level().
////////////////////////////////////////////////////////////////////
INLINE void PStatCollector::
sub_level_now(double decrement) {
  sub_level(decrement);
  flush_level();
}

////////////////////////////////////////////////////////////////////
//     Function: PStatCollector::flush_level
//       Access: Published
//  Description: Updates the PStatClient with the recent results from
//               add_level() and sub_level().
////////////////////////////////////////////////////////////////////
INLINE void PStatCollector::
flush_level() {
  if (_level != 0.0f) {
    _client->add_level(_index, 0, _level);
    _level = 0.0f;
  }
}

////////////////////////////////////////////////////////////////////
//     Function: PStatCollector::get_level
//       Access: Published
//  Description: Returns the current level value of the given
//               collector in the main thread.  This implicitly calls
//               flush_level().
////////////////////////////////////////////////////////////////////
INLINE double PStatCollector::
get_level() {
  flush_level();
  return _client->get_level(_index, 0);
}

////////////////////////////////////////////////////////////////////
//     Function: PStatCollector::clear_thread_level
//       Access: Published
//  Description: Removes the level setting associated with this
//               collector for the current thread.  The collector
//               will no longer show up on any level graphs in the
//               current thread.
////////////////////////////////////////////////////////////////////
INLINE void PStatCollector::
clear_thread_level() {
#ifndef HAVE_THREADS
  _client->clear_level(_index, 0);
#else  // HAVE_THREADS
  clear_level(_client->get_current_thread());
#endif  // HAVE_THREADS
}

////////////////////////////////////////////////////////////////////
//     Function: PStatCollector::set_thread_level
//       Access: Published
//  Description: Sets the level setting associated with this
//               collector for the current thread to the indicated
//               value.
////////////////////////////////////////////////////////////////////
INLINE void PStatCollector::
set_thread_level(double level) {
#ifndef HAVE_THREADS
  _client->set_level(_index, 0, level);
#else  // HAVE_THREADS
  set_level(_client->get_current_thread(), level);
#endif  // HAVE_THREADS
}

////////////////////////////////////////////////////////////////////
//     Function: PStatCollector::add_thread_level
//       Access: Published
//  Description: Adds the indicated increment (which may be negative)
//               to the level setting associated with this collector
//               for the current thread.  If the collector did not
//               already have a level setting for the current thread,
//               it is initialized to 0.
////////////////////////////////////////////////////////////////////
INLINE void PStatCollector::
add_thread_level(double increment) {
#ifndef HAVE_THREADS
  _client->add_level(_index, 0, increment);
#else  // HAVE_THREADS
  add_level(_client->get_current_thread(), increment);
#endif  // HAVE_THREADS
}

////////////////////////////////////////////////////////////////////
//     Function: PStatCollector::sub_thread_level
//       Access: Published
//  Description: Subtracts the indicated decrement (which may be
//               negative) to the level setting associated with this
//               collector for the current thread.  If the collector
//               did not already have a level setting for the current
//               thread, it is initialized to 0.
////////////////////////////////////////////////////////////////////
INLINE void PStatCollector::
sub_thread_level(double decrement) {
#ifndef HAVE_THREADS
  _client->add_level(_index, 0, -decrement);
#else  // HAVE_THREADS
  sub_level(_client->get_current_thread(), decrement);
#endif  // HAVE_THREADS
}

////////////////////////////////////////////////////////////////////
//     Function: PStatCollector::get_thread_level
//       Access: Published
//  Description: Returns the current level value of the given
//               collector in the current thread.
////////////////////////////////////////////////////////////////////
INLINE double PStatCollector::
get_thread_level() {
#ifndef HAVE_THREADS
  return _client->get_level(_index, 0);
#else  // HAVE_THREADS
  return get_level(_client->get_current_thread());
#endif  // HAVE_THREADS
}

////////////////////////////////////////////////////////////////////
//     Function: PStatCollector::is_active
//       Access: Published
//  Description: Returns true if this particular collector is active
//               on the indicated thread, and we are currently
//               transmitting PStats data.
////////////////////////////////////////////////////////////////////
INLINE bool PStatCollector::
is_active(const PStatThread &thread) {
  return _client->is_active(_index, thread._index);
}

////////////////////////////////////////////////////////////////////
//     Function: PStatCollector::is_started
//       Access: Published
//  Description: Returns true if this particular collector has been
//               started on the indicated thread, or false otherwise.
////////////////////////////////////////////////////////////////////
INLINE bool PStatCollector::
is_started(const PStatThread &thread) {
  return _client->is_started(_index, thread._index);
}

////////////////////////////////////////////////////////////////////
//     Function: PStatCollector::start
//       Access: Published
//  Description: Starts this timer ticking within a particular thread.
////////////////////////////////////////////////////////////////////
INLINE void PStatCollector::
start(const PStatThread &thread) {
  _client->start(_index, thread._index);
}

////////////////////////////////////////////////////////////////////
//     Function: PStatCollector::start
//       Access: Published
//  Description: Marks that the timer should have been started as of
//               the indicated time.  This must be a time based on the
//               PStatClient's clock (see PStatClient::get_clock()),
//               and care should be taken that all such calls exhibit
//               a monotonically increasing series of time values.
////////////////////////////////////////////////////////////////////
INLINE void PStatCollector::
start(const PStatThread &thread, float as_of) {
  _client->start(_index, thread._index, as_of);
}

////////////////////////////////////////////////////////////////////
//     Function: PStatCollector::stop
//       Access: Published
//  Description: Stops this timer within a particular thread.
////////////////////////////////////////////////////////////////////
INLINE void PStatCollector::
stop(const PStatThread &thread) {
  _client->stop(_index, thread._index);
}

////////////////////////////////////////////////////////////////////
//     Function: PStatCollector::stop
//       Access: Published
//  Description: Marks that the timer should have been stopped as of
//               the indicated time.  This must be a time based on the
//               PStatClient's clock (see PStatClient::get_clock()),
//               and care should be taken that all such calls exhibit
//               a monotonically increasing series of time values.
////////////////////////////////////////////////////////////////////
INLINE void PStatCollector::
stop(const PStatThread &thread, float as_of) {
  _client->stop(_index, thread._index, as_of);
}

////////////////////////////////////////////////////////////////////
//     Function: PStatCollector::clear_level
//       Access: Published
//  Description: Removes the level setting associated with this
//               collector for the indicated thread.  The collector
//               will no longer show up on any level graphs in this
//               thread.
////////////////////////////////////////////////////////////////////
INLINE void PStatCollector::
clear_level(const PStatThread &thread) {
  _client->clear_level(_index, thread._index);
}

////////////////////////////////////////////////////////////////////
//     Function: PStatCollector::set_level
//       Access: Published
//  Description: Sets the level setting associated with this
//               collector for the indicated thread to the indicated
//               value.
////////////////////////////////////////////////////////////////////
INLINE void PStatCollector::
set_level(const PStatThread &thread, double level) {
  _client->set_level(_index, thread._index, level);
}

////////////////////////////////////////////////////////////////////
//     Function: PStatCollector::add_level
//       Access: Published
//  Description: Adds the indicated increment (which may be negative)
//               to the level setting associated with this collector
//               for the indicated thread.  If the collector did not
//               already have a level setting for this thread, it is
//               initialized to 0.
////////////////////////////////////////////////////////////////////
INLINE void PStatCollector::
add_level(const PStatThread &thread, double increment) {
  _client->add_level(_index, thread._index, increment);
}

////////////////////////////////////////////////////////////////////
//     Function: PStatCollector::sub_level
//       Access: Published
//  Description: Subtracts the indicated decrement (which may be
//               negative) to the level setting associated with this
//               collector for the indicated thread.  If the collector
//               did not already have a level setting for this thread,
//               it is initialized to 0.
////////////////////////////////////////////////////////////////////
INLINE void PStatCollector::
sub_level(const PStatThread &thread, double decrement) {
  _client->add_level(_index, thread._index, -decrement);
}

////////////////////////////////////////////////////////////////////
//     Function: PStatCollector::get_level
//       Access: Published
//  Description: Returns the current level value of the given collector.
////////////////////////////////////////////////////////////////////
INLINE double PStatCollector::
get_level(const PStatThread &thread) {
  return _client->get_level(_index, thread._index);
}

////////////////////////////////////////////////////////////////////
//     Function: PStatCollector::get_index
//       Access: Published
//  Description: Returns the index number of this particular collector
//               within the PStatClient.
////////////////////////////////////////////////////////////////////
INLINE int PStatCollector::
get_index() const {
  return _index;
}

#else  // DO_PSTATS

////////////////////////////////////////////////////////////////////
//     Function: PStatCollector::Constructor
//       Access: Published
//  Description: This bogus version of the function is only defined if
//               DO_PSTATS is not defined, meaning all these functions
//               should compile to nothing.
////////////////////////////////////////////////////////////////////
INLINE PStatCollector::
PStatCollector(const string &, PStatClient *client) {
  // We need this bogus comparison just to prevent the SGI compiler
  // from dumping core.  It's perfectly meaningless.
#ifdef mips
  if (client == (PStatClient *)NULL) {
    return;
  }
#endif
}

////////////////////////////////////////////////////////////////////
//     Function: PStatCollector::Constructor
//       Access: Published
//  Description: This bogus version of the function is only defined if
//               DO_PSTATS is not defined, meaning all these functions
//               should compile to nothing.
////////////////////////////////////////////////////////////////////
INLINE PStatCollector::
PStatCollector(const PStatCollector &parent, const string &) {
  // We need this bogus comparison just to prevent the SGI compiler
  // from dumping core.  It's perfectly meaningless.
#ifdef mips
  if (&parent == (const PStatCollector *)NULL) {
    return;
  }
#endif
}


#endif  // DO_PSTATS
