/*****************************************
 *                                       *
 *  JBoss Portal: The OpenSource Portal  *
 *                                       *
 *   Distributable under LGPL license.   *
 *   See terms of license at gnu.org.    *
 *                                       *
 *****************************************/
package org.jboss.cache.aop;

import junit.framework.TestCase;

import org.jboss.cache.aop.PojoCache;
import org.jboss.cache.lock.IsolationLevel;
import org.jboss.cache.TreeCache;
import org.jboss.cache.DummyTransactionManagerLookup;
import org.jboss.cache.AbstractTreeCacheListener;
import org.jboss.cache.Fqn;
import org.jgroups.View;

import javax.transaction.TransactionManager;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Collections;
import java.util.Map;

/**
 * @author Nitzan Niv 
 * @version
 */
public class PojoCacheListenerTest extends TestCase
{

   protected boolean optLocking = false;

   public PojoCacheListenerTest(String s)
   {
      super(s);
   }

   private PojoCache cache;

   protected void setUp() throws Exception
   {
      super.setUp();
      cache = new PojoCache();
      cache.setCacheMode(TreeCache.LOCAL);
      cache.setIsolationLevel(IsolationLevel.REPEATABLE_READ);
      if (optLocking) cache.setNodeLockingScheme("OPTIMISTIC");
      cache.setTransactionManagerLookup(new DummyTransactionManagerLookup());
      cache.create();
      cache.start();
   }

   protected void tearDown() throws Exception
   {
      super.tearDown();
      cache.stop();
      cache.destroy();
   }

   public void testRemovePropertyWithCommit() throws Exception
   {
      cache.putObject("/name", "value");

      EventLog el = new EventLog();
      cache.addTreeCacheListener(el);

      TransactionManager tm = cache.getTransactionManager();
      tm.begin();
      cache.removeObject("/name");
      tm.commit();
      System.out.println("testRemovePropertyWithCommit:\n" + el.events);

      assertEquals(Arrays.asList(new Object[]{
              "modify about to modify local /name",
              "modified /name",
              "modify Modified local /name",
              "remove about to remove local /name",
              "removed /name",
              "remove Removed local /name"}),
              el.events);
      el.events.clear();
   }

   public void testNodeCreatedAndModifiedEvent() throws Exception
   {
       EventLog el = new EventLog(cache);
       cache.addTreeCacheListener(el);
       cache.putObject("/hello/world", "y");
       System.out.println("testNodeCreatedAndModifiedEvent:\n" + el.events);

       List expectedChanges = new ArrayList();
       expectedChanges.add("created /hello");
       expectedChanges.add("created /hello/world");

       //TODO: (optLocking)

       // all creations are fired as mods since these reflect as modified nodes in the workspace.  Opt locking only.
       expectedChanges.add("modify about to modify local /hello/world");
       expectedChanges.add("modified /hello/world");
       expectedChanges.add("modify Modified local /hello/world");

       expectedChanges.add("modify about to modify local /hello/world");
       expectedChanges.add("modified /hello/world");
       expectedChanges.add("modify Modified local /hello/world");

       assertEquals(expectedChanges, el.events);
       el.events.clear();
   }

   /**
    * Not a really good test to debug. No need for it now.
    * @throws Exception
    */
   public void XtestNodeCreatedAndRemovedMapEvent() throws Exception
   {
       EventLog el = new EventLog(cache);
       cache.addTreeCacheListener(el);
       Map data = new HashMap();
       data.put("key", "value");
       cache.putObject("/hello/world", data);
       cache.removeObject("/hello/world");
       System.out.println("testNodeCreatedAndModifiedMapEvent:\n" + el.events);

       List expectedChanges = new ArrayList();
       expectedChanges.add("created /hello");
       expectedChanges.add("created /hello/world");

       //TODO: (optLocking)

       // all creations are fired as mods since these reflect as modified nodes in the workspace.  Opt locking only.
       expectedChanges.add("modify about to modify local /hello/world");
       expectedChanges.add("modified /hello/world");
       expectedChanges.add("modify Modified local /hello/world");
       expectedChanges.add("modify about to modify local /hello/world");
       expectedChanges.add("modified /hello/world");
       expectedChanges.add("modify Modified local /hello/world");
       expectedChanges.add("modify about to modify local /hello/world");
       expectedChanges.add("modified /hello/world");
       expectedChanges.add("modify Modified local /hello/world");

       expectedChanges.add("created /hello/world/key");
       expectedChanges.add("modify about to modify local /hello/world/key");
       expectedChanges.add("modified /hello/world/key");
       expectedChanges.add("modify Modified local /hello/world/key");
       expectedChanges.add("modify about to modify local /hello/world/key");
       expectedChanges.add("modified /hello/world/key");
       expectedChanges.add("modify Modified local /hello/world/key");

       expectedChanges.add("modify about to modify local /hello/world");
       expectedChanges.add("modified /hello/world");
       expectedChanges.add("modify Modified local /hello/world");
       expectedChanges.add("remove about to remove local /hello/world");
       expectedChanges.add("removed /hello/world");
       expectedChanges.add("remove Removed local /hello/world");

       assertEquals(expectedChanges.size(), el.events.size());
       assertEquals(expectedChanges, el.events);
       el.events.clear();
   }

