package fr.umlv.view;

import static org.junit.Assert.*;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;

import org.junit.Test;

import fr.umlv.tpnote.views.Views.View;

@SuppressWarnings("static-method")
public class ViewsTest {
  @Test
  public void listOfStringAsViewSize() {
    ArrayList<String> list = new ArrayList<>();
    list.add("foo");
    list.add("bar");
    View<String> view = Views.listAsView(list);
    assertEquals(2, view.size());
  }

  @Test
  public void listOfStringAsViewIterator() {
    ArrayList<String> list = new ArrayList<>();
    list.add("foo");
    list.add("bar");
    list.add("baz");
    View<String> view = Views.listAsView(list);
    Iterator<String> it = view.iterator();
    assertTrue(it.hasNext());
    assertEquals("foo", it.next());
    assertTrue(it.hasNext());
    assertEquals("bar", it.next());
    assertTrue(it.hasNext());
    assertEquals("baz", it.next());
    assertFalse(it.hasNext());
  }
  
  @Test(expected = NullPointerException.class)
  public void listAsViewNull() {
    Views.listAsView(null);
  }
  
  @Test
  public void listOfIntegerAsViewSize() {
    List<Integer> list = Collections.emptyList();
    View<Integer> view = Views.listAsView(list);
    assertEquals(0, view.size());
  }
  
  @Test
  public void listViewActAsAView() {
    List<Integer> list = Arrays.asList(555, 777);
    View<Integer> view = Views.listAsView(list);
    assertEquals(2, view.size());
    assertEquals(555, (int)view.iterator().next());
    list.set(0, 666);
    assertEquals(2, view.size());
    assertEquals(666, (int)view.iterator().next());
  }
  
  @Test(expected = NoSuchElementException.class)
  public void listViewIteratorNSEE() {
    List<Integer> list = Collections.singletonList(111);
    View<Integer> view2 = Views.listAsView(list);
    Iterator<Integer> it = view2.iterator();
    assertTrue(it.hasNext());
    assertEquals(111, (int)it.next());
    assertFalse(it.hasNext());
    it.next();
  }
  
  @Test
  public void listViewManyIterators() {
    List<Integer> list = Collections.singletonList(987);
    View<Integer> view = Views.listAsView(list);
    Iterator<Integer> it1 = view.iterator();
    Iterator<Integer> it2 = view.iterator();
    assertTrue(it1.hasNext());
    assertTrue(it2.hasNext());
    it1.next();
    assertFalse(it1.hasNext());
    assertTrue(it2.hasNext());
  }
  
  @Test(expected = UnsupportedOperationException.class)
  public void listViewIteratorRemove() {
    List<Integer> list = Collections.singletonList(987);
    View<Integer> view = Views.listAsView(list);
    Iterator<Integer> it = view.iterator();
    it.next();
    it.remove();
  }
  
  @Test
  public void listOfIntegerAsObjectView() {
    List<Integer> list = Arrays.asList(2, 3, 4);
    View<Object> view = Views.listAsView(list);
    assertEquals(3, view.size());
  }
  
  
  @Test
  public void intsAsViewSize() {
    int[] array = new int[] { 2, 3, 4 };
    View<Integer> view2 = Views.intsAsView(array);
    assertEquals(3, view2.size());
  }
  
  @Test
  public void intsAsViewValues() {
    View<Integer> view2 = Views.intsAsView(4, 3);
    assertEquals(2, view2.size());
  }
  
  @Test
  public void intsAsViewEmpty() {
    View<Integer> view2 = Views.intsAsView();
    assertEquals(0, view2.size());
    assertFalse(view2.iterator().hasNext());
  }
  
  @Test
  public void intsAsViewIterator() {
    View<Integer> view = Views.intsAsView(new int[] {7, 65, 43});
    Iterator<Integer> it = view.iterator();
    assertTrue(it.hasNext());
    assertEquals(7, (int)it.next());
    assertTrue(it.hasNext());
    assertEquals(65, (int)it.next());
    assertTrue(it.hasNext());
    assertEquals(43, (int)it.next());
    assertFalse(it.hasNext());
  }
  
