// Filename: pStatClientData.cxx
// Created by:  drose (11Jul00)
//
////////////////////////////////////////////////////////////////////
//
// 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 .
//
////////////////////////////////////////////////////////////////////

#include "pStatClientData.h"
#include "pStatReader.h"

#include "pStatCollectorDef.h"

PStatCollectorDef PStatClientData::_null_collector(-1, "Unknown");



////////////////////////////////////////////////////////////////////
//     Function: PStatClientData::Constructor
//       Access: Public
//  Description:
////////////////////////////////////////////////////////////////////
PStatClientData::
PStatClientData(PStatReader *reader) :
  _reader(reader)
{
  _is_alive = true;
}

////////////////////////////////////////////////////////////////////
//     Function: PStatClientData::Destructor
//       Access: Public
//  Description:
////////////////////////////////////////////////////////////////////
PStatClientData::
~PStatClientData() {
  Collectors::const_iterator ci;
  for (ci = _collectors.begin(); ci != _collectors.end(); ++ci) {
    delete (*ci)._def;
  }
}

////////////////////////////////////////////////////////////////////
//     Function: PStatClientData::is_alive
//       Access: Public
//  Description: Returns true if the data is actively getting filled
//               by a connected client, or false if the client has
//               terminated.
////////////////////////////////////////////////////////////////////
bool PStatClientData::
is_alive() const {
  return _is_alive;
}

////////////////////////////////////////////////////////////////////
//     Function: PStatClientData::close
//       Access: Public
//  Description: Closes the client connection if it is open.
////////////////////////////////////////////////////////////////////
void PStatClientData::
close() {
  if (_is_alive && _reader != (PStatReader *)NULL) {
    _reader->close();
    _reader = (PStatReader *)NULL;
    _is_alive = false;
  }
}

////////////////////////////////////////////////////////////////////
//     Function: PStatClientData::get_num_collectors
//       Access: Public
//  Description: Returns the total number of collectors the Data
//               knows about.
////////////////////////////////////////////////////////////////////
int PStatClientData::
get_num_collectors() const {
  return _collectors.size();
}

////////////////////////////////////////////////////////////////////
//     Function: PStatClientData::has_collector
//       Access: Public
//  Description: Returns true if the indicated collector has been
//               defined by the client already, false otherwise.  It
//               is possible for the client to start streaming data
//               before all of the collectors have been defined.
////////////////////////////////////////////////////////////////////
bool PStatClientData::
has_collector(int index) const {
  return (index >= 0 && index < (int)_collectors.size() &&
          _collectors[index]._def != (PStatCollectorDef *)NULL);
}

////////////////////////////////////////////////////////////////////
//     Function: PStatClientData::get_collector_def
//       Access: Public
//  Description: Returns the nth collector definition.
////////////////////////////////////////////////////////////////////
const PStatCollectorDef &PStatClientData::
get_collector_def(int index) const {
  if (!has_collector(index)) {
    return _null_collector;
  }
  return *_collectors[index]._def;
}

////////////////////////////////////////////////////////////////////
//     Function: PStatClientData::get_collector_name
//       Access: Public
//  Description: Returns the name of the indicated collector.
////////////////////////////////////////////////////////////////////
string PStatClientData::
get_collector_name(int index) const {
  if (!has_collector(index)) {
    return "Unknown";
  }
  const PStatCollectorDef *def = _collectors[index]._def;
  return def->_name;
}

////////////////////////////////////////////////////////////////////
//     Function: PStatClientData::get_collector_fullname
//       Access: Public
//  Description: Returns the "full name" of the indicated collector.
//               This will be the concatenation of all of the
//               collector's parents' names (except Frame) and the
//               collector's own name.
////////////////////////////////////////////////////////////////////
string PStatClientData::
get_collector_fullname(int index) const {
  if (!has_collector(index)) {
    return "Unknown";
  }

  const PStatCollectorDef *def = _collectors[index]._def;
  if (def->_parent_index == 0) {
    return def->_name;
  } else {
    return get_collector_fullname(def->_parent_index) + ":" + def->_name;
  }
}

