#ifndef __JSEGMENT2D__
#define __JSEGMENT2D__

#include <utility>
#include <cmath>

#include "JIO/JSerialisable.hh"
#include "JLang/JException.hh"
#include "JMath/JMathToolkit.hh"
#include "JGeometry2D/JPosition2D.hh"


/**
 * \author mdejong
 */

namespace JGEOMETRY2D {}
namespace JPP { using namespace JGEOMETRY2D; }

namespace JGEOMETRY2D {

  using JIO::JReader;
  using JIO::JWriter;
  using JLANG::JDivisionByZero;

  /**
   * Type definition of line segment in two dimensions.
   */
  typedef std::pair<JPosition2D, JPosition2D>  JSegment2D_t;


  /**
   * Line segment in two dimensions.
   */
  class JSegment2D :
    public JSegment2D_t
  {
  public:
    /**
     * Default constructor.
     */
    JSegment2D() :
      JSegment2D_t()
    {}


    /**
     * Constructor.
     *
     * \param  A             start position
     * \param  B             end   position
     */
    JSegment2D(const JVector2D& A,
	       const JVector2D& B) :
      JSegment2D_t(A,B)
    {}


    /**
     * Get length squared.
     *
     * \return               square of length
     */
    double getLengthSquared() const
    {
      return JVector2D(this->second - this->first).getLengthSquared();
    }


    /**
     * Get length.
     *
     * \return               length
     */
    double getLength() const
    {
      return sqrt(getLengthSquared());
    }


    /**
     * Test whether two line segments intersect.
     *
     * \param  segment       line segment
     * \return               true if two line segment intersect; else false
     */
    bool intersects(const JSegment2D& segment) const
    {
      return (getCCW(this->first,   segment.first, this->second)   != getCCW(this->first,   segment.second, this->second) &&
	      getCCW(segment.first, this->first,   segment.second) != getCCW(segment.first, this->second,   segment.second));
    }


    /**
     * Get intersection of two line segments.
     *
     * \param  segment       line segment
     * \return               intersection point
     */
    JVector2D getIntersection(const JSegment2D& segment) const
    {
      JVector2D da(this->second   - this->first);
      JVector2D db(segment.second - segment.first);
      
      const double gp = JMATH::getPerpDot(da, db);

      if (gp != 0.0) {

	da.mul(JMATH::getPerpDot(segment.second, segment.first)); 
	db.mul(JMATH::getPerpDot(this->second,   this->first));

	db.sub(da);
	db.div(gp);

	return db;

      } else {
	throw JDivisionByZero("JSegment2D::getIntersection()");
      }
    }


    /**
     * Get squared of distance to point.
     *
     * \param  point         point
     * \return               square of distance
     */
    double getDistanceSquared(const JVector2D& point) const
    {
      JVector2D D(this->second - this->first);

      const double gp = D.getLengthSquared();

      if (gp != 0.0) {

	const JVector2D U(point - this->first);

	double u = D.getDot(U);

	if      (u < 0.0)
	  u  = 0.0;
	else if (u > gp)
	  u  = 1.0;
	else
	  u /= gp;

	D.mul(u);
	D.sub(U);

	return D.getLengthSquared();

      } else {

	return first.getDistanceSquared(point);
      }
    }


    /**
     * Get distance to point.
     *
     * \param  point         point
     * \return               distance
     */
    double getDistance(const JVector2D& point) const
    {
      return sqrt(getDistanceSquared(point));
    }


    /**
     * Get dot product.
     *
     * \param  segment       segment
     * \return               dot product
     */
    double getDot(const JSegment2D& segment) const
    {
      return JVector2D(this->second - this->first).getDot(segment.second - segment.first);
    }


    /**
     * Read segment from input.
     *
     * \param  in            reader
     * \param  segment       segment
     * \return               reader
     */
    friend inline JReader& operator>>(JReader& in, JSegment2D& segment)
    {
      in >> segment.first;
      in >> segment.second;

      return in;
    }


    /**
     * Write segment to output.
     *
     * \param  out           writer
     * \param  segment       segment
     * \return               writer
     */
    friend inline JWriter& operator<<(JWriter& out, const JSegment2D& segment)
    {
      out << segment.first;
      out << segment.second;

      return out;
    }
  };
}

#endif