  @Test(expected = NullPointerException.class)
  public void intsAsViewNull() {
    Views.intsAsView(null);
  }
  
  @Test
  public void intsViewActAsAView() {
    int[] array = new int[] { 555, 777 };
    View<Integer> view = Views.intsAsView(array);
    assertEquals(2, view.size());
    assertEquals(555, (int)view.iterator().next());
    array[0] = 666;
    assertEquals(2, view.size());
    assertEquals(666, (int)view.iterator().next());
  }
  
  @Test(expected = NoSuchElementException.class)
  public void intsAsViewIteratorNSEE() {
    View<Integer> view2 = Views.intsAsView(new int[] { 111 });
    Iterator<Integer> it = view2.iterator();
    assertTrue(it.hasNext());
    assertEquals(111, (int)it.next());
    assertFalse(it.hasNext());
    it.next();
  }
  
  @Test
  public void intsViewManyIterators() {
    View<Integer> view = Views.intsAsView(new int[] { 987 });
    Iterator<Integer> it1 = view.iterator();
    Iterator<Integer> it2 = view.iterator();
    assertTrue(it1.hasNext());
    assertTrue(it2.hasNext());
    it1.next();
    assertFalse(it1.hasNext());
    assertTrue(it2.hasNext());
  }
  
  @Test(expected = UnsupportedOperationException.class)
  public void intsAsViewIteratorRemove() {
    View<Integer> view = Views.intsAsView(new int[] { 987 });
    Iterator<Integer> it = view.iterator();
    it.next();
    it.remove();
  }
  
  
  @Test
  public void listAsViewForEach() {
    ArrayList<String> list = new ArrayList<>();
    list.add("hell");
    list.add("bells");
    View<String> view = Views.listAsView(list);
    ArrayList<String> list2 = new ArrayList<>();
    for(String s: view) {
      list2.add(s);
    }
    assertEquals(list, list2);
  }
  
  @Test
  public void listAsViewForEach2() {
    ArrayList<String> list = new ArrayList<>();
    list.add("fun");
    list.add("tragedy");
    View<String> view = Views.listAsView(list);
    ArrayList<String> list2 = new ArrayList<>();
    view.forEach(list2::add);
    assertEquals(list, list2);
  }
  
  @Test
  public void intsAsViewForEach() {
    ArrayList<Integer> list = new ArrayList<>();
    list.add(-56);
    list.add(-87);
    View<Integer> view = Views.intsAsView(new int[] {-56, -87});
    ArrayList<Integer> list2 = new ArrayList<>();
    for(Integer value: view) {
      list2.add(value);
    }
    assertEquals(list, list2);
  }
  
  @Test
  public void intsAsViewForEach2() {
    ArrayList<Integer> list = new ArrayList<>();
    list.add(-32);
    list.add(-64);
    View<Integer> view = Views.intsAsView(new int[] {-32, -64});
    ArrayList<Integer> list2 = new ArrayList<>();
    view.forEach(list2::add);
    assertEquals(list, list2);
  }
  
  
  @Test
  public void listAsViewContains() {
    View<String> view = Views.listAsView(Arrays.asList("ba", "be", "bi", "bo", "bu"));
    assertTrue(view.contains("ba"));
    assertFalse(view.contains("bz"));
    assertTrue(view.contains("be"));
    assertTrue(view.contains("bi"));
    assertTrue(view.contains("bo"));
    assertTrue(view.contains("bu"));
  }
  
  @Test
  public void listAsViewContains2() {
    Integer v1 = new Integer(2);
    View<Integer> view = Views.listAsView(Arrays.asList(v1));
    assertTrue(view.contains(2));
    assertTrue(view.contains(v1));
    assertTrue(view.contains(new Integer(2)));
  }
  
  @Test
  public void listAsViewContainsNull() {
    View<String> view = Views.listAsView(Arrays.asList((String)null));
    assertTrue(view.contains(null));
  }
  
  @Test
  public void listAsViewContainsNull2() {
    View<String> view = Views.listAsView(Arrays.asList("Eliot Ness"));
    assertFalse(view.contains(null));
  }
  
