package org.jboss.cache.aop.collection;

import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.cache.aop.PojoCache;
import org.jboss.cache.aop.test.Address;
import org.jboss.cache.aop.test.Person;
import org.jboss.cache.PropertyConfigurator;

import java.util.*;

/**
 * Test object graph handling in aop, e.g., circular reference, multiple reference, link, etc.
 * @author Ben Wang  
 */

public class ReplicatedSyncListTest extends TestCase
{
   Log log=LogFactory.getLog(ReplicatedSyncListTest.class);
   PojoCache cache1;
   PojoCache cache2;

   public ReplicatedSyncListTest(String name)
   {
      super(name);
   }

   protected void setUp() throws Exception
   {
      super.setUp();
      log.info("setUp() ....");
      cache1 = createCache("CacheGroup");
      cache2 = createCache("CacheGroup");
   }

   protected void tearDown() throws Exception
   {
      super.tearDown();
      cache1.remove("/");
      cache1.stop();
      cache2.stop();
   }

   private PojoCache createCache(String name) throws Exception {
      PojoCache tree=new PojoCache();
      PropertyConfigurator config=new PropertyConfigurator();
      config.configure(tree, "META-INF/replSync-service.xml"); // read in generic replAsync xml
      tree.setClusterName(name);
      tree.createService();
      tree.startService();
      return tree;
   }

//   public void testDummy() {}


   protected Person createPerson(String name, int age)
   {
      Person p = new Person();
      p.setName(name);
      p.setAge(age);
      return p;
   }

   /**
    * Test attachment and then detachment and attachment.
    * @throws Exception
    */
   public void testAttachDetach() throws Exception
   {
      log.info("testAttachDetach() ....");
      List list1 = new ArrayList();
      Address addr = new Address();
      addr.setCity("San Jose");
      addr.setZip(95123);
      list1.add(addr);

      Address addr2 = new Address();
      addr2.setCity("Santa Clara");
      addr2.setZip(95131);

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

      // Pure list
      cache1.putObject("/list", list1);
      list1 = (List)cache1.getObject("/list");
      list1.add(addr2);
      cache1.removeObject("/list");
      assertEquals("Detached list should still be", 2, list1.size());
      list1.add(addr3);
      cache1.putObject("/list", list1);

      List list2 = (List)cache2.getObject("/list");
      assertTrue("List size should not be 0 ", (list2.size() != 0));
      assertEquals("Both list values should be equal ", ((Address)list1.get(0)).getZip(),
              ((Address)list2.get(0)).getZip());
   }

   /**
    * Two different keys share same list.
    * @throws Exception
    */
   public void testRelationshipWithSharedList1() throws Exception
   {
      log.info("testRelationshipWithList() ....");
      List list1 = new ArrayList();
      Address addr = new Address();
      addr.setCity("San Jose");
      addr.setZip(95123);
      list1.add(addr);

      // Pure list
      cache1.putObject("/list", list1);
      // We specifically need to use Proxy otherwise it won't work with multiple references
      list1 = (List)cache1.getObject("/list");
      cache1.putObject("/alias", list1);

      List list2 = (List)cache1.getObject("/alias");
      Address add1 = (Address)list2.get(0);
      assertNotNull("Address should not be null", add1);
      assertEquals("Zip ", 95123, add1.getZip());

      list1 = (List)cache2.getObject("/list");
      list2 = (List)cache2.getObject("/alias");
      assertTrue("List size should not be 0 ", (list2.size() != 0));
      assertEquals("Both lists should be equal ", list1, list2);
      assertEquals("Both list values should be equal ", list1.get(0), list2.get(0));
   }

   /**
    * Shared object between two list item.
    * @throws Exception
    */
   public void testRelationshipWithSharedList2() throws Exception
   {
      log.info("testRelationshipWithList2() ....");
      // 2 lists with shared objects
      List list1 = new ArrayList();
      Address addr = new Address();
      addr.setCity("San Jose");
      addr.setZip(95123);
      list1.add(addr);

      List list2 = new ArrayList();
      list2.add(addr);

      cache1.putObject("/list1", list1);
      cache1.putObject("/list2", list2);
      Address add2 = (Address)((List)cache2.getObject("/list2")).get(0);
      Address add1 = (Address)((List)cache2.getObject("/list1")).get(0);
      assertEquals("Address should be the same", add1, add2);
      assertEquals("Both shared object should be equal ", add2.getZip(), add1.getZip());
   }

   /**
    * Shared object between regular POJO and List item.
    * @throws Exception
    */
   public void testRelationshipWithSharedList3() throws Exception
   {
      log.info("testRelationshipWithList3() ....");
      // 2 lists with shared objects
      List list1 = new ArrayList();
      Address addr = new Address();
      addr.setCity("San Jose");
      addr.setZip(95123);
      list1.add(addr);

      List list2 = new ArrayList();
      list2.add(addr);

      cache1.putObject("/address", addr);
      cache1.putObject("/list1", list1);
      Address add1 = (Address)((List)cache2.getObject("/list1")).get(0);
      Address add2 = (Address)cache2.getObject("/address");
      assertEquals("Address should be the same", add1, add2);
      assertEquals("Both shared object should be equal ", 95123, add1.getZip());
   }

   public void testNullWithSharedList1() throws Exception
   {
      log.info("testNullWithSharedList1() ....");
      List list1 = new ArrayList();
      list1.add("element 0");
      list1.add(null);  // element 1
      list1.add("element 2");
      list1.add(null);  // element 3

      // Pure set
      cache1.putObject("/list", list1);
      // We specifically need to use Proxy otherwise it won't work with multiple references
      list1 = (List)cache1.getObject("/list");
      cache1.putObject("/alias", list1);

      List list2 = (List)cache1.getObject("/alias");

      list1 = (List)cache2.getObject("/list");
      list2 =  (List)cache2.getObject("/alias");
      assertTrue("List size should not be 0 ", (list2.size() != 0));
      assertEquals("Both listss should be equal ", list1, list2);

      Object a1[] = list1.toArray();
      Object a2[] = list2.toArray();
      assertTrue("element 1 is null", (a1[1] == null));
      assertTrue("element 1 is null", (a2[1] == null));
      assertTrue("element 3 is null", (a1[3] == null));
      assertTrue("element 3 is null", (a2[3] == null));

      assertTrue("contains test for null value", list1.contains(null));
      assertTrue("contains test for null value", list2.contains(null));

      assertTrue("index of null ", list2.indexOf(null) == 1);
      assertTrue("last index of null ", list2.lastIndexOf(null) == 3);

      list1.set(0,null);   // set first element to null
      assertTrue("set first item to null",list2.get(0) == null);
      list1.set(0,"element 0");
      assertTrue("set first item to 'element 0'",list2.get(0).equals("element 0"));


      ListIterator listIter = list1.listIterator();
      assertTrue("listiter has next", listIter.hasNext());
      assertTrue("listiter 1st element is 'element 0'", listIter.next().equals("element 0"));
      assertTrue("listiter has next", listIter.hasNext());
      assertTrue("listiter 2nd element is null", listIter.next() == null);
      listIter.remove();

      assertTrue("2nd element should be 'element 2'", list2.get(1).equals("element 2"));

   }

   public static Test suite() throws Exception
   {
      return new TestSuite(ReplicatedSyncListTest.class);
   }

   public static void main(String[] args) throws Exception
   {
      junit.textui.TestRunner.run(suite());
   }

}

