// Filename: attribNodeRegistry.cxx
// Created by:  drose (07Jul07)
//
////////////////////////////////////////////////////////////////////
//
// 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 "attribNodeRegistry.h"
#include "mutexHolder.h"

AttribNodeRegistry * TVOLATILE AttribNodeRegistry::_global_ptr;

////////////////////////////////////////////////////////////////////
//     Function: AttribNodeRegistry::Constructor
//       Access: Protected
//  Description: 
////////////////////////////////////////////////////////////////////
AttribNodeRegistry::
AttribNodeRegistry() {
}

////////////////////////////////////////////////////////////////////
//     Function: AttribNodeRegistry::add_node
//       Access: Published
//  Description: Adds the indicated NodePath to the registry.  The
//               name and type of the node are noted at the time of
//               this call; if the name changes later, it will not
//               update the registry index.
//
//               The NodePath must reference some kind of an attribute
//               node, such as a LightNode or a PlaneNode.  When bam
//               files that reference an attribute node of the same
//               type and the same name are loaded, they will quietly
//               be redirected to reference this NodePath.
//
//               If there is already a node matching the indicated
//               name and type, it will be replaced.
////////////////////////////////////////////////////////////////////
void AttribNodeRegistry::
add_node(const NodePath &attrib_node) {
  nassertv(!attrib_node.is_empty());
  MutexHolder holder(_lock);

  pair<Entries::iterator, bool> result = _entries.insert(Entry(attrib_node));
  if (!result.second) {
    // Replace an existing node.
    (*result.first)._node = attrib_node;
  }
}

////////////////////////////////////////////////////////////////////
//     Function: AttribNodeRegistry::remove_node
//       Access: Published
//  Description: Removes the indicated NodePath from the registry.
//               The name of the node must not have changed since the
//               matching call to add_node(), or it will not be
//               successfully removed.
//
//               Returns true if the NodePath is found and removed,
//               false if it is not found (for instance, because the
//               name has changed).
////////////////////////////////////////////////////////////////////
bool AttribNodeRegistry::
remove_node(const NodePath &attrib_node) {
  nassertr(!attrib_node.is_empty(), false);
  MutexHolder holder(_lock);
  Entries::iterator ei = _entries.find(Entry(attrib_node));
  if (ei != _entries.end()) {
    _entries.erase(ei);
    return true;
  }
  return false;
}

////////////////////////////////////////////////////////////////////
//     Function: AttribNodeRegistry::lookup_node
//       Access: Published
//  Description: Looks up the indicated NodePath in the registry.  If
//               there is a node already in the registry with the
//               matching name and type, returns that NodePath
//               instead; otherwise, returns the original NodePath.
////////////////////////////////////////////////////////////////////
NodePath AttribNodeRegistry::
lookup_node(const NodePath &orig_node) const {
  nassertr(!orig_node.is_empty(), orig_node);

  MutexHolder holder(_lock);
  Entries::const_iterator ei = _entries.find(Entry(orig_node));
  if (ei != _entries.end()) {
    return (*ei)._node;
  }
  return orig_node;
}

////////////////////////////////////////////////////////////////////
//     Function: AttribNodeRegistry::get_num_nodes
//       Access: Published
//  Description: Returns the total number of nodes in the registry.
////////////////////////////////////////////////////////////////////
int AttribNodeRegistry::
get_num_nodes() const {
  MutexHolder holder(_lock);
  return _entries.size();
}

////////////////////////////////////////////////////////////////////
//     Function: AttribNodeRegistry::get_node
//       Access: Published
//  Description: Returns the nth NodePath recorded in the registry.
////////////////////////////////////////////////////////////////////
NodePath AttribNodeRegistry::
get_node(int n) const {
  MutexHolder holder(_lock);
  nassertr(n >= 0 && n < (int)_entries.size(), NodePath());
  return _entries[n]._node;
}

////////////////////////////////////////////////////////////////////
//     Function: AttribNodeRegistry::get_node_type
//       Access: Published
//  Description: Returns the type of the nth node, as recorded in the
//               registry.
////////////////////////////////////////////////////////////////////
TypeHandle AttribNodeRegistry::
get_node_type(int n) const {
  MutexHolder holder(_lock);
  nassertr(n >= 0 && n < (int)_entries.size(), TypeHandle::none());
  return _entries[n]._type;
}

