package fr.umlv.colls;

import java.util.AbstractMap;
import java.util.AbstractMap.SimpleImmutableEntry;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Map;
import java.util.Random;

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

public class MapsTest {
  public static class Empty {
    // empty class
  }
  
  @Test
  public void empty() {
    Assert.assertTrue(Maps.asMap(new Empty()).isEmpty());
  }
  
  public static class OneStatic {
    public static int getX() { return 0; }
    public static void setX(int x) { /* do nothing */ }
  }
  
  @Test
  public void oneStatic() {
    Assert.assertTrue(Maps.asMap(new OneStatic()).isEmpty());
  }
  
  public static class Test1 {
    private final int a = 2;
    public int getA() { return a; }
    public void setB(long l) { /* do nothing */}
    private double c;
    public double getC() { return c; }
    public void setC(double c) { this.c = c; }
  }
  
  @Test
  public void getAndSet() {
    Test1 test1 = new Test1();
    Map<String, Object> map = Maps.asMap(test1);
    map.put("c", 42.);
    Assert.assertEquals(42., map.get("c"));
  }
  
  @Test(expected=UnsupportedOperationException.class)
  public void onlyOneSetter() {
    Test1 test1 = new Test1();
    Map<String, Object> map = Maps.asMap(test1);
    map.get("b");
  }
  
  @Test(expected=UnsupportedOperationException.class)
  public void onlyOneGetter() {
    Test1 test1 = new Test1();
    Map<String, Object> map = Maps.asMap(test1);
    map.put("a", 42);
  }
  
  @Test
  public void putReturnNull() {
    Test1 test1 = new Test1();
    Map<String, Object> map = Maps.asMap(test1);
    Assert.assertNull(map.put("c", .7));
    Assert.assertNull(map.put("c", .47));
  }
  
  static class Foo {
    public int getAx00() { return 0; }
    public int getAx01() { return 0; }
    public int getAx02() { return 0; }
    public int getAx03() { return 0; }
    public int getAx04() { return 0; }
    public int getAx05() { return 0; }
    public int getAx06() { return 0; }
    public int getAx07() { return 0; }
    public int getAx08() { return 0; }
    public int getAx09() { return 0; }
    public int getAx10() { return 0; }
    public int getAx11() { return 0; }
    public int getAx12() { return 0; }
    public int getAx13() { return 0; }
    public int getAx14() { return 0; }
    public int getAx15() { return 0; }
    public int getAx16() { return 0; }
    public int getAx17() { return 0; }
    public int getAx18() { return 0; }
    public int getAx19() { return 0; }
    public int getAx20() { return 0; }
    public int getAx21() { return 0; }
    public int getAx22() { return 0; }
    public int getAx23() { return 0; }
    public int getAx24() { return 0; }
    public int getAx25() { return 0; }
    public int getAx26() { return 0; }
    public int getAx27() { return 0; }
    public int getAx28() { return 0; }
    public int getAx29() { return 0; }
    public int getAx30() { return 0; }
    public int getAx31() { return 0; }
    public int getAx32() { return 0; }
    public int getAx33() { return 0; }
    public int getAx34() { return 0; }
    public int getAx35() { return 0; }
    public int getAx36() { return 0; }
    public int getAx37() { return 0; }
    public int getAx38() { return 0; }
    public int getAx39() { return 0; }
  }
  
  @Test(timeout=3000)
  public void fullThrottle() {  // is get() fast enough ?
    Random random = new Random();
    Map<String, Object> map = Maps.asMap(new Foo());
    String[] keys = new String[40];
    for(int i=0; i<keys.length; i++) {
      keys[i] = String.format("ax%02d", i);
    }
    for(int i=0; i<10000000; i++) {
      map.get(keys[random.nextInt(40)]);
    }
  }
  
  @Test
  public void keySet() {
    Map<String, Object> map = Maps.asMap(new Foo());
    HashSet<String> set = new HashSet<>();
    for(int i=0; i<40; i++) {
      set.add(String.format("ax%02d", i));
    }
    Assert.assertEquals(set, map.keySet());
  }
  
  @Test
  public void entrySet() {
    Map<String, Object> map = Maps.asMap(new Foo());
    HashSet<Map.Entry<String, Object>> set = new HashSet<>();
    for(int i=0; i<40; i++) {
      set.add(new SimpleImmutableEntry<>(String.format("ax%02d", i), (Object)0));
    }
    Assert.assertEquals(set, map.entrySet());
  }
  
  @Test(expected=UnsupportedOperationException.class)
  public void remove() {
    Map<String, Object> map = Maps.asMap(new Test1());
    map.remove("c");
  }
  
  @Test(expected=UnsupportedOperationException.class)
  public void iteratorRemove() {
    Map<String, Object> map = Maps.asMap(new Test1());
    map.keySet().iterator().remove();
  }
  
  @Test(expected=UnsupportedOperationException.class)
  public void iteratorRemove2() {
    Map<String, Object> map = Maps.asMap(new Test1());
    map.entrySet().iterator().remove();
  }
  
  @Test(expected=UnsupportedOperationException.class)
  public void iteratorRemove3() {
    Map<String, Object> map = Maps.asMap(new Test1());
    map.values().iterator().remove();
  }
  
  @Test(expected=UnsupportedOperationException.class)
  public void clear() {
    Map<String, Object> map = Maps.asMap(new Test1());
    map.clear();
  }
}
