package org.jboss.cache.pojo.collection;

import static org.testng.AssertJUnit.assertEquals;
import static org.testng.AssertJUnit.assertFalse;
import static org.testng.AssertJUnit.assertTrue;
import static org.testng.AssertJUnit.fail;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.NoSuchElementException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.cache.pojo.PojoCache;
import org.jboss.cache.pojo.PojoCacheFactory;
import org.jboss.cache.pojo.test.Address;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

/**
 * List interface testing.
 *
 * @author Ben Wang
 */

@Test(groups = {"functional"})
public class CachedListTest
{
   Log log = LogFactory.getLog(CachedListTest.class);
   PojoCache cache_;
   List<String> languages;
   List<String> languages2;

   @BeforeMethod(alwaysRun = true)
   public void setUp() throws Exception
   {
      log.info("setUp() ....");
      String configFile = "META-INF/local-service.xml";
      boolean toStart = false;
      cache_ = PojoCacheFactory.createCache(configFile, toStart);
      cache_.start();
   }

   @AfterMethod(alwaysRun = true)
   public void tearDown() throws Exception
   {
      cache_.stop();
   }

   public void testAddAndRemoveIndex() throws Throwable
   {
      stage();

      languages.add(1, "Taiwanese");
      assertEquals("Languages size ", 4, languages.size());
      assertEquals("Language ", (Object) "Taiwanese", languages.get(1));
      languages.remove(2);
      assertEquals("Languages size ", 3, languages.size());
      assertEquals("Language ", (Object) "English", languages.get(2));

      languages.add("Mandarin");
      assertEquals("Languages size ", 4, languages.size());
      languages.remove("Mandarin");
      assertEquals("Languages size ", 3, languages.size());
   }

   @SuppressWarnings("unchecked")
   protected void stage() throws Throwable
   {
      languages = new ArrayList<String>();
      languages.add("English");
      languages.add("French");
      languages.add("English");
      cache_.attach("/person/test6", languages);
      languages = (List<String>) cache_.find("/person/test6");
      int size = languages.size();
      assertEquals("Size of list ", 3, size);

      languages2 = new ArrayList<String>();
      languages2.addAll(languages);
      assertEquals("New ArrayList().addAll(CachedList)", languages, languages2);
   }

   public void testAddAllAndClear() throws Throwable
   {
      stage();
      List<String> list = new ArrayList<String>();
      list.add("Taiwanese");
      list.add("Madarin");

      assertTrue("Language is Taiwanese ", list.contains("Taiwanese"));

      languages.addAll(list);
      assertEquals("Languages size ", 5, languages.size());

      languages.removeAll(list);
      assertEquals("Languages size ", 3, languages.size());

      assertEquals("Index of French ", 1, languages.indexOf("French"));

      languages.clear();
      assertEquals("Languages size ", 0, languages.size());

      assertTrue("Languages empty ", languages.isEmpty());
   }

   public void testEquals() throws Throwable
   {
      stage();

      @SuppressWarnings("unchecked")
      List<String> list = (List<String>) cache_.find("/person/test6");
      assertTrue("List should be the same ", list.equals(languages));
      list = new ArrayList<String>();
      list.add("German");
      list.add("test");
      list.add("English");
      assertFalse("List should not be the same ", languages.equals(list));
      assertFalse("List should not be the same ", list.equals(languages));
   }

   public void testSet() throws Throwable
   {
      stage();

      @SuppressWarnings("unchecked")
      List<String> list = (List<String>) cache_.find("/person/test6");
      assertTrue("List should be the same ", list.equals(languages));
      assertEquals("List size ", 3, list.size());
      list.set(0, "German");
      list.set(1, "test");
      list.set(2, "English");
      assertEquals("List size ", 3, list.size());
   }

   public void testIterator() throws Throwable
   {
      languages = new ArrayList<String>();
      Iterator<String> it0 = languages.iterator();
      assertFalse("Iterator should be empty ", it0.hasNext());

      stage();

      Iterator<String> it = languages.iterator();
      Iterator<String> it2 = languages2.iterator();
      int counter = 0;
      while (it.hasNext())
      {
         counter++;
         assertEquals(it.next(), it2.next());
         it.remove();
         it2.remove();
      }

      assertEquals("Size should be ", 3, counter);
      assertEquals("Skills should be empty ", 0, languages.size());
   }

