/*
 * JBoss, Home of Professional Open Source
 *
 * Distributable under LGPL license.
 * See terms of license at gnu.org.
 */

package org.jboss.cache.pojo;


import java.util.HashMap;
import java.util.LinkedList;
import java.util.Queue;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.cache.config.Configuration.CacheMode;
import org.jboss.cache.factories.UnitTestCacheConfigurationFactory;
import org.jboss.cache.notifications.annotation.CacheListener;
import org.jboss.cache.notifications.annotation.NodeActivated;
import org.jboss.cache.notifications.annotation.NodeCreated;
import org.jboss.cache.notifications.annotation.NodeModified;
import org.jboss.cache.notifications.annotation.NodePassivated;
import org.jboss.cache.notifications.annotation.NodeRemoved;
import org.jboss.cache.notifications.annotation.NodeVisited;
import org.jboss.cache.notifications.event.NodeEvent;
import org.jboss.cache.notifications.event.NodeModifiedEvent;
import org.jboss.cache.notifications.event.NodeVisitedEvent;
import org.jboss.cache.pojo.notification.annotation.ArrayModified;
import org.jboss.cache.pojo.notification.annotation.PojoCacheListener;
import org.jboss.cache.pojo.notification.event.ArrayModifiedEvent;
import org.jboss.cache.pojo.test.ArrayObject;
import org.jboss.cache.pojo.test.ArrayObject.Person;
import org.testng.AssertJUnit;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

/**
 * Basic PojoCache test case.
 *
 * @author Ben Wang
 */
@Test(groups = {"functional"})
public class ArrayTest
{
   Log log = LogFactory.getLog(ArrayTest.class);
   PojoCache cache1, cache2;

   @BeforeMethod(alwaysRun = true)
   protected void setUp() throws Exception
   {
      log.info("setUp() ....");
      cache1 = PojoCacheFactory.createCache(UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC), false);
      //cache1.getCache().addCacheListener(new MyCacheListener(false));
      cache1.start();

      cache2 = PojoCacheFactory.createCache(UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC), false);
      //cache2.getCache().addCacheListener(new MyCacheListener(false));
      cache2.start();

   }

   @AfterMethod(alwaysRun = true)
   protected void tearDown() throws Exception
   {
      cache1.stop();
   }

   public void testSimple() throws Exception
   {
      log.info("testSimple() ....");
      ArrayObject ao = new ArrayObject();
      Person joe = new Person();
      joe.setName("Joe");
      joe.setAge(19);

      Person ben = new Person();
      ben.setName("Ben");
      ben.setAge(19);

      ao.setPerson(0, joe);

      cache1.attach("/ao", ao);

      ao.setPerson(1, ben);
      ao.setPerson(2, joe);
      ao.setPerson(3, ben);
      ao.setPerson(4, joe);
      ao.setPerson(5, ben);

      AssertJUnit.assertSame(ao.getPerson(1), ben);
      AssertJUnit.assertSame(ao.getPerson(2), joe);
      AssertJUnit.assertSame(ao.getPerson(3), ben);
      AssertJUnit.assertSame(ao.getPerson(4), joe);
      AssertJUnit.assertSame(ao.getPerson(5), ben);

      ArrayObject obj = (ArrayObject) cache2.find("/ao");
      Person person = obj.getPerson(4);
      obj.setPerson(5, person);
      AssertJUnit.assertSame(ao.getPerson(5), ao.getPerson(4));

      ao.setNum(5, 4);
      AssertJUnit.assertEquals(4, obj.getNum(5));
   }

   public void testArrayNotifications() throws Exception
   {

      ArrayObject ao = new ArrayObject();
      Person joe = new Person();
      joe.setName("Joe");
      joe.setAge(19);

      Person ben = new Person();
      ben.setName("Ben");
      ben.setAge(19);
      cache1.attach("/ao", ao);

      ArrayListener listener = new ArrayListener();
      cache1.addListener(listener);
      ao.setPerson(0, joe);
      ao.setPerson(1, ben);

      ArrayModifiedEvent event = listener.take();
      AssertJUnit.assertSame(ao.getTeam(), event.getSource());
      AssertJUnit.assertSame(joe, event.getValue());
      AssertJUnit.assertEquals(0, event.getIndex());

      event = listener.take();
      AssertJUnit.assertSame(ao.getTeam(), event.getSource());
      AssertJUnit.assertSame(ben, event.getValue());
      AssertJUnit.assertEquals(1, event.getIndex());
      cache1.removeListener(listener);
   }

   public void testMulti() throws Exception
   {
      log.info("testMulti() ....");
      ArrayObject ao = new ArrayObject();
      Person joe = new Person();
      joe.setName("Joe");
      joe.setAge(19);

      Person ben = new Person();
      ben.setName("Ben");
      ben.setAge(19);

      ao.setPerson(0, joe);

      cache1.attach("/ao", ao);

      ao.setMultiPerson(1, 2, 1, ben);
      ao.setMultiPerson(2, 3, 2, joe);
      ao.setMultiPerson(1, 2, 3, ben);
      ao.setMultiPerson(2, 1, 4, joe);
      ao.setMultiPerson(1, 1, 5, ben);

      AssertJUnit.assertSame(ao.getMultiPerson(1, 2, 1), ben);
      AssertJUnit.assertSame(ao.getMultiPerson(2, 3, 2), joe);
      AssertJUnit.assertSame(ao.getMultiPerson(1, 2, 3), ben);
      AssertJUnit.assertSame(ao.getMultiPerson(2, 1, 4), joe);
      AssertJUnit.assertSame(ao.getMultiPerson(1, 1, 5), ben);

      ArrayObject obj = (ArrayObject) cache2.find("/ao");
      Person person = obj.getMultiPerson(2, 1, 4);
      obj.setMultiPerson(1, 1, 5, person);
      AssertJUnit.assertSame(ao.getMultiPerson(1, 1, 5), ao.getMultiPerson(2, 1, 4));
   }

   @PojoCacheListener
   public static class ArrayListener
   {
      private Queue<ArrayModifiedEvent> events = new LinkedList<ArrayModifiedEvent>();

      @ArrayModified
      public void record(ArrayModifiedEvent event)
      {
         events.offer(event);
      }

      public ArrayModifiedEvent take()
      {
         return events.poll();
      }
   }

   @CacheListener
   public static class MyCacheListener
   {
      private boolean visits;

      public MyCacheListener(boolean visits)
      {
         this.visits = visits;
      }

      @NodeActivated
      @NodePassivated
      @NodeCreated
      @NodeRemoved
      @NodeVisited
      public void print(NodeEvent ne)
      {
         if (visits != ne instanceof NodeVisitedEvent)
            return;

         if (!ne.isPre())
            System.out.println((!visits ? "[one] " : "[two] ") + ne.getType() + " " + ne.getFqn());
      }

      @NodeModified
      public void print(NodeModifiedEvent ne)
      {
         if (!visits && !ne.isPre())
            System.out.println((!visits ? "[one] " : "[two] ") + ne.getType() + " " + ne.getFqn() + " - " + new HashMap(ne.getData()));
      }
   }



}
