package fr.umlv.suite;

import static java.util.Arrays.asList;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.util.List;

import org.junit.Test;

@SuppressWarnings("static-method")
public class SuiteTest {
  // --- Question 2 ---
  
  @Test
  public void testFrom() {
    Suite<String> suite = Suite.from("hello", "dude");
    assertEquals(2, suite.size());
    assertEquals("hello", suite.get(0));
    assertEquals("dude", suite.get(1));
  }
  
  @Test
  public void testFrom2() {
    Suite<Integer> suite = Suite.from(42, 777, 345);
    assertEquals(3, suite.size());
    assertEquals(42, (int)suite.get(0));
    assertEquals(777, (int)suite.get(1));
    assertEquals(345, (int)suite.get(2));
  }
  
  @Test(expected = NullPointerException.class)
  public void testFromNull() {
    Suite.from((String[])null);
  }
  
  @Test(expected = NullPointerException.class)
  public void testFromNull2() {
    Suite.from("foo", null);
  }
  
  @Test
  public void testFromEmpty() {
    assertEquals(0, Suite.from().size());
  }
  
  @Test(expected = IndexOutOfBoundsException.class)
  public void testFromEmpty2() {
    Suite.from().get(0);
  }
  
  @Test
  public void testFromEncapsulation() {
    Object[] objects = new Object[] { "jazz", 3.0, 453 };
    Suite<Object> suite = Suite.from(objects);
    objects[0] = "buzz";
    assertEquals("jazz", suite.get(0));
  }
  
  
  //--- Question 3 ---
  
  @Test
  public void testEmpty() {
    Suite<String> suite = Suite.empty();
    assertEquals(0, suite.size());
  }
  
  @Test(expected = IndexOutOfBoundsException.class)
  public void testEmptyGet() {
    Suite.empty().get(0);
  }
  
  @Test
  public void testEmptyInstance() {
    assertSame(Suite.empty(), Suite.empty());
  }
  
  
  //--- Question 4 ---
  
  @Test
  public void testContains() {
    Suite<Integer> suite = Suite.from(15, 447);
    assertTrue(suite.contains(15));
    assertTrue(suite.contains(447));
  }
  
  @Test
  public void testContains2() {
    Suite<String> suite = Suite.from("booh", "bah");
    assertFalse(suite.contains("hooob"));
    assertFalse(suite.contains("fling"));
  }
  
  @Test
  public void testEmptyContains() {
    Suite<CharSequence> suite = Suite.empty();
    assertFalse(suite.contains("hjtu"));
    assertFalse(suite.contains("pekld"));
  }
  
  @Test
  public void testFunContains() {
    Suite<Integer> suite = Suite.empty();
    assertFalse(suite.contains("zoo"));
  }
  
  @Test(expected = NullPointerException.class)
  public void testFromContainsNull() {
    Suite.from("foo").contains(null);
  }
  
  @Test(expected = NullPointerException.class)
  public void testEmptyContainsNull() {
    Suite.empty().contains(null);
  }
  
  
  // --- Question 5 ---
  
  @Test
  public void testFromForEach() {
    Suite.from(2, 4, 8, 12, 32).forEach(i -> {
      assertTrue(i % 2 == 0);
    });
  }
  
  @Test
  public void testFromEmptyForEach() {
    Suite.from().forEach(__ -> {
      fail();
    });
  }
  
  @Test
  public void testEmptyForEach() {
    Suite.empty().forEach(__ -> {
      fail();
    });
  }
  
  @Test(expected = NullPointerException.class)
  public void testFromForEachNull() {
    Suite.from(2, 3.0).forEach(null);
  }
  
  @Test(expected = NullPointerException.class)
  public void testEmptyForEachNull() {
    Suite.empty().forEach(null);
  }
  
  @Test
  public void testFromForEach2() {
    List<Integer> list = asList(0, 1, 2, 3, 4);
    Suite.from(list.toArray()).forEach(i -> {
      assertEquals(list.get((Integer)i), i);
    });
  }
  
  
  // --- Question 6 ---
  
  @Test
  public void testSpan() {
    Suite<Integer> span = Suite.span(3, x -> x);
    assertEquals(3, span.size());
    assertEquals(0, (int)span.get(0));
    assertEquals(1, (int)span.get(1));
    assertEquals(2, (int)span.get(2));
  }
  
