# include "MeshVolume2D.h"

//-- system includes --
#include <cstdlib>

using std::rand;

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

// --- I/O includes
#include <iostream>

using std::make_pair;

/*!
  helper function, return random value between min & max
*/
double MeshVolume2D::m_random(double imin,double imax) const
{
  return imin+((imax-imin)*((double)(rand())/(double)(RAND_MAX)));
}


/*!
 empty contructor - does nothing
*/
MeshVolume2D::MeshVolume2D()
{}

/*!
  Constructor

  \param L the line set enclosing the volume
*/
MeshVolume2D::MeshVolume2D(const LineSet& L)
{
	m_mesh=L;
	m_MinPoint=m_mesh.getBoundingBox().first;
	m_MaxPoint=m_mesh.getBoundingBox().second;
	m_DistPoint=m_MinPoint-Vector3(1.0,1.0,1.0);
}

/*!
  get bounding box
*/
pair<Vector3,Vector3> MeshVolume2D::getBoundingBox()
{
  return m_mesh.getBoundingBox();
}

/*! 
  Get a point inside the volume. The argument is ignored.

  \warning if the mesh is incorrect the function might loop forever
*/
Vector3 MeshVolume2D::getAPoint(int) const

{
  Vector3 res;
  double px,py,pz;
 
  do{
    px=m_random(m_MinPoint.x(),m_MaxPoint.x());
    py=m_random(m_MinPoint.y(),m_MaxPoint.y());
    pz=0.0; // this is 2D
    res=Vector3(px,py,pz);
  } while (!isIn(res));

  return res;
}

/*!
  returns a map of the closest mesh facets and their distances to a given point

  \param P the point
  \param nmax the maximum number of objects to be returned
*/ 
const map<double,const AGeometricObject*> MeshVolume2D::getClosestObjects(const Vector3& P,int nmax) const
{
  map<double,const AGeometricObject*> res;

  for(vector<LineSegment2D>::const_iterator iter=m_mesh.segments_begin();
      iter!=m_mesh.segments_end();
      iter++){
    double ndist=iter->getDist(P);
    res.insert(make_pair(ndist,&(*iter)));
  }

  return res;  
}

/*!
  Check if a point is inside the volume.  Works by checking if a 
  line between the point and a point outside crosses
  an odd number of mesh facets.

  \param P the point
*/
bool MeshVolume2D::isIn(const Vector3& P) const
{
  int cross_count=0;
  for(vector<LineSegment2D>::const_iterator iter=m_mesh.segments_begin();
      iter!=m_mesh.segments_end();
      iter++){
    if(iter->crosses(P,m_DistPoint)) cross_count++;
  }
  return (cross_count%2)==1;
}

/*!
  Check if a sphere is fully inside the volume. Works by testing if center of the 
  sphere is inside and sphere doesn't intersect a mesh facet.

  \param S the sphere
*/
bool MeshVolume2D::isIn(const Sphere& S)
{
  bool res=isIn(S.Center());
  vector<LineSegment2D>::const_iterator iter=m_mesh.segments_begin();
  while((iter!=m_mesh.segments_end()) && res){
    res=(iter->getDist(S.Center())>S.Radius());
    iter++;
  }

  return res;
}

/*!
  Check if a sphere is fully outside the volume. Works by testing if center of the 
  sphere is not inside and sphere doesn't intersect a mesh facet.

  \param S the sphere
*/
bool MeshVolume2D::isFullyOutside(const Sphere& S)
{
  bool res=!isIn(S.Center());

  vector<LineSegment2D>::const_iterator iter=m_mesh.segments_begin();
  while((iter!=m_mesh.segments_end()) && res){
    res=(iter->getDist(S.Center())>S.Radius());
    iter++;
  }

  return res;
}

/*!
 always returns empty map
*/
const map<double, const Line2D*> MeshVolume2D::getClosestPlanes(const Vector3& p,int nmax) const
{
  map<double,const Line2D*> res;

  return res;
}

/*!
  Return closest plane to a given point. Assumes that the list of Planes/Lines is not empty.
  
  \param p the point
  \warning dummy implementation - returns Line (0,0,0)-(0,0,0)
*/
Line2D MeshVolume2D::getClosestPlane(const Vector3& p) 
{
  std::cout << "MeshVolume2D::getClosestPlane : " << p << std::endl;

  return Line2D();
}
