package org.jboss.cache.buddyreplication;

import org.jboss.cache.CacheSPI;
import org.jboss.cache.Fqn;
import org.jboss.cache.util.TestingUtil;
import org.jboss.cache.util.CachePrinter;
import org.testng.annotations.Test;

/**
 * @author Manik Surtani (<a href="mailto:manik@jboss.org">manik@jboss.org</a>)
 * @since 2.1.0
 */
@Test(groups = "functional")
public class GravitationCleanupTest extends BuddyReplicationTestsBase
{
   Fqn fqn = Fqn.fromString("/a/b/c");
   Object key = "key", value = "value";
   BuddyFqnTransformer fqnTransformer = new BuddyFqnTransformer();

   public void testStaleRegionOnDataOwnerPessimistic() throws Exception
   {
      testDataOwner(false);
   }

   public void testStaleRegionOnDataOwnerOptimistic() throws Exception
   {
      testDataOwner(true);
   }

   public void testStaleRegionOnBuddyPessimistic() throws Exception
   {
      testBuddy(false);
   }

   public void testStaleRegionOnBuddyOptimistic() throws Exception
   {
      testBuddy(true);
   }

   private void testDataOwner(boolean optimistic) throws Exception
   {
      caches = createCaches(1, 2, false, true, optimistic);

      // add some stuff on the primary
      CacheSPI dataOwner = caches.get(0);
      CacheSPI buddy = caches.get(1);

      dataOwner.put(fqn, key, value);

      System.out.println("dataOwner: " + CachePrinter.printCacheLockingInfo(dataOwner));
      System.out.println("buddy: " + CachePrinter.printCacheLockingInfo(buddy));

      assert dataOwner.peek(fqn, false) != null : "Should have data";
      assert dataOwner.peek(Fqn.fromRelativeElements(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN, fqnTransformer.getGroupNameFromAddress(buddy.getLocalAddress())), false) != null : "Should have backup node for buddy";
      assert dataOwner.peek(Fqn.fromRelativeElements(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN, fqnTransformer.getGroupNameFromAddress(dataOwner.getLocalAddress())), false) == null : "Should NOT have backup node for self!";

      assert buddy.peek(fqn, false) == null : "Should not have data";
      assert buddy.peek(Fqn.fromRelativeElements(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN, fqnTransformer.getGroupNameFromAddress(buddy.getLocalAddress())), false) == null : "Should NOT have backup node for self!";
      assert buddy.peek(Fqn.fromRelativeElements(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN, fqnTransformer.getGroupNameFromAddress(dataOwner.getLocalAddress())), false) != null : "Should have backup node for buddy";
      assert buddy.peek(fqnTransformer.getBackupFqn(dataOwner.getLocalAddress(), fqn), false) != null : "Should have backup data";

      // now do a gravitate call.
      assert buddy.get(fqn, key).equals(value) : "Data should have gravitated!";
      // gravitation cleanup calls are async.
      cleanupDelay();

      System.out.println("dataOwner: " + CachePrinter.printCacheLockingInfo(dataOwner));
      System.out.println("buddy: " + CachePrinter.printCacheLockingInfo(buddy));

      assert buddy.peek(fqn, false) != null : "Should have data";
      assert buddy.peek(Fqn.fromRelativeElements(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN, fqnTransformer.getGroupNameFromAddress(dataOwner.getLocalAddress())), false) != null : "Should have backup node for buddy";
      assert buddy.peek(Fqn.fromRelativeElements(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN, fqnTransformer.getGroupNameFromAddress(buddy.getLocalAddress())), false) == null : "Should NOT have backup node for self!";

      assert dataOwner.peek(fqn, false) == null : "Should not have data";
      assert dataOwner.peek(Fqn.fromRelativeElements(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN, fqnTransformer.getGroupNameFromAddress(dataOwner.getLocalAddress())), false) == null : "Should NOT have backup node for self!";
      assert dataOwner.peek(Fqn.fromRelativeElements(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN, fqnTransformer.getGroupNameFromAddress(buddy.getLocalAddress())), false) != null : "Should have backup node for buddy";
      assert dataOwner.peek(fqnTransformer.getBackupFqn(buddy.getLocalAddress(), fqn), false) != null : "Should have backup data";
   }

