/**********************************************************************
 *
 * GEOS - Geometry Engine Open Source
 * http://geos.osgeo.org
 *
 * Copyright (C) 2009 2011 Sandro Santilli <strk@kbt.io>
 * Copyright (C) 2005-2006 Refractions Research Inc.
 * Copyright (C) 2001-2002 Vivid Solutions Inc.
 *
 * This is free software; you can redistribute and/or modify it under
 * the terms of the GNU Lesser General Public Licence as published
 * by the Free Software Foundation.
 * See the COPYING file for more information.
 *
 **********************************************************************
 *
 * Last port: geom/LineSegment.java r18 (JTS-1.11)
 *
 **********************************************************************/

#ifndef GEOS_GEOM_LINESEGMENT_H
#define GEOS_GEOM_LINESEGMENT_H

#include <geos/export.h>
#include <geos/geom/Coordinate.h> // for composition

#include <geos/inline.h>

#include <iostream> // for ostream
#include <memory> // for unique_ptr

// Forward declarations
namespace geos {
	namespace geom {
		class CoordinateSequence;
		class GeometryFactory;
		class LineString;
	}
}

namespace geos {
namespace geom { // geos::geom

/**
 * Represents a line segment defined by two Coordinate.
 * Provides methods to compute various geometric properties
 * and relationships of line segments.
 *
 * This class is designed to be easily mutable (to the extent of
 * having its contained points public).
 * This supports a common pattern of reusing a single LineSegment
 * object as a way of computing segment properties on the
 * segments defined by arrays or lists of {@link Coordinate}s.
 *
 * TODO: have this class keep pointers rather then real Coordinates ?
 */
class GEOS_DLL LineSegment {
public:

	friend std::ostream& operator<< (std::ostream& o, const LineSegment& l);

	Coordinate p0; /// Segment start

	Coordinate p1; /// Segment end

	LineSegment();

	LineSegment(const LineSegment &ls);

	/// Constructs a LineSegment with the given start and end Coordinates.
	LineSegment(const Coordinate& c0, const Coordinate& c1);

	LineSegment(double x0, double y0, double x1, double y1);

	virtual ~LineSegment();

	void setCoordinates(const Coordinate& c0, const Coordinate& c1);

	// obsoleted, use operator[] instead
	//const Coordinate& getCoordinate(std::size_t i) const;

	const Coordinate& operator[](std::size_t i) const;
	Coordinate& operator[](std::size_t i);

	void setCoordinates(const LineSegment& ls);

	/// Computes the length of the line segment.
	double getLength() const;

	/// Tests whether the segment is horizontal.
	//
	/// @return <code>true</code> if the segment is horizontal
	///
	bool isHorizontal() const;

	/// Tests whether the segment is vertical.
	//
	/// @return <code>true</code> if the segment is vertical
	///
	bool isVertical() const;

	/**
	 * Determines the orientation of a LineSegment relative to this segment.
	 * The concept of orientation is specified as follows:
	 * Given two line segments A and L,
	 * <ul
	 * <li>A is to the left of a segment L if A lies wholly in the
	 * closed half-plane lying to the left of L
	 * <li>A is to the right of a segment L if A lies wholly in the
	 * closed half-plane lying to the right of L
	 * <li>otherwise, A has indeterminate orientation relative to L.
	 *     This happens if A is collinear with L or if A crosses
	 *     the line determined by L.
	 * </ul>
	 *
	 * @param seg the LineSegment to compare
	 *
	 * @return 1 if seg is to the left of this segment
	 * @return -1 if seg is to the right of this segment
	 * @return 0 if seg has indeterminate orientation relative
	 *	     to this segment
	 */
	int orientationIndex(const LineSegment& seg) const;

	// TODO: deprecate this
	int orientationIndex(const LineSegment* seg) const;