  @Test
  public void intsAsViewContains() {
    View<Integer> view = Views.intsAsView(new int[] { 10, 15, 19, 18, 17 });
    assertTrue(view.contains(10));
    assertFalse(view.contains(11));
    assertTrue(view.contains(15));
    assertTrue(view.contains(19));
    assertTrue(view.contains(18));
    assertTrue(view.contains(17));
  }
  
  @Test
  public void intsAsViewContains2() {
    View<Integer> view = Views.intsAsView(new int[] { 2 });
    assertTrue(view.contains(2));
    assertTrue(view.contains(new Integer(2)));
  }
  
  @Test
  public void intsAsViewContainsNull() {
    View<Integer> view = Views.intsAsView(new int[] { 12, 33, 45});
    assertFalse(view.contains(null));
  }
  
  @Test
  public void listOrIntsAsViewContainsSig() {
    View<String> view = Views.listAsView(Arrays.asList("12", "33", "45"));
    assertFalse(view.contains("hello"));
    View<Integer> view2 = Views.intsAsView(new int[] { 12, 33, 45});
    assertFalse(view2.contains("hello"));
  }
  
  
  @Test
  public void listAsViewCount() {
    List<String> list = Arrays.asList("foo", "go", "tag", "low", "fat", "bat");
    View<String> view = Views.listAsView(list);
    assertEquals(1, view.count(x -> x.length() == 2));
    assertEquals(3, view.count(x -> x.charAt(1) == 'o'));
    assertEquals(2, view.count(x -> x.endsWith("at")));
  }
  
  @Test(expected = NullPointerException.class)
  public void listAsViewCountNull() {
    View<String> view = Views.listAsView(Collections.singletonList("helllo"));
    view.count(null);
  }
  
  @Test
  public void intsAsViewCount() {
    int[] array = new int[] { 100, 27, 342, 147, 127, 327 };
    View<Integer> view = Views.intsAsView(array);
    assertEquals(2, view.count(x -> x % 2 == 0));
    assertEquals(4, view.count(x -> x % 10 == 7));
    assertEquals(1, view.count(x -> Integer.bitCount(x) == 7));
  }
  
  @Test(expected = NullPointerException.class)
  public void intsAsViewCountNull() {
    View<Integer> view = Views.intsAsView(new int[] { 637770 });
    view.count(null);
  }
  
  
  @Test
  public void listAsViewConcatSize() {
    View<String> view = Views.listAsView(Arrays.asList("bop", "fop"));
    View<String> view2 = Views.listAsView(Arrays.asList("bip", "fip"));
    View<String> view3 = view.concat(view2);
    assertEquals(4, view3.size());
  }
  
  @Test
  public void listAsViewConcatIterator() {
    View<String> view = Views.listAsView(Arrays.asList("bop", "fop"));
    View<String> view2 = Views.listAsView(Arrays.asList("bip", "fip"));
    View<String> view3 = view.concat(view2);
    assertEquals(4, view3.size());
    Iterator<String> it = view3.iterator();
    assertTrue(it.hasNext());
    assertEquals("bop", it.next());
    assertTrue(it.hasNext());
    assertEquals("fop", it.next());
    assertTrue(it.hasNext());
    assertEquals("bip", it.next());
    assertTrue(it.hasNext());
    assertEquals("fip", it.next());
    assertFalse(it.hasNext());
  }
  
  @Test(expected = NullPointerException.class)
  public void listAsViewConcatNull() {
    Views.listAsView(Arrays.asList("poo")).concat(null);
  }
  
  @Test
  public void listAsViewConcatContains() {
    View<String> view = Views.listAsView(Arrays.asList("bop", "fop"));
    View<String> view2 = Views.listAsView(Arrays.asList("bip", "fip"));
    View<String> view3 = view.concat(view2);
    assertTrue(view3.contains("bop"));
    assertFalse(view3.contains("bap"));
  }
  
  @Test
  public void listAsViewConcatCount() {
    View<String> view = Views.listAsView(Arrays.asList("bop", "fop"));
    View<String> view2 = Views.listAsView(Arrays.asList("bip", "fip"));
    View<String> view3 = view.concat(view2);
    assertEquals(4, view3.count(x -> true));
    assertEquals(0, view3.count(x -> false));
    assertEquals(2, view3.count(x -> x.charAt(1) == 'i'));
  }
  
