/**
 * 
 */
package org.jboss.cache.optimistic;

import junit.framework.TestCase;
import org.jboss.cache.Fqn;
import org.jboss.cache.TreeCache;
import org.jboss.cache.TreeCacheListener;
import org.jboss.cache.lock.IsolationLevel;
import org.jboss.cache.marshall.JBCMethodCall;
import org.jboss.cache.marshall.MethodCallFactory;
import org.jboss.cache.marshall.MethodDeclarations;
import org.jboss.cache.misc.TestingUtil;
import org.jboss.cache.xml.XmlHelper;
import org.w3c.dom.Element;

import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Random;

/**
 * @author manik
 */
public abstract class AbstractOptimisticTestCase extends TestCase
{
    private int instanceNumber;

    // some test data shared among all the test cases
    protected Fqn fqn = Fqn.fromString("/blah");
    protected String key = "myKey", value = "myValue";

    protected String getTempDir()
    {
        return getTempDir("tempdir");
    }

    private String getTempDir(String name)
    {
        String tempDir = System.getProperty("java.io.tmpdir", "/tmp");
        tempDir = tempDir + File.separator + name;
        System.out.println("tmpdir property: " + System.getProperty("java.io.tmpdir"));
        System.out.println("Attempting to create dir [" + tempDir + "]");
        File tempDirFile = new File(tempDir);
        if (!tempDirFile.exists())
        {
            tempDirFile.mkdirs();
        }
        return tempDir;
    }

    public AbstractOptimisticTestCase(String name)
    {
        super(name);
    }

    protected TreeCache createCacheUnstarted() throws Exception
    {
        return createCacheUnstarted(true);
    }

    protected TreeCache createCacheUnstarted(boolean optimistic) throws Exception
    {
        TreeCache cache = new TreeCache();
        if (optimistic) cache.setNodeLockingScheme("OPTIMISTIC");

        cache.setTransactionManagerLookupClass("org.jboss.cache.DummyTransactionManagerLookup");
        cache.setCacheMode("LOCAL");
        return cache;
    }

    protected TreeCache createCacheWithListener() throws Exception
    {
        return createCacheWithListener(new TestListener());
    }

    protected TreeCache createCacheWithListener(TreeCacheListener listener) throws Exception
    {
        TreeCache cache = createCacheUnstarted();
        cache.addTreeCacheListener(listener);
        cache.createService();
        cache.startService();
        return cache;
    }

    /**
     * Returns a tree cache with passivation disabled in the loader.
     *
     * @return
     * @throws Exception
     */
    protected TreeCache createCacheWithLoader() throws Exception
    {
        return createCacheWithLoader(false);
    }

    protected Element getCacheLoaderConfig(boolean shared, String filename, boolean passivation) throws Exception
    {
        String xml = "            <config>\n" +
                "                <passivation>" + passivation + "</passivation>\n" +
                "                <preload></preload>\n" +
                "                <shared>"+shared+"</shared>\n" +
                "                <cacheloader>\n" +
                "                    <class>org.jboss.cache.loader.FileCacheLoader</class>\n" +
                "                    <properties>\n" +
                "                    </properties>\n" +
                "                    <async>false</async>\n" +
                "                    <fetchPersistentState>" + (!shared) + "</fetchPersistentState>\n" +
                "                    <ignoreModifications>false</ignoreModifications>\n" +
                "                </cacheloader>\n" +
                "            </config>";
        return XmlHelper.stringToElement(xml);
    }

    protected TreeCache createCacheWithLoader(boolean passivationEnabled) throws Exception
    {
        TreeCache cache = createCacheUnstarted();
        cache.setCacheLoaderConfiguration(getCacheLoaderConfig(true, getTempDir(), passivationEnabled));
        cache.createService();
        cache.startService();
        return cache;
    }


    protected TreeCache createCache() throws Exception
    {
        TreeCache cache = createCacheUnstarted();
        cache.createService();
        cache.startService();
        return cache;
    }

    protected void destroyCache(TreeCache c)
    {
        c.stopService();
        c.destroyService();
    }


    protected TreeCache createPessimisticCache() throws Exception
    {
        TreeCache cache = new TreeCache("name", getDefaultProperties(), 5000L);

        cache.setCacheMode(TreeCache.REPL_SYNC);
        cache.setIsolationLevel(IsolationLevel.REPEATABLE_READ);
        cache.setTransactionManagerLookupClass("org.jboss.cache.DummyTransactionManagerLookup");
        cache.createService();
        cache.startService();


        return cache;
    }

