/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.commons.geometry.euclidean.twod;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.function.Consumer;

import org.apache.commons.numbers.angle.PlaneAngleRadians;
import org.apache.commons.geometry.core.precision.DoublePrecisionContext;
import org.apache.commons.geometry.core.precision.EpsilonDoublePrecisionContext;
import org.apache.commons.geometry.euclidean.EuclideanTestUtils;
import org.apache.commons.geometry.euclidean.twod.InteriorAngleSegmentConnector.Maximize;
import org.apache.commons.geometry.euclidean.twod.InteriorAngleSegmentConnector.Minimize;
import org.junit.Assert;
import org.junit.Test;

public class InteriorAngleSegmentConnectorTest {

    private static final double TEST_EPS = 1e-10;

    private static final DoublePrecisionContext TEST_PRECISION =
            new EpsilonDoublePrecisionContext(TEST_EPS);

    @Test
    public void testConnectAll_noSegments() {
        runWithMaxAndMin(connector -> {
            // arrange
            List<Segment> segments = new ArrayList<>();

            // act
            List<Polyline> paths = connector.connectAll(segments);

            // assert
            Assert.assertEquals(0, paths.size());
        });
    }

    @Test
    public void testConnectAll_singleFiniteSegment() {
        runWithMaxAndMin(connector -> {
            // arrange
            List<Segment> segments = Arrays.asList(
                        Segment.fromPoints(Vector2D.ZERO, Vector2D.Unit.PLUS_X, TEST_PRECISION)
                    );

            // act
            List<Polyline> paths = connector.connectAll(segments);

            // assert
            Assert.assertEquals(1, paths.size());

            assertFinitePath(paths.get(0), Vector2D.ZERO, Vector2D.Unit.PLUS_X);
        });
    }

    @Test
    public void testConnectAll_dualConnectedSegments() {
        runWithMaxAndMin(connector -> {
            // arrange
            List<Segment> segments = Arrays.asList(
                        Segment.fromPoints(Vector2D.ZERO, Vector2D.Unit.PLUS_X, TEST_PRECISION),
                        Segment.fromPoints(Vector2D.Unit.PLUS_X, Vector2D.ZERO, TEST_PRECISION)
                    );

            // act
            List<Polyline> paths = connector.connectAll(segments);

            // assert
            Assert.assertEquals(1, paths.size());

            Assert.assertTrue(paths.get(0).isClosed());
            assertFinitePath(paths.get(0), Vector2D.ZERO, Vector2D.Unit.PLUS_X, Vector2D.ZERO);
        });
    }

    @Test
    public void testConnectAll_singleFiniteSegmentLoop() {
        runWithMaxAndMin(connector -> {
            // arrange
            List<Segment> segments = shuffle(createSquare(Vector2D.ZERO, 1, 1));

            // act
            List<Polyline> paths = connector.connectAll(segments);

            // assert
            Assert.assertEquals(1, paths.size());

            assertFinitePath(paths.get(0),
                    Vector2D.ZERO, Vector2D.Unit.PLUS_X, Vector2D.of(1, 1),
                    Vector2D.of(0, 1), Vector2D.ZERO);
        });
    }

    @Test
    public void testConnectAll_disjointPaths() {
        runWithMaxAndMin(connector -> {
            // arrange
            List<Segment> segments = new ArrayList<>();
            segments.addAll(createSquare(Vector2D.ZERO, 1, 1));

            Vector2D pt = Vector2D.of(0, 2);
            Segment a = Line.fromPointAndAngle(pt, 0.0, TEST_PRECISION).segmentTo(pt);
            Segment b = Line.fromPointAndAngle(pt, PlaneAngleRadians.PI_OVER_TWO, TEST_PRECISION).segmentFrom(pt);

            segments.add(a);
            segments.add(b);

            shuffle(segments);

            // act
            List<Polyline> paths = connector.connectAll(segments);

            // assert
            Assert.assertEquals(2, paths.size());

            assertFinitePath(paths.get(0),
                    Vector2D.ZERO, Vector2D.Unit.PLUS_X, Vector2D.of(1, 1),
                    Vector2D.of(0, 1), Vector2D.ZERO);

            assertInfinitePath(paths.get(1), a, b, pt);
        });
    }

