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

import EDU.oswego.cs.dl.util.concurrent.Latch;
import org.jboss.cache.TreeCache;
import org.jboss.cache.misc.TestingUtil;

import java.util.Map;

/**
 * Tests basic group membership semantics
 *
 * @author <a href="mailto:manik@jboss.org">Manik Surtani (manik@jboss.org)</a>
 */
public class BuddyPoolBroadcastTest extends BuddyReplicationTestsBase
{
    private void checkConsistentPoolState(TreeCache[] caches)
    {
        for (int i=0; i<caches.length; i++)
        {
            Map groupMap = caches[i].getBuddyManager().buddyPool;
            for (int j = 0; j<caches.length; j++)
            {
                if (i != j)
                {
                    Map groupMap2 = caches[j].getBuddyManager().buddyPool;
                    for (int k=0; k<caches.length; k++)
                    {
                        assertEquals("Comparing contents of cache " + (i+1) + " pool map with cache " + (j+1), groupMap.get(caches[k]), groupMap2.get(caches[k]));
                    }
                }
            }
        }
    }

    public void test2CachesWithPoolNames() throws Exception
    {
        TreeCache[] caches = createCaches(2, true);

        try
        {
            BuddyManager m = caches[0].getBuddyManager();
            Map groupMap = m.buddyPool;

            assertEquals("A", groupMap.get(caches[0].getLocalAddress()));
            assertEquals("B", groupMap.get(caches[1].getLocalAddress()));
        }
        finally
        {
            cleanup(caches);
        }
    }

    public void test3CachesWithPoolNames() throws Exception
    {
        TreeCache[] caches = createCaches(3, true);

        try
        {
            BuddyManager m = caches[0].getBuddyManager();
            Map groupMap = m.buddyPool;

            assertEquals("A", groupMap.get(caches[0].getLocalAddress()));
            assertEquals("B", groupMap.get(caches[1].getLocalAddress()));
            assertEquals("C", groupMap.get(caches[2].getLocalAddress()));
        }
        finally
        {
            cleanup(caches);
        }
    }

    public void testBuddyPoolSync() throws Exception
    {
        TreeCache[] caches = createCaches(3, true);

        try
        {
            Map map = caches[0].getBuddyManager().buddyPool;

            // first test the values
            assertEquals("Failed on cache 1", "A", map.get(caches[0].getLocalAddress()));
            assertEquals("Failed on cache 1", "B", map.get(caches[1].getLocalAddress()));
            assertEquals("Failed on cache 1", "C", map.get(caches[2].getLocalAddress()));

            // now test against each other
            checkConsistentPoolState(caches);
        }
        finally
        {
            cleanup(caches);
        }
    }

    public void testChangingBuddyPoolMembership() throws Exception
    {
        TreeCache[] caches = createCaches(3, true);

        try
        {
            Map map = caches[0].getBuddyManager().buddyPool;

            // first test the values
            assertEquals("Failed on cache 1", "A", map.get(caches[0].getLocalAddress()));
            assertEquals("Failed on cache 1", "B", map.get(caches[1].getLocalAddress()));
            assertEquals("Failed on cache 1", "C", map.get(caches[2].getLocalAddress()));

            // now test against each other
            checkConsistentPoolState(caches);

            caches[1].stopService();
            caches[1] = createCache(1, "Z");

            TestingUtil.blockUntilViewsReceived(caches, VIEW_BLOCK_TIMEOUT);
            TestingUtil.sleepThread(getSleepTimeout());

            // first test the values
            assertEquals("Failed on cache 1", "A", map.get(caches[0].getLocalAddress()));
            assertEquals("Failed on cache 1", "Z", map.get(caches[1].getLocalAddress()));
            assertEquals("Failed on cache 1", "C", map.get(caches[2].getLocalAddress()));

            // now test against each other
            checkConsistentPoolState(caches);
        }
        finally
        {
            cleanup(caches);
        }
    }

    public void testConcurrency() throws Exception
    {
        int numCaches = 15;
        TreeCache[] caches = new TreeCache[numCaches];
        try
        {
            Latch latch = new Latch();
            CacheStarter[] starters = new CacheStarter[numCaches];

            for (int i=0; i<numCaches; i++)
            {
                caches[i] = createCache(1, new String(new char[]{(char) ('A' + i)}), false, false);
                starters[i] = new CacheStarter(latch, caches[i]);
                starters[i].start();
            }

            // now have the lot start simultaneously
            TestingUtil.sleepThread(500);
            latch.release();

            // allow a generous sleep time
            TestingUtil.blockUntilViewsReceived(caches, 180000);
            TestingUtil.sleepThread(1000 * numCaches);

            // and now look at the state of things.
            Map map = caches[0].getBuddyManager().buddyPool;
            for (int i=0; i<numCaches; i++)
            {
                assertEquals("Failed on cache " + i, new String(new char[]{(char) ('A' + i)}), map.get(caches[i].getLocalAddress()));
            }

            checkConsistentPoolState(caches);
        }
        finally
        {
            cleanup(caches);
        }

    }

    public class CacheStarter extends Thread
    {
        private Latch latch;
        private TreeCache cache;

        public CacheStarter(Latch latch, TreeCache cache)
        {
            this.latch = latch;
            this.cache = cache;
        }
        public void run()
        {
            try
            {
                latch.acquire();
                cache.startService();
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
        }
    }

}
