package fr.umlv.ir.exam2;

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

import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.TreeMap;

import org.junit.Test;

public class DynamicArrayTest {
  @Test
  public void testCreateEmpty() {
    DynamicArray.create();
  }
  
  @Test
  public void testCreate() {
    DynamicArray<Integer> array = DynamicArray.create(1, 2, 3, 1);
    assertFalse(array.isSparse());
    assertEquals(4, array.size());
  }
  
  @Test
  public void testCreate2() {
    DynamicArray<String> array = DynamicArray.create("foo", "baz", "bar");
    assertFalse(array.isSparse());
    assertEquals(3, array.size());
  }

  @Test(expected=NullPointerException.class)
  public void testCreateNull() {
    DynamicArray.create(null);
  }
  
  @Test(expected=NullPointerException.class)
  public void testCreateNull2() {
    DynamicArray.create(1, 2, 4, null, 16, 32);
  }
  
  @Test
  public void testCreateSparse() {
    DynamicArray<Object> sparseArray = DynamicArray.createSparse();
    assertTrue(sparseArray.isSparse());
  }
  
  @Test
  public void testCreateSparse2() {
    DynamicArray<String> sparseArray = DynamicArray.<String>createSparse();
    assertTrue(sparseArray.isSparse());
  }

  @Test
  public void testPush() {
    DynamicArray<String> array = DynamicArray.create("foo");
    array.push("oof");
    assertFalse(array.isSparse());
    array.push("oof");
    assertFalse(array.isSparse());
    assertEquals(3, array.size());
  }
  
  @Test
  public void testPush2() {
    DynamicArray<Object> array = DynamicArray.createSparse();
    array.push("oof");
    assertTrue(array.isSparse());
    array.push("oof");
    assertTrue(array.isSparse());
    assertEquals(2, array.size());
  }
  
  @Test(expected=NullPointerException.class)
  public void testPushNull() {
    DynamicArray<Object> array = DynamicArray.createSparse();
    array.push(null);
  }
  
  @Test(expected=NullPointerException.class)
  public void testPushNull2() {
    DynamicArray<Object> array = DynamicArray.create();
    array.push(null);
  }
  
  @Test
  public void testToString() {
    DynamicArray<String> array = DynamicArray.create("hello", "world");
    array.push("john");
    assertEquals("{0=hello, 1=world, 2=john}", array.toString());
  }
  
  @Test
  public void testToString2() {
    DynamicArray<Object> array = DynamicArray.createSparse();
    array.push("zoo");
    array.push("ghost");
    assertEquals("{0=zoo, 1=ghost}", array.toString());
  }
  
  @Test
  public void testToStringEmpty() {
    DynamicArray<Object> array = DynamicArray.create();
    assertEquals("{}", array.toString());
  }
  
  @Test
  public void testToStringEmpty2() {
    DynamicArray<Object> array = DynamicArray.createSparse();
    assertEquals("{}", array.toString());
  }

  @Test
  public void testCreateFromMap() {
    DynamicArray<String> array = DynamicArray.createFromMap(Collections.singletonMap(1, "foo"));
    assertEquals(1, array.size());
    assertEquals("{1=foo}", array.toString());
  }
  
  @Test
  public void testCreateFromMapEmpty() {
    DynamicArray<Object> array = DynamicArray.createFromMap(Collections.<Integer,Object>emptyMap());
    assertEquals(0, array.size());
    assertEquals("{}", array.toString());
  }
  
  @Test
  public void testCreateFromMap2() {
    DynamicArray<Object> array = DynamicArray.<Object>createFromMap(Collections.singletonMap(1, "foo"));
    assertEquals(1, array.size());
    assertEquals("{1=foo}", array.toString());
  }
  
  @Test
  public void testCreateFromMapImmutable() {
    DynamicArray<String> array = DynamicArray.createFromMap(Collections.unmodifiableMap(Collections.singletonMap(1, "foo")));
    assertEquals(1, array.size());
    assertEquals("{1=foo}", array.toString());
  }
  
  @Test
  public void testCreateFromMapAndPush() {
    DynamicArray<String> array = DynamicArray.createFromMap(Collections.singletonMap(450, "foo"));
    array.push("bar");
    assertEquals(2, array.size());
    assertEquals("{450=foo, 451=bar}", array.toString());
  }
  
  @Test
  public void testCreateFromMapAndPush2() {
    DynamicArray<String> array = DynamicArray.createFromMap(Collections.singletonMap(-6, "foo"));
    array.push("bar");
    assertEquals(2, array.size());
    assertEquals("{-6=foo, -5=bar}", array.toString());
  }
  
