/*private*/
void
OffsetCurveBuilder::addCircle(const Coordinate &p, double distance)
{
	// add start point
	Coordinate pt(p.x + distance, p.y);
	vertexList->addPt(pt);
	addFillet(p, 0.0, 2.0*PI, -1, distance);
}
/*private*/
void
OffsetSegmentGenerator::createCircle(const Coordinate &p, double distance)
{
  // add start point
  Coordinate pt(p.x + distance, p.y);
  segList.addPt(pt);
  addFillet(p, 0.0, 2.0*PI, -1, distance);
  segList.closeRing();
}
/*private*/
void
OffsetCurveBuilder::addLineEndCap(const Coordinate &p0, const Coordinate &p1)
{
	LineSegment seg(p0, p1);

	LineSegment offsetL;
	computeOffsetSegment(seg, Position::LEFT, distance, offsetL);
	LineSegment offsetR;
	computeOffsetSegment(seg, Position::RIGHT, distance, offsetR);

	double dx=p1.x-p0.x;
	double dy=p1.y-p0.y;
	double angle=atan2(dy, dx);

	switch (bufParams.getEndCapStyle()) {
		case BufferParameters::CAP_ROUND:
			// add offset seg points with a fillet between them
			vertexList->addPt(offsetL.p1);
			addFillet(p1, angle+PI/2.0, angle-PI/2.0,
			          CGAlgorithms::CLOCKWISE, distance);
			vertexList->addPt(offsetR.p1);
			break;
		case BufferParameters::CAP_FLAT:
			// only offset segment points are added
			vertexList->addPt(offsetL.p1);
			vertexList->addPt(offsetR.p1);
			break;
		case BufferParameters::CAP_SQUARE:
			// add a square defined by extensions of the offset
			// segment endpoints
			Coordinate squareCapSideOffset;
			squareCapSideOffset.x=fabs(distance)*cos(angle);
			squareCapSideOffset.y=fabs(distance)*sin(angle);

			Coordinate squareCapLOffset(
				offsetL.p1.x+squareCapSideOffset.x,
				offsetL.p1.y+squareCapSideOffset.y);
			Coordinate squareCapROffset(
				offsetR.p1.x+squareCapSideOffset.x,
				offsetR.p1.y+squareCapSideOffset.y);
			vertexList->addPt(squareCapLOffset);
			vertexList->addPt(squareCapROffset);
			break;
	}
}
/*private*/
void
OffsetCurveBuilder::addFillet(const Coordinate &p, const Coordinate &p0,
	const Coordinate &p1, int direction, double distance)
{
	double dx0=p0.x-p.x;
	double dy0=p0.y-p.y;
	double startAngle=atan2(dy0, dx0);
	double dx1=p1.x-p.x;
	double dy1=p1.y-p.y;
	double endAngle=atan2(dy1, dx1);
	if (direction==CGAlgorithms::CLOCKWISE) {
		if (startAngle<= endAngle) startAngle += 2.0*PI;
	} else {    // direction==COUNTERCLOCKWISE
		if (startAngle>=endAngle) startAngle-=2.0*PI;
	}
	vertexList->addPt(p0);
	addFillet(p, startAngle, endAngle, direction, distance);
	vertexList->addPt(p1);
}
/* private */
void
OffsetCurveBuilder::addCollinear(bool addStartPoint)
{
	/**
	 * This test could probably be done more efficiently,
	 * but the situation of exact collinearity should be fairly rare.
	 */

	li.computeIntersection(s0,s1,s1,s2);
	int numInt=li.getIntersectionNum();

	/**
	 * if numInt is<2, the lines are parallel and in the same direction.
	 * In this case the point can be ignored, since the offset lines
	 * will also be parallel.
	 */
	if (numInt>= 2)
	{
		/**
		 * Segments are collinear but reversing. 
		 * Add an "end-cap" fillet
		 * all the way around to other direction
		 *
		 * This case should ONLY happen for LineStrings,
		 * so the orientation is always CW (Polygons can never
		 * have two consecutive segments which are parallel but
		 * reversed, because that would be a self intersection).
		 */
		if (  bufParams.getJoinStyle() == BufferParameters::JOIN_BEVEL
		   || bufParams.getJoinStyle() == BufferParameters::JOIN_MITRE)
		{
			if (addStartPoint) vertexList->addPt(offset0.p1);
			vertexList->addPt(offset1.p0);
		}
		else
		{
			addFillet(s1, offset0.p1, offset1.p0,
				  CGAlgorithms::CLOCKWISE, distance);
		}
	}
}
/*private*/
void
OffsetSegmentGenerator::addFillet(const Coordinate &p, const Coordinate &p0,
  const Coordinate &p1, int direction, double radius)
{
  double dx0 = p0.x - p.x;
  double dy0 = p0.y - p.y;
  double startAngle = atan2(dy0, dx0);
  double dx1 = p1.x - p.x;
  double dy1 = p1.y - p.y;
  double endAngle = atan2(dy1, dx1);

  if (direction == CGAlgorithms::CLOCKWISE) {
    if (startAngle <= endAngle) startAngle += 2.0 * PI;
  }
  else {    // direction==COUNTERCLOCKWISE
    if (startAngle >= endAngle) startAngle -= 2.0 * PI;
  }

  segList.addPt(p0);
  addFillet(p, startAngle, endAngle, direction, radius);
  segList.addPt(p1);
}
/* private */
void
OffsetCurveBuilder::addOutsideTurn(int orientation, bool addStartPoint)
{
	/**
	 * Heuristic: If offset endpoints are very close together,
	 * just use one of them as the corner vertex.
	 * This avoids problems with computing mitre corners in the case
	 * where the two segments are almost parallel
	 * (which is hard to compute a robust intersection for).
	 */

	if (offset0.p1.distance(offset1.p0) <
		distance*OFFSET_SEGMENT_SEPARATION_FACTOR)
	{
		vertexList->addPt(offset0.p1);
		return;
	}

	if (bufParams.getJoinStyle() == BufferParameters::JOIN_MITRE)
	{
		addMitreJoin(s1, offset0, offset1, distance);
	}
	else if (bufParams.getJoinStyle() == BufferParameters::JOIN_BEVEL)
	{
		addBevelJoin(offset0, offset1);
	}
	else
	{
		// add a circular fillet connecting the endpoints 
		// of the offset segments
		if (addStartPoint) vertexList->addPt(offset0.p1);

		// TESTING - comment out to produce beveled joins
		addFillet(s1, offset0.p1, offset1.p0, orientation, distance);
		vertexList->addPt(offset1.p0);
	}
}
/*private*/
void
OffsetCurveBuilder::addNextSegment(const Coordinate &p, bool addStartPoint)
{
	// s0-s1-s2 are the coordinates of the previous segment and the current one
	s0=s1;
	s1=s2;
	s2=p;
	seg0.setCoordinates(s0, s1);
	computeOffsetSegment(seg0, side, distance, offset0);
	seg1.setCoordinates(s1, s2);
	computeOffsetSegment(seg1, side, distance, offset1);

	// do nothing if points are equal
	if (s1==s2) return;
	int orientation=CGAlgorithms::computeOrientation(s0, s1, s2);
	bool outsideTurn =(orientation==CGAlgorithms::CLOCKWISE
						&& side==Position::LEFT)
						||(orientation==CGAlgorithms::COUNTERCLOCKWISE 
						&& side==Position::RIGHT);
	if (orientation==0)
	{ // lines are collinear
		li.computeIntersection(s0,s1,s1,s2);
		int numInt=li.getIntersectionNum();

		/**
		 * if numInt is<2, the lines are parallel and in the same direction.
		 * In this case the point can be ignored, since the offset lines will also be
		 * parallel.
		 */
		if (numInt>= 2) {
			/**
			 * segments are collinear but reversing.  Have to add an "end-cap" fillet
			 * all the way around to other direction
			 * This case should ONLY happen for LineStrings, so the orientation is always CW.
			 * Polygons can never have two consecutive segments which are parallel but
			 * reversed, because that would be a self intersection.
			 */
			addFillet(s1, offset0.p1, offset1.p0, CGAlgorithms::CLOCKWISE, distance);
		}
	}
	else if (outsideTurn)
	{
		// add a fillet to connect the endpoints of the offset segments
		if (addStartPoint) vertexList->addPt(offset0.p1);
		// TESTING-comment out to produce beveled joins
		addFillet(s1, offset0.p1, offset1.p0, orientation, distance);
		vertexList->addPt(offset1.p0);
	}
	else
	{ // inside turn

		// add intersection point of offset segments (if any)
		li.computeIntersection( offset0.p0, offset0.p1, offset1.p0, offset1.p1);
		if (li.hasIntersection()) {
			vertexList->addPt(li.getIntersection(0));
		} else {
			/**
			 * If no intersection, it means the angle is so small and/or the offset so large
			 * that the offsets segments don't intersect.
			 * In this case we must add a offset joining curve to make sure the buffer line
			 * is continuous and tracks the buffer correctly around the corner.
			 * Note that the joining curve won't appear in the final buffer.
			 *
			 * The intersection test above is vulnerable to robustness errors;
			 * i.e. it may be that the offsets should intersect very close to their
			 * endpoints, but don't due to rounding.  To handle this situation
			 * appropriately, we use the following test:
			 * If the offset points are very close, don't add a joining curve
			 * but simply use one of the offset points
			 */
			if (offset0.p1.distance(offset1.p0)<distance / 1000.0) {
				vertexList->addPt(offset0.p1);
			} else {
				// add endpoint of this segment offset
				vertexList->addPt(offset0.p1);
				// <FIX> MD-add in centre point of corner, to make sure offset closer lines have correct topology
				vertexList->addPt(s1);
				vertexList->addPt(offset1.p0);
			}
		}
	}
}