////////////////////////////////////////////////////////////////////
//     Function: PStatClientData::set_collector_has_level
//       Access: Public
//  Description: Indicates whether the given collector has level data
//               (and consequently, whether it should appear on the
//               Levels menu).
//
//               The return value is true if anything changed, false
//               otherwise.
////////////////////////////////////////////////////////////////////
bool PStatClientData::
set_collector_has_level(int index, bool flag) {
  bool any_changed = false;
  slot_collector(index);
  nassertr(index >= 0 && index < (int)_collectors.size(), false);

  if (_collectors[index]._is_level != flag) {
    any_changed = true;
    _collectors[index]._is_level = flag;

    // Turning this on for a given collector also implicitly turns all
    // of its ancestors.
    if (flag) {
      PStatCollectorDef *def = _collectors[index]._def;
      if (def->_parent_index != 0) {
        set_collector_has_level(def->_parent_index, flag);
      }
    }
  }

  return any_changed;
}


////////////////////////////////////////////////////////////////////
//     Function: PStatClientData::get_collector_has_level
//       Access: Public
//  Description: Returns whether the given collector has level data
//               (and consequently, whether it should appear on the
//               Levels menu).
////////////////////////////////////////////////////////////////////
bool PStatClientData::
get_collector_has_level(int index) const {
  return (index >= 0 && index < (int)_collectors.size() &&
          _collectors[index]._is_level);
}

////////////////////////////////////////////////////////////////////
//     Function: PStatClientData::get_num_toplevel_collectors
//       Access: Public
//  Description: Returns the total number of collectors that are
//               toplevel collectors.  These are the collectors that
//               are the children of "Frame", which is collector 0.
////////////////////////////////////////////////////////////////////
int PStatClientData::
get_num_toplevel_collectors() const {
  return _toplevel_collectors.size();
}

////////////////////////////////////////////////////////////////////
//     Function: PStatClientData::get_toplevel_collector
//       Access: Public
//  Description: Returns the collector index of the nth toplevel
//               collector.  Use this function to iterate through the
//               n toplevel collectors indicated by
//               get_num_toplevel_collectors().
////////////////////////////////////////////////////////////////////
int PStatClientData::
get_toplevel_collector(int n) const {
  nassertr(n >= 0 && n < (int)_toplevel_collectors.size(), 0);
  return _toplevel_collectors[n];
}

////////////////////////////////////////////////////////////////////
//     Function: PStatClientData::get_num_threads
//       Access: Public
//  Description: Returns the total number of threads the Data
//               knows about.
////////////////////////////////////////////////////////////////////
int PStatClientData::
get_num_threads() const {
  return _threads.size();
}

////////////////////////////////////////////////////////////////////
//     Function: PStatClientData::has_thread
//       Access: Public
//  Description: Returns true if the indicated thread has been
//               defined by the client already, false otherwise.  It
//               is possible for the client to start streaming data
//               before all of the threads have been defined.
////////////////////////////////////////////////////////////////////
bool PStatClientData::
has_thread(int index) const {
  return (index >= 0 && index < (int)_threads.size() &&
          !_threads[index]._name.empty());
}

////////////////////////////////////////////////////////////////////
//     Function: PStatClientData::get_thread_name
//       Access: Public
//  Description: Returns the name of the indicated thread.
////////////////////////////////////////////////////////////////////
string PStatClientData::
get_thread_name(int index) const {
  if (!has_thread(index)) {
    return "Unknown";
  }
  return _threads[index]._name;
}

////////////////////////////////////////////////////////////////////
//     Function: PStatClientData::get_thread_data
//       Access: Public
//  Description: Returns the data associated with the indicated
//               thread.  This will create a thread definition if it
//               does not already exist.
////////////////////////////////////////////////////////////////////
const PStatThreadData *PStatClientData::
get_thread_data(int index) const {
  ((PStatClientData *)this)->define_thread(index);
  nassertr(index >= 0 && index < (int)_threads.size(), NULL);
  return _threads[index]._data;
}

