import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.function.Function;

import org.junit.Test;

public class HiddenTableTest {
  // --- Question 1 ---
  
  @Test
  public void testEmptySize() {
    HiddenTable<String> table = new HiddenTable<>();
    assertEquals(0, table.size());
  }

  @Test(expected = IndexOutOfBoundsException.class)
  public void testEmptyElement() {
    HiddenTable<Object> table = new HiddenTable<>();
    table.element(0);
  }

  @Test
  public void testEmptyIndex() {
    HiddenTable<String> table = new HiddenTable<>();
    assertEquals(-1, table.index(""));
  }
  @Test
  public void testIndex() {
    HiddenTable<Integer> table = new HiddenTable<>();
    assertEquals(-1, table.index("hello"));
  }
  
  @Test
  public void testAdd() {
    HiddenTable<String> table = new HiddenTable<>();
    assertEquals(0, table.add("foo"));
  }
  @Test
  public void testDoubleAdd() {
    HiddenTable<String> table = new HiddenTable<>();
    assertEquals(0, table.add("foo"));
    assertEquals(0, table.add("foo"));
  }
  @Test
  public void testAddIndex() {
    HiddenTable<String> table = new HiddenTable<>();
    table.add("foo");
    assertEquals(0, table.index("foo"));
  }
  @Test
  public void testAddElement() {
    HiddenTable<String> table = new HiddenTable<>();
    table.add("bar");
    assertEquals("bar", table.element(0));
  }
  
  
  //--- Question 2 ---
  /*
  @Test
  public void testEmptyAllMatch() {
    HiddenTable<String> table = new HiddenTable<>();
    assertTrue(table.allMatch(new Function<String, Boolean>() {
      @Override
      public Boolean apply(String t) {
        fail();
        throw null;
      }
    }::apply));
  }
  @Test
  public void testFalseAllMatch() {
    HiddenTable<Integer> table = new HiddenTable<>();
    table.add(1);
    table.add(2);
    table.add(3);
    assertFalse(table.allMatch(new Function<Integer, Boolean>() {
      @Override
      public Boolean apply(Integer value) {
        return value % 2 == 1;
      }
    }::apply));
  }
  @Test(expected = NullPointerException.class)
  public void testNullAllMatch() {
    HiddenTable<Object> table = new HiddenTable<>();
    table.allMatch(null);
  }
  
  
  //--- Question 3 ---
  
  @Test
  public void testTrueIsContainedInto() {
    HiddenTable<Integer> table = new HiddenTable<>();
    table.add(117);
    
    HiddenTable<Integer> table2 = new HiddenTable<>();
    table2.add(47);
    table2.add(117);
    
    assertTrue(table.isContainedInto(table2));
  }
  @Test
  public void testFalseIsContainedInto() {
    HiddenTable<String> table = new HiddenTable<>();
    table.add("boz");
    
    HiddenTable<String> table2 = new HiddenTable<>();
    table2.add("buz");
    table2.add("biz");
    
    assertFalse(table.isContainedInto(table2));
  }
  @Test(expected = NullPointerException.class)
  public void testNullIsContainedInto() {
    HiddenTable<Object> table = new HiddenTable<>();
    table.isContainedInto(null);
  }
  @Test
  public void testEmptyIsContainedInto() {
    HiddenTable<Object> table = new HiddenTable<>();
    HiddenTable<String> table2 = new HiddenTable<>();
    assertTrue(table.isContainedInto(table2));
  }
  
  
  //--- Question 4 ---
  
  @Test
  public void testNewCompactAdd() {
    HiddenTable<String> table = new HiddenTable<>();
    Collection<String> c = table.newCompactSet();
    assertTrue(c.add("hello"));
    assertEquals("hello", c.iterator().next());
  }
  @Test
  public void testNewCompactDoubleAdd() {
    HiddenTable<String> table = new HiddenTable<>();
    Collection<String> c = table.newCompactSet();
    assertTrue(c.add("hello"));
    assertFalse(c.add("hello"));
  }
  @Test(expected = NullPointerException.class)
  public void testNewCompactAddNull() {
    new HiddenTable<>().newCompactSet().add(null);
  }
  @Test
  public void testNewCompactIsEmpty() {
    HiddenTable<String> table = new HiddenTable<>();
    Collection<String> c = table.newCompactSet();
    assertTrue(c.isEmpty());
    c.add("bob");
    assertFalse(c.isEmpty());
    c.add("bob");
    assertFalse(c.isEmpty());
  }
  @Test
  public void testNewCompactSize() {
    HiddenTable<String> table = new HiddenTable<>();
    Collection<String> c = table.newCompactSet();
   
    assertEquals(0, c.size());
    assertTrue(c.add("ada"));
    assertEquals(1, c.size());
    assertTrue(c.add("dad"));
    assertEquals(2, c.size());
  }
  @Test
  public void testNewCompactEquals() {
    HiddenTable<String> table = new HiddenTable<>();
    Collection<String> c = table.newCompactSet();
    c.add("fob");
    c.add("bof");
    
    Collection<String> c2 = table.newCompactSet();
    c2.add("bof");
    c2.add("fob");
    assertEquals(c, c2);
  }
  @Test
  public void testNewCompactEqualsAlien() {
    HiddenTable<String> table = new HiddenTable<>();
    Collection<String> c = table.newCompactSet();
    c.add("fob");
    c.add("bof");
    
    HashSet<String> c2 = new HashSet<>();
    c2.add("bof");
    c2.add("fob");
    assertEquals(c, c2);
  }
  @Test
  public void testNewCompactEmptyIterator() {
    HiddenTable<String> table = new HiddenTable<>();
    Collection<String> c = table.newCompactSet();
    assertFalse(c.iterator().hasNext());
    assertFalse(c.iterator().hasNext());
  }
  @Test
  public void testNewCompactIterator() {
    HiddenTable<String> table = new HiddenTable<>();
    Collection<String> c = table.newCompactSet();
    c.add("guy");
    
    Iterator<String> it1 = c.iterator();
    assertTrue(it1.hasNext());
    assertEquals("guy", it1.next());
    assertFalse(it1.hasNext());
    
    Iterator<String> it2 = c.iterator();
    assertTrue(it2.hasNext());
    assertEquals("guy", it2.next());
    assertFalse(it2.hasNext());
  }
  @Test
  public void testNewCompactIteratorBig() {
    HiddenTable<Integer> table = new HiddenTable<>();
    Collection<Integer> c = table.newCompactSet();
    for(int value = 0; value < 1_000; value++) {
      c.add(value);
    }
    
    Collection<Integer> c2 = table.newCompactSet();
    for(int value: c) {
      assertTrue(c2.add(value));
    }
    assertEquals(c, c2);
  }
  @Test(expected = NoSuchElementException.class)
  public void testNewCompactEmptyIteratorNext() {
    new HiddenTable<>().newCompactSet().iterator().next();
  }
  @Test
  public void testNewCompactEmptyIteratorNextNext() {
    Collection<Object> c = new HiddenTable<>().newCompactSet();
    c.add("oh dear");
    Iterator<Object> it = c.iterator();
    assertEquals("oh dear", it.next());
    try {
      it.next();
      fail();
    } catch(NoSuchElementException e) {
      // ok, pass
    }
  }
  
  
  //--- Question 5 ---
  
  @Test(expected = IllegalStateException.class)
  public void testNewCompactEmptyIteratorRemove() {
    HiddenTable<Integer> table = new HiddenTable<>();
    Collection<Integer> c = table.newCompactSet();
    c.iterator().remove();
  }
  @Test
  public void testNewCompactSingletonIteratorRemove() {
    HiddenTable<Integer> table = new HiddenTable<>();
    Collection<Integer> c = table.newCompactSet();
    c.add(13731);
    Iterator<Integer> it = c.iterator();
    it.next();
    it.remove();
    assertTrue(c.isEmpty());
    assertEquals(0, c.size());
    try {
      it.remove();
      fail();
    } catch(IllegalStateException e) {
      // Ok, pass
    }
  }
  @Test()
  public void testNewCompactRemoveIf() {
    HiddenTable<Integer> table = new HiddenTable<>();
    Collection<Integer> c = table.newCompactSet();
    for(int value = 0; value < 100; value++) {
      c.add(value);
    }
    assertTrue(c.removeIf(__ -> true));
    assertTrue(c.isEmpty());
  }
  
  
  //--- Question 6 ---
  
  @Test()
  public void testNewCompactClear() {
    HiddenTable<Integer> table = new HiddenTable<>();
    Collection<Integer> c = table.newCompactSet();
    for(int value = 0; value < 100; value++) {
      c.add(value);
    }
    c.clear();
    assertTrue(c.isEmpty());
  }
  
  
  //--- Question 7 ---
  
  @Test
  public void testNewCompactContains() {
    HiddenTable<Integer> table = new HiddenTable<>();
    Collection<Integer> c = table.newCompactSet();
    for(int value = 0; value < 100; value += 2) {
      c.add(value);
    }
    for(int value = 0; value < 100; value++) {
      if (value % 2 == 0) {
        assertTrue(c.contains(value));
      } else {
        assertFalse(c.contains(value));
      }
    }
    assertFalse(c.isEmpty());
    assertEquals(50, c.size());
  }
  @Test(expected = NullPointerException.class)
  public void testNewCompactContainsNull() {
    new HiddenTable<>().newCompactSet().contains(null);
  }
  
  
  //--- Question 8 ---
  
  @Test()
  public void testNewCompactRemove() {
    HiddenTable<Integer> table = new HiddenTable<>();
    Collection<Integer> c = table.newCompactSet();
    for(int value = 0; value < 100; value++) {
      c.add(value);
    }
    for(int value = 0; value < 100; value++) {
      assertTrue(c.remove(value));
    }
    assertTrue(c.isEmpty());
  }
  @Test(expected = NullPointerException.class)
  public void testNewCompactRemoveNull() {
    new HiddenTable<>().newCompactSet().remove(null);
  }
  @Test()
  public void testNewCompactRemoveFalse() {
    assertFalse(new HiddenTable<>().newCompactSet().remove("foo"));
  }
  
  
  //--- Question 9 ---
  
  @Test
  public void testNewCompactAddAllMyself() {
    HiddenTable<String> table = new HiddenTable<>();
    Collection<String> c = table.newCompactSet();
    c.add("brat"); 
    assertFalse(c.addAll(c));
    assertEquals("brat", c.iterator().next());
    assertEquals(1, c.size());
  }
  @Test
  public void testNewCompactAddAllSame() {
    HiddenTable<String> table = new HiddenTable<>();
    Collection<String> c = table.newCompactSet();
    c.add("boom"); 
    Collection<String> c2 = table.newCompactSet();
    c2.add("boom"); 
    assertFalse(c.addAll(c2));
    assertEquals("boom", c.iterator().next());
    assertEquals(1, c.size());
  }
  @Test
  public void testNewCompactAddAll() {
    HiddenTable<String> table = new HiddenTable<>();
    Collection<String> c = table.newCompactSet();
    c.add("foo"); 
    Collection<String> c2 = table.newCompactSet();
    c2.add("bar"); 
    assertTrue(c.addAll(c2));
    assertEquals(2, c.size());
  }
  @Test
  public void testNewCompactAddAllOther() {
    HiddenTable<String> table = new HiddenTable<>();
    Collection<String> c = table.newCompactSet();
    c.add("foo");
    HiddenTable<String> table2 = new HiddenTable<>();
    Collection<String> c2 = table2.newCompactSet();
    c2.add("bar");
    assertTrue(c.addAll(c2));
    assertEquals(2, c.size());
  }
  @Test
  public void testNewCompactAddAllAlien() {
    HiddenTable<String> table = new HiddenTable<>();
    Collection<String> c = table.newCompactSet();
    c.add("foo"); 
    HashSet<String> c2 = new HashSet<>();
    c2.add("bar"); 
    assertTrue(c.addAll(c2));
    assertEquals(2, c.size());
  }*/
}
