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

package org.jboss.cache.pojo;

import static org.testng.AssertJUnit.assertEquals;
import static org.testng.AssertJUnit.assertNotNull;
import static org.testng.AssertJUnit.assertNull;

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

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.cache.Fqn;
import org.jboss.cache.config.Configuration.CacheMode;
import org.jboss.cache.factories.UnitTestCacheConfigurationFactory;
import org.jboss.cache.pojo.test.Link;
import org.jboss.cache.pojo.test.NodeManager;
import org.jboss.cache.pojo.test.Person;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

/**
 * Test object graph handling in aop, e.g., circular reference, multiple reference, link, etc.
 *
 * @author Ben Wang
 */

@Test(groups = {"functional"})
public class ReplicatedCircularGraphTest 
{
   Log log = LogFactory.getLog(ReplicatedCircularGraphTest.class);
   PojoCache cache1;
   PojoCache cache2;


   @BeforeMethod(alwaysRun = true)
   protected void setUp() throws Exception
   {
      log.info("setUp() ....");
      cache1 = createCache("CacheGroup");
      cache2 = createCache("CacheGroup");
   }

   @AfterMethod(alwaysRun = true)
   protected void tearDown() throws Exception
   {
      cache1.getCache().removeNode(Fqn.fromString("/"));
      cache1.stop();
      cache2.stop();
   }

   private PojoCache createCache(String name) throws Exception
   {
      boolean toStart = false;
      PojoCache tree = PojoCacheFactory.createCache(UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC), toStart);
      tree.start();
      return tree;
   }

//   public void testDummy() {}

   protected Person createPerson(String name, int age)
   {
      Person p = new Person();
      p.setName(name);
      p.setAge(age);
      return p;
   }

   public void testCircularReference1() throws Exception
   {
//        try {Thread.sleep(10000); } catch (Exception e) {};
      log.info("testCircularReference1() ...");
      Link parent = new Link("parent");
      Link child = new Link("child");
      parent.setLink(child);
      child.setLink(parent);
      cache1.attach("/link/parent", parent);
      TestingUtil.sleepThread(100);
      assertEquals("parent", ((Link) cache1.find("/link/parent")).getName());
      assertEquals("child", ((Link) cache1.find("/link/parent")).getLink().getName());
      assertEquals("parent", ((Link) cache2.find("/link/parent")).getName());
      assertEquals("child", ((Link) cache2.find("/link/parent")).getLink().getName());
      ((Link) cache2.find("/link/parent")).setLink(null);
      assertNull("Child should be null", ((Link) cache2.find("/link/parent")).getLink());
      Link link = (Link) cache1.detach("/link/parent");
      assertNotNull("Link should not be null ", link);
      System.out.println("Link: " + link);
   }

   public void testCircularReference2() throws Exception
   {
//        try {Thread.sleep(10000); } catch (Exception e) {};
      log.info("testCircularReference2() ...");
      Link parent = new Link("parent");
      Link child = new Link("child");
      cache1.attach("/link/parent", parent);
      parent.setLink(child);
      child.setLink(parent);
      assertEquals("parent", ((Link) cache1.find("/link/parent")).getName());
      assertEquals("child", ((Link) cache1.find("/link/parent")).getLink().getName());
      assertEquals("parent", ((Link) cache2.find("/link/parent")).getName());
      assertEquals("child", ((Link) cache2.find("/link/parent")).getLink().getName());
      ((Link) cache2.find("/link/parent")).setLink(null);
      assertNull("Child should be null", ((Link) cache2.find("/link/parent")).getLink());
      Link link = (Link) cache1.detach("/link/parent");
      assertNotNull("Link should not be null ", link);
   }

   public void testCircularReference3() throws Exception
   {
//        try {Thread.sleep(10000); } catch (Exception e) {};
      log.info("testCircularReference3() ...");
      Link parent = new Link("parent");
      Link child = new Link("child");
      cache1.attach("/link/parent", parent);
      cache1.attach("/link/child", child);
      TestingUtil.sleepThread(100);
      parent.setLink(child);
      child.setLink(parent);

      Link p1 = (Link) cache1.find("/link/parent");
      Link c1 = (Link) cache1.find("/link/child");
      assertEquals("parent", p1.getName());
      assertEquals("child", p1.getLink().getName());
      assertEquals("child", c1.getName());
      assertEquals("parent", c1.getLink().getName());

      Link p2 = (Link) cache1.find("/link/parent");
      Link c2 = (Link) cache1.find("/link/child");

      assertEquals("parent", p2.getName());
      assertEquals("child", p2.getLink().getName());
      assertEquals("child", c2.getName());
      assertEquals("parent", c2.getLink().getName());

      p2.setLink(null);
      assertNull("Child should be null", p2.getLink());
      Link link = (Link) cache1.detach("/link/parent");
      assertNotNull("Link should not be null ", link);
   }

   /**
    * Setting the circular relationship and also as a shared object.
    *
    * @throws Exception
    */
   public void testCircularReference4() throws Exception
   {
//        try {Thread.sleep(10000); } catch (Exception e) {};
      log.info("testCircularReference3() ...");
      Link parent = new Link("parent");
      Link child = new Link("child");
      parent.setLink(child);
      child.setLink(parent);

      List<Link> list = new ArrayList<Link>();
      list.add(parent);

      cache1.attach("/list", list);
      cache1.attach("/alias", list);

      TestingUtil.sleepThread(100);
      @SuppressWarnings("unchecked")
      List<Link> list1 = (List<Link>) cache2.find("/list");
      @SuppressWarnings("unchecked")
      List<Link> list2 = (List<Link>) cache2.find("/alias");

      assertEquals("parent", list1.get(0).getName());
      assertEquals("child", list2.get(0).getLink().getName());
   }

   public void testCircularAndSharedReferences() throws Exception
   {
      log.info("testCircularAndSharedReferences() ...");
      NodeManager pm_ = new NodeManager();

      pm_.setRootNode("root");
      pm_.addNode("root", "kanto");
      pm_.addNode("root.kanto", "tokyo");
      pm_.addNode("root.kanto", "kanagawa");

      cache1.attach("/propagation", pm_);
      assertEquals("kanagawa", pm_.findNode("root.kanto.kanagawa").getNodeRDN());
      pm_.addNode("root.kanto.tokyo", "hadanshita");
      assertEquals("hadanshita", pm_.findNode("root.kanto.tokyo.hadanshita").getNodeRDN());

      NodeManager pm2_ = (NodeManager) cache2.find("/propagation");
      assertEquals("kanagawa", pm2_.findNode("root.kanto.kanagawa").getNodeRDN());
      assertEquals("hadanshita", pm2_.findNode("root.kanto.tokyo.hadanshita").getNodeRDN());

/*
      System.out.println("\n\n");
      System.out.println("---------------------------------------------");
      System.out.println("Initial pm state");
      System.out.println("---------------------------------------------");
      pm_.printNodes();

      System.out.println("\n\n");
      System.out.println("---------------------------------------------");
      System.out.println("Initial cache content");
      System.out.println(cache_.printDetails());
      System.out.println("---------------------------------------------");
*/
   }



}