	/** \brief
	 * Determines the orientation index of a Coordinate
	 * relative to this segment.
	 *
	 * The orientation index is as defined in
	 * CGAlgorithms::computeOrientation.
	 *
	 * @param seg the LineSegment to compare
	 *
	 * @return 1 if <code>p</code> is to the left of this segment
	 * @return -1 if <code>p</code> is to the right of this segment
	 * @return 0 if <code>p</code> is collinear with this segment
	 *
	 * @see CGAlgorithms::computeOrientation(Coordinate, Coordinate,
	 *                                       Coordinate)
	 */
	int orientationIndex(const Coordinate& p) const;

	/// Reverses the direction of the line segment.
	void reverse();

	/// Puts the line segment into a normalized form.
	//
	/// This is useful for using line segments in maps and indexes when
	/// topological equality rather than exact equality is desired.
	///
	void normalize();

	/// @return the angle this segment makes with the x-axis (in radians)
	double angle() const;

	/// Computes the midpoint of the segment
	//
	/// @param ret will be set to the midpoint of the segment
	///
	void midPoint(Coordinate& ret) const;

	/// Computes the distance between this line segment and another one.
	double distance(const LineSegment& ls) const;

	/// Computes the distance between this line segment and a point.
	double distance(const Coordinate& p) const;

	/** \brief
	 * Computes the perpendicular distance between the (infinite)
	 * line defined by this line segment and a point.
	 */
	double distancePerpendicular(const Coordinate& p) const;

	/** \brief
	 * Computes the Coordinate that lies a given
	 * fraction along the line defined by this segment.
	 *
	 * A fraction of <code>0.0</code> returns the start point of
	 * the segment; a fraction of <code>1.0</code> returns the end
	 * point of the segment.
	 * If the fraction is < 0.0 or > 1.0 the point returned
	 * will lie before the start or beyond the end of the segment.
	 *
	 * @param segmentLengthFraction the fraction of the segment length
	 *        along the line
	 * @param ret will be set to the point at that distance
	 */
	void pointAlong(double segmentLengthFraction, Coordinate& ret) const;

	/** \brief
	 * Computes the {@link Coordinate} that lies a given
	 * fraction along the line defined by this segment and offset from
	 * the segment by a given distance.
	 *
	 * A fraction of <code>0.0</code> offsets
	 * from the start point of the segment;
	 * a fraction of <code>1.0</code> offsets
	 * from the end point of the segment.
	 *
	 * The computed point is offset to the left of the line
	 * if the offset distance is positive, to the right if negative.
	 *
	 * @param segmentLengthFraction the fraction of the segment
	 *                              length along the line
	 *
	 * @param offsetDistance the distance the point is offset
	 *        from the segment
	 *         (positive is to the left, negative is to the right)
	 *
	 * @param ret will be set to the point at that distance and offset
	 *
	 * @throws IllegalStateException if the segment has zero length
	 */
	void pointAlongOffset(double segmentLengthFraction,
	                      double offsetDistance,
	                      Coordinate& ret) const;

	/** \brief
	 * Compute the projection factor for the projection of the point p
	 * onto this LineSegment.
	 *
	 * The projection factor is the constant r
	 * by which the vector for this segment must be multiplied to
	 * equal the vector for the projection of p on the line
	 * defined by this segment.
	 *
	 * The projection factor returned will be in the range
	 * (-inf, +inf)
	 *
	 * @param p the point to compute the factor for
	 *
	 * @return the projection factor for the point
	 *
	 */
	double projectionFactor(const Coordinate& p) const;

	/** \brief
	 * Computes the fraction of distance (in <tt>[0.0, 1.0]</tt>)
	 * that the projection of a point occurs along this line segment.
	 *
	 * If the point is beyond either ends of the line segment,
	 * the closest fractional value (<tt>0.0</tt> or <tt>1.0</tt>)
	 * is returned.
	 *
	 * Essentially, this is the {@link #projectionFactor} clamped to
	 * the range <tt>[0.0, 1.0]</tt>.
	 *
	 * @param inputPt the point
	 * @return the fraction along the line segment the projection
	 *         of the point occurs
	 */
	double segmentFraction(const Coordinate& inputPt) const;

	/** \brief
	 * Compute the projection of a point onto the line determined
	 * by this line segment.
	 *
	 * Note that the projected point
	 * may lie outside the line segment.  If this is the case,
	 * the projection factor will lie outside the range [0.0, 1.0].
	 */
	void project(const Coordinate& p, Coordinate& ret) const;

