/////////////////////////////////////////////////////////////
//                                                         //
// Copyright (c) 2007-2017 by The University of Queensland //
// Centre for Geoscience Computing                         //
// http://earth.uq.edu.au/centre-geoscience-computing      //
//                                                         //
// Primary Business: Brisbane, Queensland, Australia       //
// Licensed under the Open Software License version 3.0    //
// http://www.apache.org/licenses/LICENSE-2.0              //
//                                                         //
/////////////////////////////////////////////////////////////

#include "UnionVol.h"

//--- 
#include <cmath>
#include <cstdlib>

using std::cos;
using std::sin; 

// --- STL includes ---
#include <utility>

using std::make_pair;

UnionVol::UnionVol()
{
  std::cout << "WARNING: UnionVol is an experimental feature and may not always work as expected. For details see doc/CSG.readme" << std::endl;
  m_vol1 = NULL; 
  m_vol2 = NULL; 
}

UnionVol::UnionVol(AVolume3D& v1, AVolume3D& v2)
{
  std::cout << "WARNING: UnionVol is an experimental feature and may not always work as expected. For details see doc/CSG.readme" << std::endl;
  m_vol1 = &v1;
  m_vol2 = &v2;

  //std::cout << m_vol1 << std::endl;
  //std::cout << m_vol2 << std::endl;
}

pair<Vector3,Vector3> UnionVol::getBoundingBox()
{
  // BoundingBox is the box containing both volumes

  pair<Vector3,Vector3> bbx1=m_vol1->getBoundingBox();
  //std::cout << "bbx1 = (" << bbx1.first.X() << " , " << bbx1.first.Y() << " , " << bbx1.first.Z() << ") ; (" << bbx1.second.X() << " , " << bbx1.second.Y() << " , " << bbx1.second.Z() << ")" << std::endl;

  pair<Vector3,Vector3> bbx2=m_vol2->getBoundingBox();
  //std::cout << "bbx2 = (" << bbx2.first.X() << " , " << bbx2.first.Y() << " , " << bbx2.first.Z() << ") ; (" << bbx2.second.X() << " , " << bbx2.second.Y() << " , " << bbx2.second.Z() << ")" << std::endl;

  double X0,X1;
  double Y0,Y1;
  double Z0,Z1;

  if (bbx1.first.X() < bbx2.first.X()) {
    X0 = bbx1.first.X();
  }
  else {
    X0 = bbx2.first.X();
  }
  if (bbx1.first.Y() < bbx2.first.Y()) {
    Y0 = bbx1.first.Y();
  }
  else {
    Y0 = bbx2.first.Y();
  }
  if (bbx1.first.Z() < bbx2.first.Z()) {
    Z0 = bbx1.first.Z();
  }
  else {
    Z0 = bbx2.first.Z();
  }
  Vector3 minPt = Vector3(X0,Y0,Z0);

  if (bbx1.second.X() > bbx2.second.X()) {
    X1 = bbx1.second.X();
  }
  else {
    X1 = bbx2.second.X();
  }
  if (bbx1.second.Y() > bbx2.second.Y()) {
    Y1 = bbx1.second.Y();
  }
  else {
    Y1 = bbx2.second.Y();
  }
  if (bbx1.second.Z() > bbx2.second.Z()) {
    Z1 = bbx1.second.Z();
  }
  else {
    Z1 = bbx2.second.Z();
  }
  Vector3 maxPt = Vector3(X1,Y1,Z1);

  //std::cout << "minPt = (" << X0 << " , " << Y0 << " , " << Z0 << ")" << std::endl;
  //std::cout << "maxPt = (" << X1 << " , " << Y1 << " , " << Z1 << ")" << std::endl;

  return make_pair(minPt,maxPt);
}

Vector3 UnionVol::getAPoint(int ivol) const
{
  double r=((double)(rand())/(double)(RAND_MAX));
  Vector3 aPoint;

  if (r < 0.5) {
    //if (ivol == 0) {
     aPoint = m_vol1->getAPoint(ivol);
  }
  else {
     aPoint = m_vol2->getAPoint(ivol);
  }

  return aPoint;
}

const map<double,const AGeometricObject*> UnionVol::getClosestObjects(const Vector3& P,int ival) const
{
  map<double,const AGeometricObject*> res;
  map<double,const AGeometricObject*> res2;

  if (!m_vol2->isIn(P)) {
    res = m_vol1->getClosestObjects(P,ival);
  } 
  if (!m_vol1->isIn(P)) {
    res2 = m_vol2->getClosestObjects(P,ival);
  }

  res.insert(res2.begin(), res2.end());

  return res;  
}

bool UnionVol::isIn(const Vector3& P) const
{
  bool res = false;

  if ((m_vol1->isIn(P)) || (m_vol2->isIn(P))) {
    res = true;
  }

  return res;
}

bool UnionVol::isIn(const Sphere& S)
{
  bool res = false;

  if ((m_vol1->isIn(S)) || (m_vol2->isIn(S))) {
    res = true;
  }

  return res;
}

/*!
  Check if sphere is fully outside the volume. Tests if the sphere
  is outside both subvolumes.

  \param S the sphere
*/
bool UnionVol::isFullyOutside(const Sphere& S)
{
  return m_vol1->isFullyOutside(S) && m_vol2->isFullyOutside(S);
}

ostream& operator << (ostream& ost,const UnionVol& T)
{
   return ost;
}
