/*
 * JBoss, Home of Professional Open Source
 *
 * Distributable under LGPL license.
 * See terms of license at gnu.org.
 */
package org.jboss.cache.options;

import junit.framework.Assert;
import junit.framework.TestCase;
import org.jboss.cache.Fqn;
import org.jboss.cache.OptimisticTreeNode;
import org.jboss.cache.TreeCache;
import org.jboss.cache.config.Option;
import org.jboss.cache.optimistic.DataVersion;
import org.jboss.cache.optimistic.DefaultDataVersion;

import javax.transaction.TransactionManager;

/**
 * Tests the passing in of explicit {@see DataVersion} instances when using optimistic locking.
 *
 * @author <a href="mailto:manik@jboss.org">Manik Surtani (manik@jboss.org)</a>
 */
public class ExplicitVersionsTest extends TestCase
{

    private TreeCache cache;
    private Fqn fqn = Fqn.fromString("/a");
    private String key = "key";
    private Option option = new Option();

    protected void setUp() throws Exception
    {
        if (cache != null) tearDown();
        cache = new TreeCache();
        cache.setCacheMode( TreeCache.LOCAL );
        cache.setNodeLockingScheme("OPTIMISTIC");
        cache.setTransactionManagerLookupClass("org.jboss.cache.DummyTransactionManagerLookup");
        cache.startService();
    }

    protected void tearDown()
    {
        if (cache != null)
        {
            cache.stopService();
            cache = null;
        }
    }

    public void testSimplePut() throws Exception
    {
        DataVersion version = new TestVersion("99");
        option.setDataVersion( version );
        cache.put(fqn, key, "value", option);

        //now retrieve the data from the cache.
        Assert.assertEquals("value", cache.get(fqn,key));

        // get a hold of the node
        OptimisticTreeNode node = (OptimisticTreeNode) cache.get(fqn);
        DataVersion versionFromCache = node.getVersion();

        Assert.assertEquals(TestVersion.class, versionFromCache.getClass());
        Assert.assertEquals("99", ((TestVersion) versionFromCache).getInternalVersion());
    }

    public void testFailingPut() throws Exception
    {
        DataVersion version = new TestVersion("99");
        option.setDataVersion(version);
        cache.put(fqn, key, "value", option);

        version = new TestVersion("25");
        option.setDataVersion(version);
        TransactionManager mgr = cache.getTransactionManager();
        mgr.begin();
        cache.put(fqn, key, "value2", option);
        try
        {
            mgr.commit();
            Assert.assertTrue("expected to fail", false);
        }
        catch (Exception e)
        {
            // should fail.
            Assert.assertTrue("expected to fail", true);
        }
    }

    public void testIncompatibleVersionTypes() throws Exception
    {
        DataVersion version = new TestVersion("99");
        option.setDataVersion(version);
        cache.put(fqn, key, "value", option);
        TransactionManager mgr = cache.getTransactionManager();
        mgr.begin();
        option.setDataVersion(new DefaultDataVersion(777));
        cache.put(fqn, key, "value2", option);
        try
        {
            // this call will use implicit versioning and will hence fail.
            mgr.commit();
            Assert.assertTrue("expected to fail", false);
        }
        catch (Exception e)
        {
            // should fail.
            Assert.assertTrue("expected to fail", true);
        }
    }

   public void testExplicitVersionOnLeaf() throws Exception
   {
      cache.put("/org/domain/Entity", null);
      assertEquals(1, ((DefaultDataVersion)((OptimisticTreeNode)cache.get("/org/domain/Entity")).getVersion()).getRawVersion());

      TestVersion v = new TestVersion("Arse");
      Option o = new Option();
      o.setDataVersion(v);

      cache.put(Fqn.fromString("/org/domain/Entity/EntityInstance#1"), "k", "v", o);

      assertEquals(1, ((DefaultDataVersion)((OptimisticTreeNode)cache.get("/org/domain/Entity")).getVersion()).getRawVersion());
      assertEquals(v, ((OptimisticTreeNode)cache.get("/org/domain/Entity/EntityInstance#1")).getVersion());
   }

   public void testExplicitVersionOnLeafImplicitParentCreation() throws Exception
   {
      TestVersion v = new TestVersion("Arse");
      Option o = new Option();
      o.setDataVersion(v);

      cache.put(Fqn.fromString("/org/domain/Entity/EntityInstance#1"), "k", "v", o);

      assertEquals(1, ((DefaultDataVersion)((OptimisticTreeNode)cache.get("/org/domain/Entity")).getVersion()).getRawVersion());
      assertEquals(v, ((OptimisticTreeNode)cache.get("/org/domain/Entity/EntityInstance#1")).getVersion());
   }

   public void testExplicitVersionOnParentAndChild() throws Exception
   {
      TestVersion vParent = new TestVersion("Parent-Version");
      Option o = new Option();
      o.setDataVersion(vParent);

      cache.getTransactionManager().begin();
      cache.put(Fqn.fromString("/parent"), "k", "v", o);
      cache.getTransactionManager().commit();

      assertEquals(0, ((DefaultDataVersion)((OptimisticTreeNode)cache.get("/")).getVersion()).getRawVersion());
      assertEquals(vParent, ((OptimisticTreeNode)cache.get("/parent")).getVersion());
      
      TestVersion vChild = new TestVersion("Child-Version");
      o = new Option();
      o.setDataVersion(vChild);

      cache.getTransactionManager().begin();
      cache.put(Fqn.fromString("/parent/child"), "k", "v", o);
      cache.getTransactionManager().commit();

      assertEquals(0, ((DefaultDataVersion)((OptimisticTreeNode)cache.get("/")).getVersion()).getRawVersion());
      assertEquals(vParent, ((OptimisticTreeNode)cache.get("/parent")).getVersion());
      assertEquals(vChild, ((OptimisticTreeNode)cache.get("/parent/child")).getVersion());
   }

}

/**
 * Note that this uses STRING comparisons!!
 */
class TestVersion implements DataVersion
{
    private String myVersion;

    public TestVersion(String version)
    {
        myVersion = version;
    }

    public String getInternalVersion()
    {
        return myVersion;
    }

    public void setInternalVersion(String version)
    {
        myVersion = version;
    }

    public boolean newerThan(DataVersion other)
    {
        if (other instanceof TestVersion)
        {
            return myVersion.compareTo(((TestVersion) other).getInternalVersion()) > 0;
        }
        else throw new IllegalArgumentException("version type mismatch");
    }

   public String toString()
   {
      return "TestVersion-" + myVersion;
   }

   public boolean equals(Object other)
   {
      if (other instanceof TestVersion)
      {
         TestVersion oVersion = (TestVersion) other;
         if (oVersion.myVersion == null && myVersion == null) return true;
         if (myVersion != null) return myVersion.equals(oVersion.myVersion);
      }
      return false;
   }
}