/*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::initSideSegments(const Coordinate &nS1, const Coordinate &nS2, int nSide)
{
	s1=nS1;
	s2=nS2;
	side=nSide;
	seg1.setCoordinates(s1, s2);
	computeOffsetSegment(seg1, side, distance, offset1);
}
/*public*/
void
OffsetSegmentGenerator::addNextSegment(const Coordinate &p, bool addStartPoint)
{

  // do nothing if points are equal
  if (s2==p) return;

  // 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);

  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
    addCollinear(addStartPoint);
  }
  else if (outsideTurn)
  {
    addOutsideTurn(orientation, addStartPoint);
  }
  else
  {
    // inside turn
    addInsideTurn(orientation, addStartPoint);
  }
}
/*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);
			}
		}
	}
}