////////////////////////////////////////////////////////////////////
//     Function: PStatClientData::get_child_distance
//       Access: Public
//  Description: Returns the number of Collectors between the
//               indicated parent and the child Collector in the
//               relationship graph.  If child is the same as parent,
//               returns zero.  If child is an immediate child of
//               parent, returns 1.  If child is a grandchild of
//               parent, returns 2, and so on.  If child is not a
//               descendant of parent at all, returns -1.
////////////////////////////////////////////////////////////////////
int PStatClientData::
get_child_distance(int parent, int child) const {
  if (parent == child) {
    return 0;
  }
  if (!has_collector(child) || child == 0) {
    return -1;
  }
  int dist = get_child_distance(parent, get_collector_def(child)._parent_index);
  if (dist == -1) {
    return -1;
  } else {
    return dist + 1;
  }
}

////////////////////////////////////////////////////////////////////
//     Function: PStatClientData::add_collector
//       Access: Public
//  Description: Adds a new collector definition to the dataset.
//               Presumably this is information just arrived from the
//               client.
//
//               The pointer will become owned by the PStatClientData
//               object and will be freed on destruction.
////////////////////////////////////////////////////////////////////
void PStatClientData::
add_collector(PStatCollectorDef *def) {
  slot_collector(def->_index);
  nassertv(def->_index >= 0 && def->_index < (int)_collectors.size());

  if (_collectors[def->_index]._def != (PStatCollectorDef *)NULL) {
    // Free the old definition, if any.
    delete _collectors[def->_index]._def;
  }

  _collectors[def->_index]._def = def;
  update_toplevel_collectors();

  // If we already had the _is_level flag set, it should be
  // immediately applied to all ancestors.
  if (_collectors[def->_index]._is_level) {
    set_collector_has_level(def->_parent_index, true);
  }
}

////////////////////////////////////////////////////////////////////
//     Function: PStatClientData::define_thread
//       Access: Public
//  Description: Adds a new thread definition to the dataset.
//               Presumably this is information just arrived from the
//               client.
////////////////////////////////////////////////////////////////////
void PStatClientData::
define_thread(int thread_index, const string &name) {
  // A sanity check on the index number.
  nassertv(thread_index < 1000);

  // Make sure we have enough slots allocated.
  while ((int)_threads.size() <= thread_index) {
    _threads.push_back(Thread());
  }

  if (!name.empty()) {
    _threads[thread_index]._name = name;
  }

  if (_threads[thread_index]._data.is_null()) {
    _threads[thread_index]._data = new PStatThreadData(this);
  }
}


////////////////////////////////////////////////////////////////////
//     Function: PStatClientData::record_new_frame
//       Access: Public
//  Description: Makes room for and stores a new frame's worth of
//               data associated with some particular thread (which
//               may or may not have already been defined).
//
//               The pointer will become owned by the PStatThreadData
//               object and will be freed on destruction.
////////////////////////////////////////////////////////////////////
void PStatClientData::
record_new_frame(int thread_index, int frame_number,
                 PStatFrameData *frame_data) {
  define_thread(thread_index);
  nassertv(thread_index >= 0 && thread_index < (int)_threads.size());
  _threads[thread_index]._data->record_new_frame(frame_number, frame_data);
}

////////////////////////////////////////////////////////////////////
//     Function: PStatClientData::slot_collector
//       Access: Private
//  Description: Makes sure there is an entry in the array for a
//               collector with the given index number.
////////////////////////////////////////////////////////////////////
void PStatClientData::
slot_collector(int collector_index) {
  // A sanity check on the index number.
  nassertv(collector_index < 10000);

  while ((int)_collectors.size() <= collector_index) {
    Collector collector;
    collector._def = (PStatCollectorDef *)NULL;
    collector._is_level = false;
    _collectors.push_back(collector);
  }
}

////////////////////////////////////////////////////////////////////
//     Function: PStatClientData::update_toplevel_collectors
//       Access: Private
//  Description: Rebuilds the list of toplevel collectors.
////////////////////////////////////////////////////////////////////
void PStatClientData::
update_toplevel_collectors() {
  _toplevel_collectors.clear();

  Collectors::const_iterator ci;
  for (ci = _collectors.begin(); ci != _collectors.end(); ++ci) {
    PStatCollectorDef *def = (*ci)._def;
    if (def->_parent_index == 0) {
      _toplevel_collectors.push_back(def->_index);
    }
  }
}
