/////////////////////////////////////////////////////////////
//                                                         //
// 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 "BoxWithLines2D.h"

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

using std::rand;

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

using std::make_pair;
using std::map;

//--- IO includes ---
#include <iostream>


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

BoxWithLines2D::BoxWithLines2D()
{}

/*!
  Construct box 

  \param pmin minimum point of bounding box
  \param pmax maximum point of bounding box
*/
BoxWithLines2D::BoxWithLines2D(const Vector3& pmin,const Vector3& pmax)
  : m_pmin(pmin),m_pmax(pmax)
{}

/*!
  add a line

  \param l the line
*/
void BoxWithLines2D::addLine(const Line2D& l)
{
  m_lines.push_back(l);
}

/*!
  get bounding box
*/
pair<Vector3,Vector3> BoxWithLines2D::getBoundingBox()
{
  return make_pair(m_pmin,m_pmax);
}
  
/*!
  get point inside the box. The Argument is ignored
*/
Vector3 BoxWithLines2D::getAPoint(int) const
{
  double px,py;
  
  px=m_random(m_pmin.x(),m_pmax.x());
  py=m_random(m_pmin.y(),m_pmax.y());

  return Vector3(px,py,0.0);  
}

/*!
  Return closest plane to a given point. Assumes that the list of Planes/Lines is not empty.
  
  \param p the point
  \warning check non-empty list of lines (via hasPlane()) before invoking
*/
Line2D BoxWithLines2D::getClosestPlane(const Vector3& p)
{
  std::cout << "getClosestPlane : " << p << std::endl;

  vector<Line2D>::iterator PL=m_lines.begin();
  double dist=PL->getDist(p);

  for(vector<Line2D>::iterator iter=m_lines.begin();
      iter!=m_lines.end();
      iter++){
    double ndist=iter->getDist(p);
    std::cout << "Line: " << *iter << " Distance: " << ndist << std::endl;
    if(ndist<dist){
      PL=iter;
      dist=ndist;
    }
  }
  std::cout << "closest line: " << *PL <<  " Distance: " << dist << std::endl;

  return (*PL);
}

const map<double, const Line2D*> BoxWithLines2D::getClosestPlanes(const Vector3& p,int nmax) const
{
  map<double,const Line2D*> res;

  for(vector<Line2D>::const_iterator iter=m_lines.begin();
      iter!=m_lines.end();
      iter++){
    double ndist=iter->getDist(p);
    const Line2D *l=&(*iter);
    res.insert(make_pair(ndist,l));
  }

  return res;
}


/*!
  check if point is inside

  \param p the point
*/
bool BoxWithLines2D::isIn(const Vector3& p) const
{
  return ((p.x()>m_pmin.x()) && (p.x()<m_pmax.x()) && (p.y()>m_pmin.y()) && (p.y()<m_pmax.y()));
}

/*!
  check if a Sphere is inside and doesn't intersect any lines

  \param S the Sphere
*/
bool BoxWithLines2D::isIn(const Sphere& S)
{
  double r=S.Radius();
  Vector3 p=S.Center();

  //  std::cout << "rad: " << r << "  pos: " << p << std::endl;

  bool inside=(p.X()>m_pmin.X()+r) && (p.X()<m_pmax.X()-r) && (p.Y()>m_pmin.Y()+r) && (p.Y()<m_pmax.Y()-r);

  vector<Line2D>::iterator iter=m_lines.begin();

  double dist=2*r;
  while(iter!=m_lines.end() && dist>r){
    dist=iter->getDist(p);
    iter++;
  }

  return inside && (dist>r);  
}

ostream& operator<< (ostream& ost, const BoxWithLines2D& L)
{
  ost << L.m_pmin << " to " << L.m_pmax;

  return ost;
}

