package org.jboss.cache.notifications;

import org.jboss.cache.Cache;
import org.jboss.cache.CacheFactory;
import org.jboss.cache.DefaultCacheFactory;
import org.jboss.cache.util.TestingUtil;
import org.jboss.cache.buddyreplication.BuddyGroup;
import org.jboss.cache.buddyreplication.BuddyReplicationTestsBase;
import org.jboss.cache.config.BuddyReplicationConfig;
import org.jboss.cache.config.Configuration;
import org.jboss.cache.notifications.annotation.BuddyGroupChanged;
import org.jboss.cache.notifications.annotation.CacheListener;
import org.jboss.cache.notifications.annotation.ViewChanged;
import org.jboss.cache.notifications.event.BuddyGroupChangedEvent;
import org.jboss.cache.notifications.event.ViewChangedEvent;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

import java.util.concurrent.CountDownLatch;

/**
 * @author Manik Surtani (<a href="mailto:manik@jboss.org">manik@jboss.org</a>)
 * @since 2.1.0
 */
@Test(groups = "functional")
public class BuddyGroupChangeNotificationTest extends BuddyReplicationTestsBase
{
   Cache c1, c2, c3;
   Listener listener;
   static CountDownLatch latch1 = new CountDownLatch(1);
   static CountDownLatch latch2 = new CountDownLatch(1);
   static boolean stage2 = false;
   static boolean notificationsReceived = true;

   @BeforeMethod
   public void setUp() throws CloneNotSupportedException
   {
      CacheFactory cf = new DefaultCacheFactory();
      c1 = cf.createCache(false);

      c1.getConfiguration().setCacheMode(Configuration.CacheMode.REPL_SYNC);
      BuddyReplicationConfig brc = new BuddyReplicationConfig();
      brc.setEnabled(true);
      c1.getConfiguration().setBuddyReplicationConfig(brc);

      c2 = cf.createCache(c1.getConfiguration().clone(), false);
      c3 = cf.createCache(c1.getConfiguration().clone(), false);

      c1.start();
      c2.start();
      c3.start();

      // make sure views are received and groups are formed first
      TestingUtil.blockUntilViewsReceived(60000, c1, c2, c3);

      Cache[] caches = new Cache[]{c1, c2, c3};

      listener = new Listener(caches);

      c2.addCacheListener(listener);
   }

   @AfterMethod
   public void tearDown()
   {
      TestingUtil.killCaches(c1, c2, c3);
   }

   @Test(timeOut = 60000)
   public void testChangingGroups() throws InterruptedException
   {
      // initial state
      assertIsBuddy(c1, c2, true);
      assertIsBuddy(c2, c3, true);
      assertIsBuddy(c3, c1, true);

      // kill c3
      c3.stop();
      latch1.await();

      assertIsBuddy(c1, c2, true);
      assertIsBuddy(c2, c1, true);

      stage2 = true;
      c3.start();
      latch2.await();

      assertIsBuddy(c1, c2, true);
      assertIsBuddy(c2, c3, true);
      assertIsBuddy(c3, c1, true);

      assert notificationsReceived;
   }

   @CacheListener
   public static class Listener
   {
      Cache[] caches;
      int numActiveCaches;

      public Listener(Cache[] caches)
      {
         this.caches = caches;
      }

      @ViewChanged
      public void viewChanged(ViewChangedEvent e)
      {
         numActiveCaches = e.getNewView().getMembers().size();
      }

      @BuddyGroupChanged
      public void buddyChanged(BuddyGroupChangedEvent e)
      {
         System.out.println("Received event " + e);
         if (!e.isPre())
         {
            BuddyGroup bg = e.getBuddyGroup();

            boolean passed = bg.getDataOwner().equals(caches[1].getLocalAddress()) &&
                  bg.getBuddies().size() == 1 &&
                  bg.getBuddies().contains(caches[(numActiveCaches == 3) ? 2 : 0].getLocalAddress());

            notificationsReceived = notificationsReceived && passed;

            if (stage2)
               latch2.countDown();
            else
               latch1.countDown();
         }
      }
   }
}