   /**
    * Not a really good test to debug. No need for it now.
    * @throws Exception
    */
   public void XtestNodeCreatedAndRemovedListEvent() throws Exception
   {
       EventLog el = new EventLog(cache);
       cache.addTreeCacheListener(el);
       List data = new ArrayList();
       data.add("value");
       cache.putObject("/hello/world", data);
       cache.removeObject("/hello/world");
       System.out.println("testNodeCreatedAndRemovedListEvent:\n" + el.events);

       List expectedChanges = new ArrayList();
       expectedChanges.add("created /hello");
       expectedChanges.add("created /hello/world");

       //TODO: (optLocking)

       // all creations are fired as mods since these reflect as modified nodes in the workspace.  Opt locking only.
       expectedChanges.add("modify about to modify local /hello/world");
       expectedChanges.add("modified /hello/world");
       expectedChanges.add("modify Modified local /hello/world");
       expectedChanges.add("modify about to modify local /hello/world");
       expectedChanges.add("modified /hello/world");
       expectedChanges.add("modify Modified local /hello/world");
       expectedChanges.add("modify about to modify local /hello/world");
       expectedChanges.add("modified /hello/world");
       expectedChanges.add("modify Modified local /hello/world");

       expectedChanges.add("created /hello/world/0");
       expectedChanges.add("modify about to modify local /hello/world/0");
       expectedChanges.add("modified /hello/world/0");
       expectedChanges.add("modify Modified local /hello/world/0");
       expectedChanges.add("modify about to modify local /hello/world/0");
       expectedChanges.add("modified /hello/world/0");
       expectedChanges.add("modify Modified local /hello/world/0");

       expectedChanges.add("modify about to modify local /hello/world");
       expectedChanges.add("modified /hello/world");
       expectedChanges.add("modify Modified local /hello/world");
       expectedChanges.add("remove about to remove local /hello/world");
       expectedChanges.add("removed /hello/world");
       expectedChanges.add("remove Removed local /hello/world");

       // assertEquals(expectedChanges.size(), el.events.size());
       assertEquals(expectedChanges, el.events);
       el.events.clear();
   }

   public void testRemoveNodeWithRollback() throws Exception
   {
      cache.putObject("/child", new HashMap());

      EventLog el = new EventLog();
      cache.addTreeCacheListener(el);

      TransactionManager tm = cache.getTransactionManager();
      tm.begin();
      cache.removeObject("/child");
      tm.rollback();
      System.out.println("testRemoveNodeWithRollback:\n" + el.events);
      if (optLocking)
      {
        // in opt locking, a rollback will result in nothing having changed in the cache and hence
        // no notifications will fire.
        assertEquals(Collections.EMPTY_LIST, el.events);
      }
      else
      {
        // with pess locking, a rollback consists of modifications + equal and opposite modifications.
        assertEquals(Arrays.asList(new Object[]{
                "modify about to modify local /child",
                "modified /child",
                "modify Modified local /child",
                "remove about to remove local /child",
                "removed /child",
                "remove Removed local /child",
                "created /child",
                "modify about to modify local /child",
                "modified /child",
                "modify Modified local /child"}),
                el.events);
      }
      el.events.clear();
   }

   public static class EventLog extends AbstractTreeCacheListener
   {
	   private PojoCache cache;
	   public EventLog() { };
	   public EventLog(PojoCache cache) { this.cache = cache; };

      public final List events = new ArrayList();
      public void nodeCreated(Fqn fqn)
      {
         events.add("created " + fqn);
      }
      public void nodeRemove(Fqn fqn, boolean pre, boolean isLocal)
      {
         events.add("remove "+
                 (pre? "about to remove " : "Removed ") +
                 (isLocal? "local " : "remote ") + fqn);
      }
      public void nodeRemoved(Fqn fqn)
      {
         events.add("removed " + fqn);
      }
      public void nodeLoaded(Fqn fqn)
      {
         throw new UnsupportedOperationException();
      }
      public void nodeEvicted(Fqn fqn)
      {
         throw new UnsupportedOperationException();
      }
      public void nodeModify(Fqn fqn, boolean pre, boolean isLocal)
      {
         events.add("modify "+
                 (pre? "about to modify " : "Modified ") +
                 (isLocal? "local " : "remote ") + fqn);
		 if(this.cache != null && !pre) {
			try
			{
				cache.getObject(fqn);
			}
			catch (Exception e)
			{
				events.add(fqn + ":" + e);
                System.err.println("exception on fqn " + fqn);
                e.printStackTrace(System.err);
			}
		 }
      }
      public void nodeModified(Fqn fqn)
      {
         events.add("modified " + fqn);
      }
      public void nodeVisited(Fqn fqn)
      {
         throw new UnsupportedOperationException();
      }
      public void cacheStarted(TreeCache treeCache)
      {
         throw new UnsupportedOperationException();
      }
      public void cacheStopped(TreeCache treeCache)
      {
         throw new UnsupportedOperationException();
      }
      public void viewChange(View view)
      {
         throw new UnsupportedOperationException();
      }
   }


}