    protected TreeCache createPessimisticCacheLocal() throws Exception
    {
        TreeCache cache = new TreeCache("name", getDefaultProperties(), 5000L);

        cache.setCacheMode(TreeCache.LOCAL);
        cache.setIsolationLevel(IsolationLevel.REPEATABLE_READ);
        cache.setTransactionManagerLookupClass("org.jboss.cache.DummyTransactionManagerLookup");
        cache.createService();
        cache.startService();


        return cache;
    }

    protected String getDefaultProperties()
    {
        return "UDP(mcast_addr=228.1.2.3;mcast_port=48866;ip_ttl=32;" +
                "mcast_send_buf_size=150000;mcast_recv_buf_size=80000;loopback=true;ip_mcast=true;bind_addr=127.0.0.1):" +
                "PING(timeout=1000;num_initial_members=2):" +
                "MERGE2(min_interval=5000;max_interval=10000):" +
                "FD_SOCK:" +
                "VERIFY_SUSPECT(timeout=1500):" +
                "pbcast.NAKACK(gc_lag=50;max_xmit_size=8192;retransmit_timeout=600,1200,2400,4800):" +
                "UNICAST(timeout=600,1200,2400,4800):" +
                "pbcast.STABLE(desired_avg_gossip=20000):" +
                "FRAG(frag_size=8192;down_thread=false;up_thread=false):" +
                "pbcast.GMS(join_timeout=5000;join_retry_timeout=2000;" +
                "shun=false;print_local_addr=true):" +
                "pbcast.STATE_TRANSFER";
    }

    protected TreeCache createReplicatedCache(int mode) throws Exception
    {
        return createReplicatedCache("test", mode);
    }

    protected TreeCache createReplicatedCache(String name, int mode) throws Exception
    {
        TreeCache cache = new TreeCache(name, getDefaultProperties(), 5000L);

        cache.setCacheMode(mode);
        if (mode == TreeCache.REPL_SYNC)
        {
            // make sure commits and rollbacks are sync as well
            cache.setSyncCommitPhase(true);
            cache.setSyncRollbackPhase(true);
        }
        cache.setNodeLockingScheme("OPTIMISTIC");
        cache.setTransactionManagerLookupClass("org.jboss.cache.DummyTransactionManagerLookup");
        cache.createService();
        cache.startService();

        return cache;
    }

    protected TreeCache createReplicatedCacheWithLoader(boolean shared, int cacheMode) throws Exception
    {
        return createReplicatedCacheWithLoader("temp-loader", shared, cacheMode);
    }

    protected TreeCache createReplicatedCacheWithLoader(boolean shared) throws Exception
    {
        return createReplicatedCacheWithLoader("temp-loader", shared, TreeCache.REPL_SYNC);
    }

    protected TreeCache createReplicatedCacheWithLoader(String name, boolean shared) throws Exception
    {
        return createReplicatedCacheWithLoader(name, shared, TreeCache.REPL_SYNC);
    }

    protected TreeCache createReplicatedCacheWithLoader(String name, boolean shared, int cacheMode) throws Exception
    {
        TreeCache cache = new TreeCache(name, getDefaultProperties(), 5000L);
        cache.setCacheMode(cacheMode);
        cache.setSyncCommitPhase(true);
        cache.setSyncRollbackPhase(true);
        cache.setNodeLockingScheme("OPTIMISTIC");
        cache.setTransactionManagerLookupClass("org.jboss.cache.DummyTransactionManagerLookup");
        cache.setCacheLoaderConfiguration(getCacheLoaderConfig(shared, shared ? getTempDir(name + "-shared") : getTempDir(name + instanceNumber++), false));

        cache.createService();
        cache.startService();
        return cache;
    }

    protected Random random;

    protected void randomSleep(int min, int max)
    {
        if (random == null) random = new Random();
        long l = -1;
        while (l < min) l = random.nextInt(max);
        TestingUtil.sleepThread(l);
    }

    public abstract class ExceptionThread extends Thread
    {
        protected Exception exception;

        public void setException(Exception e)
        {
            exception = e;
        }

        public Exception getException()
        {
            return exception;
        }
    }

   protected List injectDataVersion(List modifications)
   {
      List newList = new ArrayList();
      Iterator mi = modifications.iterator();
      while (mi.hasNext())
      {
         JBCMethodCall c = (JBCMethodCall) mi.next();
         Object[] oa = c.getArgs();
         Object[] na = new Object[oa.length + 1];
         System.out.println("*** " + oa.length);
         for (int i=0; i<oa.length; i++) na[i] = oa[i];
         na[oa.length] = new DefaultDataVersion();
         newList.add(MethodCallFactory.create(MethodDeclarations.getVersionedMethod(c.getMethodId()), na));
      }
      return newList;
   }
}

