package org.jboss.cache.optimistic;

import org.jboss.cache.Fqn;
import org.jboss.cache.Node;
import org.jboss.cache.TreeCache;
import org.jboss.cache.OptimisticTreeNode;

import javax.transaction.TransactionManager;

/**
 * @author <a href="mailto:manik@jboss.org">Manik Surtani</a>
 */
public class ParentVersionTest extends AbstractOptimisticTestCase
{
   private TreeCache cache;
   private TransactionManager tm;

   protected boolean lockParentForChildInsertRemove = false; // the default
   private Fqn parent = Fqn.fromString("/parent");
   private Fqn child1 = Fqn.fromString("/parent/child1");
   private Fqn child2 = Fqn.fromString("/parent/child2");
   private Fqn deepchild = Fqn.fromString("/parent/deep/child");

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

   protected void setUp() throws Exception
   {
      if (lockParentForChildInsertRemove)
      {
         cache = createCacheUnstarted();
         cache.setLockParentForChildInsertRemove(true);
         cache.start();
      }
      else
         cache = createCache();

      tm = cache.getTransactionManager();
   }

   protected void tearDown()
   {
      destroyCache(cache);
   }

   private long getVersion(Node n)
   {
      return ((DefaultDataVersion) ((OptimisticTreeNode) n).getVersion()).getRawVersion();
   }

   public void testSimpleAdd() throws Exception
   {
      cache.put(parent, "k", "v");
      long parentVersion = getVersion(cache.get(parent));
      cache.put(child1, "k", "v");

      if (lockParentForChildInsertRemove)
         assertEquals(parentVersion + 1, getVersion(cache.get(parent)));
      else
         assertEquals(parentVersion, getVersion(cache.get(parent)));

      assertTrue(cache.exists(parent));
      assertTrue(cache.exists(child1));
   }

   public void testSimpleRemove() throws Exception
   {
      cache.put(parent, "k", "v");
      cache.put(child1, "k", "v");
      assertTrue(cache.exists(parent));
      assertTrue(cache.exists(child1));

      long parentVersion = getVersion(cache.get(parent));

      cache.remove(child1);

      if (lockParentForChildInsertRemove)
         assertEquals(parentVersion + 1, getVersion(cache.get(parent)));
      else
         assertEquals(parentVersion, getVersion(cache.get(parent)));

      assertTrue(cache.exists(parent));
      assertFalse("Should have removed child1", cache.exists(child1));
   }

   public void testAddAndRemove() throws Exception
   {
      cache.put(parent, "k", "v");
      cache.put(child1, "k", "v");

      assertTrue(cache.exists(parent));
      assertTrue(cache.exists(child1));
      assertFalse(cache.exists(child2));

      long parentVersion = getVersion(cache.get(parent));

      tm.begin();
      cache.put(child2, "k", "v");
      cache.remove(child1);
      tm.commit();

      if (lockParentForChildInsertRemove)
         assertEquals(parentVersion + 1, getVersion(cache.get(parent)));
      else
         assertEquals(parentVersion, getVersion(cache.get(parent)));

      assertTrue(cache.exists(parent));
      assertFalse("Should have removed child1", cache.exists(child1));
      assertTrue(cache.exists(child2));
   }

   public void testAddAndRemoveOverlap() throws Exception
   {
      cache.put(parent, "k", "v");
      cache.put(child1, "k", "v");

      assertTrue(cache.exists(parent));
      assertTrue(cache.exists(child1));
      assertFalse(cache.exists(child2));

      long parentVersion = getVersion(cache.get(parent));

      tm.begin();
      cache.put(child2, "k", "v");
      cache.remove(child1);
      cache.remove(child2);
      cache.remove(child1);
      cache.put(child1, "k", "v");
      cache.remove(child1);
      cache.remove(child2);
      cache.put(child2, "k", "v");
      tm.commit();

      if (lockParentForChildInsertRemove)
         assertEquals(parentVersion + 1, getVersion(cache.get(parent)));
      else
         assertEquals(parentVersion, getVersion(cache.get(parent)));

      assertTrue(cache.exists(parent));
      assertFalse("Should have removed child1", cache.exists(child1));
      assertTrue(cache.exists(child2));
   }

   public void testRemoveAndAdd() throws Exception
   {
      cache.put(parent, "k", "v");
      cache.put(child1, "k", "v");

      assertTrue(cache.exists(parent));
      assertTrue(cache.exists(child1));
      assertFalse(cache.exists(child2));

      long parentVersion = getVersion(cache.get(parent));

      tm.begin();
      cache.remove(child1);
      cache.put(child2, "k", "v");
      tm.commit();

      if (lockParentForChildInsertRemove)
         assertEquals(parentVersion + 1, getVersion(cache.get(parent)));
      else
         assertEquals(parentVersion, getVersion(cache.get(parent)));

      assertTrue(cache.exists(parent));
      assertFalse("Should have removed child1", cache.exists(child1));
      assertTrue(cache.exists(child2));
   }

   public void testDeepRemove() throws Exception
   {
      cache.put(parent, "k", "v");
      cache.put(deepchild, "k", "v");

      assertTrue(cache.exists(parent));
      assertTrue(cache.exists(deepchild));

      long parentVersion = getVersion(cache.get(parent));

      cache.remove(deepchild);

      assertEquals(parentVersion, getVersion(cache.get(parent)));

      assertTrue(cache.exists(parent));
      assertFalse("Should have removed deepchild", cache.exists(deepchild));
   }

   public void testDeepAdd() throws Exception
   {
      cache.put(parent, "k", "v");

      assertTrue(cache.exists(parent));
      assertFalse(cache.exists(deepchild));

      long parentVersion = getVersion(cache.get(parent));

      cache.put(deepchild, "k", "v");

      if (lockParentForChildInsertRemove)
         assertEquals(parentVersion + 1, getVersion(cache.get(parent)));
      else
         assertEquals(parentVersion, getVersion(cache.get(parent)));

      assertTrue(cache.exists(parent));
      assertTrue(cache.exists(deepchild));
   }
}
