// Filename: intersectionBoundingVolume.cxx
// Created by:  drose (08Feb12)
//
////////////////////////////////////////////////////////////////////
//
// 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 "intersectionBoundingVolume.h"
#include "unionBoundingVolume.h"
#include "config_mathutil.h"
#include "dcast.h"

TypeHandle IntersectionBoundingVolume::_type_handle;

////////////////////////////////////////////////////////////////////
//     Function: IntersectionBoundingVolume::Copy Constructor
//       Access: Public
//  Description: 
////////////////////////////////////////////////////////////////////
IntersectionBoundingVolume::
IntersectionBoundingVolume(const IntersectionBoundingVolume &copy) :
  GeometricBoundingVolume(copy),
  _components(copy._components)
{
}

////////////////////////////////////////////////////////////////////
//     Function: IntersectionBoundingVolume::make_copy
//       Access: Public, Virtual
//  Description: 
////////////////////////////////////////////////////////////////////
BoundingVolume *IntersectionBoundingVolume::
make_copy() const {
  return new IntersectionBoundingVolume(*this);
}

////////////////////////////////////////////////////////////////////
//     Function: IntersectionBoundingVolume::get_approx_center
//       Access: Public, Virtual
//  Description: 
////////////////////////////////////////////////////////////////////
LPoint3 IntersectionBoundingVolume::
get_approx_center() const {
  nassertr(!is_empty(), LPoint3::zero());
  nassertr(!is_infinite(), LPoint3::zero());

  LPoint3 center = LPoint3::zero();
  for (Components::const_iterator ci = _components.begin();
       ci != _components.end();
       ++ci) {
    center += (*ci)->get_approx_center();
  }

  return center / (PN_stdfloat)_components.size();
}

////////////////////////////////////////////////////////////////////
//     Function: IntersectionBoundingVolume::xform
//       Access: Public, Virtual
//  Description: 
////////////////////////////////////////////////////////////////////
void IntersectionBoundingVolume::
xform(const LMatrix4 &mat) {
  nassertv(!mat.is_nan());

  for (Components::iterator ci = _components.begin();
       ci != _components.end();
       ++ci) {
    PT(GeometricBoundingVolume) copy = DCAST(GeometricBoundingVolume, (*ci)->make_copy());
    copy->xform(mat);
    (*ci) = copy;
  }
}

////////////////////////////////////////////////////////////////////
//     Function: IntersectionBoundingVolume::output
//       Access: Public, Virtual
//  Description: 
////////////////////////////////////////////////////////////////////
void IntersectionBoundingVolume::
output(ostream &out) const {
  if (is_empty()) {
    out << "intersection, empty";
  } else if (is_infinite()) {
    out << "intersection, infinite";
  } else {
    out << "intersection [";
    for (Components::const_iterator ci = _components.begin();
         ci != _components.end();
         ++ci) {
      out << " " << *(*ci);
    }
    out << " ]";
  }
}

////////////////////////////////////////////////////////////////////
//     Function: IntersectionBoundingVolume::write
//       Access: Public, Virtual
//  Description: 
////////////////////////////////////////////////////////////////////
void IntersectionBoundingVolume::
write(ostream &out, int indent_level) const {
  if (is_empty()) {
    indent(out, indent_level) << "intersection, empty\n";
  } else if (is_infinite()) {
    indent(out, indent_level) << "intersection, infinite\n";
  } else {
    indent(out, indent_level) << "intersection {\n";
    for (Components::const_iterator ci = _components.begin();
         ci != _components.end();
         ++ci) {
      (*ci)->write(out, indent_level + 2);
    }
    indent(out, indent_level) << "}\n";
  }
}

////////////////////////////////////////////////////////////////////
//     Function: IntersectionBoundingVolume::clear_components
//       Access: Published
//  Description: Removes all components from the volume.
////////////////////////////////////////////////////////////////////
void IntersectionBoundingVolume::
clear_components() {
  _components.clear();
  _flags = F_infinite;
}

