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

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.cache.Cache;
import org.jboss.cache.Fqn;
import org.jboss.cache.util.TestingUtil;
import org.jboss.cache.util.CachePrinter;
import org.testng.annotations.Test;

/**
 * Tests how groups are formed and disbanded
 *
 * @author <a href="mailto:manik@jboss.org">Manik Surtani (manik@jboss.org)</a>
 */
@Test(groups = {"functional", "jgroups"})
public class BuddyGroupAssignmentTest extends BuddyReplicationTestsBase
{
   private Log log = LogFactory.getLog(BuddyGroupAssignmentTest.class);
   BuddyFqnTransformer fqnTransformer = new BuddyFqnTransformer();

   public void testSingleBuddy() throws Exception
   {
      log.debug("Running testSingleBuddy");
      caches = createCaches(3, false);

      System.out.println("*** Testing cache 0:");
      assertIsBuddy(caches.get(0), caches.get(1), true);
      System.out.println("*** Testing cache 1:");
      assertIsBuddy(caches.get(1), caches.get(2), true);
      System.out.println("*** Testing cache 2:");
      assertIsBuddy(caches.get(2), caches.get(0), true);

      System.out.println("Cache 0 = " + CachePrinter.printCacheLockingInfo(caches.get(0)));
      System.out.println("Cache 1 = " + CachePrinter.printCacheLockingInfo(caches.get(1)));
      System.out.println("Cache 2 = " + CachePrinter.printCacheLockingInfo(caches.get(2)));
   }

   public void test2Buddies() throws Exception
   {
      log.debug("Running test2Buddies");
      caches = createCaches(2, 3, false);

      TestingUtil.blockUntilViewsReceived(5000, caches.toArray(new Cache[0]));
//      TestingUtil.sleepThread(2000);

      System.out.println("*** Testing cache 0");
      assertIsBuddy(caches.get(0), caches.get(1), false);
      assertIsBuddy(caches.get(0), caches.get(2), false);
      System.out.println("*** Testing cache 1");
      assertIsBuddy(caches.get(1), caches.get(2), false);
      assertIsBuddy(caches.get(1), caches.get(0), false);
      System.out.println("*** Testing cache 2");
      assertIsBuddy(caches.get(2), caches.get(1), false);
      assertIsBuddy(caches.get(2), caches.get(0), false);
   }

   public void testRemovalFromClusterSingleBuddy() throws Exception
   {
      log.debug("Running testRemovalFromClusterSingleBuddy");
      caches = createCaches(3, false);

      System.out.println("*** Testing cache 0");
      assertIsBuddy(caches.get(0), caches.get(1), true);
      System.out.println("*** Testing cache 1");
      assertIsBuddy(caches.get(1), caches.get(2), true);
      System.out.println("*** Testing cache 2");
      assertIsBuddy(caches.get(2), caches.get(0), true);

      // now remove a cache from the cluster
      caches.get(1).stop();
      caches.set(1, null);

      TestingUtil.sleepThread(getSleepTimeout());

      // now test new buddy groups
      System.out.println("*** Testing cache 0");
      assertIsBuddy(caches.get(0), caches.get(2), true);
      System.out.println("*** Testing cache 2");
      assertIsBuddy(caches.get(2), caches.get(0), true);
      System.out.println("*** Completed successfully ***");
      assertNoLocks(caches);
   }

   public void testRemovalFromCluster2Buddies() throws Exception
   {
      log.debug("Running testRemovalFromCluster2Buddies");
      caches = createCaches(2, 4, false);
      assertNoLocks(caches);

      System.out.println("*** Testing cache 0");
      assertIsBuddy(caches.get(0), caches.get(1), false);
      assertIsBuddy(caches.get(0), caches.get(2), false);
      System.out.println("*** Testing cache 1");
      assertIsBuddy(caches.get(1), caches.get(2), false);
      assertIsBuddy(caches.get(1), caches.get(3), false);
      System.out.println("*** Testing cache 2");
      assertIsBuddy(caches.get(2), caches.get(3), false);
      assertIsBuddy(caches.get(2), caches.get(0), false);
      System.out.println("*** Testing cache 3");
      assertIsBuddy(caches.get(3), caches.get(0), false);
      assertIsBuddy(caches.get(3), caches.get(1), false);

      // now remove a cache from the cluster
      caches.get(1).stop();
      caches.set(1, null);

      TestingUtil.sleepThread(getSleepTimeout());

      // now test new buddy groups
      System.out.println("*** Testing cache 0");
      assertIsBuddy(caches.get(0), caches.get(2), false);
      assertIsBuddy(caches.get(0), caches.get(3), false);
      System.out.println("*** Testing cache 2");
      assertIsBuddy(caches.get(2), caches.get(3), false);
      assertIsBuddy(caches.get(2), caches.get(0), false);
      System.out.println("*** Testing cache 3");
      assertIsBuddy(caches.get(3), caches.get(0), false);
      assertIsBuddy(caches.get(3), caches.get(2), false);
      assertNoLocks(caches);
   }