    @Test
    public void testConnectAll_squaresJoinedAtVertex_maximize() {
        // arrange
        Maximize connector = new Maximize();

        List<Segment> segments = new ArrayList<>();
        segments.addAll(createSquare(Vector2D.ZERO, 1, 1));
        segments.addAll(createSquare(Vector2D.of(1, 1), 1, 1));

        shuffle(segments);

        // act
        List<Polyline> paths = connector.connectAll(segments);

        // assert
        Assert.assertEquals(1, paths.size());

        assertFinitePath(paths.get(0),
                Vector2D.ZERO, Vector2D.Unit.PLUS_X, Vector2D.of(1, 1),
                Vector2D.of(2, 1), Vector2D.of(2, 2),
                Vector2D.of(1, 2), Vector2D.of(1, 1),
                Vector2D.of(0, 1), Vector2D.ZERO);
    }

    @Test
    public void testConnectAll_mutipleSegmentsAtVertex_maximize() {
        // arrange
        Maximize connector = new Maximize();

        List<Segment> segments = new ArrayList<>();
        segments.add(Segment.fromPoints(Vector2D.ZERO, Vector2D.of(2, 2), TEST_PRECISION));

        segments.add(Segment.fromPoints(Vector2D.of(2, 2), Vector2D.of(2, 4), TEST_PRECISION));
        segments.add(Segment.fromPoints(Vector2D.of(2, 2), Vector2D.of(1, 3), TEST_PRECISION));

        // act
        List<Polyline> paths = connector.connectAll(segments);

        // assert
        Assert.assertEquals(2, paths.size());

        assertFinitePath(paths.get(0),
                Vector2D.ZERO, Vector2D.of(2, 2), Vector2D.of(2, 4));

        assertFinitePath(paths.get(1), Vector2D.of(2, 2), Vector2D.of(1, 3));
    }

    @Test
    public void testConnectAll_squaresJoinedAtVertex_minimize() {
        // arrange
        Minimize connector = new Minimize();

        List<Segment> segments = new ArrayList<>();
        segments.addAll(createSquare(Vector2D.ZERO, 1, 1));
        segments.addAll(createSquare(Vector2D.of(1, 1), 1, 1));

        shuffle(segments);

        // act
        List<Polyline> paths = connector.connectAll(segments);

        // assert
        Assert.assertEquals(2, paths.size());

        assertFinitePath(paths.get(0),
                Vector2D.ZERO, Vector2D.Unit.PLUS_X, Vector2D.of(1, 1),
                Vector2D.of(0, 1), Vector2D.ZERO);

        assertFinitePath(paths.get(1),
                Vector2D.of(1, 1), Vector2D.of(2, 1), Vector2D.of(2, 2),
                Vector2D.of(1, 2), Vector2D.of(1, 1));
    }

    @Test
    public void testConnectAll_mutipleSegmentsAtVertex_minimize() {
        // arrange
        Minimize connector = new Minimize();

        List<Segment> segments = new ArrayList<>();
        segments.add(Segment.fromPoints(Vector2D.ZERO, Vector2D.of(2, 2), TEST_PRECISION));

        segments.add(Segment.fromPoints(Vector2D.of(2, 2), Vector2D.of(2, 4), TEST_PRECISION));
        segments.add(Segment.fromPoints(Vector2D.of(2, 2), Vector2D.of(1, 3), TEST_PRECISION));

        // act
        List<Polyline> paths = connector.connectAll(segments);

        // assert
        Assert.assertEquals(2, paths.size());

        assertFinitePath(paths.get(0),
                Vector2D.ZERO, Vector2D.of(2, 2), Vector2D.of(1, 3));

        assertFinitePath(paths.get(1), Vector2D.of(2, 2), Vector2D.of(2, 4));
    }