////////////////////////////////////////////////////////////////////
//     Function: IntersectionBoundingVolume::add_component
//       Access: Published
//  Description: Adds a new component to the volume.  This does not
//               necessarily increase the total number of components
//               by one, and you may or may not be able to find this
//               component in the volume by a subsequent call to
//               get_component(); certain optimizations may prevent
//               the component from being added, or have other
//               unexpected effects on the total set of components.
////////////////////////////////////////////////////////////////////
void IntersectionBoundingVolume::
add_component(const GeometricBoundingVolume *component) {
  CPT(GeometricBoundingVolume) gbv;

  if (component->is_exact_type(UnionBoundingVolume::get_class_type())) {
    // Here's a special case.  We'll construct a new union that
    // includes only those components that have some intersection with
    // our existing components.  (No need to include the components
    // that have no intersection.)
    PT(UnionBoundingVolume) unionv = DCAST(UnionBoundingVolume, component->make_copy());
    unionv->filter_intersection(this);

    // Save the modified union in a PT() so it won't be destructed.
    gbv = unionv.p();

    if (unionv->get_num_components() == 1) {
      // If there's only one component left, use just that one.
      gbv = unionv->get_component(0);
    }

    component = gbv;
  }

  if (component->is_empty()) {
    _flags = F_empty;
    _components.clear();

  } else if (component->is_infinite() || is_empty()) {
    // No-op.

  } else if (component->is_exact_type(IntersectionBoundingVolume::get_class_type())) {
    // Another special case.  Just more components.
    const IntersectionBoundingVolume *other = DCAST(IntersectionBoundingVolume, component);
    for (Components::const_iterator ci = other->_components.begin();
         ci != other->_components.end();
         ++ci) {
      add_component(*ci);
    }
    
  } else {
    // The general case.
    size_t i = 0;
    while (i < _components.size()) {
      const GeometricBoundingVolume *existing = _components[i];
      ++i;

      int result = component->contains(existing);
      if ((result & IF_all) != 0) {
        // The existing component is entirely within this one; no need
        // to do anything with it.
        return;

      } else if (result == 0) {
        // No intersection between these components; we're now empty.
        _flags = F_empty;
        _components.clear();
        return;
      }

      result = existing->contains(component);
      if ((result & IF_all) != 0) {
        // This new component is entirely within an existing
        // component; no need to keep the existing one.
        --i;
        _components.erase(_components.begin() + i);

      } else if (result == 0) {
        // No intersection between these components; we're now empty.
        _flags = F_empty;
        _components.clear();
        return;
      }
    }

    _flags &= ~F_infinite;
    _components.push_back(component);
  }
}

////////////////////////////////////////////////////////////////////
//     Function: IntersectionBoundingVolume::extend_other
//       Access: Protected, Virtual
//  Description: 
////////////////////////////////////////////////////////////////////
bool IntersectionBoundingVolume::
extend_other(BoundingVolume *other) const {
  return other->extend_by_intersection(this);
}

////////////////////////////////////////////////////////////////////
//     Function: IntersectionBoundingVolume::around_other
//       Access: Protected, Virtual
//  Description: 
////////////////////////////////////////////////////////////////////
bool IntersectionBoundingVolume::
around_other(BoundingVolume *other,
             const BoundingVolume **first,
             const BoundingVolume **last) const {
  return other->around_intersections(first, last);
}

////////////////////////////////////////////////////////////////////
//     Function: IntersectionBoundingVolume::contains_other
//       Access: Protected, Virtual
//  Description: 
////////////////////////////////////////////////////////////////////
int IntersectionBoundingVolume::
contains_other(const BoundingVolume *other) const {
  return other->contains_intersection(this);
}

////////////////////////////////////////////////////////////////////
//     Function: IntersectionBoundingVolume::contains_point
//       Access: Protected, Virtual
//  Description: 
////////////////////////////////////////////////////////////////////
int IntersectionBoundingVolume::
contains_point(const LPoint3 &point) const {
  nassertr(!point.is_nan(), IF_no_intersection);

  int result = IF_possible | IF_some | IF_all;
  for (Components::const_iterator ci = _components.begin();
       ci != _components.end();
       ++ci) {
    int this_result = (*ci)->contains(point);
    if ((this_result & IF_dont_understand) != 0) {
      result |= IF_dont_understand;
      break;
    }
    result &= this_result;
    if ((result & IF_possible) == 0) {
      // No point in looking further.
      break;
    }
  }

  return result;
}

////////////////////////////////////////////////////////////////////
//     Function: IntersectionBoundingVolume::contains_lineseg
//       Access: Protected, Virtual
//  Description: 
////////////////////////////////////////////////////////////////////
int IntersectionBoundingVolume::
contains_lineseg(const LPoint3 &a, const LPoint3 &b) const {
  nassertr(!a.is_nan() && !b.is_nan(), IF_no_intersection);

  int result = IF_possible | IF_some | IF_all;
  for (Components::const_iterator ci = _components.begin();
       ci != _components.end();
       ++ci) {
    int this_result = (*ci)->contains(a, b);
    if ((this_result & IF_dont_understand) != 0) {
      result |= IF_dont_understand;
      break;
    }
    result &= this_result;
    if ((result & IF_possible) == 0) {
      // No point in looking further.
      break;
    }
  }

  return result;
}

