// Filename: occluderEffect.cxx
// Created by:  drose (17Mar11)
//
////////////////////////////////////////////////////////////////////
//
// 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 "occluderEffect.h"
#include "pandaNode.h"
#include "graphicsStateGuardianBase.h"
#include "bamReader.h"
#include "bamWriter.h"
#include "datagram.h"
#include "datagramIterator.h"
#include "config_pgraph.h"
#include "attribNodeRegistry.h"

CPT(RenderEffect) OccluderEffect::_empty_effect;
TypeHandle OccluderEffect::_type_handle;

////////////////////////////////////////////////////////////////////
//     Function: OccluderEffect::make
//       Access: Published, Static
//  Description: Constructs a new OccluderEffect object that does
//               nothing.
////////////////////////////////////////////////////////////////////
CPT(RenderEffect) OccluderEffect::
make() {
  // We make it a special case and store a pointer to the empty effect
  // forever once we find it the first time, as an optimization.
  if (_empty_effect == (RenderEffect *)NULL) {
    _empty_effect = return_new(new OccluderEffect);
  }

  return _empty_effect;
}

////////////////////////////////////////////////////////////////////
//     Function: OccluderEffect::add_on_occluder
//       Access: Published
//  Description: Returns a new OccluderEffect, just like this one, but
//               with the indicated occluder added to the list of occluders
//               enabled by this effect.
////////////////////////////////////////////////////////////////////
CPT(RenderEffect) OccluderEffect::
add_on_occluder(const NodePath &occluder) const {
  nassertr(!occluder.is_empty() && occluder.node()->is_of_type(OccluderNode::get_class_type()), this);
  OccluderEffect *effect = new OccluderEffect(*this);
  effect->_on_occluders.insert(occluder);

  pair<Occluders::iterator, bool> insert_result = 
    effect->_on_occluders.insert(Occluders::value_type(occluder));

  return return_new(effect);
}

////////////////////////////////////////////////////////////////////
//     Function: OccluderEffect::remove_on_occluder
//       Access: Published
//  Description: Returns a new OccluderEffect, just like this one, but
//               with the indicated occluder removed from the list of
//               occluders enabled by this effect.
////////////////////////////////////////////////////////////////////
CPT(RenderEffect) OccluderEffect::
remove_on_occluder(const NodePath &occluder) const {
  nassertr(!occluder.is_empty() && occluder.node()->is_of_type(OccluderNode::get_class_type()), this);
  OccluderEffect *effect = new OccluderEffect(*this);
  effect->_on_occluders.erase(occluder);
  return return_new(effect);
}

////////////////////////////////////////////////////////////////////
//     Function: OccluderEffect::output
//       Access: Public, Virtual
//  Description: 
////////////////////////////////////////////////////////////////////
void OccluderEffect::
output(ostream &out) const {
  out << get_type() << ":";
  if (_on_occluders.empty()) {
    out << "identity";
  } else {
    out << "on";
    
    Occluders::const_iterator li;
    for (li = _on_occluders.begin(); li != _on_occluders.end(); ++li) {
      NodePath occluder = (*li);
      out << " " << occluder;
    }
  }
}

////////////////////////////////////////////////////////////////////
//     Function: OccluderEffect::compare_to_impl
//       Access: Protected, Virtual
//  Description: Intended to be overridden by derived OccluderEffect
//               types to return a unique number indicating whether
//               this OccluderEffect is equivalent to the other one.
//
//               This should return 0 if the two OccluderEffect
//               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 OccluderEffect
//               objects whose get_type() functions return the same.
////////////////////////////////////////////////////////////////////
int OccluderEffect::
compare_to_impl(const RenderEffect *other) const {
  const OccluderEffect *ta;
  DCAST_INTO_R(ta, other, 0);

  Occluders::const_iterator li = _on_occluders.begin();
  Occluders::const_iterator oli = ta->_on_occluders.begin();

  while (li != _on_occluders.end() && oli != ta->_on_occluders.end()) {
    NodePath occluder = (*li);
    NodePath other_occluder = (*oli);

    int compare = occluder.compare_to(other_occluder);
    if (compare != 0) {
      return compare;
    }

    ++li;
    ++oli;
  }

  if (li != _on_occluders.end()) {
    return 1;
  }
  if (oli != ta->_on_occluders.end()) {
    return -1;
  }
  
  return 0;
}

