// Filename: characterJoint.cxx
// Created by:  drose (23Feb99)
//
////////////////////////////////////////////////////////////////////
//
// 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 "characterJoint.h"
#include "config_char.h"
#include "jointVertexTransform.h"
#include "characterJointEffect.h"
#include "datagram.h"
#include "datagramIterator.h"
#include "bamReader.h"
#include "bamWriter.h"

TypeHandle CharacterJoint::_type_handle;

////////////////////////////////////////////////////////////////////
//     Function: CharacterJoint::Default Constructor
//       Access: Protected
//  Description: For internal use only.
////////////////////////////////////////////////////////////////////
CharacterJoint::
CharacterJoint() :
  _character(NULL)
{
}

////////////////////////////////////////////////////////////////////
//     Function: CharacterJoint::Copy Constructor
//       Access: Protected
//  Description:
////////////////////////////////////////////////////////////////////
CharacterJoint::
CharacterJoint(const CharacterJoint &copy) :
  MovingPartMatrix(copy),
  _character(NULL),
  _net_transform(copy._net_transform),
  _initial_net_transform_inverse(copy._initial_net_transform_inverse)
{
  // We don't copy the sets of transform nodes.
}

////////////////////////////////////////////////////////////////////
//     Function: CharacterJoint::Constructor
//       Access: Public
//  Description:
////////////////////////////////////////////////////////////////////
CharacterJoint::
CharacterJoint(Character *character,
               PartBundle *root, PartGroup *parent, const string &name,
               const LMatrix4f &initial_value) :
  MovingPartMatrix(parent, name, initial_value),
  _character(character)
{
  Thread *current_thread = Thread::get_current_thread();

  // Now that we've constructed and we're in the tree, let's call
  // update_internals() to get our _net_transform set properly.
  update_internals(root, parent, true, false, current_thread);

  // And then compute its inverse.  This is needed for
  // ComputedVertices, during animation.
  _initial_net_transform_inverse = invert(_net_transform);
}

////////////////////////////////////////////////////////////////////
//     Function: CharacterJoint::Destructor
//       Access: Public, Virtual
//  Description:
////////////////////////////////////////////////////////////////////
CharacterJoint::
~CharacterJoint() {
  nassertv(_vertex_transforms.empty());
  nassertv(_character == (Character *)NULL);
}

////////////////////////////////////////////////////////////////////
//     Function: CharacterJoint::make_copy
//       Access: Public, Virtual
//  Description: Allocates and returns a new copy of the node.
//               Children are not copied, but see copy_subgraph().
////////////////////////////////////////////////////////////////////
PartGroup *CharacterJoint::
make_copy() const {
  return new CharacterJoint(*this);
}

////////////////////////////////////////////////////////////////////
//     Function: CharacterJoint::update_internals
//       Access: Public, Virtual
//  Description: This is called by do_update() whenever the part or
//               some ancestor has changed values.  It is a hook for
//               derived classes to update whatever cache they may
//               have that depends on these.
//
//               The return value is true if the part has changed as a
//               result of the update, or false otherwise.
//
//               In the case of a CharacterJoint, of course, it means
//               to recompute the joint angles and associated
//               transforms for this particular joint.
////////////////////////////////////////////////////////////////////
bool CharacterJoint::
update_internals(PartBundle *root, PartGroup *parent, bool self_changed, 
                 bool parent_changed, Thread *current_thread) {

  nassertr(parent != (PartGroup *)NULL, false);

  bool net_changed = false;
  if (parent->is_of_type(CharacterJoint::get_class_type())) {
    // The joint is not a toplevel joint; its parent therefore affects
    // its net transform.
    if (parent_changed || self_changed) {
      CharacterJoint *parent_joint = DCAST(CharacterJoint, parent);
      
      _net_transform = _value * parent_joint->_net_transform;
      net_changed = true;
    }

  } else {
    // The joint is a toplevel joint, so therefore it gets its root
    // transform from the bundle.
    if (self_changed) {
      _net_transform = _value * root->get_root_xform();
      net_changed = true;
    }
  }

  if (net_changed) {
    if (!_net_transform_nodes.empty()) {
      CPT(TransformState) t = TransformState::make_mat(_net_transform);
      
      NodeList::iterator ai;
      ai = _net_transform_nodes.begin();
      while (ai != _net_transform_nodes.end()) {
        PandaNode *node = *ai;
        node->set_transform(t, current_thread);
        ++ai;
      }
    }

    // Also tell our related JointVertexTransforms that they now need
    // to recompute themselves.
    VertexTransforms::iterator vti;
    for (vti = _vertex_transforms.begin(); vti != _vertex_transforms.end(); ++vti) {
      (*vti)->_matrix_stale = true;
      (*vti)->mark_modified(current_thread);
    }
  }

  if (self_changed && !_local_transform_nodes.empty()) {
    CPT(TransformState) t = TransformState::make_mat(_value);

    NodeList::iterator ai;
    ai = _local_transform_nodes.begin();
    while (ai != _local_transform_nodes.end()) {
      PandaNode *node = *ai;
      node->set_transform(t, current_thread);
      ++ai;
    }
  }

  return self_changed || net_changed;
}