////////////////////////////////////////////////////////////////////
//     Function: IntersectionBoundingVolume::contains_sphere
//       Access: Protected, Virtual
//  Description: Double-dispatch support: called by contains_other()
//               when the type we're testing for intersection is known
//               to be a sphere.
////////////////////////////////////////////////////////////////////
int IntersectionBoundingVolume::
contains_sphere(const BoundingSphere *sphere) const {
  int result = IF_possible | IF_some | IF_all;
  for (Components::const_iterator ci = _components.begin();
       ci != _components.end();
       ++ci) {
    int this_result = (*ci)->contains_sphere(sphere);
    if ((this_result & IF_dont_understand) != 0) {
      result |= IF_dont_understand;
      break;
    }
    result &= this_result;
    if ((result & IF_possible) == 0) {
      // No point in looking further.
      break;
    }
  }

  return result;
}

////////////////////////////////////////////////////////////////////
//     Function: IntersectionBoundingVolume::contains_box
//       Access: Protected, Virtual
//  Description: Double-dispatch support: called by contains_other()
//               when the type we're testing for intersection is known
//               to be a box.
////////////////////////////////////////////////////////////////////
int IntersectionBoundingVolume::
contains_box(const BoundingBox *box) const {
  int result = IF_possible | IF_some | IF_all;
  for (Components::const_iterator ci = _components.begin();
       ci != _components.end();
       ++ci) {
    int this_result = (*ci)->contains_box(box);
    if ((this_result & IF_dont_understand) != 0) {
      result |= IF_dont_understand;
      break;
    }
    result &= this_result;
    if ((result & IF_possible) == 0) {
      // No point in looking further.
      break;
    }
  }

  return result;
}

////////////////////////////////////////////////////////////////////
//     Function: IntersectionBoundingVolume::contains_hexahedron
//       Access: Protected, Virtual
//  Description: Double-dispatch support: called by contains_other()
//               when the type we're testing for intersection is known
//               to be a hexahedron.
////////////////////////////////////////////////////////////////////
int IntersectionBoundingVolume::
contains_hexahedron(const BoundingHexahedron *hexahedron) const {
  int result = IF_possible | IF_some | IF_all;
  for (Components::const_iterator ci = _components.begin();
       ci != _components.end();
       ++ci) {
    int this_result = (*ci)->contains_hexahedron(hexahedron);
    if ((this_result & IF_dont_understand) != 0) {
      result |= IF_dont_understand;
      break;
    }
    result &= this_result;
    if ((result & IF_possible) == 0) {
      // No point in looking further.
      break;
    }
  }

  return result;
}

////////////////////////////////////////////////////////////////////
//     Function: IntersectionBoundingVolume::contains_line
//       Access: Protected, Virtual
//  Description: Double-dispatch support: called by contains_other()
//               when the type we're testing for intersection is known
//               to be a line.
////////////////////////////////////////////////////////////////////
int IntersectionBoundingVolume::
contains_line(const BoundingLine *line) const {
  int result = IF_possible | IF_some | IF_all;
  for (Components::const_iterator ci = _components.begin();
       ci != _components.end();
       ++ci) {
    int this_result = (*ci)->contains_line(line);
    if ((this_result & IF_dont_understand) != 0) {
      result |= IF_dont_understand;
      break;
    }
    result &= this_result;
    if ((result & IF_possible) == 0) {
      // No point in looking further.
      break;
    }
  }

  return result;
}

////////////////////////////////////////////////////////////////////
//     Function: IntersectionBoundingVolume::contains_plane
//       Access: Protected, Virtual
//  Description: Double-dispatch support: called by contains_other()
//               when the type we're testing for intersection is known
//               to be a plane.
////////////////////////////////////////////////////////////////////
int IntersectionBoundingVolume::
contains_plane(const BoundingPlane *plane) const {
  int result = IF_possible | IF_some | IF_all;
  for (Components::const_iterator ci = _components.begin();
       ci != _components.end();
       ++ci) {
    int this_result = (*ci)->contains_plane(plane);
    if ((this_result & IF_dont_understand) != 0) {
      result |= IF_dont_understand;
      break;
    }
    result &= this_result;
    if ((result & IF_possible) == 0) {
      // No point in looking further.
      break;
    }
  }

  return result;
}

