/**
   * Find the innermost enclosing shell EdgeRing containing the argument EdgeRing, if any. The
   * innermost enclosing ring is the <i>smallest</i> enclosing ring. The algorithm used depends on
   * the fact that: <br>
   * ring A contains ring B iff envelope(ring A) contains envelope(ring B) <br>
   * This routine is only safe to use if the chosen point of the hole is known to be properly
   * contained in a shell (which is guaranteed to be the case if the hole does not touch its shell)
   *
   * @return containing EdgeRing, if there is one or null if no containing EdgeRing is found
   */
  public static EdgeRing findEdgeRingContaining(EdgeRing testEr, List shellList) {
    LinearRing testRing = testEr.getRing();
    Envelope testEnv = testRing.getEnvelopeInternal();
    Coordinate testPt = testRing.getCoordinateN(0);

    EdgeRing minShell = null;
    Envelope minShellEnv = null;
    for (Iterator it = shellList.iterator(); it.hasNext(); ) {
      EdgeRing tryShell = (EdgeRing) it.next();
      LinearRing tryShellRing = tryShell.getRing();
      Envelope tryShellEnv = tryShellRing.getEnvelopeInternal();
      // the hole envelope cannot equal the shell envelope
      // (also guards against testing rings against themselves)
      if (tryShellEnv.equals(testEnv)) continue;
      // hole must be contained in shell
      if (!tryShellEnv.contains(testEnv)) continue;

      testPt =
          CoordinateArrays.ptNotInList(testRing.getCoordinates(), tryShellRing.getCoordinates());
      boolean isContained = false;
      if (CGAlgorithms.isPointInRing(testPt, tryShellRing.getCoordinates())) isContained = true;

      // check if this new containing ring is smaller than the current minimum ring
      if (isContained) {
        if (minShell == null || minShellEnv.contains(tryShellEnv)) {
          minShell = tryShell;
          minShellEnv = minShell.getRing().getEnvelopeInternal();
        }
      }
    }
    return minShell;
  }
  /** reverses the order of points in lr (is CW -> CCW or CCW->CW) */
  LinearRing reverseRing(LinearRing lr) {
    int numPoints = lr.getNumPoints();
    Coordinate[] newCoords = new Coordinate[numPoints];

    for (int t = 0; t < numPoints; t++) {
      newCoords[t] = lr.getCoordinateN(numPoints - t - 1);
    }

    return new LinearRing(newCoords, new PrecisionModel(), 0);
  }
  /*
   * Test method for 'org.locationtech.udig.tools.edit.support.PixelCoordMap.addCoord(int, int, Coordinate)'
   */
  @Test
  public void testAddCoord() {
    EventListener l = new EventListener();

    GeometryFactory factory = new GeometryFactory();
    Geometry geom = factory.createPoint(new Coordinate(10, 5));
    EditBlackboard map = new EditBlackboard(SCREEN.x, SCREEN.y, transform, layerToWorld);
    map.getListeners().add(l);

    map.setGeometries(geom, null);
    assertPixMapState(map, 1, 1, 0, 0);

    EditGeom geomShape = map.getGeoms().get(0);
    map.addPoint(10, 5, geomShape.getShell());
    assertPixMapState(map, 1, 2, 0, 0);
    assertEquals(1, map.getCoords(10, 5).size());
    assertEquals(EventType.ADD_POINT, l.getEditBlackboardEvent().getType());
    EditBlackboardEvent editBlackboardEvent = l.getEditBlackboardEvent();
    assertEquals(null, editBlackboardEvent.getOldValue());
    assertEquals(Point.valueOf(10, 5), editBlackboardEvent.getNewValue());

    LinearRing ring = createShellRing(factory, 10);

    map = new EditBlackboard(SCREEN.x, SCREEN.y, transform, layerToWorld);
    map.getListeners().add(l);
    Map<Geometry, EditGeom> mapping = map.setGeometries(ring, null);
    assertPixMapState(map, 1, 5, 0, 0);

    // add at a particular index.
    map.insertCoord(25, 15, 3, mapping.get(ring).getShell());
    assertEquals(Point.valueOf(20, 10), mapping.get(ring).getShell().getPoint(0));
    assertEquals(Point.valueOf(30, 10), mapping.get(ring).getShell().getPoint(1));
    assertEquals(Point.valueOf(30, 15), mapping.get(ring).getShell().getPoint(2));
    assertEquals(Point.valueOf(25, 15), mapping.get(ring).getShell().getPoint(3));
    assertEquals(Point.valueOf(20, 15), mapping.get(ring).getShell().getPoint(4));
    assertEquals(EventType.ADD_POINT, l.event.getType());
    editBlackboardEvent = l.getEditBlackboardEvent();
    assertEquals(null, editBlackboardEvent.getOldValue());
    assertEquals(Point.valueOf(25, 15), editBlackboardEvent.getNewValue());
    assertEquals(mapping.get(ring).getShell(), editBlackboardEvent.getSource());

    // create a geom one point at a time   test ordering of geometries
    map = new EditBlackboard(SCREEN.x, SCREEN.y, transform, layerToWorld);
    map.getListeners().add(l);
    geomShape = map.getGeoms().get(0);
    map.addPoint(0, 0, geomShape.getShell());
    map.addPoint(0, 100, geomShape.getShell());
    map.addPoint(50, 100, geomShape.getShell());
    map.addPoint(100, 100, geomShape.getShell());
    map.addPoint(50, 150, geomShape.getShell());

    assertEquals(Point.valueOf(0, 0), geomShape.getShell().getPoint(0));
    assertEquals(Point.valueOf(0, 100), geomShape.getShell().getPoint(1));
    assertEquals(Point.valueOf(50, 100), geomShape.getShell().getPoint(2));
    assertEquals(Point.valueOf(100, 100), geomShape.getShell().getPoint(3));
    assertEquals(Point.valueOf(50, 150), geomShape.getShell().getPoint(4));

    // test the coordinates were created correcly
    assertEquals(new Coordinate(-9.5, -4.5), geomShape.getShell().getCoord(0));
    assertEquals(new Coordinate(-9.5, 95.5), geomShape.getShell().getCoord(1));
    assertEquals(new Coordinate(40.5, 95.5), geomShape.getShell().getCoord(2));
    assertEquals(new Coordinate(90.5, 95.5), geomShape.getShell().getCoord(3));
    assertEquals(new Coordinate(40.5, 145.5), geomShape.getShell().getCoord(4));

    // now making sure the the CoordMap is correctly updated too.
    assertEquals(1, map.getCoords(0, 0).size());
    assertEquals(new Coordinate(-9.5, -4.5), map.getCoords(0, 0).get(0));
    assertEquals(1, map.getCoords(0, 100).size());
    assertEquals(new Coordinate(-9.5, 95.5), map.getCoords(0, 100).get(0));
    assertEquals(1, map.getCoords(0, 100).size());
    assertEquals(new Coordinate(40.5, 95.5), map.getCoords(50, 100).get(0));
    assertEquals(1, map.getCoords(0, 100).size());
    assertEquals(new Coordinate(90.5, 95.5), map.getCoords(100, 100).get(0));
    assertEquals(1, map.getCoords(50, 150).size());
    assertEquals(new Coordinate(40.5, 145.5), map.getCoords(50, 150).get(0));

    // insert a vertex in a hole
    Polygon polygon = createPolygon(factory, 10);
    map = new EditBlackboard(SCREEN.x, SCREEN.y, transform, layerToWorld);
    map.getListeners().add(l);
    mapping = map.setGeometries(polygon, null);
    assertPixMapState(map, 1, 5, 1, 5);

    // add at a particular index.
    PrimitiveShape hole = mapping.get(polygon).getHoles().get(0);
    map.insertCoord(26, 13, 3, hole);
    assertEquals(Point.valueOf(25, 12), hole.getPoint(0));
    assertEquals(Point.valueOf(28, 12), hole.getPoint(1));
    assertEquals(Point.valueOf(28, 13), hole.getPoint(2));
    assertEquals(Point.valueOf(26, 13), hole.getPoint(3));
    assertEquals(Point.valueOf(25, 13), hole.getPoint(4));
    editBlackboardEvent = l.getEditBlackboardEvent();
    assertEquals(null, editBlackboardEvent.getOldValue());
    assertEquals(Point.valueOf(26, 13), editBlackboardEvent.getNewValue());
    assertEquals(hole, editBlackboardEvent.getSource());

    // add on an edge test ordering of geometries
    LinearRing ring2 = createShellRing(factory, 30);
    LinearRing ring3 = createShellRing(factory, 32);

    ring3.getCoordinateN(2).y = 20;

    mapping = map.setGeometries(ring2, null);
    mapping.putAll(map.addGeometry(ring3, null));

    map.addToNearestEdge(54, 12, mapping.get(ring2), true);
    assertEquals(1, map.getCoords(54, 12).size());
    assertEquals(new Coordinate(44.5, 7.5), mapping.get(ring2).getShell().getCoord(2));
    editBlackboardEvent = l.getEditBlackboardEvent();
    assertEquals(null, editBlackboardEvent.getOldValue());
    assertEquals(Point.valueOf(54, 12), editBlackboardEvent.getNewValue());
    assertEquals(mapping.get(ring2).getShell(), editBlackboardEvent.getSource());

    mapping = map.setGeometries(ring2, null);
    mapping.putAll(map.addGeometry(ring3, null));

    List<ClosestEdge> added = map.addToNearestEdge(51, 12, true);
    assertEquals(2, added.size());
    assertTrue(
        added.get(0).getGeom() == mapping.get(ring3)
            || added.get(1).getGeom() == mapping.get(ring3));
    assertTrue(
        added.get(0).getGeom() == mapping.get(ring2)
            || added.get(1).getGeom() == mapping.get(ring2));
    assertEquals(2, map.getCoords(51, 12).size());
    assertEquals(new Coordinate(41.5, 7.5), mapping.get(ring2).getShell().getCoord(2));
    assertEquals(new Coordinate(41.5, 7.5), mapping.get(ring3).getShell().getCoord(2));
    editBlackboardEvent = l.getEditBlackboardEvent();
    assertEquals(null, editBlackboardEvent.getOldValue());
    assertEquals(Point.valueOf(51, 12), editBlackboardEvent.getNewValue());
    assertTrue(((Collection) l.event.getSource()).contains(added.get(0).getPart()));
    assertTrue(((Collection) l.event.getSource()).contains(added.get(1).getPart()));

    // create hole one point at a time
    map = new EditBlackboard(SCREEN.x, SCREEN.y, transform, layerToWorld);
    map.getListeners().add(l);
    geomShape = map.getGeoms().get(0);
    hole = geomShape.newHole();
    try {
      map.addPoint(0, 0, null);
      fail();
    } catch (Exception e) {
      // good
    }

    map.addPoint(0, 0, hole);
    assertEquals(Point.valueOf(0, 0), l.getEditBlackboardEvent().getNewValue());
    map.addPoint(0, 100, hole);
    assertEquals(Point.valueOf(0, 100), l.getEditBlackboardEvent().getNewValue());
    map.addPoint(50, 100, hole);
    assertEquals(Point.valueOf(50, 100), l.getEditBlackboardEvent().getNewValue());
    map.addPoint(100, 100, hole);
    assertEquals(Point.valueOf(100, 100), l.getEditBlackboardEvent().getNewValue());
    map.addPoint(50, 150, hole);
    assertEquals(null, l.getEditBlackboardEvent().getOldValue());
    assertEquals(Point.valueOf(50, 150), l.getEditBlackboardEvent().getNewValue());
    assertEquals(hole, l.getEditBlackboardEvent().getSource());

    assertEquals(Point.valueOf(0, 0), geomShape.getHoles().get(0).getPoint(0));
    assertEquals(Point.valueOf(0, 100), geomShape.getHoles().get(0).getPoint(1));
    assertEquals(Point.valueOf(50, 100), geomShape.getHoles().get(0).getPoint(2));
    assertEquals(Point.valueOf(100, 100), geomShape.getHoles().get(0).getPoint(3));
    assertEquals(Point.valueOf(50, 150), geomShape.getHoles().get(0).getPoint(4));

    // test the coordinates were created correcly
    assertEquals(new Coordinate(-9.5, -4.5), geomShape.getHoles().get(0).getCoord(0));
    assertEquals(new Coordinate(-9.5, 95.5), geomShape.getHoles().get(0).getCoord(1));
    assertEquals(new Coordinate(40.5, 95.5), geomShape.getHoles().get(0).getCoord(2));
    assertEquals(new Coordinate(90.5, 95.5), geomShape.getHoles().get(0).getCoord(3));
    assertEquals(new Coordinate(40.5, 145.5), geomShape.getHoles().get(0).getCoord(4));

    // now making sure the the CoordMap is correctly updated too.
    assertEquals(1, map.getCoords(0, 0).size());
    assertEquals(new Coordinate(-9.5, -4.5), map.getCoords(0, 0).get(0));
    assertEquals(1, map.getCoords(0, 100).size());
    assertEquals(new Coordinate(-9.5, 95.5), map.getCoords(0, 100).get(0));
    assertEquals(1, map.getCoords(0, 100).size());
    assertEquals(new Coordinate(40.5, 95.5), map.getCoords(50, 100).get(0));
    assertEquals(1, map.getCoords(0, 100).size());
    assertEquals(new Coordinate(90.5, 95.5), map.getCoords(100, 100).get(0));
    assertEquals(1, map.getCoords(50, 150).size());
    assertEquals(new Coordinate(40.5, 145.5), map.getCoords(50, 150).get(0));

    // call add to edge so that it is added to a hole
    polygon = createPolygon(factory, 10);
    LinearRing ring1 = createShellRing(factory, 10);
    mapping = map.setGeometries(ring1, null);
    mapping.putAll(map.addGeometry(polygon, null));

    added = map.addToNearestEdge(26, 12, true);
    assertEquals(1, added.size());
    assertEquals(6, added.iterator().next().getGeom().getHoles().get(0).getNumCoords());

    map = new EditBlackboard(SCREEN.x, SCREEN.y, transform, this.layerToWorld);
    map.getListeners().add(l);
    added = map.addToNearestEdge(26, 12, true);
    assertEquals(Point.valueOf(26, 12), map.getGeoms().get(0).getShell().getPoint(0));
  }