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

import junit.framework.TestCase;
import org.jboss.cache.TreeCache;
import org.jboss.cache.Fqn;
import org.jboss.cache.CacheException;
import org.jboss.cache.config.Option;
import org.jboss.cache.misc.TestingUtil;
import org.jboss.cache.xml.XmlHelper;

import java.io.File;

/**
 * Base class for BR tests
 *
 * @author <a href="mailto:manik@jboss.org">Manik Surtani (manik@jboss.org)</a>
 */
public class BuddyReplicationTestsBase extends TestCase
{
    protected static int VIEW_BLOCK_TIMEOUT = 5000;

    protected TreeCache createCache(int numBuddies, String buddyPoolName) throws Exception
    {
       return createCache(numBuddies, buddyPoolName, false, true);
    }

    protected TreeCache createCache(int numBuddies, String buddyPoolName, boolean useDataGravitation) throws Exception
    {
       return createCache(numBuddies, buddyPoolName, useDataGravitation, true);
    }

    protected TreeCache createCache(int numBuddies, String buddyPoolName, boolean useDataGravitation, boolean start) throws Exception
    {
        return createCache(false, numBuddies, buddyPoolName, useDataGravitation, true, start);
    }

    protected TreeCache createCache(boolean optimisticLocks, int numBuddies, String buddyPoolName, boolean useDataGravitation, boolean start) throws Exception
    {
        return createCache(optimisticLocks, numBuddies, buddyPoolName, useDataGravitation, true, start);
    }

    protected TreeCache createCache(int numBuddies, String buddyPoolName, boolean useDataGravitation, boolean removeOnFind, boolean start) throws Exception
    {
        return createCache(false, numBuddies, buddyPoolName, useDataGravitation, removeOnFind, start);
    }

    protected TreeCache createCache(boolean optimisticLocks, int numBuddies, String buddyPoolName, boolean useDataGravitation, boolean removeOnFind, boolean start) throws Exception
    {
        TreeCache c = new TreeCache();
        c.setCacheMode(TreeCache.REPL_SYNC);
        c.setClusterName("BuddyReplicationTest");
        // basic config
        String xmlString = "<config><buddyReplicationEnabled>true</buddyReplicationEnabled>\n" +
                "<buddyCommunicationTimeout>600000</buddyCommunicationTimeout>\n" +
                "          <buddyLocatorClass>org.jboss.cache.buddyreplication.NextMemberBuddyLocator</buddyLocatorClass>\n" +
                "          <autoDataGravitation>"+useDataGravitation+"</autoDataGravitation>\n" +
                "          <dataGravitationRemoveOnFind>"+removeOnFind+"</dataGravitationRemoveOnFind>\n" +
                "          <buddyLocatorProperties>numBuddies = "+numBuddies+"</buddyLocatorProperties>\n";

        if (buddyPoolName != null) xmlString += "<buddyPoolName>"+buddyPoolName+"</buddyPoolName>";
        xmlString += "</config>";
        c.setBuddyReplicationConfig( XmlHelper.stringToElement(xmlString) );

        c.setFetchInMemoryState(true);
        if (optimisticLocks)
        {
            c.setNodeLockingScheme("OPTIMISTIC");
        }
        c.setTransactionManagerLookupClass("org.jboss.cache.DummyTransactionManagerLookup");
        c.setSyncCommitPhase(true); // helps track down breakages

        if (start)
           c.startService();
        return c;
    }

    protected TreeCache[] createCaches(int numCaches, boolean useBuddyPool) throws Exception
    {
        return createCaches(1, numCaches, useBuddyPool, false);
    }

    protected TreeCache[] createCaches(int numCaches, boolean useBuddyPool, boolean useDataGravitation, boolean optimisticLocks) throws Exception
    {
        return createCaches(1, numCaches, useBuddyPool, useDataGravitation, optimisticLocks);
    }

    protected TreeCache[] createCaches(int numCaches, boolean useBuddyPool, boolean useDataGravitation) throws Exception
    {
        return createCaches(1, numCaches, useBuddyPool, useDataGravitation);
    }

    protected TreeCache[] createCachesWithCacheLoader(int numCaches, boolean useDataGravitation, boolean removeOnFind, boolean passivation) throws Exception
    {
        return this.createCachesWithCacheLoader(numCaches, useDataGravitation, removeOnFind, passivation, false);
    }

    protected TreeCache[] createCachesWithCacheLoader(int numCaches, boolean useDataGravitation, boolean removeOnFind, boolean passivation, boolean fetchPersistent) throws Exception
    {
        String tmpLoc = System.getProperty("java.io.tmpdir", "/tmp");
        tmpLoc += File.separator + "BuddyReplicationTestsBase-";

        TreeCache[] caches = new TreeCache[numCaches];
        for (int i=0; i< numCaches; i++)
        {
           caches[i] = createCacheWithCacheLoader((tmpLoc + i), useDataGravitation, removeOnFind, passivation, fetchPersistent, true);
        }

        // allow some time for the caches to start up and discover each other
        TestingUtil.blockUntilViewsReceived(caches, VIEW_BLOCK_TIMEOUT);
        TestingUtil.sleepThread(getSleepTimeout());
        return caches;
    }

