package org.jboss.cache.pojo.collection;

import static org.testng.AssertJUnit.assertEquals;
import static org.testng.AssertJUnit.assertNotNull;
import static org.testng.AssertJUnit.assertTrue;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

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.jboss.cache.pojo.test.Person;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

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

@Test(groups = {"functional"})
@SuppressWarnings("unchecked")
public class ObjectGraphTest 
{
   Log log = LogFactory.getLog(ObjectGraphTest.class);
   PojoCache cache_;


   @BeforeMethod(alwaysRun = true)
   protected 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)
   protected void tearDown() throws Exception
   {
      cache_.stop();
   }

//   public void testDummy() {}

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


   public void testListWithMultipleSharedObjccts() throws Exception
   {
      log.info("testListWithMultipleSharedObjects() ....");
      List<Address> list1 = new ArrayList<Address>();
      Address addr = new Address();
      addr.setCity("San Jose");
      addr.setZip(95123);
      list1.add(addr);

      List<Address> list2 = new ArrayList<Address>();
      List<Address> list3 = new ArrayList<Address>();
      list2.add(addr);
      list3.add(addr);

      cache_.attach("/list1", list1);
      cache_.attach("/list2", list2);
      cache_.attach("/list3", list3);

      list3 = (List<Address>) cache_.find("/list3");
      assertTrue("List size should not be 0 ", (list3.size() != 0));
      assertEquals("Address ", addr.getZip(), list3.get(0).getZip());
      addr.setCity("Sunnyvale");
      assertEquals("Address ", addr.getCity(), list3.get(0).getCity());

      cache_.detach("/list1");
      cache_.detach("/list2");
      cache_.detach("/list3");
   }

   public void testRelationshipWithSharedList1() throws Exception
   {
      log.info("testRelationshipWithList() ....");
      List<Address> list1 = new ArrayList<Address>();
      Address addr = new Address();
      addr.setCity("San Jose");
      addr.setZip(95123);
      list1.add(addr);

      cache_.attach("/list", list1);
      list1 = (List<Address>) cache_.find("/list");
      assertEquals("List size should be ", 1, list1.size());
      cache_.attach("/alias", list1);

      list1 = (List<Address>) cache_.find("/list");
      assertEquals("List size should be ", 1, list1.size());
      List list2 = (List) cache_.find("/alias");
//      System.out.println(cache_.printDetails());
      assertEquals("List size should be ", 1, list2.size());
      assertEquals("Both lists should be equal ", list1, list2);
      assertEquals("Both list values should be equal ", list1.get(0), list2.get(0));
   }

   public void testRelationshipWithSharedList2() throws Exception
   {
      log.info("testRelationshipWithList2() ....");
      List<Address> list1 = new ArrayList<Address>();
      Address addr = new Address();
      addr.setCity("San Jose");
      addr.setZip(95123);
      list1.add(addr);

      List<Address> list2 = new ArrayList<Address>();
      list2.add(addr);

      cache_.attach("/list1", list1);
      cache_.attach("/list2", list2);
      Address add2 = (Address) ((List) cache_.find("/list2")).get(0);
      Address add1 = (Address) ((List) cache_.find("/list1")).get(0);
      assertEquals("Address should be the same", add1, add2);
      assertEquals("Both shared object should be equal ", add2.getZip(), add1.getZip());
   }

   public void testListWithAttachAndDetach() throws Exception
   {
      log.info("testListWithAttachAndDetach() ....");
      List<Address> list1 = new ArrayList<Address>();
      Address addr1 = new Address();
      addr1.setCity("San Jose");
      addr1.setZip(95123);

      Address addr2 = new Address();
      addr2.setCity("Sunnyvale");
      addr2.setZip(94086);
      list1.add(addr2);

      cache_.attach("/list", list1);
      list1 = (List<Address>) cache_.find("/list");
      assertEquals("List size should be ", 1, list1.size());
      cache_.attach("/alias", list1);

      list1 = (List<Address>) cache_.find("/list");
      assertEquals("List size should be ", 1, list1.size());
      List list2 = (List) cache_.find("/alias");
//      System.out.println(cache_.printDetails());
      assertEquals("List size should be ", 1, list2.size());
      assertEquals("Both lists should be equal ", list1, list2);
      assertEquals("Both list values should be equal ", list1.get(0), list2.get(0));
   }

   public void testRelationshipWithSharedSet1() throws Exception
   {
      log.info("testRelationshipWithSet() ....");
      Set<Address> set1 = new HashSet<Address>();
      Address addr = new Address();
      addr.setCity("San Jose");
      addr.setZip(95123);
      set1.add(addr);

      // Pure set
      cache_.attach("/set", set1);
      // We specifically need to use Proxy otherwise it won't work with multiple references
      set1 = (Set<Address>) cache_.find("/set");
      cache_.attach("/alias", set1);

      set1 = (Set<Address>) cache_.find("/set");
      Set set2 = (Set) cache_.find("/alias");
      assertTrue("Set size should not be 0 ", (set2.size() != 0));
      assertEquals("Both sets should be equal ", set1, set2);
      Address add1 = (Address) set2.iterator().next();
      assertNotNull("Address should not be null", add1);
      assertEquals("Zip ", 95123, add1.getZip());
   }

   public void testRelationshipWithSharedSet2() throws Exception
   {
      log.info("testRelationshipWithSet2() ....");
      Set<Address> set1 = new HashSet<Address>();
      Address addr = new Address();
      addr.setCity("San Jose");
      addr.setZip(95123);
      set1.add(addr);

      Set<Address> set2 = new HashSet<Address>();
      set2.add(addr);

      cache_.attach("/set1", set1);
      cache_.attach("/set2", set2);

      Address add2 = (Address) ((Set) cache_.find("/set2")).iterator().next();
      Address add1 = (Address) ((Set) cache_.find("/set1")).iterator().next();
      assertEquals("Address should be the same", add1, add2);
      assertEquals("Both shared object should be equal ", add2.getZip(), add1.getZip());
   }

   public void testRelationshipWithSharedMap1() throws Exception
   {
      log.info("testRelationshipWithMap() ....");
      Map<String, Address> map1 = new HashMap<String, Address>();
      Address addr = new Address();
      addr.setCity("San Jose");
      addr.setZip(95123);
      map1.put("key1", addr);

      cache_.attach("/map", map1);
      cache_.attach("/alias", map1);

      map1 = (Map<String, Address>) cache_.find("/map");
      Map map2 = (Map) cache_.find("/alias");
      assertTrue("Map size should not be 0 ", (map2.size() != 0));
      assertEquals("Both maps should be equal ", map1, map2);
      Address add1 = (Address) ((Map.Entry) map2.entrySet().iterator().next()).getValue();
      assertNotNull("Address should not be null", add1);
      assertEquals("Zip ", 95123, add1.getZip());
   }

   public void testRelationshipWithSharedMap2() throws Exception
   {
      log.info("testRelationshipWithMap2() ....");
      Map<String, Address> map1 = new HashMap<String, Address>();
      Address addr = new Address();
      addr.setCity("San Jose");
      addr.setZip(95123);
      map1.put("key1", addr);

      Map<String, Address> map2 = new HashMap<String, Address>();
      map2.put("key2", addr);

      // Pure map
      cache_.attach("/map", map1);
      cache_.attach("/alias", map2);

      map1 = (Map<String, Address>) cache_.find("/map");
      map2 = (Map<String, Address>) cache_.find("/alias");
      assertTrue("Map size should not be 0 ", (map2.size() != 0));
      Address add1 = (Address) ((Map.Entry) map2.entrySet().iterator().next()).getValue();
      assertNotNull("Address should not be null", add1);
      assertEquals("Zip ", 95123, add1.getZip());
   }


   public void testObjectIdentity() throws Exception
   {
   }



}

