package maze;

import static org.junit.Assert.*;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;

import org.junit.Test;

public class PathHeapTest {
	
	@Test
	public void childrenAndParent() {
		for (int i = 0; i < 100; i++) {
			assertEquals(i, PathHeap.parentOf(PathHeap.leftOf(i)));
			assertEquals(i, PathHeap.parentOf(PathHeap.rightOf(i)));
			assertEquals(PathHeap.leftOf(i) + 1, PathHeap.rightOf(i));
		}
	}
	
	@Test
	public void childParentExamples() {
		assertEquals(1, PathHeap.leftOf(0));
		assertEquals(2, PathHeap.rightOf(0));
		assertEquals(3, PathHeap.leftOf(PathHeap.leftOf(0)));
		assertEquals(4, PathHeap.rightOf(PathHeap.leftOf(0)));
	}
	
	public void testPut(PathHeap target, Path value) {
		PathHeap old = new PathHeap(target);
		target.put(value);
		assertTrue(target.isConsistent());
		testDifferBy(old, target, value);
	}
	
	public void testRemove(PathHeap target) {
		PathHeap old = new PathHeap(target);
		Path removed = target.remove();
		assertTrue(target.isConsistent());
		testDifferBy(target, old, removed);
		testSmallerThanAll(removed, target);
	}
	
	public void testDifferBy(PathHeap smaller, PathHeap bigger, Path value) {
		HashSet<Path> smallSet = toSet(smaller);
		HashSet<Path> bigSet = toSet(bigger);
		smallSet.add(value);
		assertEquals(smallSet, bigSet);
	}
	
	public HashSet<Path> toSet(PathHeap ph) {
		PathHeap copy = new PathHeap(ph);
		HashSet<Path> result = new HashSet<>();
		while (!copy.isEmpty()) {
			result.add(copy.remove());
		}
		return result;
	}
	
	public void testSmallerThanAll(Path candidate, PathHeap target) {
		PathHeap testee = new PathHeap(target);
		int allegedLowest = candidate.totalDistanceTo(exit);
		while (!testee.isEmpty()) {
			Path removed = testee.remove();
			assertTrue(testee.isConsistent());
			assertTrue(allegedLowest <= removed.totalDistanceTo(exit));
		}
	}
	
	Cell exit = new Cell(200, 200);
	PathHeap ph = new PathHeap(exit);

	@Test
	public void putTest() {
		ArrayList<Path> paths = new ArrayList<>();
		paths.add(new Path(new Cell(0, 0)));
		for (int i = 1; i < 50; i++) {
			paths.add(new Path(new Cell(i, i-1), paths.get(paths.size() - 1)));
		}
		Collections.shuffle(paths);
		for (Path p: paths) {
			testPut(ph, p);
		}
	}
	
	@Test
	public void removeTest() {
		putTest();
		while (!ph.isEmpty()) {
			testRemove(ph);
		}
	}

}