////////////////////////////////////////////////////////////////////
//     Function: CharacterJoint::do_xform
//       Access: Public, Virtual
//  Description: Called by PartBundle::xform(), this indicates the
//               indicated transform is being applied to the root
//               joint.
////////////////////////////////////////////////////////////////////
void CharacterJoint::
do_xform(const LMatrix4f &mat, const LMatrix4f &inv_mat) {
  _initial_net_transform_inverse = inv_mat * _initial_net_transform_inverse;

  MovingPartMatrix::do_xform(mat, inv_mat);
}



////////////////////////////////////////////////////////////////////
//     Function: CharacterJoint::add_net_transform
//       Access: Published
//  Description: Adds the indicated node to the list of nodes that will
//               be updated each frame with the joint's net transform
//               from the root.  Returns true if the node is
//               successfully added, false if it had already been
//               added.
//
//               A CharacterJointEffect for this joint's Character
//               will automatically be added to the specified node.
////////////////////////////////////////////////////////////////////
bool CharacterJoint::
add_net_transform(PandaNode *node) {
  if (_character != (Character *)NULL) {
    node->set_effect(CharacterJointEffect::make(_character));
  }
  CPT(TransformState) t = TransformState::make_mat(_net_transform);
  node->set_transform(t, Thread::get_current_thread());
  return _net_transform_nodes.insert(node).second;
}

////////////////////////////////////////////////////////////////////
//     Function: CharacterJoint::remove_net_transform
//       Access: Published
//  Description: Removes the indicated node from the list of nodes that
//               will be updated each frame with the joint's net
//               transform from the root.  Returns true if the node is
//               successfully removed, false if it was not on the
//               list.
//
//               If the node has a CharacterJointEffect that matches
//               this joint's Character, it will be cleared.
////////////////////////////////////////////////////////////////////
bool CharacterJoint::
remove_net_transform(PandaNode *node) {
  CPT(RenderEffect) effect = node->get_effect(CharacterJointEffect::get_class_type());
  if (effect != (RenderEffect *)NULL &&
      DCAST(CharacterJointEffect, effect)->get_character() == _character) {
    node->clear_effect(CharacterJointEffect::get_class_type());
  }

  return (_net_transform_nodes.erase(node) > 0);
}

////////////////////////////////////////////////////////////////////
//     Function: CharacterJoint::has_net_transform
//       Access: Published
//  Description: Returns true if the node is on the list of nodes that
//               will be updated each frame with the joint's net
//               transform from the root, false otherwise.
////////////////////////////////////////////////////////////////////
bool CharacterJoint::
has_net_transform(PandaNode *node) const {
  return (_net_transform_nodes.count(node) > 0);
}

