package nxm.sys.test.junit.libg;

import java.awt.geom.Rectangle2D;
import java.util.Arrays;

import nxm.sys.lib.Midas;
import nxm.sys.libg.GeometricUtil;
import nxm.sys.libg.GeometricUtil.LineSegmentGroup;
import nxm.sys.libg.IntersectingLine.IntersectLocation;
import nxm.sys.test.junit.NMTestCase;


  /** Test methods in view dealing with the intersection of a polygon
      with the view area

      @author Jeanette McCall
      @since NeXtMidas 3.9.1
*/
public final class GeometricUtilTest extends NMTestCase {

  /** public constructor to run this JUnit test case outside of NeXtMidas (e.g. Eclipse) */
  public GeometricUtilTest (String methodName) { this(methodName, null); }

  /** Creates a new instance of the class.
      @param methodName The name of the method being called.
      @param midas      The midas context testTo use when running the tests.
   */
  public GeometricUtilTest (String methodName, Midas midas) {
    super(methodName, midas);
  }

//////////////////////////////////////////////////////////////////////////////



  public void testConnectTheDots () {
    // 80  X                             X
    // 70  P                             P
    // 60        --------------------
    // 50        |    X             *    X
    // 40        *                  |
    // 30  X     -------------------|
    // 20
    // 10
    // 0   10   20   30   40   50   60   70   80
    //
    // X - original polygon points
    // * - intersects with view area
    // P - points added to intersect and interior point to form shape to be filled
    // Note: assumes margin of 10
    int[] visibleX_X1X2 = {20, 30, 60};
    int[] visibleY_X1X2 = {40, 50, 50};
    double[] fullPolygonX_X1X2 = {10, 30, 70, 70, 10, 10};
    double[] fullPolygonY_X1X2_U = {30, 50, 50, 80, 80, 30};
    int[][] expectedX1X2ShadeUp = {{20, 30, 60, 70, 10, 20}, {40, 50, 50, 70, 70, 40}};
    IntersectLocation firstIntersectLoc = IntersectLocation.X1_BORDER;
    IntersectLocation secondIntersectLoc = IntersectLocation.X2_BORDER;
    Rectangle2D.Double viewBox = new Rectangle2D.Double(20.0, 30.0, 40.0, 30.0);
    int[][] actualX1X2ShadeUp = GeometricUtil.connectTheDots(visibleX_X1X2, visibleY_X1X2, firstIntersectLoc, secondIntersectLoc,
        viewBox, fullPolygonX_X1X2, fullPolygonY_X1X2_U, 6, 0);

    boolean xEqual = Arrays.equals(expectedX1X2ShadeUp[0], actualX1X2ShadeUp[0]);
    boolean yEqual = Arrays.equals(expectedX1X2ShadeUp[1], actualX1X2ShadeUp[1]);
    assertTrue("connectTheDots with X1,X2 bounds intercept - shade up", xEqual && yEqual);

    // 80
    // 70
    // 60        --------------------
    // 50        |    X             *    X
    // 40        *                  |
    // 30  X     --------------------
    // 20  P                             P
    // 10  X                             X
    // 0   10   20   30   40   50   60   70   80
    //
    // X - original polygon points
    // * - intersects with view area
    // P - points added to intersect and interior point to form shape to be filled
    // Note: assumes margin of 10
    double[] fullPolygonY_X1X2_D = {30, 50, 50, 10, 10, 30};
    int[][] expectedX1X2ShadeDown = {{20, 30, 60, 70, 10, 20}, {40, 50, 50, 20, 20, 40}};

    int[][] actualX1X2ShadeDown = GeometricUtil.connectTheDots(visibleX_X1X2, visibleY_X1X2, firstIntersectLoc, secondIntersectLoc,
        viewBox, fullPolygonX_X1X2, fullPolygonY_X1X2_D, 6, 0);
    xEqual = Arrays.equals(expectedX1X2ShadeDown[0], actualX1X2ShadeDown[0]);
    yEqual = Arrays.equals(expectedX1X2ShadeDown[1], actualX1X2ShadeDown[1]);
    assertTrue("connectTheDots with X1,X2 bounds intercept - shade down", xEqual && yEqual);

    // 80
    // 70
    // 60        --------------------         X
    // 50        |                  |
    // 40        |                  *
    // 30        --------------*----|
    // 20                                P
    // 10            X                        X
    // 0   10   20   30   40   50   60   70   80
    //
    // X - original polygon points
    // * - intersects with view area
    // P - points added to intersect and interior point to form shape to be filled

    int[] visibleX_Y1X2 = {50, 60};
    int[] visibleY_Y1X2 = {30, 40};
    double[] fullPolygonX_Y1X2 = {30, 80, 80, 30};
    double[] fullPolygonY_Y1X2_U = {10, 60, 10, 10};
    int[][] expectedY1X2ShadeDown = {{50, 60, 70, 50}, {30, 40, 20, 30}};
    firstIntersectLoc = IntersectLocation.Y1_BORDER;
    secondIntersectLoc = IntersectLocation.X2_BORDER;

    int[][] actualY1X2ShadeDown = GeometricUtil.connectTheDots(visibleX_Y1X2, visibleY_Y1X2, firstIntersectLoc, secondIntersectLoc,
        viewBox, fullPolygonX_Y1X2, fullPolygonY_Y1X2_U, 4, 0);


    xEqual = Arrays.equals(expectedY1X2ShadeDown[0], actualY1X2ShadeDown[0]);
    yEqual = Arrays.equals(expectedY1X2ShadeDown[1], actualY1X2ShadeDown[1]);
    assertTrue("connectTheDots with X1,X2 bounds intercept - shade down", xEqual && yEqual);
  }

