/**
   * Assumes that the polygons are disjoint. Find the vertex indices corresponding to the end points
   * of the two connecting edges.
   *
   * @param polygon1 the first polygon
   * @param polygon2 the second polygon
   * @param verticesIndices in[2][2] contains the indexes of the connecting edges end points. The
   *     row index refers to which polygon the vertex index belongs to, whereas the column index
   *     refers to which connecting edge the vertex index belongs to. For example,
   *     vertexIndexes[0][1] is the index of the vertex of the first polygon, also end point the
   *     second connecting edge.
   * @return success (false = failed, true = succeeded)
   */
  public static boolean findConnectingEdgesVerticesIndexes(
      ConvexPolygon2d polygon1, ConvexPolygon2d polygon2, int[][] verticesIndices) {
    boolean success = false;

    if (polygon1.isEmpty() || polygon2.isEmpty()) return false;

    if (polygon1.hasExactlyOneVertex() && polygon2.hasExactlyOneVertex()) {
      verticesIndices[0][0] = 0;
      verticesIndices[0][1] = 0;
      verticesIndices[1][0] = 0;
      verticesIndices[1][1] = 0;
      return true;
    }

    if (polygon1.hasExactlyOneVertex()) {
      verticesIndices[0][0] = 0;
      verticesIndices[0][1] = 0;
      success = polygon2.getLineOfSightVerticesIndices(polygon1.getVertex(0), verticesIndices[1]);
      return success;
    }

    if (polygon2.hasExactlyOneVertex()) {
      verticesIndices[1][0] = 0;
      verticesIndices[1][1] = 0;
      success = polygon1.getLineOfSightVerticesIndices(polygon2.getVertex(0), verticesIndices[0]);
      return success;
    }

    // First pick a random vertex from polygon1
    Point2d vertex = polygon1.getVertex(0);

    int[] lineOfSight1 = new int[2];
    int[] lineOfSight2 = new int[2];
    int L1, R1, L2, R2;

    // Then find its two line of sight points on polygon 2:
    success = polygon2.getLineOfSightVerticesIndices(vertex, lineOfSight1);
    if (!success) return false;

    L2 = lineOfSight1[0];
    R2 = lineOfSight1[1];

    // Then find the line of sight vertices on polygon 1:
    success = polygon1.getLineOfSightVerticesIndices(polygon2.getVertex(R2), lineOfSight1);
    if (!success) return false;
    success = polygon1.getLineOfSightVerticesIndices(polygon2.getVertex(L2), lineOfSight2);
    if (!success) return false;

    L1 = lineOfSight1[0];
    R1 = lineOfSight2[1];

    // Find the line of sight vertices back and forth between the two polygons until they are
    // constant between two iterations.
    boolean done = false;
    while (!done) {
      success = polygon2.getLineOfSightVerticesIndices(polygon1.getVertex(L1), lineOfSight1);
      if (!success) return false;
      success = polygon2.getLineOfSightVerticesIndices(polygon1.getVertex(R1), lineOfSight2);
      if (!success) return false;

      if ((L2 == lineOfSight2[0]) && (R2 == lineOfSight1[1])) {
        done = true;
        break;
      }

      L2 = lineOfSight2[0];
      R2 = lineOfSight1[1];

      success = polygon1.getLineOfSightVerticesIndices(polygon2.getVertex(L2), lineOfSight1);
      if (!success) return false;
      success = polygon1.getLineOfSightVerticesIndices(polygon2.getVertex(R2), lineOfSight2);
      if (!success) return false;

      if ((L1 == lineOfSight2[0]) && (R1 == lineOfSight1[1])) {
        done = true;
        break;
      }

      L1 = lineOfSight2[0];
      R1 = lineOfSight1[1];
    }

    verticesIndices[0][0] = R1;
    verticesIndices[0][1] = L1;
    verticesIndices[1][0] = L2;
    verticesIndices[1][1] = R2;
    return true;
  }
  /**
   * Computes the intersection of two convex polygons. For references see:
   * http://www.iro.umontreal.ca/~plante/compGeom/algorithm.html Returns null if the polygons do not
   * intersect Returns the inside polygon if the two polygons are inside one another.
   *
   * @param polygonP ConvexPolygon2d
   * @param polygonQ ConvexPolygon2d
   * @return ConvexPolygon2d Intersection of polygonP and polygonQ
   */
  public static boolean computeIntersectionOfPolygons(
      ConvexPolygon2d polygonP,
      ConvexPolygon2d polygonQ,
      ConvexPolygon2d intersectingPolygonToPack) {
    // return false if either polygon null
    if (polygonP == null || polygonP.isEmpty()) return false;
    if (polygonQ == null || polygonQ.isEmpty()) return false;

    if (polygonP.hasExactlyTwoVertices() && polygonQ.hasAtLeastTwoVertices()) {
      return ConvexPolygonTools
          .computeIntersectionOfPolygonsIfOnePolygonHasExactlyTwoVerticesAndTheOtherHasAtLeastTwoVertices(
              polygonP, polygonQ, intersectingPolygonToPack);
    } else if (polygonQ.hasExactlyTwoVertices()) {
      return ConvexPolygonTools
          .computeIntersectionOfPolygonsIfOnePolygonHasExactlyTwoVerticesAndTheOtherHasAtLeastTwoVertices(
              polygonQ, polygonP, intersectingPolygonToPack);
    }

    if (polygonP.hasExactlyOneVertex() && polygonQ.hasAtLeastOneVertex()) {
      return ConvexPolygonTools.computeIntersectionOfPolygonsIfOnePolygonHasExactlyOneVertex(
          polygonP, polygonQ, intersectingPolygonToPack);
    } else if (polygonQ.hasExactlyOneVertex()) {
      return ConvexPolygonTools.computeIntersectionOfPolygonsIfOnePolygonHasExactlyOneVertex(
          polygonQ, polygonP, intersectingPolygonToPack);
    }

    // Find left most point on polygon1
    int currentPPolygonPointIndex = polygonP.getMinXIndex();
    int initialPolygonPIndex = polygonP.getMinXIndex();
    Point2d currentPolygonPPoint = polygonP.getVertex(currentPPolygonPointIndex);

    // Find left most point on polygon2
    int currentQPolygonPointIndex = polygonQ.getMinXIndex();
    int initialPolygonQIndex = polygonQ.getMinXIndex();
    Point2d currentPolygonQPoint = polygonQ.getVertex(currentQPolygonPointIndex);

    // At each of those two vertices, place a vertical line passing through it. Associate that line
    // to the polygon to which the vertex belongs.
    Vector2d caliperForPolygonP = new Vector2d(0.0, 1.0);
    Vector2d caliperForPolygonQ = new Vector2d(0.0, 1.0);

    // determine which side polygon2's caliper line is on relative to polygon1's caliper line
    Point2d lineStart = new Point2d(currentPolygonPPoint);
    Point2d lineEnd = new Point2d(currentPolygonPPoint);
    lineEnd.add(caliperForPolygonP);
    boolean isOnLeft =
        GeometryTools.isPointOnLeftSideOfLine(currentPolygonQPoint, lineStart, lineEnd);
    boolean wasOnLeft = isOnLeft;

    //    System.out.println("wasOnLeft = " + wasOnLeft);

    boolean gotAroundPOnce = false;
    boolean gotAroundQOnce = false;
    boolean DONE = false;

    int bridgeCount = 0;
    ArrayList<Integer> bridgeIndicesP = new ArrayList<Integer>();
    ArrayList<Integer> bridgeIndicesQ = new ArrayList<Integer>();
    ArrayList<Boolean> bridgeWasOnLeft = new ArrayList<Boolean>();

    do {
      if (gotAroundPOnce && gotAroundQOnce) {
        DONE = true;
      }

      // Rotate these two lines (called calipers) by the smallest angle between a caliper and the
      // segment following the vertex it passes
      // through (in clockwise order). The rotation is done about the vertex through which the line
      // passes on the associated polygon.
      // If the line passes through more than one vertex of the assciated polygon, the farthest (in
      // clockwise order) is taken.

      // find angle from current to next point for polygon1
      Vector2d vectorToNextPointOnPolygonP =
          new Vector2d(polygonP.getNextVertex(currentPPolygonPointIndex));
      vectorToNextPointOnPolygonP.sub(polygonP.getVertex(currentPPolygonPointIndex));
      vectorToNextPointOnPolygonP.normalize();

      // +++JEP: Don't actually compute the angle! Just look at the dot products!
      //       double angleToNextPointOnPolygonP =
      // caliperForPolygonP.angle(vectorToNextPointOnPolygonP);
      // //Math.acos(vectorToNextPointOnPolygon1.getY());
      double dotProductToNextPointOnPolygonP =
          caliperForPolygonP.dot(
              vectorToNextPointOnPolygonP); // Math.acos(vectorToNextPointOnPolygon1.getY());

      // find angle from current to next point for polygon2
      Vector2d vectorToNextPointOnPolygonQ =
          new Vector2d(polygonQ.getNextVertex(currentQPolygonPointIndex));
      vectorToNextPointOnPolygonQ.sub(polygonQ.getVertex(currentQPolygonPointIndex));
      vectorToNextPointOnPolygonQ.normalize();

      //       double angleToNextPointOnPolygonQ =
      // caliperForPolygonQ.angle(vectorToNextPointOnPolygonQ);
      // //Math.acos(vectorToNextPointOnPolygon2.getY());
      double dotProductToNextPointOnPolygonQ =
          caliperForPolygonQ.dot(
              vectorToNextPointOnPolygonQ); // Math.acos(vectorToNextPointOnPolygon2.getY());

      // determine the smallest angle and rotate both calipers by this amount
      boolean moveCaliperP = false;
      boolean moveCaliperQ = false;

      //       if (angleToNextPointOnPolygonP == angleToNextPointOnPolygonQ)
      if (dotProductToNextPointOnPolygonP == dotProductToNextPointOnPolygonQ) {
        caliperForPolygonP.set(vectorToNextPointOnPolygonP);
        caliperForPolygonQ.set(caliperForPolygonP);

        moveCaliperP = true;
        moveCaliperQ = true;
      }

      //       else if (angleToNextPointOnPolygonP < angleToNextPointOnPolygonQ)
      else if (dotProductToNextPointOnPolygonP > dotProductToNextPointOnPolygonQ) {
        caliperForPolygonP.set(vectorToNextPointOnPolygonP);
        caliperForPolygonQ.set(caliperForPolygonP);
        moveCaliperP = true;
      } else {
        caliperForPolygonQ.set(vectorToNextPointOnPolygonQ);
        caliperForPolygonP.set(caliperForPolygonQ);

        moveCaliperQ = true;
      }

      // Whenever the order of the two calipers change, a pocket has been found. To detect this, a
      // direction is associated with one of
      // the lines (for example the green one, associated to P). Then all points of the red line
      // (associated to Q) are either to the
      // left or to the right of the green line. When a rotation makes them change from one side to
      // the other of the green line, then
      // the order of the two lines has changed.

      // detemine which side polygon Q's caliper line is on relative to polygon P's caliper lline
      lineStart = new Point2d(currentPolygonPPoint);
      lineEnd = new Point2d(currentPolygonPPoint);
      lineEnd.add(caliperForPolygonP);
      isOnLeft = GeometryTools.isPointOnLeftSideOfLine(currentPolygonQPoint, lineStart, lineEnd);

      //       System.out.println("new isOnLeft = " + isOnLeft);

      if (wasOnLeft != isOnLeft) {
        // find all of the bridges
        //          System.out.println("Found bridge. wasOnLeft = " + wasOnLeft + ", isOnLeft = " +
        // isOnLeft);

        // Some weird fence post thing here. Sometime you want to consider the start ones at the
        // end, sometimes you don't.
        // So that's why DONE is computed at the top and checked on the bottom...

        boolean addThisOne = true;
        if ((DONE) && (!bridgeIndicesP.isEmpty())) {
          if ((bridgeIndicesP.get(0) == currentPPolygonPointIndex)
              && (bridgeIndicesQ.get(0) == currentQPolygonPointIndex)) {
            addThisOne = false;
          }
        }

        if (addThisOne) {
          bridgeIndicesP.add(currentPPolygonPointIndex);
          bridgeIndicesQ.add(currentQPolygonPointIndex);
          bridgeWasOnLeft.add(wasOnLeft);

          // bridgeIndices1[bridgeCount] = currentPPolygonPointIndex;
          // bridgeIndices2[bridgeCount] = currentQPolygonPointIndex;
          // bridgeWasOnLeft[bridgeCount] = wasOnLeft;

          bridgeCount++;

          // update current caliper relationship
          wasOnLeft = isOnLeft;
        }
      }

      // The algorithm terminates once it has gone around both polygons
      if (moveCaliperP) {
        currentPPolygonPointIndex = polygonP.getNextVertexIndex(currentPPolygonPointIndex);
        currentPolygonPPoint = polygonP.getVertex(currentPPolygonPointIndex);

        if (currentPPolygonPointIndex == (initialPolygonPIndex) % polygonP.getNumberOfVertices()) {
          gotAroundPOnce = true;
        }
      }

      if (moveCaliperQ) {
        currentQPolygonPointIndex = polygonQ.getNextVertexIndex(currentQPolygonPointIndex);
        currentPolygonQPoint = polygonQ.getVertex(currentQPolygonPointIndex);

        if (currentQPolygonPointIndex == (initialPolygonQIndex) % polygonQ.getNumberOfVertices()) {
          gotAroundQOnce = true;
        }
      }
    } while (!DONE);

    // If no bridges, then check if the polygons are contained in each other.
    if (bridgeCount == 0) {
      // check to see if a polygons is contained in another.
      if (polygonP.isPointInside(polygonQ.getVertex(0))) {
        intersectingPolygonToPack.setAndUpdate(polygonQ);
      }

      if (polygonQ.isPointInside(polygonP.getVertex(0))) {
        intersectingPolygonToPack.setAndUpdate(polygonP);
      }
    } else {
      boolean success =
          ConvexPolygonTools.buildCommonPolygonFromBridges(
              bridgeIndicesP,
              bridgeIndicesQ,
              bridgeWasOnLeft,
              polygonP,
              polygonQ,
              intersectingPolygonToPack);
      if (!success) {
        intersectingPolygonToPack.clearAndUpdate();
        return false;
      }
    }

    // Merge the inner chains to determine intersection
    return true;
  }