package org.jboss.cache.loader;

import junit.framework.Assert;
import junit.framework.Test;
import junit.framework.TestSuite;
import org.jboss.cache.CacheException;
import org.jboss.cache.Fqn;
import org.jboss.cache.TreeCache;
import org.jboss.cache.Modification;
import java.util.Map;
import java.util.HashMap;
import java.util.Collections;

public class AsyncFileCacheLoaderTest extends AbstractCacheLoaderTestBase
{

   private TreeCache cache;

   protected void configureCache() throws Exception
   {
      configureCache("");
   }

   protected void configureCache(String props) throws Exception
   {
       cache = new TreeCache();
       cache.setCacheMode(TreeCache.LOCAL);
       // cache.setCacheLoaderConfiguration(getSingleCacheLoaderConfig("", "org.jboss.cache.loader.jdbm.JdbmCacheLoader", 
       cache.setCacheLoaderConfiguration(getSingleCacheLoaderConfig("", "org.jboss.cache.loader.FileCacheLoader", 
                props, true, false, true));
       cache.create();
       cache.start();
   }

   public static Test suite() {
      return new TestSuite(AsyncFileCacheLoaderTest.class);
   }

   protected void tearDown() throws Exception {
      if (cache != null)
         cache.stop();
   }

   public void testRestrictionOnAddingToQueue() throws Exception
   {
       configureCache();
       CacheLoader loader = cache.getCacheLoader();
       loader.remove(Fqn.fromString("/blah"));
       
       loader.put(Fqn.fromString("/blah"), "one", "two");
       loader.put(Fqn.fromString("/blah"), "three", "four");
       loader.put(Fqn.fromString("/blah"), "five", "six");
       loader.put(Fqn.fromString("/blah"), "seven", "eight");

       // stop the cache loader
       loader.stop();
       try
       {
            loader.remove(Fqn.fromString("/blah"));
            Assert.assertTrue("Should have restricted this entry from being made", false);
       }
       catch (CacheException e)
       {
           Assert.assertTrue(true);
       }

       // clean up
       loader.start();
       loader.remove(Fqn.fromString("/blah"));
   }

    public void testPutImmediate() throws Exception
    {
       configureCache(
             "cache.async.put=false\n" +
             "cache.async.pollWait=10000\n" +
             "");
       CacheLoader loader = cache.getCacheLoader();
       Fqn fqn = Fqn.fromString("/a/b/c/d");
       HashMap map = new HashMap();
       map.put("c", "d");
       // Three kinds of puts!
       Modification mod = new Modification(Modification.PUT_KEY_VALUE, fqn, "e", "f");
       loader.put(fqn, "a", "b");
       loader.put(fqn, map);
       loader.put(Collections.singletonList(mod));
       assertEquals("put right away", 3, loader.get(fqn).size());
       loader.remove(fqn);
    }

    public void testBounded() throws Exception
    {
       configureCache(
             "cache.async.queueSize=1\n" +
             "cache.async.pollWait=10\n" +
             "");
       CacheLoader loader = cache.getCacheLoader();
       Fqn fqn = Fqn.fromString("/bound");
       loader.remove(fqn);
       // You can't really see it block though :-/
       for (int i = 0; i < 100; i++)
          cache.put(fqn, "key" + i, "value1");
       Thread.sleep(1000);
       assertEquals(100, loader.get(fqn).size());
       loader.remove(fqn);
    }

    public void testNoReturnOld() throws Exception
    {
       configureCache(
             "cache.async.returnOld=false\n" +
             "cache.async.pollWait=10\n" +
             "");
       CacheLoader loader = cache.getCacheLoader();
       System.out.println("Loader " + loader); 
       cache.put(Fqn.ROOT, "key1", "value1");
       Thread.sleep(100);
       assertEquals(null, loader.put(Fqn.ROOT, "key1", "value1"));
       assertEquals(null, loader.remove(Fqn.ROOT, "key1"));
       loader.remove(Fqn.ROOT);
    }

    public void testStoreState() throws Exception
    {
       configureCache();
       Fqn X = Fqn.fromString("/x");
       CacheLoader loader = cache.getCacheLoader();
       loader.remove(X);
       cache.put(X, "key1", "value1");
       Thread.sleep(1000);
       byte b[] = loader.loadEntireState();
       assertTrue(b.length > 0);
       loader.remove(X);
       loader.storeEntireState(b);
       assertEquals("X found", true, loader.exists(X));
       loader.remove(X);
    }

    public void testWritesGetWritten() throws Exception
    {
        configureCache();
        Fqn fqn = Fqn.fromString("/blah");
        CacheLoader loader = cache.getCacheLoader();
        loader.remove(fqn);
        // a bit brute-force,unfortunately ...
        // enqueue a crap load of writes...
        int numPuts = 750;
        long start = System.currentTimeMillis();
        for (int i=0; i<numPuts; i++)
            cache.put(fqn, "key" + i, "value" + i);

        System.out.println("Finished " + numPuts + " in "  + (System.currentTimeMillis() - start) + "ms");
        // stop the cache loader
        // blocks till pending writes are completed
        start = System.currentTimeMillis();
        loader.stop();
        System.out.println("Finished stopping the loader in " + (System.currentTimeMillis() - start) + "ms");
        System.out.println(loader);

        // the loader should have written everything to file now.
        loader.start();
        Map m = loader.get(fqn);
        for (int i=0;i<numPuts; i++)
        {
            Assert.assertEquals("value" + i, m.get("key" + i));
        }

        // clean up
        loader.remove(fqn);
    }

}
