/*
 * JBoss, Home of Professional Open Source
 *
 * Distributable under LGPL license.
 * See terms of license at gnu.org.
 */

package org.jboss.cache.api;

import org.jboss.cache.Cache;
import org.jboss.cache.CacheSPI;
import org.jboss.cache.DefaultCacheFactory;
import org.jboss.cache.Fqn;
import org.jboss.cache.InvocationContext;
import org.jboss.cache.Node;
import org.jboss.cache.config.Configuration.CacheMode;
import org.jboss.cache.config.Option;
import org.jboss.cache.factories.UnitTestCacheConfigurationFactory;
import org.jboss.cache.util.TestingUtil;
import static org.testng.AssertJUnit.*;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

import javax.transaction.HeuristicMixedException;
import javax.transaction.HeuristicRollbackException;
import javax.transaction.NotSupportedException;
import javax.transaction.RollbackException;
import javax.transaction.Status;
import javax.transaction.SystemException;
import javax.transaction.Transaction;
import javax.transaction.TransactionManager;
import java.util.ArrayList;
import java.util.List;

/**
 * @author <a href="mailto:manik@jboss.org">Manik Surtani (manik@jboss.org)</a>
 */
@Test(groups = {"functional", "jgroups", "transaction"})
public class SyncReplTxTest
{
   private List<CacheSPI<Object, Object>> caches;

   @BeforeMethod(alwaysRun = true)
   public void setUp()
   {
      System.out.println("*** In setUp()");
      caches = new ArrayList<CacheSPI<Object, Object>>();
      caches.add((CacheSPI<Object, Object>) new DefaultCacheFactory().createCache(UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC)));
      caches.add((CacheSPI<Object, Object>) new DefaultCacheFactory().createCache(UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC)));

      TestingUtil.blockUntilViewsReceived(caches.toArray(new Cache[0]), 5000);
      System.out.println("*** Finished setUp()");
   }

   @AfterMethod(alwaysRun = true)
   public void tearDown()
   {
      System.out.println("*** In tearDown()");
      if (caches != null)
      {
         for (CacheSPI c : caches)
         {
            if (c != null)
            {
               Transaction t;

               try
               {
                  if ((t = c.getTransactionManager().getTransaction()) != null)
                  {
                     try
                     {
                        if (t.getStatus() == Status.STATUS_ACTIVE)
                           c.getTransactionManager().rollback();
                     }
                     catch (SystemException e)
                     {
                        // do nothing
                     }
                  }
               }
               catch (SystemException e)
               {
                  // do nothing
               }
               c.stop();
            }
         }
         caches = null;
      }
      System.out.println("*** Finished tearDown()");
   }

   private TransactionManager beginTransaction(Cache<Object, Object> cache) throws NotSupportedException, SystemException
   {
      TransactionManager mgr = cache.getConfiguration().getRuntimeConfig().getTransactionManager();
      mgr.begin();
      return mgr;
   }

   public void testBasicOperation() throws SystemException, NotSupportedException, HeuristicMixedException, HeuristicRollbackException, RollbackException
   {
      assertClusterSize("Should only be 2  caches in the cluster!!!", 2);
      assertInvocationContextInitState();

      Fqn<String> f = Fqn.fromString("/test/data");
      String k = "key", v = "value";

      assertNull("Should be null", caches.get(0).getRoot().getChild(f));
      assertNull("Should be null", caches.get(1).getRoot().getChild(f));

      Node<Object, Object> node = caches.get(0).getRoot().addChild(f);

      assertNotNull("Should not be null", node);

      TransactionManager tm = beginTransaction(caches.get(0));
      node.put(k, v);
      Transaction tx1 = caches.get(0).getInvocationContext().getTransaction();
      assertNotNull("Transaction can't be null ", tx1);
      tm.commit();

      assertEquals(v, node.get(k));
      assertEquals(v, caches.get(0).get(f, k));
      assertEquals("Should have replicated", v, caches.get(1).get(f, k));
   }

   private void assertClusterSize(String message, int size)
   {
      for (Cache<Object, Object> c : caches)
      {
         assertClusterSize(message, size, c);
      }
   }

   private void assertClusterSize(String message, int size, Cache<Object, Object> c)
   {
      assertEquals(message, size, c.getMembers().size());
   }

   private void assertInvocationContextInitState()
   {
      for (Cache<Object, Object> c : caches)
      {
         assertInvocationContextInitState(c);
      }
   }

   private void assertInvocationContextInitState(Cache<Object, Object> c)
   {
      InvocationContext ctx = c.getInvocationContext();
      InvocationContext control = null;
      try
      {
         control = ctx.clone();
      }
      catch (CloneNotSupportedException e)
      {
         throw new RuntimeException(e);
      }

      control.reset();
      control.setOptionOverrides(new Option());

      assertEquals("Should be equal", control, ctx);
   }
}