package nl.tudelft.jpacman.npc.ghost;

import static org.mockito.Mockito.mock;

import static org.assertj.core.api.Assertions.assertThat;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

import nl.tudelft.jpacman.board.Board;
import nl.tudelft.jpacman.board.BoardFactory;
import nl.tudelft.jpacman.board.Direction;
import nl.tudelft.jpacman.board.Square;
import nl.tudelft.jpacman.board.Unit;
import nl.tudelft.jpacman.level.LevelFactory;
import nl.tudelft.jpacman.level.MapParser;
import nl.tudelft.jpacman.level.Pellet;
import nl.tudelft.jpacman.sprite.PacManSprites;

import com.google.common.collect.Lists;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

/**
 * Tests the various methods provided by the {@link Navigation} class.
 *
 * @author Jeroen Roosen
 *
 */
@SuppressWarnings({"magicnumber", "PMD.AvoidDuplicateLiterals",
"initialization.fields.uninitialized", "argument.type.incompatible"})
class NavigationTest {

	/**
	 * Map parser used to construct boards.
	 */
	private MapParser parser;

	/**
	 * Set up the map parser.
	 */
	@BeforeEach
	void setUp() {
		PacManSprites sprites = new PacManSprites();
		parser = new MapParser(new LevelFactory(sprites, new GhostFactory(
				sprites)), new BoardFactory(sprites));
	}

	/**
	 * Verifies that the path to the same square is empty.
	 */
	@Test
	void testShortestPathEmpty() {
		Board b = parser.parseMap(Lists.newArrayList(" ")).getBoard();
		Square s1 = b.squareAt(0, 0);
		Square s2 = b.squareAt(0, 0);
		List<Direction> path = Navigation
				.shortestPath(s1, s2, mock(Unit.class));
		assertThat(path).isEmpty();
	}

	/**
	 * Verifies that if no path exists, the result is <code>null</code>.
	 */
	@Test
	void testNoShortestPath() {
		Board b = parser
				.parseMap(Lists.newArrayList("#####", "# # #", "#####"))
				.getBoard();
		Square s1 = b.squareAt(1, 1);
		Square s2 = b.squareAt(3, 1);
		List<Direction> path = Navigation
				.shortestPath(s1, s2, mock(Unit.class));
		assertThat(path).isNull();
	}

	/**
	 * Verifies that having no traveller ignores terrain.
	 */
	@Test
	void testNoTraveller() {
		Board b = parser
				.parseMap(Lists.newArrayList("#####", "# # #", "#####"))
				.getBoard();
		Square s1 = b.squareAt(1, 1);
		Square s2 = b.squareAt(3, 1);
		List<Direction> path = Navigation.shortestPath(s1, s2, null);
		assertThat(path).containsExactly(Direction.EAST, Direction.EAST);
	}

	/**
	 * Tests if the algorithm can find a path in a straight line.
	 */
	@Test
	void testSimplePath() {
		Board b = parser.parseMap(Lists.newArrayList("####", "#  #", "####"))
				.getBoard();
		Square s1 = b.squareAt(1, 1);
		Square s2 = b.squareAt(2, 1);
		List<Direction> path = Navigation
				.shortestPath(s1, s2, mock(Unit.class));
        assertThat(path).containsExactly(Direction.EAST);
	}

	/**
	 * Verifies that the algorithm can find a path when it has to take corners.
	 */
	@Test
	void testCornerPath() {
		Board b = parser.parseMap(
				Lists.newArrayList("####", "#  #", "## #", "####")).getBoard();
		Square s1 = b.squareAt(1, 1);
		Square s2 = b.squareAt(2, 2);
		List<Direction> path = Navigation
				.shortestPath(s1, s2, mock(Unit.class));
        assertThat(path).containsExactly(Direction.EAST, Direction.SOUTH);
	}

	/**
	 * Verifies that the nearest object is detected.
	 */
	@Test
	void testNearestUnit() {
		Board b = parser
				.parseMap(Lists.newArrayList("#####", "# ..#", "#####"))
				.getBoard();
		Square s1 = b.squareAt(1, 1);
		Square s2 = b.squareAt(2, 1);
		@SuppressWarnings("nullness")
		@NonNull Unit nearestPellet = Navigation.findNearest(Pellet.class, s1);
		assertThat(nearestPellet).isNotNull();
		assertThat(nearestPellet.getSquare()).isEqualTo(s2);
	}

	/**
	 * Verifies that there is no such location if there is no nearest object.
	 */
	@Test
	void testNoNearestUnit() {
		Board b = parser.parseMap(Lists.newArrayList(" ")).getBoard();
		Square s1 = b.squareAt(0, 0);
		Unit unit = Navigation.findNearest(Pellet.class, s1);
		assertThat(unit).isNull();
	}

	/**
	 * Verifies that there is ghost on the default board
	 * next to cell [1, 1].
	 *
	 * @throws IOException if board reading fails.
	 */
	@Test
	void testFullSizedLevel() throws IOException {
		try (InputStream i = getClass().getResourceAsStream("/board.txt")) {
			Board b = parser.parseMap(i).getBoard();
			Square s1 = b.squareAt(1, 1);
			Unit unit = Navigation.findNearest(Ghost.class, s1);
			assertThat(unit).isNotNull();
		}
	}
}

@@ -640,67 +640,8 @@
 ts;%0A
-import org.checkerframework.checker.nullness.qual.NonNull;%0A
 impo
@@ -3324,32 +3324,78 @@
 ed.%0A%09 */%0A%09@Test%0A
+%09@SuppressWarnings(%22dereference.of.nullable%22)%0A
 %09void testNeares
@@ -3571,49 +3571,8 @@
 ;%0A%09%09
-@SuppressWarnings(%22nullness%22)%0A%09%09@NonNull 
 Unit

Revert "Put warning suppression in a smaller scope"