   public void testListIterator() throws Throwable
   {
      languages = new ArrayList<String>();
      ListIterator<String> it0 = languages.listIterator();
      assertFalse("Iterator should be empty ", it0.hasNext());
      assertFalse("Iterator should be empty ", it0.hasPrevious());

      stage();

      ListIterator<String> li = languages.listIterator();
      ListIterator<String> li2 = languages2.listIterator();
      assertFalse("LI has no previous element ", li.hasPrevious());
      assertFalse("LI2 has no previous element ", li2.hasPrevious());
      assertTrue("LI has next element ", li.hasNext());
      assertTrue("LI2 has next element ", li2.hasNext());
      assertEquals(li.next(), li2.next());
      assertEquals("Index is ", 1, li.nextIndex());
      assertEquals("Index is ", 1, li2.nextIndex());
      assertEquals("Index is ", 0, li.previousIndex());
      assertEquals("Index is ", 0, li2.previousIndex());
      assertEquals(li.next(), li2.next());
      assertEquals(li.next(), li2.next()); // the end
      try
      {
         li.next();
         fail("Should throw an exception here ");
      }
      catch (NoSuchElementException ex)
      {
         ;
      }
      try
      {
         li2.next();
         fail("Should throw an exception here ");
      }
      catch (NoSuchElementException ex)
      {
         ;
      }

      assertEquals("Index is ", 3, li.nextIndex());
      assertEquals("Index is ", 3, li2.nextIndex());
      assertEquals("Index is ", 2, li.previousIndex());
      assertEquals("Index is ", 2, li2.previousIndex());
      li.previous();
      li2.previous();
      assertEquals("Index is ", 2, li.nextIndex());
      assertEquals("Index is ", 2, li2.nextIndex());
      assertEquals("Index is ", 1, li.previousIndex());
      assertEquals("Index is ", 1, li2.previousIndex());
      li.previous();
      li2.previous();
      li.previous();
      li2.previous();

      try
      {
         li.previous();
         fail("Should throw an exception here ");
      }
      catch (NoSuchElementException ex)
      {
         ;
      }

      try
      {
         li2.previous();
         fail("Should throw an exception here ");
      }
      catch (NoSuchElementException ex)
      {
         ;
      }

      try
      {
         assertEquals(li.next(), li2.next());
         li.remove();
         li2.remove();
      }
      catch (Exception e)
      {
         fail("ListIterator.remove failed" + e);
      }


      try
      {
         assertEquals(li.next(), li2.next());
         li.remove();
         li2.remove();
      }
      catch (Exception e)
      {
         fail("ListIterator.remove failed" + e);
      }

      try
      {
         assertEquals(li.next(), li2.next());
         assertEquals("ListIterator.remove test problem with nextIndex, cache next index=" + li.nextIndex() +
                 ", jdk next index=" + li2.nextIndex() + "cache list size = " + languages.size() + ", jdk list size = " + languages.size(),
                 li.nextIndex(), li2.nextIndex());
         li2.set("German");
         li.set("German");
         String s1 = li.previous();
         String s2 = li2.previous();
         assertEquals(s1, s2);
         assertEquals(s2, "German");
      }
      catch (Exception e)
      {
         fail("ListIterator.remove failed" + e + ", cache list size = " + languages.size() + ", jdk list size = " + languages.size());
      }

      try
      {
         assertEquals(li.next(), li2.next());
         li2.add("Vulcan");
         li.add("Vulcan");
         String s1 = li.previous();
         String s2 = li2.previous();
         assertEquals(s1, s2);
         assertEquals(s2, "Vulcan");
      }
      catch (Exception e)
      {
         fail("ListIterator.add failed" + e + ", cache list size = " + languages.size() + ", jdk list size = " + languages.size());
      }

   }