	/** \brief
	 * Project a line segment onto this line segment and return the resulting
	 * line segment.
	 *
	 * The returned line segment will be a subset of
	 * the target line line segment.  This subset may be null, if
	 * the segments are oriented in such a way that there is no projection.
	 *
	 * Note that the returned line may have zero length (i.e. the same endpoints).
	 * This can happen for instance if the lines are perpendicular to one another.
	 *
	 * @param seg the line segment to project
	 * @param ret the projected line segment
	 * @return true if there is an overlap, false otherwise
	 */
	bool project(const LineSegment& seg, LineSegment& ret) const;

	/// Computes the closest point on this line segment to another point.
	//
	/// @param p the point to find the closest point to
	/// @param ret the Coordinate to which the closest point on the line segment
	///            to the point p will be written
	///
	void closestPoint(const Coordinate& p, Coordinate& ret) const;

	/** \brief
	 * Compares this object with the specified object for order.
	 *
	 * Uses the standard lexicographic ordering for the points in the LineSegment.
	 *
	 * @param  o  the LineSegment with which this LineSegment
	 *            is being compared
	 * @return a negative integer, zero, or a positive integer as this
	 *         LineSegment is less than, equal to, or greater than the
	 *         specified LineSegment
	 */
	int compareTo(const LineSegment& other) const;

	/** \brief
	 *  Returns <code>true</code> if <code>other</code> is
	 *  topologically equal to this LineSegment (e.g. irrespective
	 *  of orientation).
	 *
	 * @param  other  a <code>LineSegment</code> with which to do the comparison.
	 * @return true if other is a LineSegment
	 *      with the same values for the x and y ordinates.
	 */
	bool equalsTopo(const LineSegment& other) const;

	/**
	 * Computes the closest points on two line segments.
	 * @param p the point to find the closest point to
	 * @return a pair of Coordinates which are the closest points on
	 * the line segments.
	 * The returned CoordinateSequence must be deleted by caller
	 */
	CoordinateSequence* closestPoints(const LineSegment& line);

	CoordinateSequence* closestPoints(const LineSegment* line);

	/**
	 * Computes an intersection point between two segments,
	 * if there is one.
	 * There may be 0, 1 or many intersection points between two segments.
	 * If there are 0, null is returned. If there is 1 or more, a single
	 * one is returned (chosen at the discretion of the algorithm).
	 * If more information is required about the details of the
	 * intersection, the LineIntersector class should be used.
	 *
	 * @param line
	 * @param coord the Coordinate to write the result into
	 * @return true if an intersection was found, false otherwise
	 */
	bool intersection(const LineSegment& line, Coordinate& coord) const;

	/** \brief
	 * Computes the intersection point of the lines defined
	 * by two segments, if there is one.
	 *
	 * There may be 0, 1 or an infinite number of intersection points
	 * between two lines.
	 * If there is a unique intersection point, it is returned.
	 * Otherwise, <tt>null</tt> is returned.
	 * If more information is required about the details of the
	 * intersection, the algorithms::LineIntersector class should
	 * be used.
	 *
	 * @param line a line segment defining a straight line
	 * @param ret will be set to the intersection point (if any)
	 * @return true if an intersection was found, false otherwise
	 *
	 */
	bool lineIntersection(const LineSegment& line, Coordinate& ret) const;

	/**
	 * Creates a LineString with the same coordinates as this segment
	 *
	 * @param gf the geometery factory to use
	 * @return a LineString with the same geometry as this segment
	 */
	std::unique_ptr<LineString> toGeometry(const GeometryFactory& gf) const;

};

std::ostream& operator<< (std::ostream& o, const LineSegment& l);

/// Checks if two LineSegment are equal (2D only check)
bool operator==(const LineSegment& a, const LineSegment& b);


} // namespace geos::geom
} // namespace geos

#ifdef GEOS_INLINE
# include "geos/geom/LineSegment.inl"
#endif

#endif // ndef GEOS_GEOM_LINESEGMENT_H