////////////////////////////////////////////////////////////////////
//     Function: CharacterJoint::clear_net_transforms
//       Access: Published
//  Description: Removes all nodes from the list of nodes that will be
//               updated each frame with the joint's net transform
//               from the root.
////////////////////////////////////////////////////////////////////
void CharacterJoint::
clear_net_transforms() {
  NodeList::iterator ai;
  for (ai = _net_transform_nodes.begin();
       ai != _net_transform_nodes.end();
       ++ai) {
    PandaNode *node = *ai;

    CPT(RenderEffect) effect = node->get_effect(CharacterJointEffect::get_class_type());
    if (effect != (RenderEffect *)NULL &&
        DCAST(CharacterJointEffect, effect)->get_character() == _character) {
      node->clear_effect(CharacterJointEffect::get_class_type());
    }
  }

  _net_transform_nodes.clear();
}

////////////////////////////////////////////////////////////////////
//     Function: CharacterJoint::get_net_transforms
//       Access: Published
//  Description: Returns a list of the net transforms set for this
//               node.  Note that this returns a list of NodePaths,
//               even though the net transforms are actually a list of
//               PandaNodes.
////////////////////////////////////////////////////////////////////
NodePathCollection CharacterJoint::
get_net_transforms() {
  NodePathCollection npc;

  NodeList::iterator ai;
  for (ai = _net_transform_nodes.begin();
       ai != _net_transform_nodes.end();
       ++ai) {
    PandaNode *node = *ai;
    npc.add_path(NodePath::any_path(node));
  }

  return npc;
}

////////////////////////////////////////////////////////////////////
//     Function: CharacterJoint::add_local_transform
//       Access: Published
//  Description: Adds the indicated node to the list of nodes that will
//               be updated each frame with the joint's local
//               transform from its parent.  Returns true if the node
//               is successfully added, false if it had already been
//               added.
//
//               The Character pointer should be the Character object
//               that owns this joint; this will be used to create a
//               CharacterJointEffect for this node.  If it is NULL,
//               no such effect will be created.
//
//               A CharacterJointEffect for this joint's Character
//               will automatically be added to the specified node.
////////////////////////////////////////////////////////////////////
bool CharacterJoint::
add_local_transform(PandaNode *node) {
  if (_character != (Character *)NULL) {
    node->set_effect(CharacterJointEffect::make(_character));
  }
  CPT(TransformState) t = TransformState::make_mat(_value);
  node->set_transform(t, Thread::get_current_thread());
  return _local_transform_nodes.insert(node).second;
}

////////////////////////////////////////////////////////////////////
//     Function: CharacterJoint::remove_local_transform
//       Access: Published
//  Description: Removes the indicated node from the list of nodes that
//               will be updated each frame with the joint's local
//               transform from its parent.  Returns true if the node
//               is successfully removed, false if it was not on the
//               list.
//
//               If the node has a CharacterJointEffect that matches
//               this joint's Character, it will be cleared.
////////////////////////////////////////////////////////////////////
bool CharacterJoint::
remove_local_transform(PandaNode *node) {
  CPT(RenderEffect) effect = node->get_effect(CharacterJointEffect::get_class_type());
  if (effect != (RenderEffect *)NULL &&
      DCAST(CharacterJointEffect, effect)->get_character() == _character) {
    node->clear_effect(CharacterJointEffect::get_class_type());
  }

  return (_local_transform_nodes.erase(node) > 0);
}

////////////////////////////////////////////////////////////////////
//     Function: CharacterJoint::has_local_transform
//       Access: Published
//  Description: Returns true if the node is on the list of nodes that
//               will be updated each frame with the joint's local
//               transform from its parent, false otherwise.
////////////////////////////////////////////////////////////////////
bool CharacterJoint::
has_local_transform(PandaNode *node) const {
  return (_local_transform_nodes.count(node) > 0);
}