////////////////////////////////////////////////////////////////////
//     Function: IntersectionBoundingVolume::contains_union
//       Access: Protected, Virtual
//  Description: Double-dispatch support: called by contains_other()
//               when the type we're testing for intersection is known
//               to be a union object.
////////////////////////////////////////////////////////////////////
int IntersectionBoundingVolume::
contains_union(const UnionBoundingVolume *unionv) const {
  int result = IF_possible | IF_some | IF_all;
  for (Components::const_iterator ci = _components.begin();
       ci != _components.end();
       ++ci) {
    int this_result = (*ci)->contains_union(unionv);
    if ((this_result & IF_dont_understand) != 0) {
      result |= IF_dont_understand;
      break;
    }
    result &= this_result;
    if ((result & IF_possible) == 0) {
      // No point in looking further.
      break;
    }
  }

  return result;
}

////////////////////////////////////////////////////////////////////
//     Function: IntersectionBoundingVolume::contains_intersection
//       Access: Protected, Virtual
//  Description: Double-dispatch support: called by contains_other()
//               when the type we're testing for intersection is known
//               to be an intersection object.
////////////////////////////////////////////////////////////////////
int IntersectionBoundingVolume::
contains_intersection(const IntersectionBoundingVolume *intersection) const {
  int result = IF_possible | IF_some | IF_all;
  for (Components::const_iterator ci = _components.begin();
       ci != _components.end();
       ++ci) {
    int this_result = (*ci)->contains_intersection(intersection);
    if ((this_result & IF_dont_understand) != 0) {
      result |= IF_dont_understand;
      break;
    }
    result &= this_result;
    if ((result & IF_possible) == 0) {
      // No point in looking further.
      break;
    }
  }

  return result;
}

////////////////////////////////////////////////////////////////////
//     Function: IntersectionBoundingVolume::contains_finite
//       Access: Protected, Virtual
//  Description: Generic handler for a FiniteBoundingVolume.
////////////////////////////////////////////////////////////////////
int IntersectionBoundingVolume::
contains_finite(const FiniteBoundingVolume *volume) const {
  int result = IF_possible | IF_some | IF_all;
  for (Components::const_iterator ci = _components.begin();
       ci != _components.end();
       ++ci) {
    int this_result = (*ci)->contains_finite(volume);
    if ((this_result & IF_dont_understand) != 0) {
      result |= IF_dont_understand;
      break;
    }
    result &= this_result;
    if ((result & IF_possible) == 0) {
      // No point in looking further.
      break;
    }
  }

  return result;
}

////////////////////////////////////////////////////////////////////
//     Function: IntersectionBoundingVolume::contains_geometric
//       Access: Protected, Virtual
//  Description: Generic handler for a GeometricBoundingVolume.
////////////////////////////////////////////////////////////////////
int IntersectionBoundingVolume::
contains_geometric(const GeometricBoundingVolume *volume) const {
  int result = IF_possible | IF_some | IF_all;
  for (Components::const_iterator ci = _components.begin();
       ci != _components.end();
       ++ci) {
    int this_result = (*ci)->contains_geometric(volume);
    if ((this_result & IF_dont_understand) != 0) {
      result |= IF_dont_understand;
      break;
    }
    result &= this_result;
    if ((result & IF_possible) == 0) {
      // No point in looking further.
      break;
    }
  }

  return result;
}


////////////////////////////////////////////////////////////////////
//     Function: IntersectionBoundingVolume::other_contains_intersection
//       Access: Protected, Virtual
//  Description: Generic reverse-direction comparison.  Called by
//               BoundingVolumes that do not implement
//               contains_intersection() explicitly.  This returns the test
//               of whether the other volume contains this volume.
////////////////////////////////////////////////////////////////////
int IntersectionBoundingVolume::
other_contains_intersection(const BoundingVolume *volume) const {
  int result = IF_possible | IF_some | IF_all;
  for (Components::const_iterator ci = _components.begin();
       ci != _components.end();
       ++ci) {
    int this_result = volume->contains(*ci);
    if ((this_result & IF_dont_understand) != 0) {
      result |= IF_dont_understand;
      break;
    }
    result &= this_result;
    if ((result & IF_possible) == 0) {
      // No point in looking further.
      break;
    }
  }

  return result;
}