  @Test
  public void testSpanEmptySize() {
    assertEquals(0, Suite.span(0, x -> x).size());
  }
  
  @Test(expected = IndexOutOfBoundsException.class)
  public void testSpanEmptyGet() {
    Suite.span(0, x -> x).get(0);
  }
  
  @Test
  public void testSpanContains() {
    Suite<String> suite = Suite.span(1, Integer::toString);
    assertTrue(suite.contains("0"));
    assertFalse(suite.contains(0));
  }
  
  @Test(expected = NullPointerException.class)
  public void testSpanContainsNull() {
    Suite.span(2, Integer::toString).contains(null);
  }
  
  @Test
  public void testSpanForEachEmpty() {
    Suite.span(0, x -> x).forEach(__ -> {
      fail();
    });
  }
  
  @Test(expected = NullPointerException.class)
  public void testSpanForEachNull() {
    Suite.span(1, x -> x).forEach(null);
  }
  
  //--- Question 7 ---
  // All the test should still work 
  
  //--- Question 8 ---
  
  @Test
  public void testFromMap() {
    Suite<Integer> suite = Suite.from(1, 2).map(x -> x * 2);
    assertEquals(2, suite.size());
    assertEquals(2, (int)suite.get(0));
    assertEquals(4, (int)suite.get(1));
  }
  
  @Test
  public void testFromMapString() {
    Suite<Integer> suite = Suite.from("34", "78", "23").map(Integer::parseInt);
    assertEquals(3, suite.size());
    assertEquals(34, (int)suite.get(0));
    assertEquals(78, (int)suite.get(1));
    assertEquals(23, (int)suite.get(2));
  }
  
  @Test
  public void testEmptyMap() {
    Suite<String> suite = Suite.empty().map(Object::toString);
    assertEquals(0, suite.size());
  }
  
  @Test(expected = IndexOutOfBoundsException.class)
  public void testEmptyMapGet() {
    Suite.empty().map(Object::toString).get(0);
  }
  
  @Test(expected = IndexOutOfBoundsException.class)
  public void testFromEmptyMapGet() {
    Suite.from().map(Object::toString).get(0);
  }
  
  @Test(expected = IndexOutOfBoundsException.class)
  public void testSpanMapGet() {
    Suite.span(0, x -> x).map(Object::toString).get(0);
  }
  
  @Test
  public void testFromMapContains() {
    Suite<Integer> suite = Suite.from("fo", "bar")
        .map(String::length);
    assertTrue(suite.contains(2));
    assertTrue(suite.contains(3));
    assertFalse(suite.contains(4));
  }
  
  @Test
  public void testFromMapForEach() {
    Suite
      .from("abc", "def", "ghi")
      .map(String::length)
      .forEach(i -> assertEquals(3, (int)i));
  }
  
  @Test(expected = NullPointerException.class)
  public void testEmptyMapNull() {
    Suite.empty().map(null);
  }
  
  @Test(expected = NullPointerException.class)
  public void testFromSpanMapNull() {
    Suite.from().map(null);
  }
  
  @Test(expected = NullPointerException.class)
  public void testEmptySpanMapNull() {
    Suite.span(0, x -> x).map(null);
  }
  
  
  // --- Question 9 ---
  
  @Test
  public void testEmptyMapSame() {
    Suite<Double> empty = Suite.empty();
    assertSame(empty, empty.map(x -> x));
  }
  
  
  // --- Question 10 ---
  
  @Test
  public void testFromMapMap() {
    Suite<Integer> suite = Suite.from(1, 2, 7);
    Suite<Integer> suite2 = suite.map(x -> x * 2).map(x -> x / 2);
    assertEquals(3, suite2.size());
    assertEquals(1, (int)suite2.get(0));
    assertEquals(2, (int)suite2.get(1));
    assertEquals(7, (int)suite2.get(2));
  }
  
  @Test(expected = NullPointerException.class)
  public void testEmptyMapMapNull() {
    Suite.empty().map(x -> x).map(null);
  }
  
  @Test(expected = NullPointerException.class)
  public void testFromSpanMapMapNull() {
    Suite.from().map(x -> x).map(null);
  }
  
  @Test(expected = NullPointerException.class)
  public void testEmptySpanMapMapNull() {
    Suite.span(0, x -> x).map(x -> x).map(null);
  }
}
