/***************************************************************************
**
**  This file is part of QGpCoreMath.
**
**  This library is free software; you can redistribute it and/or
**  modify it under the terms of the GNU Lesser General Public
**  License as published by the Free Software Foundation; either
**  version 2.1 of the License, or (at your option) any later version.
**
**  This file is distributed in the hope that it will be useful, but WITHOUT
**  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
**  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
**  License for more details.
**
**  You should have received a copy of the GNU Lesser General Public
**  License along with this library; if not, write to the Free Software
**  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
**
**  See http://www.geopsy.org for more information.
**
**  Created: 2012-04-16
**  Copyright: 2012-2019
**    Marc Wathelet (ISTerre, Grenoble, France)
**
***************************************************************************/

#include "Parallelepiped.h"
#include "Rect.h"

namespace QGpCoreMath {

  /*!
    \class Parallelepiped Parallelepiped.h
    \brief Brief description of class still missing

    Full description of class still missing
  */


  Parallelepiped::Parallelepiped()
  {
    _x1=std::numeric_limits<double>::infinity();
    _y1=std::numeric_limits<double>::infinity();
    _z1=std::numeric_limits<double>::infinity();
    _x2=-std::numeric_limits<double>::infinity();
    _y2=-std::numeric_limits<double>::infinity();
    _z2=-std::numeric_limits<double>::infinity();
  }

  Parallelepiped::Parallelepiped(const Parallelepiped &o)
  {
    _x1=o._x1;
    _y1=o._y1;
    _z1=o._z1;
    _x2=o._x2;
    _y2=o._y2;
    _z2=o._z2;
  }

  /*!
    Adjust the top-left-front and the bottom-rigth-back corners to include the points (\a nx1, \a ny1, \a nz1),
    and (\a nx2, \a ny2, \a nz2).
  */
  void Parallelepiped::setLimits(const Point& p1, const Point& p2)
  {
    TRACE;
    _x1=p1.x();
    _y1=p1.y();
    _z1=p1.z();
    _x2=p2.x();
    _y2=p2.y();
    _z2=p2.z();
    if(_x2<_x1) {
      std::swap(_x1, _x2);
    }
    if(_y2<_y1) {
      std::swap(_y1, _y2);
    }
    if(_z2<_z1) {
      std::swap(_z1, _z2);
    }
  }

  /*!
    Tests if the point is inside the rectangle
  */
  bool Parallelepiped::includes (const Point & p) const
  {
    TRACE;
    static const double prec=1e-10;
    double px1=fabs(p.x())*prec;
    double py1=fabs(p.y())*prec;
    double pz1=fabs(p.z())*prec;
    return (_x1<=p.x()+px1 && p.x()-px1<=_x2 &&
            _y1<=p.y()+py1 && p.y()-py1<=_y2 &&
            _z1<=p.z()+pz1 && p.z()-pz1<=_z2);
  }

  /*!
    Tests if the segment defined by \a p1 and \a p2 is inside or crosses the rectangle
  */
  bool Parallelepiped::includes(const Point & p1, const Point& p2) const
  {
    TRACE;
    if(p1.x()>_x1 && p1.x()<_x2 &&
       p1.y()>_y1 && p1.y()<_y2 &&
       p1.z()>_z1 && p1.z()<_z2) return true;
    if(p2.x()>_x1 && p2.x()<_x2 &&
       p2.y()>_y1 && p2.y()<_y2 &&
       p2.z()>_z1 && p2.z()<_z2) return true;
    // p1 is outside, p2 is outside
    // Try to find one intersection with sides of the rectangle
    Point u(p2.x()-p1.x(), p2.y()-p1.y(), p2.z()-p1.z());
    Rect rx(_y1, _z1, _y2, _z2);
    Rect ry(_x1, _z1, _x2, _z2);
    Rect rz(_x1, _y1, _x2, _y2);
    double k;
    Point p;

    if(u.x()!=0.0) {
      k=(_x1-p1.x())/u.x();
      p=u*k+p1;
      if(rx.includes(Point2D(p.y(), p.z()))) return true;
      k=(_x2-p1.x())/u.x();
      p=u*k+p1;
      if(rx.includes(Point2D(p.y(), p.z()))) return true;
    }

    if(u.y()!=0.0) {
      k=(_y1-p1.y())/u.y();
      p=u*k+p1;
      if(ry.includes(Point2D(p.x(), p.z()))) return true;
      k=(_y2-p1.y())/u.y();
      p=u*k+p1;
      if(ry.includes(Point2D(p.x(), p.z()))) return true;
    }

    if(u.z()!=0.0) {
      k=(_z1-p1.y())/u.z();
      p=u*k+p1;
      if(rz.includes(Point2D(p.x(), p.y()))) return true;
      k=(_z2-p1.y())/u.z();
      p=u*k+p1;
      if(rz.includes(Point2D(p.x(), p.y()))) return true;
    }

    return false;
  }

  void Parallelepiped::add(const Point& p)
  {
    if((p.x()>-std::numeric_limits<double>::infinity() && p.x()<_x1) || _x1==-std::numeric_limits<double>::infinity()) _x1=p.x();
    if((p.x()<std::numeric_limits<double>::infinity() && p.x()>_x2) || _x2==std::numeric_limits<double>::infinity()) _x2=p.x();
    if((p.y()>-std::numeric_limits<double>::infinity() && p.y()<_y1) || _y1==-std::numeric_limits<double>::infinity()) _y1=p.y();
    if((p.y()<std::numeric_limits<double>::infinity() && p.y()>_y2) || _y2==std::numeric_limits<double>::infinity()) _y2=p.y();
    if((p.z()>-std::numeric_limits<double>::infinity() && p.z()<_z1) || _z1==-std::numeric_limits<double>::infinity()) _z1=p.z();
    if((p.z()<std::numeric_limits<double>::infinity() && p.z()>_z2) || _z2==std::numeric_limits<double>::infinity()) _z2=p.z();
  }

  QList<Point> Parallelepiped::vertice() const
  {
    TRACE;
    QList<Point> l;
    l.append(Point(_x1, _y1, _z1));
    l.append(Point(_x1, _y1, _z2));
    l.append(Point(_x1, _y2, _z1));
    l.append(Point(_x1, _y2, _z2));
    l.append(Point(_x2, _y1, _z1));
    l.append(Point(_x2, _y1, _z2));
    l.append(Point(_x2, _y2, _z1));
    l.append(Point(_x2, _y2, _z2));
    return l;
  }

} // namespace QGpCoreMath