   public void testAddingNewCaches() throws Exception
   {
      log.debug("Running testAddingNewCaches");
      caches = createCaches(2, false);

      // get some data in there.

      caches.get(0).put("/cache0", "k", "v");
      caches.get(1).put("/cache1", "k", "v");

      System.out.println("*** Testing cache 0:");
      assertIsBuddy(caches.get(0), caches.get(1), true);
      System.out.println("*** Testing cache 1:");
      assertIsBuddy(caches.get(1), caches.get(0), true);

      assert caches.get(0).peek(fqnTransformer.getBackupRoot(caches.get(0).getLocalAddress()), false) == null : "Should not have backup region for self";
      assert caches.get(0).peek(fqnTransformer.getBackupRoot(caches.get(1).getLocalAddress()), false) != null : "Should have backup region for buddy";

      assert caches.get(1).peek(fqnTransformer.getBackupRoot(caches.get(0).getLocalAddress()), false) != null : "Should have backup region for buddy";
      assert caches.get(1).peek(fqnTransformer.getBackupRoot(caches.get(1).getLocalAddress()), false) == null : "Should not have backup region for self";

      caches.add(createCache(1, null));

      TestingUtil.blockUntilViewsReceived(60000, caches);

      System.out.println("*** Testing cache 0:");
      assertIsBuddy(caches.get(0), caches.get(1), true);
      System.out.println("*** Testing cache 1:");
      assertIsBuddy(caches.get(1), caches.get(2), true);
      System.out.println("*** Testing cache 2:");
      assertIsBuddy(caches.get(2), caches.get(0), true);


      System.out.println("0 Lock info: " + CachePrinter.printCacheLockingInfo(caches.get(0)));
      System.out.println("1 Lock info: " + CachePrinter.printCacheLockingInfo(caches.get(1)));
      System.out.println("2 Lock info: " + CachePrinter.printCacheLockingInfo(caches.get(2)));

      assert caches.get(0).peek(fqnTransformer.getBackupRoot(caches.get(0).getLocalAddress()), false) == null : "Should not have backup region for self";
      assert caches.get(0).peek(fqnTransformer.getBackupRoot(caches.get(1).getLocalAddress()), false) == null : "Should have backup region for non-buddy";
      assert caches.get(0).peek(fqnTransformer.getBackupRoot(caches.get(2).getLocalAddress()), false) != null : "Should have backup region for buddy";

      assert caches.get(1).peek(fqnTransformer.getBackupRoot(caches.get(0).getLocalAddress()), false) != null : "Should have backup region for buddy";
      assert caches.get(1).peek(fqnTransformer.getBackupRoot(caches.get(1).getLocalAddress()), false) == null : "Should not have backup region for self";
      assert caches.get(1).peek(fqnTransformer.getBackupRoot(caches.get(2).getLocalAddress()), false) == null : "Should not have backup region for non-buddy";

      assert caches.get(2).peek(fqnTransformer.getBackupRoot(caches.get(0).getLocalAddress()), false) == null : "Should not have backup region for non-buddy";
      assert caches.get(2).peek(fqnTransformer.getBackupRoot(caches.get(1).getLocalAddress()), false) != null : "Should have backup region for buddy";
      assert caches.get(2).peek(fqnTransformer.getBackupRoot(caches.get(2).getLocalAddress()), false) == null : "Should not have backup region for self";

      // ensure no state transfer has happened!!
      assert caches.get(2).peek(Fqn.fromString("/cache0"), false) == null : "Unnecessary state should not have been transferred!";
      assert caches.get(2).peek(Fqn.fromString("/cache1"), false) == null : "Unnecessary state should not have been transferred!";

      // ensure backup state has been transferred.
      assert caches.get(2).peek(fqnTransformer.getBackupFqn(caches.get(1).getLocalAddress(), Fqn.fromString("/cache1")), false) != null : "Backup state should have transferred!";
   }
}
