package org.jboss.cache.pojo.collection;

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

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.cache.Fqn;
import org.jboss.cache.config.Configuration.CacheMode;
import org.jboss.cache.factories.UnitTestCacheConfigurationFactory;
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"})
public class ReplicatedSyncSetTest 
{
   Log log = LogFactory.getLog(ReplicatedSyncSetTest.class);
   PojoCache cache1;
   PojoCache cache2;


   @BeforeMethod(alwaysRun = true)
   protected void setUp() throws Exception
   {
      log.info("setUp() ....");
      cache1 = createCache("CacheGroup");
      cache2 = createCache("CacheGroup");
   }

   @AfterMethod(alwaysRun = true)
   protected void tearDown() throws Exception
   {
      cache1.getCache().removeNode(Fqn.fromString("/"));
      cache1.stop();
      cache2.stop();
   }

   private PojoCache createCache(String name) throws Exception
   {
      boolean toStart = false;
      PojoCache cache = PojoCacheFactory.createCache(UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC), toStart);
      cache.start();
      return cache;
   }

//   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
    */
   @SuppressWarnings("unchecked")
   public void testAttachDetach() throws Exception
   {
      log.info("testAttachDetach() ....");
      Set<Address> set1 = new HashSet<Address>();
      Address addr = new Address();
      addr.setCity("San Jose");
      addr.setZip(95123);
      set1.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.attach("/set", set1);
      set1 = (Set<Address>) cache1.find("/set");
      set1.add(addr2);
      set1 = (Set<Address>) cache1.detach("/set");
      assertEquals("Detached set should still be", 2, set1.size());
      set1.add(addr3);
      cache1.attach("/set", set1);

      Set set2 = (Set) cache2.find("/set");
      assertEquals("Set size should be ", 3, set2.size());
   }

   @SuppressWarnings("unchecked")
   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
      cache1.attach("/set", set1);
      // We specifically need to use Proxy otherwise it won't work with multiple references
      set1 = (Set<Address>) cache1.find("/set");
      cache1.attach("/alias", set1);

      Set set2 = (Set) cache1.find("/alias");
      Address add1 = (Address) set2.iterator().next();
      assertNotNull("Address should not be null", add1);
      assertEquals("Zip ", 95123, add1.getZip());

      set1 = (Set<Address>) cache2.find("/set");
      set2 = (Set) cache2.find("/alias");
      assertTrue("Set size should not be 0 ", (set2.size() != 0));
      assertEquals("Both sets should be equal ", set1, set2);
      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);

      cache1.attach("/set1", set1);
      cache1.attach("/set2", set2);
      @SuppressWarnings("unchecked")
      Address add2 = ((Set<Address>) cache2.find("/set2")).iterator().next();
      @SuppressWarnings("unchecked")
      Address add1 = ((Set<Address>) cache2.find("/set1")).iterator().next();
      assertEquals("Address should be the same", add1, add2);
      assertEquals("Both shared object should be equal ", add2.getZip(), add1.getZip());
   }

   @SuppressWarnings("unchecked")
   public void testNullWithSharedSet1() throws Exception
   {
      log.info("testNullWithSharedSet1() ....");
      Set<String> set1 = new HashSet<String>();
      set1.add("element 0");
      set1.add(null);  // element 1
      set1.add("element 2");
      assertTrue("contains test for null value", set1.contains(null));
      Object a1[] = set1.toArray();
      for (int looper = 0; looper < a1.length; looper++)
      {
         System.out.println("contained values:" + a1[looper]);
      }

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

      Set<String> set2 = (Set<String>) cache1.find("/alias");

      set1 = (Set<String>) cache2.find("/set");
      set2 = (Set<String>) cache2.find("/alias");
      assertTrue("Set size should not be 0 ", (set2.size() != 0));
      assertEquals("Both sets should be equal ", set1, set2);

      a1 = set1.toArray();
      for (int looper = 0; looper < a1.length; looper++)
      {
         System.out.println("contained values:" + a1[looper]);
      }
      assertTrue("contains test for null value", set1.contains(null));
      assertTrue("contains test for null value", set2.contains(null));

      Iterator<String> iter = set1.iterator();
      while (iter.hasNext())
      {
         Object val = iter.next();
         if ("element 2".equals(val))
         {
            iter.remove();  // remove element 2
         }
      }
      assertFalse("element 2 is removed", set2.contains("element 2"));
   }

   /* This test won't pass since the recursive set will fail during replication as well
   because of HashSet that calls out to HashMap hashCode. This causes the recursion.
   public void testRecursion2() throws Exception
   {
      Set set = new HashSet();
      set.add("1");
      set.add("2");
      set.add(set);

      cache1.attach("set", set);
   }
   */



}

