package editor.wordcount;

import static org.junit.Assert.*;

import org.junit.Test;

public class TrieTestRevised {
	
	public void checkBump1(Trie testee, String word) {
		Trie old = testee.makeCopy();
		testee.bump(word);
		assertEquals(old.get(word) + 1, testee.get(word));
	}
	
	public void checkBump2(Trie testee, String word) {
		checkBump1(testee, word);
		assertTrue(testee.isConsistent());
	}
	
	public void checkDock1(Trie testee, String word) {
		Trie old = testee.makeCopy();
		testee.dock(word);
		assertEquals(Math.max(0, old.get(word) - 1), testee.get(word));
	}
	
	public void checkDock2(Trie testee, String word) {
		checkDock1(testee, word);
		assertTrue(testee.isConsistent());
	}
	
	@Test
	public void test1() {
		Trie testee = new Trie();
		checkBump1(testee, "a");
	}

	@Test
	public void test2() {
		Trie testee = new Trie();
		checkBump1(testee, "a");
		checkBump1(testee, "b");
	}
	
	@Test
	public void test3() {
		Trie testee = new Trie();
		checkBump1(testee, "hello");
		checkBump1(testee, "hello");
	}

	@Test
	public void test4() {
		Trie testee = new Trie();
		checkBump1(testee, "goodbye");
		checkBump1(testee, "hello");
	}

	@Test
	public void test5() {
		Trie testee = new Trie();
		for (char c = 'A'; c <= 'z'; c++) {
			checkBump1(testee, Character.toString(c) + Character.toString((char)(c + 1)));
		}
	}

	@Test
	public void test6() {
		Trie testee = new Trie();
		for (char c = 'z'; c >= 'A'; c--) {
			checkBump1(testee, Character.toString(c) + Character.toString((char)(c + 1)));
		}
	}	
	
	@Test
	public void test1b() {
		Trie testee = new Trie();
		checkBump2(testee, "a");
	}

	@Test
	public void test2b() {
		Trie testee = new Trie();
		checkBump2(testee, "a");
		checkBump2(testee, "b");
	}
	
	@Test
	public void test3b() {
		Trie testee = new Trie();
		checkBump2(testee, "hello");
		checkBump2(testee, "hello");
	}

	@Test
	public void test4b() {
		Trie testee = new Trie();
		checkBump2(testee, "goodbye");
		checkBump2(testee, "hello");
	}

	@Test
	public void test5b() {
		Trie testee = new Trie();
		int insertions = 0;
		for (char c = 'A'; c <= 'z'; c++) {
			checkBump2(testee, Character.toString(c) + Character.toString((char)(c + 1)));
			insertions += 1;
		}
		testIterator(testee, insertions);
	}
	
	public void testIterator(Trie testee, int target) {
		WordCounterEntry prev = null;
		for (WordCounterEntry entry: testee) {
			assertTrue(prev == null || prev.getWord().compareTo(entry.getWord()) < 0);
			target -= 1;
		}
		assertEquals(0, target);
	}

	@Test
	public void test6b() {
		Trie testee = new Trie();
		int insertions = 0;
		for (char c = 'z'; c >= 'A'; c--) {
			checkBump2(testee, Character.toString(c) + Character.toString((char)(c + 1)));
			insertions += 1;
		}
		testIterator(testee, insertions);
	}
	
	@Test
	public void test7() {
		Trie testee = new Trie();
		checkBump1(testee, "art");
		checkBump1(testee, "arm");
		checkBump1(testee, "bee");
		checkBump1(testee, "be");
		checkBump1(testee, "bear");

		checkBump1(testee, "arm");
		checkBump1(testee, "bee");
		
		checkDock1(testee, "be");
		checkDock1(testee, "bee");
		checkDock1(testee, "be");
		checkDock1(testee, "art");
		checkDock1(testee, "art");
	}
	
	@Test
	public void test7b() {
		Trie testee = new Trie();
		checkBump2(testee, "art");
		checkBump2(testee, "arm");
		checkBump2(testee, "bee");
		checkBump2(testee, "be");
		checkBump2(testee, "bear");

		checkBump2(testee, "arm");
		checkBump2(testee, "bee");
		
		assertEquals(1, testee.get("art"));
		assertEquals(2, testee.get("arm"));
		assertEquals(2, testee.get("bee"));
		assertEquals(1, testee.get("be"));
		assertEquals(1, testee.get("bear"));
		
		checkDock2(testee, "be");
		checkDock2(testee, "bee");
		checkDock2(testee, "be");
		checkDock2(testee, "art");
		checkDock2(testee, "art");
		checkDock2(testee, "ark");
	}
	
	@Test
	public void testMB1() {
		String[] words = new String[]{"**The", "Dick,", "Etext", "Gutenberg", "Herman", "Melville**", "Moby", "Project", "by", "of"};
		Trie testee = new Trie();
		
		for (String s: words) {
			checkBump2(testee, s);
		}
		
		for (WordCounterEntry entry: testee) {
			findIn(testee, entry.getWord(), words);
		}
	}
	
	public void findIn(Trie testee, String word, String[] words) {
		for (String s: words) {
			if (word.equals(s)) {
				assertEquals(1, testee.get(s));
				return;
			}
		}
		fail("Word " + word + " not in original list");
	}
}
