package fr.umlv.exam1.test;

import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;

import junit.framework.Assert;

import org.junit.Test;

import fr.umlv.exam1.MultiMap;
import fr.umlv.exam1.MultiMap.Kind;
import fr.umlv.exam1.MultiMaps;


public class MultiMapTest {
  @Test(expected=NullPointerException.class)
  public void nullPut() {
    MultiMaps.createMultiMap().put(null, "foo");
  }
  
  @Test(expected=NullPointerException.class)
  public void nullPut2() {
    MultiMaps.createMultiMap().put("bar", null);
  }
  
  @Test
  public void put() {
    MultiMap<String, String> multiMap = MultiMaps.createMultiMap();
    Assert.assertTrue(multiMap.put("foo", "bar"));
    Assert.assertTrue(multiMap.put("foo", "baz"));
    Assert.assertFalse(multiMap.put("foo", "bar"));
    Assert.assertEquals(2, multiMap.size());
    Assert.assertEquals(2, multiMap.get("foo").size());
  }
  
  @Test(expected=NullPointerException.class)
  public void nullGet() {
    MultiMaps.createMultiMap().get(null);
  }
  
  @Test
  public void emptyGet() {
    MultiMap<Object, Object> multiMap = MultiMaps.createMultiMap();
    Assert.assertTrue(multiMap.get("foo").isEmpty());
  }
  
  @Test
  public void getAndPut() {
    MultiMap<Integer, Integer> multiMap = MultiMaps.createMultiMap();
    Assert.assertTrue(multiMap.put(2, 22));
    Assert.assertTrue(multiMap.put(5, 55));
    Assert.assertTrue(multiMap.put(5, 66));
    Assert.assertEquals(1, multiMap.get(2).size());
    Assert.assertEquals(22, (int)multiMap.get(2).iterator().next());
    Assert.assertEquals(2, multiMap.get(5).size());
    Assert.assertTrue(multiMap.get(5).contains(55));
    Assert.assertTrue(multiMap.get(5).contains(66));
  }
  
  @Test
  public void getAndPutAllKind() {
    for(Kind kind: Kind.values()) {
      MultiMap<Integer, Integer> multiMap = MultiMaps.createMultiMap2(kind);
      for(int i=0; i<1000; i++) {
        multiMap.put(i%10, i);
      }
      
      Assert.assertEquals(1000, multiMap.size());
      
      for(int i=0; i<1000; i++) {
        Set<Integer> set = multiMap.get(i%10);
        for(int value: set) {
          Assert.assertEquals(i%10, value%10);
        }
      }
    }
  }
  
  @Test
  public void sortByKey() {
    MultiMap<String, Integer> multiMap = MultiMaps.createMultiMap2(Kind.SORT_BY_KEY);
    for(int i=10; --i>=0;) {
      multiMap.put(Integer.toString(i), i);
    }
    int i=0;
    for(int value: multiMap) {
      Assert.assertEquals(i++, value);
    }
  }
  
  @Test
  public void sortByInsertionOrder() {
    MultiMap<Integer, Integer> multiMap = MultiMaps.createMultiMap2(Kind.SORT_BY_INSERTION_ORDER);
    for(int i=1000; --i>=0;) {
      multiMap.put(i, i);
    }
    int i=1000;
    for(int value: multiMap) {
      Assert.assertEquals(--i, value);
    }
  }
  
  @Test
  public void iterator() {
    Random random = new Random(0);
    MultiMap<Integer, Integer> multiMap = MultiMaps.createMultiMap();
    for(int i=0; i<100; i++) {
      multiMap.put(random.nextInt(10), i);
    }
    ArrayList<Integer> list = new ArrayList<>();
    Iterator<Integer> it = multiMap.iterator();
    for(int i=0; i<multiMap.size(); i++) {
      if (i%7 == 0) {
        for(int j=0; j<10; j++) {
          Assert.assertTrue(it.hasNext());
        }
      }
      list.add(it.next());
    }
    Assert.assertFalse(it.hasNext());
    
    Collections.sort(list);
    for(int i=0; i<100; i++) {
      Assert.assertEquals(i, (int)list.get(i));
    }
  }
  
  @Test(expected=NoSuchElementException.class)
  public void iterator2() {
    MultiMap<Integer, Integer> multiMap = MultiMaps.createMultiMap();
    multiMap.iterator().next();
  }
  
  @Test(expected=IllegalStateException.class)
  public void iteratorRemove() {
    MultiMap<Object, Object> multiMap = MultiMaps.createMultiMap();
    Iterator<Object> it = multiMap.iterator();
    it.remove();
  }
  
  @Test
  public void iteratorRemove2() {
    MultiMap<Object, Object> multiMap = MultiMaps.createMultiMap();
    multiMap.put("foo", 3);
    Iterator<Object> it = multiMap.iterator();
    it.next();
    it.remove();
    Assert.assertFalse(it.hasNext());
    Assert.assertEquals(0, multiMap.size());
    Assert.assertTrue(multiMap.get("foo").isEmpty());
  }
  
  @Test(expected=NullPointerException.class)
  public void createValueSetClass() {
    MultiMaps.createMultiMap4(Kind.UNSORTED, null);
  }
  
  @Test(expected=IllegalArgumentException.class)
  public void createValueSetClass2() {
    MultiMaps.createMultiMap4(Kind.UNSORTED, AbstractSet.class);
  }
  
  @Test(expected=IllegalArgumentException.class)
  public void createValueSetClass3() {
    MultiMaps.createMultiMap4(Kind.UNSORTED, EnumSet.class);
  }

  @Test(expected=IllegalArgumentException.class)
  public void createValueSetClass4() {
    MultiMaps.createMultiMap4(Kind.UNSORTED, FakeSet.class);
  }
  
  @Test
  public void createValueSetClass5() {
    MultiMap<String, String> multiMap = MultiMaps.createMultiMap4(Kind.UNSORTED, HashSet.class);
    multiMap.put("foo", "bar");
    Assert.assertTrue(multiMap.get("foo") instanceof HashSet);
  }
  
  @Test
  public void createValueSetClass6() {
    MultiMap<String, String> multiMap = MultiMaps.createMultiMap4(Kind.UNSORTED, CopyOnWriteArraySet.class);
    multiMap.put("foo", "bar");
    Assert.assertTrue(multiMap.get("foo") instanceof CopyOnWriteArraySet);
  }
}