  @Test(expected=NullPointerException.class)
  public void testCreateFromMapNull() {
    DynamicArray.createFromMap(null);
  }
  
  @Test(expected=NullPointerException.class)
  public void testCreateFromMapNull2() {
    DynamicArray.createFromMap(Collections.<Integer, String>singletonMap(null, "foo"));
  }
  
  @Test(expected=NullPointerException.class)
  public void testCreateFromMapNull3() {
    DynamicArray.createFromMap(Collections.<Integer, String>singletonMap(1, null));
  }

  @Test
  public void testGet() {
    DynamicArray<String> array = DynamicArray.create("hello");
    assertEquals("hello", array.get(0));
    assertNull(array.get(-1));
    assertNull(array.get(-34));
    assertNull(array.get(1));
    assertNull(array.get(75));
  }
  
  @Test
  public void testGet2() {
    DynamicArray<Integer> array = DynamicArray.createSparse();
    array.push(1);
    assertEquals(1, (int)array.get(0));
    assertNull(array.get(-1));
    assertNull(array.get(-78));
    assertNull(array.get(1));
    assertNull(array.get(56));
  }

  @Test
  public void testGet3() {
    HashMap<Integer, String> map = new HashMap<>();
    map.put(157, "hayoo");
    DynamicArray<String> array = DynamicArray.createFromMap(map);
    assertEquals("hayoo", array.get(157));
    assertNull(array.get(156));
    assertNull(array.get(-43));
    assertNull(array.get(158));
    assertNull(array.get(89));
  }
  
  @Test
  public void testValueGetBig() {
    DynamicArray<Integer> array = DynamicArray.create();
    for(int i=0; i<100; i++) {
      array.push(i);
    }
    for(int i=0; i<100; i++) {
      assertEquals(i, (int)array.get(i));
    }
    assertEquals(100, array.size());
  }
  
  @Test
  public void testSet() {
    DynamicArray<String> array = DynamicArray.create("goo");
    array.set(1, "gle");
    assertFalse(array.isSparse());
    assertEquals(2, array.size());
    assertEquals("google", array.get(0) + array.get(1));
  }
  
  @Test
  public void testSet2() {
    DynamicArray<String> array = DynamicArray.create("goo");
    array.set(10, "oog");
    assertTrue(array.isSparse());
    assertEquals(2, array.size());
    assertEquals("goo", array.get(0));
    assertEquals("oog", array.get(10));
  }
  
  @Test
  public void testSet3() {
    DynamicArray<String> array = DynamicArray.create("geo");
    array.set(-1, "oog");
    assertTrue(array.isSparse());
    assertEquals(2, array.size());
    assertEquals("geo", array.get(0));
    assertEquals("oog", array.get(-1));
  }
  
  @Test
  public void testSet4() {
    DynamicArray<String> array = DynamicArray.create("hack");
    array.set(45, "quack");
    assertTrue(array.isSparse());
    assertEquals(2, array.size());
    assertEquals("hack", array.get(0));
    assertEquals("quack", array.get(45));
  }
  
  @Test(expected=NullPointerException.class)
  public void testSetNull() {
    DynamicArray<String> array = DynamicArray.create("val");
    array.set(0, null);
  }
  
  @Test(expected=NullPointerException.class)
  public void testSetNull2() {
    DynamicArray<String> array = DynamicArray.create();
    array.set(10, null);
  }

  @Test
  public void testSparseSet() {
    DynamicArray<String> array = DynamicArray.createSparse();
    array.set(10, "foo");
    array.set(45, "bar");
    assertTrue(array.isSparse());
    assertEquals(2, array.size());
    assertEquals("foo", array.get(10));
    assertEquals("bar", array.get(45));
  }
  
  @Test
  public void testSparseSet2() {
    DynamicArray<String> array = DynamicArray.createSparse();
    array.set(11, "foo");
    array.set(11, "hoo");
    assertTrue(array.isSparse());
    assertEquals(1, array.size());
    assertEquals("hoo", array.get(11));
  }
  
  @Test
  public void testMutableSet() {
    String[] args = { "hello kitty" };
    DynamicArray<String> array = DynamicArray.create(args);
    array.set(0, "bad kitty");
    assertEquals("hello kitty", args[0]);
  }
  
