/*
 * 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.Fqn;
import org.jboss.cache.marshall.MethodCallFactory;
import org.jboss.cache.marshall.MethodDeclarations;
import org.jboss.cache.marshall.JBCMethodCall;
import org.jboss.cache.xml.XmlHelper;
import org.jgroups.blocks.MethodCall;
import org.w3c.dom.Element;

import java.util.ArrayList;
import java.util.List;

/**
 * Tests the BuddyManager class
 *
 * @author <a href="mailto:manik@jboss.org">Manik Surtani (manik@jboss.org)</a>
 */
public class BuddyManagerTest extends TestCase
{
    /**
     * Constructs a buddy manager using the default buddy locator but with some specific properties.
     * @throws Exception
     */
    public void testConstruction1() throws Exception
    {
        String xmlConfig = "<config><buddyReplicationEnabled>true</buddyReplicationEnabled>\n" +
                "          <buddyLocatorProperties>numBuddies = 3</buddyLocatorProperties>\n" +
                "          <buddyPoolName>groupOne</buddyPoolName></config>";
        Element config = XmlHelper.stringToElement(xmlConfig);
        BuddyManager mgr = new BuddyManager(config);

        assertTrue(mgr.isEnabled());
        assertEquals("groupOne", mgr.getBuddyPoolName());
        assertEquals(NextMemberBuddyLocator.class, mgr.buddyLocator.getClass());
        assertEquals(3, ((NextMemberBuddyLocator)mgr.buddyLocator).numBuddies);
        assertTrue(((NextMemberBuddyLocator)mgr.buddyLocator).ignoreColocatedBuddies);
    }

    /**
     * Constructs a buddy manager using a nonexistent buddy locator but with some specific properties.
     * @throws Exception
     */
    public void testConstruction2() throws Exception
    {
        String xmlConfig = "<config><buddyReplicationEnabled>true</buddyReplicationEnabled>\n" +
                "          <buddyLocatorClass>org.i.dont.exist.PhantomBuddyLocator</buddyLocatorClass>\n" +
                "          <buddyLocatorProperties>numBuddies = 3</buddyLocatorProperties>\n" +
                "          <buddyPoolName>groupOne</buddyPoolName></config>";
        Element config = XmlHelper.stringToElement(xmlConfig);
        BuddyManager mgr = new BuddyManager(config);

        assertTrue(mgr.isEnabled());
        assertEquals("groupOne", mgr.getBuddyPoolName());
        assertEquals(NextMemberBuddyLocator.class, mgr.buddyLocator.getClass());

        // since the properties are not passed on to the next member buddy locator - they were obviously meant for a different impl.
        assertEquals(1, ((NextMemberBuddyLocator)mgr.buddyLocator).numBuddies);
        assertTrue(((NextMemberBuddyLocator)mgr.buddyLocator).ignoreColocatedBuddies);
    }

    /**
     * Constructs a disabled buddy manager
     * @throws Exception
     */
    public void testConstruction3() throws Exception
    {
        String xmlConfig = "<config><buddyReplicationEnabled>false</buddyReplicationEnabled></config>";
        Element config = XmlHelper.stringToElement(xmlConfig);
        BuddyManager mgr = new BuddyManager(config);

        assertTrue(!mgr.isEnabled());
    }

    /**
     * Constructs a buddy manager using a minimal config set
     * @throws Exception
     */
    public void testConstruction4() throws Exception
    {
        String xmlConfig = "<config><buddyReplicationEnabled>true</buddyReplicationEnabled></config>";
        Element config = XmlHelper.stringToElement(xmlConfig);
        BuddyManager mgr = new BuddyManager(config);

        assertTrue(mgr.isEnabled());
        assertNull(mgr.getBuddyPoolName());
        assertEquals(NextMemberBuddyLocator.class, mgr.buddyLocator.getClass());
        assertEquals(1, ((NextMemberBuddyLocator)mgr.buddyLocator).numBuddies);
        assertTrue(((NextMemberBuddyLocator)mgr.buddyLocator).ignoreColocatedBuddies);
    }

