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 replicated Map
 * @author Ben Wang
 * @author Scott Marlow
 */

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

   public ReplicatedSyncMapTest(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() ....");
      Map map1 = new HashMap();
      Address addr = new Address();
      addr.setCity("San Jose");
      addr.setZip(95123);
      map1.put("key1",addr);

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

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

      // Pure list
      cache1.putObject("/map", map1);
      map1 = (Map)cache1.getObject("/map");
      map1.put("key2",addr2);
      cache1.removeObject("/map");
      assertEquals("Detached map should still be", 2, map1.size());
      map1.put("key3", addr3);
      cache1.putObject("/map", map1);

      Map map2 = (Map)cache2.getObject("/map");
      assertEquals("Map size should be ", 3, map2.size());
   }

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

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

      Map map2 = (Map)cache1.getObject("/alias");
      Address add1 = (Address)((Map.Entry)map2.entrySet().iterator().next()).getValue();
      assertNotNull("Address should not be null", add1);
      assertEquals("Zip ", 95123, add1.getZip());

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

   public void testNullWithSharedMap1() throws Exception
   {
      log.info("testNullWithSharedMap1() ....");
      Map map1 = new HashMap();
      map1.put("null value test",null);
      map1.put(null,"null key test");

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

      Map map2 = (Map)cache1.getObject("/alias");

      map1 = (Map)cache2.getObject("/map");
      map2 = (Map)cache2.getObject("/alias");
      assertTrue("Map size should not be 0 ", (map2.size() != 0));
      assertEquals("Both maps should be equal ", map1, map2);

      assertTrue("Get null key returns non-null value", map2.get(null) != null);
      assertTrue("Get null key returns correct value", map2.get(null).equals("null key test"));

      assertTrue("Get null value returns null value", map2.get("null value test") == null);
      assertTrue("containsKey test for null value", map2.containsKey("null value test"));
      assertTrue("containsKey test for null key", map2.containsKey(null));
      assertTrue("containsValue test for null value", map2.containsValue(null));

      assertTrue("values (positive) null test",map2.values().contains(null));
      Collection walk = map1.values();

      assertTrue(walk.remove(null));
      assertFalse("values (negative) null test",map2.values().contains(null));

      assertTrue("values (positive) null test",map2.values().contains("null key test"));
      assertTrue(walk.remove("null key test"));
      assertFalse("values (negative) null test",map2.values().contains("null key test"));

      map1.put("null value test",null);
      map1.put(null,"null key test");


      assertTrue("remove null item test",  map1.remove(null).equals("null key test"));
      assertFalse("null item removed", map2.containsKey(null));


   }

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

      Map map2 = new HashMap();
      map2.put("key2", addr);

      cache2.putObject("/map1", map1);
      cache2.putObject("/map2", map2);

      map1 = (Map)cache2.getObject("/map1");
      map2 = (Map)cache2.getObject("/map2");
      assertTrue("Map size should not be 0 ", (map2.size() != 0));
      assertEquals("Both maps should be equal ", map1.get("key1"), map2.get("key2"));
      Address add1 = (Address)((Map.Entry)map2.entrySet().iterator().next()).getValue();
      assertNotNull("Address should not be null", add1);
      assertEquals("Zip ", 95123, add1.getZip());
   }

   public void testKeySetRemoveWithSharedMap1() throws Exception
   {
      log.info("testKeySetRemoveWithSharedMap1() ....");
      Map map1 = new HashMap();
      Address addr = new Address();
      addr.setCity("San Jose");
      addr.setZip(95123);
      map1.put("key1_map1", addr);
      map1.put("key2_map1", null);
      map1.put(null,"null value test");
      Map map2 = new HashMap();
      map2.put("key1_map2", addr);
      map2.put("key2_map2", "round");

      assertTrue("key1 exists", map1.containsKey("key1_map1"));


      cache2.putObject("/map1", map1);
      cache2.putObject("/map2", map2);

      map1 = (Map)cache1.getObject("/map1");
      map2 = (Map)cache1.getObject("/map2");

      assertTrue("key1 exists", map1.containsKey("key1_map1"));
      assertTrue("null key exists", map1.containsKey(null));
      assertTrue("key2 exists", map1.containsKey("key2_map1"));

      Set set = map1.keySet();
      Iterator iter = set.iterator();
      while(iter.hasNext()) {
         Object o = iter.next();
         System.out.println("testKeySetRemoveWithSharedMap1 iter.next returned: " + o);

         if(o == null || "key1_map1".equals(o))
            iter.remove();
      }
      assertTrue("key2 exists", map1.containsKey("key2_map1"));
      assertFalse("key1 doesn't exist", map1.containsKey("key1_map1"));
      assertFalse("null key doesn't exist", map1.containsKey(null));

      map1 = (Map)cache2.getObject("/map1");
      map2 = (Map)cache2.getObject("/map2");

      assertTrue("key2 exists", map1.containsKey("key2_map1"));
      assertFalse("key1 doesn't exist", map1.containsKey("key1_map1"));
      assertFalse("null key doesn't exist", map1.containsKey(null));

   }


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

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

}

