// Filename: renderEffect.cxx
// Created by:  drose (14Mar02)
//
////////////////////////////////////////////////////////////////////
//
// 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 "renderEffect.h"
#include "bamReader.h"
#include "indent.h"
#include "config_pgraph.h"

RenderEffect::Effects *RenderEffect::_effects = NULL;
TypeHandle RenderEffect::_type_handle;

////////////////////////////////////////////////////////////////////
//     Function: RenderEffect::Constructor
//       Access: Protected
//  Description:
////////////////////////////////////////////////////////////////////
RenderEffect::
RenderEffect() {
  if (_effects == (Effects *)NULL) {
    // Make sure the global _effects map is allocated.  This only has
    // to be done once.  We could make this map static, but then we
    // run into problems if anyone creates a RenderState object at
    // static init time; it also seems to cause problems when the
    // Panda shared library is unloaded at application exit time.
    _effects = new Effects;
  }
  _saved_entry = _effects->end();
}

////////////////////////////////////////////////////////////////////
//     Function: RenderEffect::Copy Constructor
//       Access: Private
//  Description: RenderEffects are not meant to be copied.
////////////////////////////////////////////////////////////////////
RenderEffect::
RenderEffect(const RenderEffect &) {
  nassertv(false);
}

////////////////////////////////////////////////////////////////////
//     Function: RenderEffect::Copy Assignment Operator
//       Access: Private
//  Description: RenderEffects are not meant to be copied.
////////////////////////////////////////////////////////////////////
void RenderEffect::
operator = (const RenderEffect &) {
  nassertv(false);
}

////////////////////////////////////////////////////////////////////
//     Function: RenderEffect::Destructor
//       Access: Public, Virtual
//  Description: The destructor is responsible for removing the
//               RenderEffect from the global set if it is there.
////////////////////////////////////////////////////////////////////
RenderEffect::
~RenderEffect() {
  if (_saved_entry != _effects->end()) {
    // We cannot make this assertion, because the RenderEffect has
    // already partially destructed--this means we cannot look up the
    // object in the map.  In fact, the map is temporarily invalid
    // until we finish destructing, since we screwed up the ordering
    // when we changed the return value of get_type().
    //    nassertv(_effects->find(this) == _saved_entry);

    // Note: this isn't thread-safe, because once the derived class
    // destructor exits and before this destructor completes, the map
    // is invalid, and other threads may inadvertently attempt to read
    // the invalid map.  To make it thread-safe, we need to move this
    // functionality to a separate method, that is to be called from
    // *each* derived class's destructor (and then we can put the
    // above assert back in).
    _effects->erase(_saved_entry);
    _saved_entry = _effects->end();
  }
}

////////////////////////////////////////////////////////////////////
//     Function: RenderEffect::safe_to_transform
//       Access: Public, Virtual
//  Description: Returns true if it is generally safe to transform
//               this particular kind of RenderEffect by calling the
//               xform() method, false otherwise.
////////////////////////////////////////////////////////////////////
bool RenderEffect::
safe_to_transform() const {
  return true;
}

////////////////////////////////////////////////////////////////////
//     Function: RenderEffect::prepare_flatten_transform
//       Access: Public, Virtual
//  Description: Preprocesses the accumulated transform that is about
//               to be applied to (or through) this node due to a
//               flatten operation.  The returned value will be used
//               instead.
////////////////////////////////////////////////////////////////////
CPT(TransformState) RenderEffect::
prepare_flatten_transform(const TransformState *net_transform) const {
  return net_transform;
}

////////////////////////////////////////////////////////////////////
//     Function: RenderEffect::safe_to_combine
//       Access: Public, Virtual
//  Description: Returns true if this kind of effect can safely be
//               combined with sibling nodes that share the exact same
//               effect, or false if this is not a good idea.
////////////////////////////////////////////////////////////////////
bool RenderEffect::
safe_to_combine() const {
  return true;
}

////////////////////////////////////////////////////////////////////
//     Function: RenderEffect::xform
//       Access: Public, Virtual
//  Description: Returns a new RenderEffect transformed by the
//               indicated matrix.
////////////////////////////////////////////////////////////////////
CPT(RenderEffect) RenderEffect::
xform(const LMatrix4 &) const {
  return this;
}

////////////////////////////////////////////////////////////////////
//     Function: RenderEffect::has_cull_callback
//       Access: Public, Virtual
//  Description: Should be overridden by derived classes to return
//               true if cull_callback() has been defined.  Otherwise,
//               returns false to indicate cull_callback() does not
//               need to be called for this effect during the cull
//               traversal.
////////////////////////////////////////////////////////////////////
bool RenderEffect::
has_cull_callback() const {
  return false;
}