  @Test
  public void testMapSet2() {
    DynamicArray<String> array = DynamicArray.createFromMap(Collections.<Integer, String>emptyMap());
    array.set(11, "foo");
    array.set(11, "hoo");
    assertTrue(array.isSparse());
    assertEquals(1, array.size());
    assertEquals("hoo", array.get(11));
  }
  
  @Test(expected=NullPointerException.class)
  public void testMapSetNull() {
    DynamicArray<String> array = DynamicArray.createFromMap(Collections.singletonMap(0, "pffff"));
    array.set(0, null);
  }
  
  @Test(expected=NullPointerException.class)
  public void testMapSetNull2() {
    DynamicArray<String> array = DynamicArray.createFromMap(Collections.<Integer, String>emptyMap());
    array.set(11, null);
  }
  
  
  @Test
  public void testValueIterator() {
    DynamicArray<String> array = DynamicArray.create("foo", "bar", "baz", "bzh");
    Iterator<String> iterator = array.valueIterator();
    assertTrue(iterator.hasNext());
    assertEquals("foo", iterator.next());
    assertTrue(iterator.hasNext());
    assertEquals("bar", iterator.next());
    assertTrue(iterator.hasNext());
    assertEquals("baz", iterator.next());
    assertTrue(iterator.hasNext());
    assertEquals("bzh", iterator.next());
    assertFalse(iterator.hasNext());
  }
  
  @Test
  public void testValueIterator2() {
    DynamicArray<String> array = DynamicArray.create("jkl", "qsd", "wxc", "aze");
    Iterator<String> iterator = array.valueIterator();
    assertEquals("jkl", iterator.next());
    assertEquals("qsd", iterator.next());
    assertEquals("wxc", iterator.next());
    assertEquals("aze", iterator.next());
    assertFalse(iterator.hasNext());
  }
  
  @Test
  public void testValueIterator3() {
    DynamicArray<String> array = DynamicArray.create("jkl", "qsd", "wxc", "aze");
    Iterator<String> iterator = array.valueIterator();
    for(int i=0; i<10; i++) iterator.hasNext();
    assertEquals("jkl", iterator.next());
    for(int i=0; i<10; i++) iterator.hasNext();
    assertEquals("qsd", iterator.next());
    for(int i=0; i<10; i++) iterator.hasNext();
    assertEquals("wxc", iterator.next());
    for(int i=0; i<10; i++) iterator.hasNext();
    assertEquals("aze", iterator.next());
    assertFalse(iterator.hasNext());
  }

  @Test
  public void testValueIterator4() {
    TreeMap<Integer, String> map = new TreeMap<>();
    map.put(34, "goat");
    map.put(43, "abey");
    DynamicArray<String> array = DynamicArray.createFromMap(map);
    Iterator<String> iterator = array.valueIterator();
    assertEquals("goat", iterator.next());
    assertEquals("abey", iterator.next());
    assertFalse(iterator.hasNext());
  }
  
  @Test(expected=NoSuchElementException.class)
  public void testValueIterator5() {
    DynamicArray<Object> array = DynamicArray.createSparse();
    array.valueIterator().next();
  }
  
  @Test(expected=UnsupportedOperationException.class)
  public void testValueIterator6() {
    DynamicArray<String> array = DynamicArray.create("a", "b", "c");
    Iterator<String> it = array.valueIterator();
    it.next();
    it.remove();
  }
  
  @Test
  public void testValueIteratorBig() {
    DynamicArray<Integer> array = DynamicArray.create();
    for(int i=0; i<100; i++) {
      array.push(i);
    }
    Iterator<Integer> it = array.valueIterator();
    for(int i=0; i<100; i++) {
      assertEquals(i, (int)it.next());
    }
    assertFalse(it.hasNext());
  }
  
  @Test
  public void testDelete() {
    DynamicArray<String> array = DynamicArray.create("from");
    String value = array.delete(0);
    assertEquals("from", value);
    assertEquals(0, array.size());
  }
  
  @Test
  public void testDeleteSparse() {
    DynamicArray<String> array = DynamicArray.createSparse();
    array.push("solid");
    String value = array.delete(0);
    assertEquals("solid", value);
    assertEquals(0, array.size());
  }
  
  @Test
  public void testDeleteNothing() {
    DynamicArray<Integer> array = DynamicArray.create();
    Integer value = array.delete(0);
    assertNull(value);
  }

  @Test
  public void testDeleteNothing2() {
    DynamicArray<Integer> array = DynamicArray.createSparse();
    Integer value = array.delete(0);
    assertNull(value);
  }
}