    protected TreeCache createCacheWithCacheLoader(String location, boolean useDataGravitation, boolean removeOnFind, boolean passivation, boolean fetchPersistent, boolean start) throws Exception
    {
       TreeCache cache = createCache(1, null, useDataGravitation, removeOnFind, false);

       String cloader = "<config>\n" +
           "<passivation>"+passivation+"</passivation>\n" +
           "<preload></preload>\n" +

           "<cacheloader>\n" +
           "<class>org.jboss.cache.loader.FileCacheLoader</class>\n" +
           "<properties>location=" +(location)+ "</properties>\n" +
           "<async>false</async>\n" +
           "<shared>false</shared>\n" +
           "<fetchPersistentState>"+ fetchPersistent + "</fetchPersistentState>\n" +
           "</cacheloader>\n" +
           "</config>";

       cache.setCacheLoaderConfiguration(XmlHelper.stringToElement(cloader));
       if (start)
          cache.startService();

       return cache;
    }

    protected TreeCache[] createCaches(int numBuddies, int numCaches, boolean useBuddyPool) throws Exception
    {
        return createCaches(numBuddies, numCaches, useBuddyPool, false);
    }

    protected TreeCache[] createCaches(int numBuddies, int numCaches, boolean useBuddyPool, boolean useDataGravitation) throws Exception
    {
        return createCaches(numBuddies, numCaches, useBuddyPool, useDataGravitation, false);
    }

    protected TreeCache[] createCaches(int numBuddies, int numCaches, boolean useBuddyPool, boolean useDataGravitation, boolean optimisticLocks) throws Exception
    {
        TreeCache[] caches = new TreeCache[numCaches];
        for (int i=0; i<numCaches; i++)
        {
            caches[i] = createCache(optimisticLocks, numBuddies, useBuddyPool ? Character.toString((char) ('A' + i)) : null, useDataGravitation,  true);
        }

        // allow some time for the caches to start up and discover each other
        TestingUtil.blockUntilViewsReceived(caches, VIEW_BLOCK_TIMEOUT);
        TestingUtil.sleepThread(getSleepTimeout());
        return caches;
    }

    protected void cleanup(TreeCache[] caches)
    {
        System.out.println("*** cleaning up");
        Option localOnly = new Option();
        localOnly.setCacheModeLocal(true);


        if (caches != null)
        {
            for (int i=0; i<caches.length;i++)
            {
                if (caches[i] != null)
                {
                   try
                   {
                       try
                       {
                           caches[i].remove(Fqn.ROOT, localOnly);
                       }
                       catch (CacheException e)
                       {
                           e.printStackTrace(System.out);
                       }
                       caches[i].stopService();
                   }
                   catch (Exception e)
                   {
                      e.printStackTrace(System.out);
                   }
                   caches[i] = null;
                }
            }
        }
    }

    protected void printBuddyGroup(TreeCache cache)
    {
        BuddyGroup bg = cache.getBuddyManager().buddyGroup;
        System.out.println("*** " + bg);
        System.out.println("    Groups I participate in: " + cache.getBuddyManager().buddyGroupsIParticipateIn.keySet());
    }

    /**
     * This is to allow for any state transfers involved (when assigning a buddy) to complete
     */
    protected int getSleepTimeout()
    {
        return 1000;
    }

    protected void assertIsBuddy(TreeCache dataOwner, TreeCache buddy, boolean onlyBuddy)
    {
        System.out.println("*** assert with groups");
        printBuddyGroup(dataOwner);

        BuddyManager dataOwnerBuddyManager = dataOwner.getBuddyManager();
        BuddyManager buddyBuddyManager = buddy.getBuddyManager();

        // lets test things on the data owner's side of things
        if (onlyBuddy) assertEquals("Should only have one buddy", 1, dataOwnerBuddyManager.getBuddyAddresses().size());
        Object dataOwnerLocalAddress = dataOwner.getLocalAddress();
        Object buddyLocalAddress = buddy.getLocalAddress();

        assertTrue(buddyLocalAddress + " should be a buddy to " + dataOwnerLocalAddress, dataOwnerBuddyManager.getBuddyAddresses().contains(buddyLocalAddress));

        // and now on the buddy end
        BuddyGroup group = (BuddyGroup) buddyBuddyManager.buddyGroupsIParticipateIn.get(BuddyManager.getGroupNameFromAddress(dataOwnerLocalAddress));
        System.out.println("*** Buddy's version of dataOwner's group " + group);

        assertTrue("buddy's list of groups it participates in should contain data owner's group name", buddyBuddyManager.buddyGroupsIParticipateIn.containsKey(BuddyManager.getGroupNameFromAddress(dataOwnerLocalAddress)));
        if (onlyBuddy) assertEquals(1, group.getBuddies().size());
        assertTrue(buddyLocalAddress + " should be a buddy to " + group.getGroupName(), group.getBuddies().contains(buddyLocalAddress));
    }

    protected void dumpCacheContents(TreeCache[] caches)
    {
        System.out.println("**** START: Cache Contents ****");
        for (int i=0; i<caches.length; i++)
        {
            if (caches[i] == null)
                System.out.println("  ** Cache " + i + " is null!");
            else
            {
                System.out.println("  ** Cache " + i + " is " + caches[i].getLocalAddress());
                System.out.println("    " + caches[i].printLockInfo());
            }
        }
        System.out.println("**** END: Cache Contents ****");
    }

    protected void assertNoLocks(TreeCache[] caches)
    {
        for (int i=0;i<caches.length; i++)
        {
            if (caches[i] != null) assertEquals(0, caches[i].getNumberOfLocksHeld());
        }
    }

}