////////////////////////////////////////////////////////////////////
//     Function: RenderEffect::cull_callback
//       Access: Public, Virtual
//  Description: If has_cull_callback() returns true, 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.
//
//               At the time this function is called, the current
//               node's transform and state have not yet been applied
//               to the net_transform and net_state.  This callback
//               may modify the node_transform and node_state to apply
//               an effective change to the render state at this
//               level.
////////////////////////////////////////////////////////////////////
void RenderEffect::
cull_callback(CullTraverser *, CullTraverserData &,
              CPT(TransformState) &, CPT(RenderState) &) const {
}

////////////////////////////////////////////////////////////////////
//     Function: RenderEffect::has_adjust_transform
//       Access: Public, Virtual
//  Description: Should be overridden by derived classes to return
//               true if adjust_transform() has been defined, and
//               therefore the RenderEffect has some effect on the
//               node's apparent local and net transforms.
////////////////////////////////////////////////////////////////////
bool RenderEffect::
has_adjust_transform() const {
  return false;
}

////////////////////////////////////////////////////////////////////
//     Function: RenderEffect::adjust_transform
//       Access: Public, Virtual
//  Description: Performs some operation on the node's apparent net
//               and/or local transforms.  This will only be called if
//               has_adjust_transform() is redefined to return true.
//
//               Both parameters are in/out.  The original transforms
//               will be passed in, and they may (or may not) be
//               modified in-place by the RenderEffect.
////////////////////////////////////////////////////////////////////
void RenderEffect::
adjust_transform(CPT(TransformState) &, CPT(TransformState) &,
                 PandaNode *) const {
}

////////////////////////////////////////////////////////////////////
//     Function: RenderEffect::output
//       Access: Published, Virtual
//  Description: 
////////////////////////////////////////////////////////////////////
void RenderEffect::
output(ostream &out) const {
  out << get_type();
}

////////////////////////////////////////////////////////////////////
//     Function: RenderEffect::write
//       Access: Published, Virtual
//  Description: 
////////////////////////////////////////////////////////////////////
void RenderEffect::
write(ostream &out, int indent_level) const {
  indent(out, indent_level) << *this << "\n";
}

////////////////////////////////////////////////////////////////////
//     Function: RenderEffect::get_num_effects
//       Access: Published, Static
//  Description: Returns the total number of unique RenderEffect
//               objects allocated in the world.  This will go up and
//               down during normal operations.
////////////////////////////////////////////////////////////////////
int RenderEffect::
get_num_effects() {
  if (_effects == (Effects *)NULL) {
    return 0;
  }
  return _effects->size();
}

////////////////////////////////////////////////////////////////////
//     Function: RenderEffect::list_effects
//       Access: Published, Static
//  Description: Lists all of the RenderEffects in the cache to the
//               output stream, one per line.  This can be quite a lot
//               of output if the cache is large, so be prepared.
////////////////////////////////////////////////////////////////////
void RenderEffect::
list_effects(ostream &out) {
  out << _effects->size() << " effects:\n";
  Effects::const_iterator si;
  for (si = _effects->begin(); si != _effects->end(); ++si) {
    const RenderEffect *effect = (*si);
    effect->write(out, 2);
  }
}

////////////////////////////////////////////////////////////////////
//     Function: RenderEffect::validate_effects
//       Access: Published, Static
//  Description: Ensures that the cache is still stored in sorted
//               order.  Returns true if so, false if there is a
//               problem (which implies someone has modified one of
//               the supposedly-const RenderEffect objects).
////////////////////////////////////////////////////////////////////
bool RenderEffect::
validate_effects() {
  if (_effects->empty()) {
    return true;
  }

  Effects::const_iterator si = _effects->begin();
  Effects::const_iterator snext = si;
  ++snext;
  while (snext != _effects->end()) {
    if ((*si)->compare_to(*(*snext)) >= 0) {
      pgraph_cat.error()
        << "RenderEffects out of order!\n";
      (*si)->write(pgraph_cat.error(false), 2);
      (*snext)->write(pgraph_cat.error(false), 2);
      return false;
    }
    si = snext;
    ++snext;
  }

  return true;
}