////////////////////////////////////////////////////////////////////
//     Function: CharacterJoint::clear_local_transforms
//       Access: Published
//  Description: Removes all nodes from the list of nodes that will be
//               updated each frame with the joint's local transform
//               from its parent.
////////////////////////////////////////////////////////////////////
void CharacterJoint::
clear_local_transforms() {
  NodeList::iterator ai;
  for (ai = _local_transform_nodes.begin();
       ai != _local_transform_nodes.end();
       ++ai) {
    PandaNode *node = *ai;

    CPT(RenderEffect) effect = node->get_effect(CharacterJointEffect::get_class_type());
    if (effect != (RenderEffect *)NULL &&
        DCAST(CharacterJointEffect, effect)->get_character() == _character) {
      node->clear_effect(CharacterJointEffect::get_class_type());
    }
  }

  _local_transform_nodes.clear();
}

////////////////////////////////////////////////////////////////////
//     Function: CharacterJoint::get_local_transforms
//       Access: Published
//  Description: Returns a list of the local transforms set for this
//               node.  Note that this returns a list of NodePaths,
//               even though the local transforms are actually a list of
//               PandaNodes.
////////////////////////////////////////////////////////////////////
NodePathCollection CharacterJoint::
get_local_transforms() {
  NodePathCollection npc;

  NodeList::iterator ai;
  for (ai = _local_transform_nodes.begin();
       ai != _local_transform_nodes.end();
       ++ai) {
    PandaNode *node = *ai;
    npc.add_path(NodePath::any_path(node));
  }

  return npc;
}

////////////////////////////////////////////////////////////////////
//     Function: CharacterJoint::get_transform
//       Access: Published
//  Description: Copies the joint's current transform into the
//               indicated matrix.
////////////////////////////////////////////////////////////////////
void CharacterJoint::
get_transform(LMatrix4f &transform) const {
  transform = _value;
}

////////////////////////////////////////////////////////////////////
//     Function: CharacterJoint::get_net_transform
//       Access: Published
//  Description: Copies the joint's current net transform (composed
//               from the root of the character joint hierarchy) into
//               the indicated matrix.
////////////////////////////////////////////////////////////////////
void CharacterJoint::
get_net_transform(LMatrix4f &transform) const {
  transform = _net_transform;
}

////////////////////////////////////////////////////////////////////
//     Function: CharacterJoint::get_character
//       Access: Published
//  Description: Returns the Character that owns this joint.
////////////////////////////////////////////////////////////////////
Character *CharacterJoint::
get_character() const {
  return _character;
}

////////////////////////////////////////////////////////////////////
//     Function: CharacterJoint::set_character
//       Access: Private
//  Description: Changes the Character that owns this joint.
////////////////////////////////////////////////////////////////////
void CharacterJoint::
set_character(Character *character) {
  if (character != _character) {

    if (character != (Character *)NULL) {
      // Change or set a _character pointer on each joint's exposed
      // node.
      NodeList::iterator ai;
      for (ai = _net_transform_nodes.begin();
           ai != _net_transform_nodes.end();
           ++ai) {
        PandaNode *node = *ai;
        node->set_effect(CharacterJointEffect::make(character));
      }
      for (ai = _local_transform_nodes.begin();
           ai != _local_transform_nodes.end();
           ++ai) {
        PandaNode *node = *ai;
        node->set_effect(CharacterJointEffect::make(character));
      }

    } else {  // (character == (Character *)NULL)
      // Clear the _character pointer on each joint's exposed node.
      NodeList::iterator ai;
      for (ai = _net_transform_nodes.begin();
           ai != _net_transform_nodes.end();
           ++ai) {
        PandaNode *node = *ai;
        
        CPT(RenderEffect) effect = node->get_effect(CharacterJointEffect::get_class_type());
        if (effect != (RenderEffect *)NULL &&
            DCAST(CharacterJointEffect, effect)->get_character() == _character) {
          node->clear_effect(CharacterJointEffect::get_class_type());
        }
      }
      for (ai = _local_transform_nodes.begin();
           ai != _local_transform_nodes.end();
           ++ai) {
        PandaNode *node = *ai;
        
        CPT(RenderEffect) effect = node->get_effect(CharacterJointEffect::get_class_type());
        if (effect != (RenderEffect *)NULL &&
            DCAST(CharacterJointEffect, effect)->get_character() == _character) {
          node->clear_effect(CharacterJointEffect::get_class_type());
        }
      }
    }
  }
    
  _character = character;
}