  public void testPolygonRectangleIntersect () {
    // 80  X                             X
    // 70  P                             P
    // 60        --------------------
    // 50        |    X             *    X
    // 40        *                  |
    // 30  X     -------------------|
    // 20
    // 10
    // 0   10   20   30   40   50   60   70   80
    //
    // X - original polygon points
    // * - intersects with view area
    // P - points added to intersect and interior point to form shape to be filled
    // Note: assumes margin of 10
    int[] visibleX_X1X2 = {20, 30, 60};
    int[] visibleY_X1X2 = {40, 50, 50};
    double[] fullPolygonX_X1X2 = {10, 30, 70, 70, 10, 10};
    double[] fullPolygonY_X1X2 = {30, 50, 50, 80, 80, 30};
    Rectangle2D.Double rectangle = new Rectangle2D.Double(20.0, 30.0, 40.0, 30.0);
    LineSegmentGroup[] intersectingLineSegGrps = GeometricUtil.polygonRectangleIntersect(fullPolygonX_X1X2, fullPolygonY_X1X2, fullPolygonX_X1X2.length,
        0, rectangle);

    boolean xEqual = Arrays.equals(visibleX_X1X2, intersectingLineSegGrps[0].getXcoords());
    boolean yEqual = Arrays.equals(visibleY_X1X2, intersectingLineSegGrps[0].getYcoords());
    assertTrue("testPolygonRectangleIntersect with X1,X2 bounds intercept - 1st point not enclosed", xEqual && yEqual);

    // same polygon, etc., except first point in middle of shape
    double[] fullPolygonX_X1X2_E = {30, 70, 70, 10, 10, 30};
    double[] fullPolygonY_X1X2_E = {50, 50, 80, 80, 30, 50};
    intersectingLineSegGrps = GeometricUtil.polygonRectangleIntersect(fullPolygonX_X1X2_E, fullPolygonY_X1X2_E, fullPolygonX_X1X2.length,
        0, rectangle);

    xEqual = Arrays.equals(visibleX_X1X2, intersectingLineSegGrps[0].getXcoords());
    yEqual = Arrays.equals(visibleY_X1X2, intersectingLineSegGrps[0].getYcoords());
    assertTrue("testPolygonRectangleIntersect with X1,X2 bounds intercept - 1st point enclosed", xEqual && yEqual);

    // Test that a line crosses exactly over a corner of the viewable area.
    // 80  X         X
    // 70
    // 60        ----*---------------
    // 50        |                  |
    // 40        |   X              |
    // 30        *------------------|
    // 20  X
    // 10
    // 0   10   20   30   40   50   60   70   80
    visibleX_X1X2 = new int[]{20, 30, 30};
    visibleY_X1X2 = new int[]{30, 40, 60};
    fullPolygonX_X1X2 = new double[]{10, 30, 30, 10, 10};
    fullPolygonY_X1X2 = new double[]{20, 40, 80, 80, 20};
    rectangle = new Rectangle2D.Double(20.0, 30.0, 40.0, 30.0);
    intersectingLineSegGrps = GeometricUtil.polygonRectangleIntersect(fullPolygonX_X1X2, fullPolygonY_X1X2, fullPolygonX_X1X2.length,
        0, rectangle);

    xEqual = Arrays.equals(visibleX_X1X2, intersectingLineSegGrps[0].getXcoords());
    yEqual = Arrays.equals(visibleY_X1X2, intersectingLineSegGrps[0].getYcoords());
    assertTrue("testPolygonRectangleIntersect with X1,X2 bounds intercept - 1st point not enclosed", xEqual && yEqual);

    //  Tests that the polygon will enter and exit 2 different locations
    // 80  X         X         X
    // 70
    // 60        --------------*-----
    // 50        |                  |
    // 40        |   X         X    *    X
    // 30        *---*--------------|
    // 20  X         X                   X
    // 10
    // 0   10   20   30   40   50   60   70   80
    visibleX_X1X2 = new int[]{20, 30, 30};
    visibleY_X1X2 = new int[]{30, 40, 30};
    int[] visibleX_X1X2Second = new int[]{60, 50, 50};
    int[] visibleY_X1X2Second = new int[]{40, 40, 60};
    fullPolygonX_X1X2 = new double[]{10, 30, 30, 70, 70, 50, 50, 30, 10, 10};
    fullPolygonY_X1X2 = new double[]{20, 40, 20, 20, 40, 40, 80, 80, 80, 20};
    rectangle = new Rectangle2D.Double(20.0, 30.0, 40.0, 30.0);
    intersectingLineSegGrps = GeometricUtil.polygonRectangleIntersect(fullPolygonX_X1X2, fullPolygonY_X1X2, fullPolygonX_X1X2.length,
        0, rectangle);

    for (int i = 0; i < intersectingLineSegGrps.length; i++) {
      xEqual = Arrays.equals(i==0 ? visibleX_X1X2 : visibleX_X1X2Second, intersectingLineSegGrps[i].getXcoords());
      yEqual = Arrays.equals(i==0 ? visibleY_X1X2 : visibleY_X1X2Second, intersectingLineSegGrps[i].getYcoords());
      assertTrue("testPolygonRectangleIntersect with X1,X2 bounds intercept - 1st point not enclosed", xEqual && yEqual);
    }

    // Test that the polygon will enter and exit at three different locations.
    // 80  X         X         X
    // 70
    // 60        ----*---------*-----
    // 50     X  *   X               |
    // 40        |   X         X    *    X
    // 30        *---*--------------|
    // 20  X         X                   X
    // 10
    // 0   10   20   30   40   50   60   70   80
    visibleX_X1X2 = new int[]{20, 30, 30};
    visibleY_X1X2 = new int[]{30, 40, 30};
    visibleX_X1X2Second = new int[]{60, 50, 50};
    visibleY_X1X2Second = new int[]{40, 40, 60};
    int[] visibleX_X1X2Third = new int[]{30, 30, 20};
    int[] visibleY_X1X2Third = new int[]{60, 50, 50};
    fullPolygonX_X1X2 = new double[]{10, 30, 30, 70, 70, 50, 50, 30, 30, 15, 10, 10};
    fullPolygonY_X1X2 = new double[]{20, 40, 20, 20, 40, 40, 80, 80, 50, 50, 80, 20};
    rectangle = new Rectangle2D.Double(20.0, 30.0, 40.0, 30.0);
    intersectingLineSegGrps = GeometricUtil.polygonRectangleIntersect(fullPolygonX_X1X2, fullPolygonY_X1X2, fullPolygonX_X1X2.length,
        0, rectangle);

    for (int i = 0; i < intersectingLineSegGrps.length; i++) {
      xEqual = Arrays.equals(i==0 ? visibleX_X1X2 : i==1? visibleX_X1X2Second : visibleX_X1X2Third, intersectingLineSegGrps[i].getXcoords());
      yEqual = Arrays.equals(i==0 ? visibleY_X1X2 : i==1? visibleY_X1X2Second : visibleY_X1X2Third, intersectingLineSegGrps[i].getYcoords());
      assertTrue("testPolygonRectangleIntersect with X1,X2 bounds intercept - 1st point not enclosed", xEqual && yEqual);
    }

    // Test that a line crosses exactly over a corner of the viewable area.
    // 80  X         X
    // 70
    // 60        ----*---------------
    // 50        |                  |
    // 40        |   X              |
    // 30        *------------------|
    // 20  X
    // 10
    // 0   10   20   30   40   50   60   70   80
    visibleX_X1X2 = new int[]{20, 30, 30};
    visibleY_X1X2 = new int[]{30, 40, 60};
    fullPolygonX_X1X2 = new double[]{30, 30, 10, 10, 30};
    fullPolygonY_X1X2 = new double[]{40, 80, 80, 20, 40};
    rectangle = new Rectangle2D.Double(20.0, 30.0, 40.0, 30.0);
    intersectingLineSegGrps = GeometricUtil.polygonRectangleIntersect(fullPolygonX_X1X2, fullPolygonY_X1X2, fullPolygonX_X1X2.length,
        0, rectangle);

    xEqual = Arrays.equals(visibleX_X1X2, intersectingLineSegGrps[0].getXcoords());
    yEqual = Arrays.equals(visibleY_X1X2, intersectingLineSegGrps[0].getYcoords());
    assertTrue("testPolygonRectangleIntersect with X1,X2 bounds intercept - 1st point not enclosed", xEqual && yEqual);

    // Tests that the triangle points are calculated correctly
    // 80
    // 70
    // 60        --------------------
    // 50        |   X              |
    // 40        |                  |
    // 30        --*----*-----------|
    // 20
    // 10       X         X
    // 0   10   20   30   40   50   60   70   80
    visibleX_X1X2 = new int[]{25, 30, 35};
    visibleY_X1X2 = new int[]{30, 50, 30};
    fullPolygonX_X1X2 = new double[]{20, 30, 40};
    fullPolygonY_X1X2 = new double[]{10, 50, 10};
    rectangle = new Rectangle2D.Double(20.0, 30.0, 40.0, 30.0);
    intersectingLineSegGrps = GeometricUtil.polygonRectangleIntersect(fullPolygonX_X1X2, fullPolygonY_X1X2, fullPolygonX_X1X2.length,
        0, rectangle);

    xEqual = Arrays.equals(visibleX_X1X2, intersectingLineSegGrps[0].getXcoords());
    yEqual = Arrays.equals(visibleY_X1X2, intersectingLineSegGrps[0].getYcoords());
    assertTrue("testPolygonRectangleIntersect with X1,X2 bounds intercept - 1st point not enclosed", xEqual && yEqual);

    // Tests that the triangle points are calculated correctly and the extra points are not going to interfere.
    // 80  X                             X
    // 70
    // 60        --------------------
    // 50        |   X              |
    // 40        |                  |
    // 30        --*----*-----------|
    // 20
    // 10  X    X         X               X
    // 0   10   20   30   40   50   60   70   80
    visibleX_X1X2 = new int[]{25, 30, 35};
    visibleY_X1X2 = new int[]{30, 50, 30};
    fullPolygonX_X1X2 = new double[]{20, 30, 40, 70, 70, 10, 10, 20};
    fullPolygonY_X1X2 = new double[]{10, 50, 10, 10, 80, 80, 10, 10};
    rectangle = new Rectangle2D.Double(20.0, 30.0, 40.0, 30.0);
    intersectingLineSegGrps = GeometricUtil.polygonRectangleIntersect(fullPolygonX_X1X2, fullPolygonY_X1X2, fullPolygonX_X1X2.length,
        0, rectangle);

    xEqual = Arrays.equals(visibleX_X1X2, intersectingLineSegGrps[0].getXcoords());
    yEqual = Arrays.equals(visibleY_X1X2, intersectingLineSegGrps[0].getYcoords());
    assertTrue("testPolygonRectangleIntersect with X1,X2 bounds intercept - 1st point not enclosed", xEqual && yEqual);

    // Tests that the lines cross directly over the top left and bottom left corners of viewbox
    // 80
    // 75  X
    // 70
    // 65
    // 60        *-------------------
    // 55        |                   |
    // 50        |                   |
    // 45        |   X               |
    // 40        |                   |
    // 35        |                   |
    // 30        *-------------------|
    // 25
    // 20
    // 15  X
    // 10
    // 0   10   20   30   40   50   60   70
    visibleX_X1X2 = new int[]{20, 30, 20};
    visibleY_X1X2 = new int[]{30, 45, 60};
    fullPolygonX_X1X2 = new double[]{10, 30, 10};
    fullPolygonY_X1X2 = new double[]{15, 45, 75};
    rectangle = new Rectangle2D.Double(20.0, 30.0, 40.0, 30.0);
    intersectingLineSegGrps = GeometricUtil.polygonRectangleIntersect(fullPolygonX_X1X2, fullPolygonY_X1X2, fullPolygonX_X1X2.length,
        0, rectangle);

    xEqual = Arrays.equals(visibleX_X1X2, intersectingLineSegGrps[0].getXcoords());
    yEqual = Arrays.equals(visibleY_X1X2, intersectingLineSegGrps[0].getYcoords());
    assertTrue("testPolygonRectangleIntersect with X1,X2 bounds intercept - 1st point not enclosed", xEqual && yEqual);

    // Test the the polygon contains the entire viewbox
    // 80  X                             X
    // 70
    // 60        --------------------
    // 50        |                  |
    // 40        |                  |
    // 30        -------------------|
    // 20
    // 10  X                             X
    // 0   10   20   30   40   50   60   70   80

    visibleX_X1X2 = new int[]{20, 20, 60, 60, 20};
    visibleY_X1X2 = new int[]{30, 60, 60, 30, 30};
    fullPolygonX_X1X2 = new double[]{10, 70, 70, 10, 10};
    fullPolygonY_X1X2 = new double[]{10, 10, 80, 80, 10};
    rectangle = new Rectangle2D.Double(20.0, 30.0, 40.0, 30.0);
    GeometricUtil.setViewBoxMargin(0); // no margin for this test
    intersectingLineSegGrps = GeometricUtil.allOrNone(fullPolygonX_X1X2, fullPolygonY_X1X2, fullPolygonX_X1X2.length,
        0, rectangle);
    GeometricUtil.setViewBoxMargin(GeometricUtil.DEFAULT_MARGIN); // restore default margin

    xEqual = Arrays.equals(visibleX_X1X2, intersectingLineSegGrps[0].getXcoords());
    yEqual = Arrays.equals(visibleY_X1X2, intersectingLineSegGrps[0].getYcoords());
    assertTrue("testPolygonRectangleIntersect with X1,X2 bounds intercept - 1st point not enclosed", xEqual && yEqual);

    // Tests that the polygon is completely outside the viewbox
    // 80  X  X
    // 70
    // 60        --------------------
    // 50        |                  |
    // 40        |                  |
    // 30        -------------------|
    // 20
    // 10  X  X
    // 0   10   20   30   40   50   60   70   80

    fullPolygonX_X1X2 = new double[]{10, 15, 15, 10, 10};
    fullPolygonY_X1X2 = new double[]{10, 10, 80, 80, 10};
    rectangle = new Rectangle2D.Double(20.0, 30.0, 40.0, 30.0);
    intersectingLineSegGrps = GeometricUtil.allOrNone(fullPolygonX_X1X2, fullPolygonY_X1X2, fullPolygonX_X1X2.length,
        0, rectangle);

    assertNull("Should have a null value as there are no intersecting line groups", intersectingLineSegGrps);
  }
}