   @SuppressWarnings("unchecked")
   public void testAttachAndDetach() throws Exception
   {
      List<String> list = new ArrayList<String>();
      list.add("English");
      list.add("French");
      list.add("Taiwanese");

      cache_.attach("/test", list); // attach
      list = (List<String>) cache_.find("/test");
      assertEquals("Size ", 3, list.size());

      list = (List<String>) cache_.detach("/test");
      assertEquals("Size ", 3, list.size());

      System.out.println("**** End of cache content **** ");
      list.remove(2);
      list.add("Hoklo");
      assertEquals("Size ", 3, list.size());
      assertEquals("Content ", "Hoklo", list.get(2));

      // Try to re-attach
      cache_.attach("/test", list);
      list.remove(2);
      assertEquals("Size ", 2, list.size());
   }

   @SuppressWarnings("unchecked")
   public void testSerialize() throws Exception
   {
      List<String> list = new ArrayList<String>();
      list.add("English");
      list.add("French");
      list.add("Taiwanese");

      cache_.attach("/test", list); // attach
      list = (List<String>) cache_.find("/test");
      ByteArrayOutputStream bout = new ByteArrayOutputStream();
      ObjectOutputStream out = new ObjectOutputStream(bout);
      out.writeObject(list);
      List<String> newList = (List<String>)
         (new ObjectInputStream(new ByteArrayInputStream(bout.toByteArray()))).readObject();

      assertTrue(newList instanceof ArrayList);
      assertEquals(list, newList);
   }


   @SuppressWarnings("unchecked")
   public void testPojoAttachAndDetach() throws Exception
   {
      Address add1 = new Address();
      add1.setCity("San Jose");
      add1.setZip(95123);

      Address add2 = new Address();
      add2.setCity("Sunnyvale");
      add2.setZip(94086);

      Address add3 = new Address();
      add3.setCity("Santa Clara");
      add3.setZip(951131);

      List<Address> list = new ArrayList<Address>();
      list.add(add1);
      list.add(add2);
      list.add(add3);

      cache_.attach("/test", list); // attach
      list = (List<Address>) cache_.find("/test");
      assertEquals("Size ", 3, list.size());

      list = (List<Address>) cache_.detach("/test");
      assertEquals("Size ", 3, list.size());

      System.out.println("**** End of cache content **** ");
      list.remove(2);
      list.add(add2);
      assertEquals("Size ", 3, list.size());
      assertEquals("Content ", add2, list.get(2));

      // Try to re-attach
      cache_.attach("/test", list);
      list.remove(2);
      assertEquals("Size ", 2, list.size());
   }

   @SuppressWarnings("unchecked")
   public void testEqual1() throws Exception
   {
      List<String> list1 = new ArrayList<String>();
      list1.add("ID1");
      list1.add("ID2");
      cache_.attach("test1", list1);
      list1 = (List<String>)cache_.find("test1");

      List<String> list2 = new ArrayList<String>();
      list2.add("ID1");
      list2.add("ID2");
      cache_.attach("test2", list2);
      list2 = (List<String>)cache_.find("test2");

      List<String> list3 = new ArrayList<String>();
      list3.add("ID2");
      list3.add("ID1");
      cache_.attach("test3", list3);
      list3 = (List<String>)cache_.find("test3");

      assertEquals("List should be equal: ", list1, list1);
      assertTrue("List should be equal: ", list1.equals(list1));
      assertTrue("List should be equal: ", list1.equals(list2));
      assertFalse("List should not be equal: ", list1.equals(list3));
   }

   @SuppressWarnings("unchecked")
   public void testEqual2() throws Exception
   {
      List<String> list1 = new ArrayList<String>();
      cache_.attach("test1", list1);
      list1 = (List<String>)cache_.find("test1");
      list1.add("ID1");
      list1.add("ID2");

      List<String> list2 = new ArrayList<String>();
      cache_.attach("test2", list2);
      list2 = (List<String>)cache_.find("test2");
      list2.add("ID1");
      list2.add("ID2");

      List<String> list3 = new ArrayList<String>();
      cache_.attach("test3", list3);
      list3 = (List<String>)cache_.find("test3");
      list3.add("ID2");
      list3.add("ID1");

      assertEquals("List should be equal: ", list1, list1);
      assertTrue("List should be equal: ", list1.equals(list1));
      assertTrue("List should be equal: ", list1.equals(list2));
      assertFalse("List should not be equal: ", list1.equals(list3));
   }
}