////////////////////////////////////////////////////////////////////
//     Function: AttribNodeRegistry::get_node_name
//       Access: Published
//  Description: Returns the name of the nth node, as recorded in the
//               registry.  This will be the node name as it was at
//               the time the node was recorded; if the node has
//               changed names since then, this will still return the
//               original name.
////////////////////////////////////////////////////////////////////
string AttribNodeRegistry::
get_node_name(int n) const {
  MutexHolder holder(_lock);
  nassertr(n >= 0 && n < (int)_entries.size(), string());
  return _entries[n]._name;
}

////////////////////////////////////////////////////////////////////
//     Function: AttribNodeRegistry::find_node
//       Access: Published
//  Description: Returns the index number of the indicated NodePath in
//               the registry (assuming its name hasn't changed since
//               it was recorded in the registry), or -1 if the
//               NodePath cannot be found (for instance, because its
//               name has changed).
////////////////////////////////////////////////////////////////////
int AttribNodeRegistry::
find_node(const NodePath &attrib_node) const {
  nassertr(!attrib_node.is_empty(), -1);
  MutexHolder holder(_lock);
  Entries::const_iterator ei = _entries.find(Entry(attrib_node));
  if (ei != _entries.end()) {
    return ei - _entries.begin();
  }
  return -1;
}

////////////////////////////////////////////////////////////////////
//     Function: AttribNodeRegistry::find_node
//       Access: Published
//  Description: Returns the index number of the node with the
//               indicated type and name in the registry, or -1 if
//               there is no such node in the registry.
////////////////////////////////////////////////////////////////////
int AttribNodeRegistry::
find_node(TypeHandle type, const string &name) const {
  MutexHolder holder(_lock);
  Entries::const_iterator ei = _entries.find(Entry(type, name));
  if (ei != _entries.end()) {
    return ei - _entries.begin();
  }
  return -1;
}

////////////////////////////////////////////////////////////////////
//     Function: AttribNodeRegistry::remove_node
//       Access: Published
//  Description: Removes the nth node from the registry.
////////////////////////////////////////////////////////////////////
void AttribNodeRegistry::
remove_node(int n) {
  MutexHolder holder(_lock);
  nassertv(n >= 0 && n < (int)_entries.size());
  _entries.erase(_entries.begin() + n);
}

////////////////////////////////////////////////////////////////////
//     Function: AttribNodeRegistry::clear
//       Access: Published
//  Description: Removes all nodes from the registry.
////////////////////////////////////////////////////////////////////
void AttribNodeRegistry::
clear() {
  MutexHolder holder(_lock);
  _entries.clear();
}

////////////////////////////////////////////////////////////////////
//     Function: AttribNodeRegistry::output
//       Access: Published
//  Description: 
////////////////////////////////////////////////////////////////////
void AttribNodeRegistry::
output(ostream &out) const {
  MutexHolder holder(_lock);

  typedef pmap<TypeHandle, int> Counts;
  Counts counts;

  Entries::const_iterator ei;
  for (ei = _entries.begin(); ei != _entries.end(); ++ei) {
    TypeHandle type = (*ei)._type;
    Counts::iterator ci = counts.insert(Counts::value_type(type, 0)).first;
    ++((*ci).second);
  }

  out << _entries.size() << " entries";

  if (!counts.empty()) {
    Counts::iterator ci = counts.begin();
    out << " (" << (*ci).first << ":" << (*ci).second;
    ++ci;
    while (ci != counts.end()) {
      out << ", " << (*ci).first << ":" << (*ci).second;
      ++ci;
    }
    out << ")";
  }
}

////////////////////////////////////////////////////////////////////
//     Function: AttribNodeRegistry::write
//       Access: Published
//  Description: 
////////////////////////////////////////////////////////////////////
void AttribNodeRegistry::
write(ostream &out) const {
  MutexHolder holder(_lock);

  Entries::const_iterator ei;
  for (ei = _entries.begin(); ei != _entries.end(); ++ei) {
    const Entry &entry = (*ei);
    out << entry._type << ", \"" << entry._name << "\": " << entry._node
        << "\n";
  }
}

////////////////////////////////////////////////////////////////////
//     Function: AttribNodeRegistry::make_global_ptr
//       Access: Private, Static
//  Description: 
////////////////////////////////////////////////////////////////////
void AttribNodeRegistry::
make_global_ptr() {
  AttribNodeRegistry *ptr = new AttribNodeRegistry;
  void *result = AtomicAdjust::compare_and_exchange_ptr
    ((void * TVOLATILE &)_global_ptr, (void *)NULL, (void *)ptr);
  if (result != NULL) {
    // Someone else got there first.
    delete ptr;
  }
  assert(_global_ptr != (AttribNodeRegistry *)NULL);
}