////////////////////////////////////////////////////////////////////
//     Function: RenderEffect::return_new
//       Access: Protected, Static
//  Description: This function is used by derived RenderEffect types
//               to share a common RenderEffect pointer for all
//               equivalent RenderEffect objects.
//
//               The make() function of the derived type should create
//               a new RenderEffect and pass it through return_new(),
//               which will either save the pointer and return it
//               unchanged (if this is the first similar such object)
//               or delete it and return an equivalent pointer (if
//               there was already a similar object saved).
////////////////////////////////////////////////////////////////////
CPT(RenderEffect) RenderEffect::
return_new(RenderEffect *effect) {
  nassertr(effect != (RenderEffect *)NULL, effect);

  // This should be a newly allocated pointer, not one that was used
  // for anything else.
  nassertr(effect->_saved_entry == _effects->end(), effect);

#ifndef NDEBUG
  if (paranoid_const) {
    nassertr(validate_effects(), effect);
  }
#endif

  // Save the effect in a local PointerTo so that it will be freed at
  // the end of this function if no one else uses it.
  CPT(RenderEffect) pt_effect = effect;

  pair<Effects::iterator, bool> result = _effects->insert(effect);
  if (result.second) {
    // The effect was inserted; save the iterator and return the
    // input effect.
    effect->_saved_entry = result.first;
    return pt_effect;
  }

  // The effect was not inserted; there must be an equivalent one
  // already in the set.  Return that one.
  return *(result.first);
}

////////////////////////////////////////////////////////////////////
//     Function: RenderEffect::compare_to_impl
//       Access: Protected, Virtual
//  Description: Intended to be overridden by derived RenderEffect
//               types to return a unique number indicating whether
//               this RenderEffect is equivalent to the other one.
//
//               This should return 0 if the two RenderEffect objects
//               are equivalent, a number less than zero if this one
//               should be sorted before the other one, and a number
//               greater than zero otherwise.
//
//               This will only be called with two RenderEffect
//               objects whose get_type() functions return the same.
////////////////////////////////////////////////////////////////////
int RenderEffect::
compare_to_impl(const RenderEffect *other) const {
  return 0;
}

////////////////////////////////////////////////////////////////////
//     Function: RenderEffect::write_datagram
//       Access: Public, Virtual
//  Description: Writes the contents of this object to the datagram
//               for shipping out to a Bam file.
////////////////////////////////////////////////////////////////////
void RenderEffect::
write_datagram(BamWriter *manager, Datagram &dg) {
  TypedWritable::write_datagram(manager, dg);
}

////////////////////////////////////////////////////////////////////
//     Function: RenderEffect::change_this
//       Access: Public, Static
//  Description: Called immediately after complete_pointers(), this
//               gives the object a chance to adjust its own pointer
//               if desired.  Most objects don't change pointers after
//               completion, but some need to.
//
//               Once this function has been called, the old pointer
//               will no longer be accessed.
////////////////////////////////////////////////////////////////////
TypedWritable *RenderEffect::
change_this(TypedWritable *old_ptr, BamReader *manager) {
  // First, uniquify the pointer.
  RenderEffect *effect = DCAST(RenderEffect, old_ptr);
  CPT(RenderEffect) pointer = return_new(effect);

  // But now we have a problem, since we have to hold the reference
  // count and there's no way to return a TypedWritable while still
  // holding the reference count!  We work around this by explicitly
  // upping the count, and also setting a finalize() callback to down
  // it later.
  if (pointer == effect) {
    pointer->ref();
    manager->register_finalize(effect);
  }
  
  // We have to cast the pointer back to non-const, because the bam
  // reader expects that.
  return (RenderEffect *)pointer.p();
}

////////////////////////////////////////////////////////////////////
//     Function: RenderEffect::finalize
//       Access: Public, Virtual
//  Description: Called by the BamReader to perform any final actions
//               needed for setting up the object after all objects
//               have been read and all pointers have been completed.
////////////////////////////////////////////////////////////////////
void RenderEffect::
finalize(BamReader *) {
  // Unref the pointer that we explicitly reffed in change_this().
  unref();

  // We should never get back to zero after unreffing our own count,
  // because we expect to have been stored in a pointer somewhere.  If
  // we do get to zero, it's a memory leak; the way to avoid this is
  // to call unref_delete() above instead of unref(), but this is
  // dangerous to do from within a virtual function.
  nassertv(get_ref_count() != 0);
}

////////////////////////////////////////////////////////////////////
//     Function: RenderEffect::fillin
//       Access: Protected
//  Description: This internal function is called by make_from_bam to
//               read in all of the relevant data from the BamFile for
//               the new RenderEffect.
////////////////////////////////////////////////////////////////////
void RenderEffect::
fillin(DatagramIterator &scan, BamReader *manager) {
  TypedWritable::fillin(scan, manager);
}
