package fr.umlv.bag;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.NoSuchElementException;

import org.junit.Assert;
import org.junit.Test;

@SuppressWarnings("static-method")
public class BagTest {  
  @Test
  public void simpleBag() {
    Bags.createSimpleBag();
  }
  
  @Test
  public void add() {
    Bag<String> bag = Bags.createSimpleBag();
    Assert.assertEquals(1, bag.add("foo"));
    Assert.assertEquals(1, bag.add("bar"));
    Assert.assertEquals(2, bag.add("foo"));
  }
  
  @Test(expected=NullPointerException.class)
  public void addNull() {
    Bags.createSimpleBag().add(null);
  }
  
  @Test
  public void count() {
    Bag<Integer> bag = Bags.createSimpleBag();
    bag.add(1);
    bag.add(1);
    bag.add(42);
    Assert.assertEquals(2, bag.count(1));
    Assert.assertEquals(1, bag.count(42));
    Assert.assertEquals(0, bag.count(153));
    Assert.assertEquals(0, bag.count("foo"));
  }
  
  @Test(expected=NullPointerException.class)
  public void countNull() {
    Bags.createSimpleBag().count(null);
  }
  
  @Test
  public void remove() {
    Bag<Object> bag = Bags.createSimpleBag();
    bag.add(1);
    bag.add(1);
    Assert.assertEquals(2, bag.count(1));
    Assert.assertEquals(1, bag.remove(1));
    Assert.assertEquals(1, bag.count(1));
    Assert.assertEquals(0, bag.remove(1));
    Assert.assertEquals(0, bag.remove(1));
    Assert.assertEquals(0, bag.remove("foobar"));
  }
  
  @Test(expected=NullPointerException.class)
  public void removeNull() {
    Bags.createSimpleBag().remove(null);
  }
  
  @Test
  public void addWithCount() {
    Bag<Object> bag = Bags.createSimpleBag();
    bag.addWithCount(1, 3);
    Assert.assertEquals(3, bag.count(1));
    bag.addWithCount("foobar", 7);
    Assert.assertEquals(7, bag.count("foobar"));
  }
  
  @Test
  public void addWithCount2() {
    Bag<Object> bag = Bags.createSimpleBag();
    bag.addWithCount(1, 1);
    try {
      bag.addWithCount("boooz", -1);
      Assert.fail();
    } catch(IllegalArgumentException e) {
      // do nothing
    }
  }
  
  @Test(expected=NullPointerException.class)
  public void addWithcountNull() {
    Bags.createSimpleBag().addWithCount(null, 1);
  }
  
  @Test
  public void iterator() {
    Bag<Integer> bag = Bags.createSimpleBag();
    HashSet<Integer> set = new HashSet<>();
    for(int i=0; i<1000; i++) {
      bag.add(i%3);
      set.add(i%3);
    }
    HashSet<Integer> set2 = new HashSet<>();
    Iterator<Integer> it = bag.iterator();
    Assert.assertTrue(it.hasNext());
    for(; it.hasNext();) {
      set2.add(it.next());
    }
    Assert.assertEquals(set, set2);
    Assert.assertFalse(it.hasNext());
  }
  
  @Test
  public void iterator2() {
    Bag<String> bag = Bags.createSimpleBag();
    for(int i=0; i<1000; i++) {
      bag.addWithCount(Integer.toString(i), 1);
    }
    Iterator<String> it = bag.iterator();
    Assert.assertTrue(it.hasNext());
    for(int i=0; i<1000; i++) {
      if (i%11 == 0) {
        for(int j=0; j<7; j++) {
          it.hasNext();
        }
      }
      it.next();
    }
    Assert.assertFalse(it.hasNext());
  }
  
  @Test(expected=NoSuchElementException.class)
  public void iteratorNext() {
    Bags.createSimpleBag().iterator().next();
  }
  
  @Test(expected=UnsupportedOperationException.class)
  public void iteratorRemove() {
    Bags.createSimpleBag().iterator().remove();
  }
  
  @Test
  public void orderedBag() {
    Bag<Integer> bag = Bags.createOrderedByInsertionBag();
    ArrayList<Integer> list = new ArrayList<>();
    for(int i=0; i<1000; i++) {
      bag.add(i);
      list.add(i);
    }
    ArrayList<Integer> list2 = new ArrayList<>();
    for(Iterator<Integer> it = bag.iterator(); it.hasNext();) {
      list2.add(it.next());
    }
    Assert.assertEquals(list, list2);
  }
  
  @Test
  public void orderedBag2() {
    Bag<Integer> bag = Bags.createOrderedByInsertionBag();
    ArrayList<Integer> list = new ArrayList<>();
    for(int j=0; j<5; j++) {
      for(int i=0; i<100; i++) {
        bag.add(i);
      }
    }
    
    for(int i=0; i<100; i++) {
      for(int j=0; j<5; j++) {
        list.add(i);
      }
    }
    
    ArrayList<Integer> list2 = new ArrayList<>();
    for(Iterator<Integer> it = bag.iterator(); it.hasNext();) {
      list2.add(it.next());
    }
    Assert.assertEquals(list, list2);
  }
  
  @Test
  public void addAll() {
    Bag<Object> bag = Bags.createSimpleBag();
    bag.add("foo");
    bag.add(42);
    Bag<Integer> bag2 = Bags.createSimpleBag();
    bag2.add(77);
    bag2.add(42);
    bag.addAll(bag2);
    Assert.assertEquals(1, bag.count("foo"));
    Assert.assertEquals(2, bag.count(42));
    Assert.assertEquals(1, bag.count(77));
  }
  
  @Test(expected=NullPointerException.class)
  public void addAllNull() {
    Bags.createSimpleBag().addAll(null);
  }
}
