package org.jboss.cache.api;

import org.jboss.cache.CacheSPI;
import org.jboss.cache.DefaultCacheFactory;
import org.jboss.cache.util.TestingUtil;
import org.jboss.cache.config.Configuration;
import org.jboss.cache.config.Configuration.CacheMode;
import org.jboss.cache.factories.UnitTestCacheConfigurationFactory;
import org.jboss.cache.util.internals.ViewChangeListener;
import static org.testng.AssertJUnit.*;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

import java.util.List;
import java.util.concurrent.TimeUnit;

@Test(groups = "functional")
public class CacheSPITest
{
   private CacheSPI<Object, Object> cache1;
   private CacheSPI<Object, Object> cache2;

   protected boolean optimistic = false;

   @BeforeMethod(alwaysRun = true)
   public void setUp() throws Exception
   {
      Configuration conf1 = UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC);

      Configuration conf2 = UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC);

      conf1.setNodeLockingScheme(optimistic ? Configuration.NodeLockingScheme.OPTIMISTIC : Configuration.NodeLockingScheme.PESSIMISTIC);
      conf2.setNodeLockingScheme(optimistic ? Configuration.NodeLockingScheme.OPTIMISTIC : Configuration.NodeLockingScheme.PESSIMISTIC);

      cache1 = (CacheSPI<Object, Object>) new DefaultCacheFactory().createCache(conf1, false);
      cache2 = (CacheSPI<Object, Object>) new DefaultCacheFactory().createCache(conf2, false);
   }

   @AfterMethod(alwaysRun = true)
   public void tearDown() throws Exception
   {
      if (cache1 != null)
      {
         try
         {
            cache1.stop();
         }
         catch (Exception e)
         {
         }
      }

      if (cache2 != null)
      {
         try
         {
            cache2.stop();
         }
         catch (Exception e)
         {
         }
      }
   }

   public void testGetMembers() throws Exception
   {

      cache1.start();
      List memb1 = cache1.getMembers();
      assertEquals("View has one member", 1, memb1.size());

      Object coord = memb1.get(0);

      cache2.start();
      memb1 = cache1.getMembers();
      TestingUtil.blockUntilViewsReceived(5000, false, cache1, cache2);
      List memb2 = cache2.getMembers();
      assertEquals("View has two members", 2, memb1.size());
      assertEquals("Both caches have same view", memb1, memb2);

      cache1.stop();
      TestingUtil.blockUntilViewsReceived(5000, false, cache2);
      memb2 = cache2.getMembers();
      assertEquals("View has one member", 1, memb2.size());
      assertFalse("Coordinator changed", coord.equals(memb2.get(0)));
   }

   public void testIsCoordinator() throws Exception
   {
      Configuration conf1 = UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC);
      Configuration conf2 = UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC);

      cache1 = (CacheSPI<Object, Object>) new DefaultCacheFactory<Object, Object>().createCache(conf1, false);
      cache2 = (CacheSPI<Object, Object>) new DefaultCacheFactory<Object, Object>().createCache(conf2, false);

      cache1.start();
      assertTrue("Cache1 is coordinator", cache1.getRPCManager().isCoordinator());

      cache2.start();
      assertTrue("Cache1 is still coordinator", cache1.getRPCManager().isCoordinator());
      assertFalse("Cache2 is not coordinator", cache2.getRPCManager().isCoordinator());
      ViewChangeListener viewChangeListener = new ViewChangeListener(cache2);
      cache1.stop();
      // wait till cache2 gets the view change notification
      assert viewChangeListener.waitForViewChange(60, TimeUnit.SECONDS) : "Should have received a view change!";
      assertTrue("Cache2 is coordinator", cache2.getRPCManager().isCoordinator());
   }
}