  @Test
  public void intsViewConcatSize() {
    View<Integer> view = Views.intsAsView(new int[] { 102, 202 });
    View<Integer> view2 = Views.intsAsView(new int[] { 108, 208 });
    View<Integer> view3 = view.concat(view2);
    assertEquals(4, view3.size());
  }
  
  @Test
  public void intsAsViewConcatIterator() {
    View<Integer> view = Views.intsAsView(new int[] { 102, 202 });
    View<Integer> view2 = Views.intsAsView(new int[] { 108, 208 });
    View<Integer> view3 = view.concat(view2);
    assertEquals(4, view3.size());
    Iterator<Integer> it = view3.iterator();
    assertTrue(it.hasNext());
    assertEquals(102, (int)it.next());
    assertTrue(it.hasNext());
    assertEquals(202, (int)it.next());
    assertTrue(it.hasNext());
    assertEquals(108, (int)it.next());
    assertTrue(it.hasNext());
    assertEquals(208, (int)it.next());
    assertFalse(it.hasNext());
  }
  
  @Test(expected = NullPointerException.class)
  public void intsAsViewConcatNull() {
    Views.intsAsView(new int[] { 188 }).concat(null);
  }
  
  @Test
  public void intsAsViewConcatContains() {
    View<Integer> view = Views.intsAsView(new int[] { 102, 202 });
    View<Integer> view2 = Views.intsAsView(new int[] { 108, 208 });
    View<Integer> view3 = view.concat(view2);
    assertTrue(view3.contains(102));
    assertFalse(view3.contains(103));
  }
  
  @Test
  public void intsAsViewConcatCount() {
    View<Integer> view = Views.intsAsView(new int[] { 102, 202 });
    View<Integer> view2 = Views.intsAsView(new int[] { 108, 208 });
    View<Integer> view3 = view.concat(view2);
    assertEquals(4, view3.count(x -> true));
    assertEquals(0, view3.count(x -> false));
    assertEquals(2, view3.count(x -> x % 100 == 2));
  }
  
  @Test
  public void mixedViewConcatSize() {
    View<Integer> view = Views.listAsView(Arrays.asList(102, 202));
    View<Integer> view2 = Views.intsAsView(new int[] { 108, 208 });
    View<Integer> view3 = view.concat(view2);
    assertEquals(4, view3.size());
  }
  
  @Test
  public void mixedAsViewConcatIterator() {
    View<Integer> view = Views.listAsView(Arrays.asList(102, 202));
    View<Integer> view2 = Views.intsAsView(new int[] { 108, 208 });
    View<Integer> view3 = view.concat(view2);
    assertEquals(4, view3.size());
    Iterator<Integer> it = view3.iterator();
    assertTrue(it.hasNext());
    assertEquals(102, (int)it.next());
    assertTrue(it.hasNext());
    assertEquals(202, (int)it.next());
    assertTrue(it.hasNext());
    assertEquals(108, (int)it.next());
    assertTrue(it.hasNext());
    assertEquals(208, (int)it.next());
    assertFalse(it.hasNext());
  }
  
  @Test
  public void mixedAsViewConcatContains() {
    View<Integer> view = Views.listAsView(Arrays.asList(102, 202));
    View<Integer> view2 = Views.intsAsView(new int[] { 108, 208 });
    View<Integer> view3 = view.concat(view2);
    assertTrue(view3.contains(102));
    assertFalse(view3.contains(103));
  }
  
  @Test
  public void mixedAsViewConcatCount() {
    View<Integer> view = Views.listAsView(Arrays.asList(102, 202));
    View<Integer> view2 = Views.intsAsView(new int[] { 108, 208 });
    View<Integer> view3 = view.concat(view2);
    assertEquals(4, view3.count(x -> true));
    assertEquals(0, view3.count(x -> false));
    assertEquals(2, view3.count(x -> x % 100 == 2));
  }
  
  @Test
  public void mixedAsViewConcatSig() {
    View<Object> view = Views.listAsView(Arrays.asList());
    View<Integer> view2 = Views.intsAsView();
    view.concat(view2);
  }
}