   private void testBuddy(boolean optimistic) throws Exception
   {
      caches = createCaches(1, 3, false, true, optimistic);

      // add some stuff on the primary
      CacheSPI dataOwner = caches.get(0);
      CacheSPI buddy = caches.get(1);
      CacheSPI thirdInstance = caches.get(2);

      assertIsBuddy(dataOwner, buddy, true);
      assertIsBuddy(buddy, thirdInstance, true);
      assertIsBuddy(thirdInstance, dataOwner, true);

      dataOwner.put(fqn, key, value);

      System.out.println("dataOwner: " + CachePrinter.printCacheLockingInfo(dataOwner));
      System.out.println("buddy: " + CachePrinter.printCacheLockingInfo(buddy));
      System.out.println("thirdInstance: " + CachePrinter.printCacheLockingInfo(thirdInstance));

      assert dataOwner.peek(fqn, false) != null : "Should have data";
      assert dataOwner.peek(Fqn.fromRelativeElements(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN, fqnTransformer.getGroupNameFromAddress(thirdInstance.getLocalAddress())), false) != null : "Should have backup node for buddy";
      assert dataOwner.peek(Fqn.fromRelativeElements(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN, fqnTransformer.getGroupNameFromAddress(dataOwner.getLocalAddress())), false) == null : "Should NOT have backup node for self!";
      assert dataOwner.peek(Fqn.fromRelativeElements(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN, fqnTransformer.getGroupNameFromAddress(buddy.getLocalAddress())), false) == null : "Should NOT have backup node for 2nd instance!";

      assert buddy.peek(fqn, false) == null : "Should not have data";
      assert buddy.peek(Fqn.fromRelativeElements(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN, fqnTransformer.getGroupNameFromAddress(buddy.getLocalAddress())), false) == null : "Should NOT have backup node for self!";
      assert buddy.peek(Fqn.fromRelativeElements(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN, fqnTransformer.getGroupNameFromAddress(dataOwner.getLocalAddress())), false) != null : "Should have backup node for buddy";
      assert buddy.peek(fqnTransformer.getBackupFqn(dataOwner.getLocalAddress(), fqn), false) != null : "Should have backup data";

      // now do a gravitate call.
      assert thirdInstance.get(fqn, key).equals(value) : "Data should have gravitated!";

      // gravitation cleanup calls are async.
      cleanupDelay();

      System.out.println("dataOwner: " + CachePrinter.printCacheLockingInfo(dataOwner));
      System.out.println("buddy: " + CachePrinter.printCacheLockingInfo(buddy));
      System.out.println("thirdInstance: " + CachePrinter.printCacheLockingInfo(thirdInstance));

      assert thirdInstance.peek(fqn, false) != null : "Should have data";
      assert thirdInstance.peek(Fqn.fromRelativeElements(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN, fqnTransformer.getGroupNameFromAddress(buddy.getLocalAddress())), false) != null : "Should have backup node for buddy";
      assert thirdInstance.peek(Fqn.fromRelativeElements(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN, fqnTransformer.getGroupNameFromAddress(thirdInstance.getLocalAddress())), false) == null : "Should NOT have backup node for self!";

      assert dataOwner.peek(fqn, false) == null : "Should not have data";
      assert dataOwner.peek(Fqn.fromRelativeElements(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN, fqnTransformer.getGroupNameFromAddress(dataOwner.getLocalAddress())), false) == null : "Should NOT have backup node for self!";
      assert dataOwner.peek(Fqn.fromRelativeElements(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN, fqnTransformer.getGroupNameFromAddress(thirdInstance.getLocalAddress())), false) != null : "Should have backup node for buddy";
      assert dataOwner.peek(fqnTransformer.getBackupFqn(thirdInstance.getLocalAddress(), fqn), false) != null : "Should have backup data";
      assert buddy.peek(fqn, false) == null : "Should not have data";
      assert buddy.peek(fqn.getParent(), false) == null : "Should not have any part of the data";
      assert buddy.peek(fqnTransformer.getBackupFqn(dataOwner.getLocalAddress(), fqn), false) == null : "Should NOT have backup data";
   }

   private void cleanupDelay()
   {
      TestingUtil.sleepThread(250);
   }
}