////////////////////////////////////////////////////////////////////
//     Function: OccluderEffect::register_with_read_factory
//       Access: Public, Static
//  Description: Tells the BamReader how to create objects of type
//               OccluderEffect.
////////////////////////////////////////////////////////////////////
void OccluderEffect::
register_with_read_factory() {
  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
}

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

  // write the number of on occluders
  dg.add_uint16(get_num_on_occluders());
  // write the on occluders pointers if any
  Occluders::const_iterator nti;
  for (nti = _on_occluders.begin(); nti != _on_occluders.end(); ++nti) {
    NodePath occluder = (*nti);
    manager->write_pointer(dg, occluder.node());
  }
}

////////////////////////////////////////////////////////////////////
//     Function: OccluderEffect::complete_pointers
//       Access: Public, Virtual
//  Description: Receives an array of pointers, one for each time
//               manager->read_pointer() was called in fillin().
//               Returns the number of pointers processed.
////////////////////////////////////////////////////////////////////
int OccluderEffect::
complete_pointers(TypedWritable **p_list, BamReader *manager) {
  int pi = RenderEffect::complete_pointers(p_list, manager);
  AttribNodeRegistry *areg = AttribNodeRegistry::get_global_ptr();

  Occluders::iterator ci;
  ci = _on_occluders.begin();
  while (ci != _on_occluders.end()) {
    PandaNode *node;
    DCAST_INTO_R(node, p_list[pi++], pi);

    int ni = areg->find_node(node->get_type(), node->get_name());
    if (ni != -1) {
      (*ci) = areg->get_node(ni);
    } else {
      (*ci) = NodePath(node);
    }
    ++ci;
  }
  _on_occluders.sort();

  return pi;
}

////////////////////////////////////////////////////////////////////
//     Function: OccluderEffect::require_fully_complete
//       Access: Public, Virtual
//  Description: Some objects require all of their nested pointers to
//               have been completed before the objects themselves can
//               be completed.  If this is the case, override this
//               method to return true, and be careful with circular
//               references (which would make the object unreadable
//               from a bam file).
////////////////////////////////////////////////////////////////////
bool OccluderEffect::
require_fully_complete() const {
  return true;
}

////////////////////////////////////////////////////////////////////
//     Function: OccluderEffect::make_from_bam
//       Access: Protected, Static
//  Description: This function is called by the BamReader's factory
//               when a new object of type OccluderEffect is encountered
//               in the Bam file.  It should create the OccluderEffect
//               and extract its information from the file.
////////////////////////////////////////////////////////////////////
TypedWritable *OccluderEffect::
make_from_bam(const FactoryParams &params) {
  OccluderEffect *effect = new OccluderEffect;
  DatagramIterator scan;
  BamReader *manager;

  parse_params(params, scan, manager);
  effect->fillin(scan, manager);

  return effect;
}

////////////////////////////////////////////////////////////////////
//     Function: OccluderEffect::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 OccluderEffect.
////////////////////////////////////////////////////////////////////
void OccluderEffect::
fillin(DatagramIterator &scan, BamReader *manager) {
  RenderEffect::fillin(scan, manager);
    
  // Push back an empty NodePath for each Occluder for now, until we
  // get the actual list of pointers later in complete_pointers().
  int num_on_occluders = scan.get_uint16();
  _on_occluders.reserve(num_on_occluders);
  for (int i = 0; i < num_on_occluders; i++) {
    manager->read_pointer(scan);
    _on_occluders.push_back(NodePath());
  }
}