    @Test
    public void testConnectMaximized() {
        // arrange
        List<Segment> segments = new ArrayList<>();
        segments.add(Segment.fromPoints(Vector2D.ZERO, Vector2D.of(2, 2), TEST_PRECISION));

        segments.add(Segment.fromPoints(Vector2D.of(2, 2), Vector2D.of(2, 4), TEST_PRECISION));
        segments.add(Segment.fromPoints(Vector2D.of(2, 2), Vector2D.of(1, 3), TEST_PRECISION));

        // act
        List<Polyline> paths = InteriorAngleSegmentConnector.connectMaximized(segments);

        // assert
        Assert.assertEquals(2, paths.size());

        assertFinitePath(paths.get(0),
                Vector2D.ZERO, Vector2D.of(2, 2), Vector2D.of(2, 4));

        assertFinitePath(paths.get(1), Vector2D.of(2, 2), Vector2D.of(1, 3));
    }

    @Test
    public void testConnectMinimized() {
        // arrange
        List<Segment> segments = new ArrayList<>();
        segments.add(Segment.fromPoints(Vector2D.ZERO, Vector2D.of(2, 2), TEST_PRECISION));

        segments.add(Segment.fromPoints(Vector2D.of(2, 2), Vector2D.of(2, 4), TEST_PRECISION));
        segments.add(Segment.fromPoints(Vector2D.of(2, 2), Vector2D.of(1, 3), TEST_PRECISION));

        // act
        List<Polyline> paths = InteriorAngleSegmentConnector.connectMinimized(segments);

        // assert
        Assert.assertEquals(2, paths.size());

        assertFinitePath(paths.get(0),
                Vector2D.ZERO, Vector2D.of(2, 2), Vector2D.of(1, 3));

        assertFinitePath(paths.get(1), Vector2D.of(2, 2), Vector2D.of(2, 4));
    }

    /**
     * Run the given consumer function twice, once with a Maximize instance and once with
     * a Minimize instance.
     */
    private static void runWithMaxAndMin(Consumer<InteriorAngleSegmentConnector> body) {
        body.accept(new Maximize());
        body.accept(new Minimize());
    }

    private static List<Segment> createSquare(final Vector2D lowerLeft, final double width, final double height) {
        final Vector2D lowerRight = Vector2D.of(lowerLeft.getX() + width, lowerLeft.getY());
        final Vector2D upperRight = Vector2D.of(lowerLeft.getX() + width, lowerLeft.getY() + height);
        final Vector2D upperLeft = Vector2D.of(lowerLeft.getX(), lowerLeft.getY() + height);

        return Arrays.asList(
                    Segment.fromPoints(lowerLeft, lowerRight, TEST_PRECISION),
                    Segment.fromPoints(lowerRight, upperRight, TEST_PRECISION),
                    Segment.fromPoints(upperRight, upperLeft, TEST_PRECISION),
                    Segment.fromPoints(upperLeft, lowerLeft, TEST_PRECISION)
                );
    }

    private static List<Segment> shuffle(final List<Segment> segments) {
        return shuffle(segments, 1);
    }

    private static List<Segment> shuffle(final List<Segment> segments, final int seed) {
        Collections.shuffle(segments, new Random(seed));

        return segments;
    }

    private static void assertInfinitePath(Polyline path, Segment start, Segment end,
            Vector2D ... vertices) {
        Assert.assertTrue(path.isInfinite());
        Assert.assertFalse(path.isFinite());

        Assert.assertEquals(start, path.getStartSegment());
        Assert.assertEquals(end, path.getEndSegment());

        assertPathVertices(path, vertices);
    }

    private static void assertFinitePath(Polyline path, Vector2D ... vertices)
    {
        Assert.assertFalse(path.isInfinite());
        Assert.assertTrue(path.isFinite());

        assertPathVertices(path, vertices);
    }

    private static void assertPathVertices(Polyline path, Vector2D ... vertices) {
        List<Vector2D> expectedVertices = Arrays.asList(vertices);
        List<Vector2D> actualVertices = path.getVertices();

        String msg = "Expected path vertices to equal " + expectedVertices + " but was " + actualVertices;
        Assert.assertEquals(msg, expectedVertices.size(), actualVertices.size());

        for (int i=0; i<expectedVertices.size(); ++i) {
            EuclideanTestUtils.assertCoordinatesEqual(expectedVertices.get(i), actualVertices.get(i), TEST_EPS);
        }
    }
}