////////////////////////////////////////////////////////////////////
//     Function: CharacterJoint::write_datagram
//       Access: Public
//  Description: Function to write the important information in
//               the particular object to a Datagram
////////////////////////////////////////////////////////////////////
void CharacterJoint::
write_datagram(BamWriter *manager, Datagram &me) {
  NodeList::iterator ni;
  MovingPartMatrix::write_datagram(manager, me);

  manager->write_pointer(me, _character);

  me.add_uint16(_net_transform_nodes.size());
  for (ni = _net_transform_nodes.begin(); 
       ni != _net_transform_nodes.end(); 
       ni++) {
    manager->write_pointer(me, (*ni));
  }

  me.add_uint16(_local_transform_nodes.size());
  for (ni = _local_transform_nodes.begin(); 
       ni != _local_transform_nodes.end(); 
       ni++) {
    manager->write_pointer(me, (*ni));
  }

  _initial_net_transform_inverse.write_datagram(me);
}

////////////////////////////////////////////////////////////////////
//     Function: CharacterJoint::fillin
//       Access: Protected
//  Description: Function that reads out of the datagram (or asks
//               manager to read) all of the data that is needed to
//               re-create this object and stores it in the appropiate
//               place
////////////////////////////////////////////////////////////////////
void CharacterJoint::
fillin(DatagramIterator &scan, BamReader *manager) {
  int i;
  MovingPartMatrix::fillin(scan, manager);

  if (manager->get_file_minor_ver() >= 4) {
    manager->read_pointer(scan);
  }

  _num_net_nodes = scan.get_uint16();
  for(i = 0; i < _num_net_nodes; i++) {
    manager->read_pointer(scan);
  }
  
  _num_local_nodes = scan.get_uint16();
  for(i = 0; i < _num_local_nodes; i++) {
    manager->read_pointer(scan);
  }

  _initial_net_transform_inverse.read_datagram(scan);
}

////////////////////////////////////////////////////////////////////
//     Function: CharacterJoint::complete_pointers
//       Access: Public
//  Description: Takes in a vector of pointers to TypedWritable
//               objects that correspond to all the requests for
//               pointers that this object made to BamReader.
////////////////////////////////////////////////////////////////////
int CharacterJoint::
complete_pointers(TypedWritable **p_list, BamReader* manager) {
  int pi = MovingPartMatrix::complete_pointers(p_list, manager);

  if (manager->get_file_minor_ver() >= 4) {
    _character = DCAST(Character, p_list[pi++]);
  } else {
    _character = NULL;
  }

  int i;
  for (i = 0; i < _num_net_nodes; i++) {
    PandaNode *node = DCAST(PandaNode, p_list[pi++]);
    _net_transform_nodes.insert(node);
  }

  for (i = 0; i < _num_local_nodes; i++) {
    PandaNode *node = DCAST(PandaNode, p_list[pi++]);
    _local_transform_nodes.insert(node);
  }

  return pi;
}

////////////////////////////////////////////////////////////////////
//     Function: CharacterJoint::make_CharacterJoint
//       Access: Protected
//  Description: Factory method to generate a CharacterJoint object
////////////////////////////////////////////////////////////////////
TypedWritable* CharacterJoint::
make_CharacterJoint(const FactoryParams &params) {
  CharacterJoint *me = new CharacterJoint;
  DatagramIterator scan;
  BamReader *manager;

  parse_params(params, scan, manager);
  me->fillin(scan, manager);
  return me;
}

////////////////////////////////////////////////////////////////////
//     Function: CharacterJoint::register_with_factory
//       Access: Public, Static
//  Description: Factory method to generate a CharacterJoint object
////////////////////////////////////////////////////////////////////
void CharacterJoint::
register_with_read_factory() {
  BamReader::get_factory()->register_factory(get_class_type(), make_CharacterJoint);
}


