package org.jboss.cache.marshall;

import junit.framework.TestCase;
import org.jboss.cache.TreeCache;
import org.jboss.cache.lock.IsolationLevel;

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


public class CustomCollectionTest extends TestCase
{
   private TreeCache cache1 =null;
   private TreeCache cache2 =null;
   private String myListClass = MyList.class.getName();
   private String mySetClass = MySet.class.getName();
   private String myMapClass = MyMap.class.getName();

   protected void setUp() throws Exception
   {
      super.setUp();

      cache1 =new TreeCache();
      cache1.setCacheMode(TreeCache.REPL_SYNC);
      cache1.setTransactionManagerLookupClass("org.jboss.cache.DummyTransactionManagerLookup");
      cache1.setIsolationLevel(IsolationLevel.REPEATABLE_READ);
      cache1.createService();
      cache1.startService();

      cache2 =new TreeCache();
      cache2.setCacheMode(TreeCache.REPL_SYNC);
      cache2.setTransactionManagerLookupClass("org.jboss.cache.DummyTransactionManagerLookup");
      cache2.setIsolationLevel(IsolationLevel.REPEATABLE_READ);
      cache2.createService();
      cache2.startService();

   }

   protected void tearDown() throws Exception
   {
      cache1.stopService();
      cache2.stopService();
   }

   public void testMap() throws Exception
   {
      doMapTest(false);
   }
   
   public void testMapWithRegions() throws Exception
   {
      doMapTest(true);
   }

   public void testSet() throws Exception
   {
      doSetTest(false);
   }

   public void testSetWithRegions() throws Exception
   {
      doSetTest(true);
   }

   public void testList() throws Exception
   {
      doListTest(false);
   }

   public void testListWithRegions() throws Exception
   {
      doListTest(true);
   }

   private void doMapTest(boolean contextClassLoader) throws Exception
   {

      ClassLoader customClassLoader = getClassLoader();
      Class mapClass = customClassLoader.loadClass(myMapClass);
      Map map = (Map) (contextClassLoader ? mapClass.newInstance() : new MyMap());

      map.put("k", "v");

      if (contextClassLoader)
      {
         enableRegionBasedClassLoading(customClassLoader);
      }

      cache1.put("/a", "key", map);
      Object o = cache2.get("/a", "key");
      assertTrue(o instanceof Map);
      if (contextClassLoader)
      {
         assertNotSame(MyMap.class, o.getClass());
         assertSame(mapClass, o.getClass());
      }
      else
      {
         assertSame(MyMap.class, o.getClass());
         assertNotSame(mapClass, o.getClass());
      }
      assertEquals("v", ((Map) o).get("k"));
   }

   private void doSetTest(boolean contextClassLoader) throws Exception
   {

      ClassLoader customClassLoader = getClassLoader();
      Class setClass = customClassLoader.loadClass(mySetClass);
      Set set = (Set) (contextClassLoader ? setClass.newInstance() : new MySet());

      set.add("k");

      if (contextClassLoader)
      {
         enableRegionBasedClassLoading(customClassLoader);
      }

      cache1.put("/a", "key", set);
      Object o = cache2.get("/a", "key");
      assertTrue(o instanceof Set);
      if (contextClassLoader)
      {
         assertNotSame(MySet.class, o.getClass());
         assertSame(setClass, o.getClass());
      }
      else
      {
         assertSame(MySet.class, o.getClass());
         assertNotSame(setClass, o.getClass());
      }
      assertTrue(((Set) o).contains("k"));
   }

   private void doListTest(boolean contextClassLoader) throws Exception
   {

      ClassLoader customClassLoader = getClassLoader();
      Class listClass = customClassLoader.loadClass(myListClass);
      List list = (List) (contextClassLoader ? listClass.newInstance() : new MyList());

      list.add("k");

      if (contextClassLoader)
      {
         enableRegionBasedClassLoading(customClassLoader);
      }

      cache1.put("/a", "key", list);
      Object o = cache2.get("/a", "key");
      assertTrue(o instanceof List);
      if (contextClassLoader)
      {
         assertSame(listClass, o.getClass());
         assertNotSame(MyList.class, o.getClass());
      }
      else
      {
         assertSame(MyList.class, o.getClass());
         assertNotSame(listClass, o.getClass());
      }
      assertTrue(((List) o).contains("k"));
   }


   public void testHarnessClassLoader() throws Exception
   {
      Class myListFromCustomLoader = getClassLoader().loadClass(myListClass);
      assertNotNull(myListFromCustomLoader);
      assertFalse(MyList.class.equals(myListFromCustomLoader));

      Object customLoaderMyList = myListFromCustomLoader.newInstance();
      try
      {
         MyList m = (MyList) customLoaderMyList;
         fail("Should have barfed");
      }
      catch (ClassCastException cce)
      {
         // expected
      }
   }

   private void enableRegionBasedClassLoading(ClassLoader customClassLoader) throws Exception
   {
      cache1.setUseRegionBasedMarshalling(true);
      cache1.getRegionManager().createRegion("/a", customClassLoader);
      cache1.getMarshaller().defaultMarshaller.registerClassLoader("/a", customClassLoader);
      cache1.getMarshaller().defaultMarshaller.useRegionBasedMarshalling = true;
      cache2.setUseRegionBasedMarshalling(true);
      cache2.getRegionManager().createRegion("/a", customClassLoader);
      cache2.getMarshaller().defaultMarshaller.registerClassLoader("/a", customClassLoader);
      cache2.getMarshaller().defaultMarshaller.useRegionBasedMarshalling = true;
   }

   private ClassLoader getClassLoader() throws Exception
   {
      String[] includesClasses = { myListClass, mySetClass, myMapClass };
      String [] excludesClasses = {};
      ClassLoader cl = Thread.currentThread().getContextClassLoader();
      return new SelectedClassnameClassLoader(includesClasses, excludesClasses, cl);
   }
}