    private BuddyManager createBasicBuddyManager()
    {
        BuddyManager bm = null;
        try
        {
            bm = new BuddyManager(XmlHelper.stringToElement("<config><buddyReplicationEnabled>true</buddyReplicationEnabled></config>"));
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        return bm;
    }

    public void testFqnManipulation()
    {
        Fqn fqn1 = Fqn.fromString("/hello/world");

        JBCMethodCall call1 = MethodCallFactory.create(MethodDeclarations.putKeyValMethodLocal, new Object[]{fqn1, "key", "value"});
        JBCMethodCall call2 = MethodCallFactory.create(MethodDeclarations.replicateMethod, new Object[]{call1});

        BuddyManager bm = createBasicBuddyManager();

        JBCMethodCall newReplicatedCall = bm.transformFqns(call2);
        JBCMethodCall newPutCall = (JBCMethodCall) newReplicatedCall.getArgs()[0];

        // should use object refs to transform the original MethodCall.
        String expected = "/" + BuddyManager.BUDDY_BACKUP_SUBTREE + "/" + null + "/hello/world";
        assertEquals(expected, newPutCall.getArgs()[0].toString());

    }

    public void testRootFqnManipulation()
    {
        Fqn fqn1 = Fqn.ROOT;

        JBCMethodCall call1 = MethodCallFactory.create(MethodDeclarations.putKeyValMethodLocal, new Object[]{fqn1, "key", "value"});
        JBCMethodCall call2 = MethodCallFactory.create(MethodDeclarations.replicateMethod, new Object[]{call1});

        BuddyManager bm = createBasicBuddyManager();

        JBCMethodCall newReplicatedCall = bm.transformFqns(call2);
        JBCMethodCall newPutCall = (JBCMethodCall) newReplicatedCall.getArgs()[0];

        // should use object refs to transform the original MethodCall.
        String expected = "/" + BuddyManager.BUDDY_BACKUP_SUBTREE + "/" + null;
        assertEquals(expected, newPutCall.getArgs()[0].toString());
    }

    public void testMultiFqnManipulation()
    {
        Fqn fqn1 = Fqn.ROOT;
        Fqn fqn2 = Fqn.fromString("/hello/world");
        Fqn fqn3 = Fqn.fromString("/hello/again");
        Fqn fqn4 = Fqn.fromString("/buddy/replication");

        JBCMethodCall call1 = MethodCallFactory.create(MethodDeclarations.putKeyValMethodLocal, new Object[]{fqn1, "key", "value"});
        JBCMethodCall call2 = MethodCallFactory.create(MethodDeclarations.putKeyValMethodLocal, new Object[]{fqn2, "key", "value"});
        JBCMethodCall call3 = MethodCallFactory.create(MethodDeclarations.putKeyValMethodLocal, new Object[]{fqn3, "key", "value"});
        JBCMethodCall call4 = MethodCallFactory.create(MethodDeclarations.putKeyValMethodLocal, new Object[]{fqn4, "key", "value"});
        List list = new ArrayList();
        list.add(call1);
        list.add(call2);
        list.add(call3);
        list.add(call4);

        JBCMethodCall call5 = MethodCallFactory.create(MethodDeclarations.replicateAllMethod, new Object[]{list});

        BuddyManager bm = createBasicBuddyManager();

        JBCMethodCall newReplicatedCall = bm.transformFqns(call5);
        List l  = (List) newReplicatedCall.getArgs()[0];

        // should use object refs to transform the original MethodCall.
        String expected = "/" + BuddyManager.BUDDY_BACKUP_SUBTREE + "/null";

        int i=0;
        assertEquals(expected, ((MethodCall)l.get(i++)).getArgs()[0].toString());
        assertEquals(expected + "/hello/world", ((MethodCall)l.get(i++)).getArgs()[0].toString());
        assertEquals(expected + "/hello/again", ((MethodCall)l.get(i++)).getArgs()[0].toString());
        assertEquals(expected + "/buddy/replication", ((MethodCall)l.get(i)).getArgs()[0].toString());
    }
}
