/*
 * 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.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.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 PojoCache, e.g., circular reference, multiple reference, link, etc.
 *
 * @author Ben Wang
 */

@Test(groups = {"functional"})
public class CircularGraphTest 
{
   Log log = LogFactory.getLog(CircularGraphTest.class);
   PojoCache cache_;


   @BeforeMethod(alwaysRun = true)
   protected void setUp() throws Exception
   {
      log.info("setUp() ....");
      String configFile = "META-INF/local-service.xml";
      boolean toStart = false;
      cache_ = PojoCacheFactory.createCache(configFile, toStart);
      cache_.start();
   }

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

   //   public void testDummy() {}

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

   /**
    * Pure parent child relationsip
    *
    * @throws Exception
    */
   public void testCircularReference1() throws Exception
   {
      log.info("testCircularReference1() ...");
      Link parent = new Link("parent");
      Link child = new Link("child");
      parent.setLink(child);
      child.setLink(parent);
      cache_.attach("/link/parent", parent);
      assertEquals("parent", ((Link) cache_.find("/link/parent")).getName());
      assertEquals("child", ((Link) cache_.find("/link/parent")).getLink().getName());
   }

   /**
    * Pure parent child relationsip. Attach both parent and child.
    *
    * @throws Exception
    */
   public void testCircularReference2() throws Exception
   {
      log.info("testCircularReference2() ...");
      Link parent = new Link("parent");
      Link child = new Link("child");
      parent.setLink(child);
      child.setLink(parent);
      cache_.attach("/link/parent", parent);
      cache_.attach("/link/child", child);
      assertEquals("parent", ((Link) cache_.find("/link/parent")).getName());
      assertEquals("child", ((Link) cache_.find("/link/parent")).getLink().getName());
   }

   /**
    * Pure parent child relationsip with detach
    *
    * @throws Exception
    */
   public void testCircularReference3() throws Exception
   {
      log.info("testCircularReference3() ...");
      Link parent = new Link("parent");
      Link child = new Link("child");
      parent.setLink(child);
      child.setLink(parent);
      cache_.attach("/link/parent", parent);
      cache_.attach("/link/child", child);
      assertEquals("parent", ((Link) cache_.find("/link/parent")).getName());
      assertEquals("child", ((Link) cache_.find("/link/parent")).getLink().getName());
      Link link = (Link) cache_.detach("/link/parent");
      cache_.detach("/link/child");
      assertEquals("child", link.getLink().getName());
      assertNull("Cache should be null ", (cache_.getCache().getRoot().getChild(Fqn.fromString("/link/parent"))));
      assertNull("Cache should be null ", (cache_.getCache().getRoot().getChild(Fqn.fromString("/link/child"))));
   }

   /**
    * Pure parent child relationsip with detach reversed
    *
    * @throws Exception
    */
   public void testCircularReference3a() throws Exception
   {
      log.info("testCircularReference3() ...");
      Link parent = new Link("parent");
      Link child = new Link("child");
      parent.setLink(child);
      child.setLink(parent);
      cache_.attach("/link/parent", parent);
      cache_.attach("/link/child", child);
      assertEquals("parent", ((Link) cache_.find("/link/parent")).getName());
      assertEquals("child", ((Link) cache_.find("/link/parent")).getLink().getName());
      cache_.detach("/link/child");
      Link link = (Link) cache_.detach("/link/parent");
      assertEquals("child", link.getLink().getName());
      assertNull("Cache should be null ", (cache_.getCache().getRoot().getChild(Fqn.fromString("/link/parent"))));
      assertNull("Cache should be null ", (cache_.getCache().getRoot().getChild(Fqn.fromString("/link/child"))));
   }

   /**
    * cache managed first before put in the relationsip.
    *
    * @throws Exception
    */
   public void testCircularReference4() throws Exception
   {
      log.info("testCircularReference4() ...");
      Link parent = new Link("parent");
      Link child = new Link("child");
      cache_.attach("/link/parent", parent);
      cache_.attach("/link/child", child);
      parent.setLink(child);
      child.setLink(parent);
      assertEquals("parent", ((Link) cache_.find("/link/parent")).getName());
      assertEquals("child", ((Link) cache_.find("/link/parent")).getLink().getName());
      Link link = (Link) cache_.detach("/link/parent");
      assertEquals("child", link.getLink().getName());
      assertNull("Cache should be null ", cache_.getCache().getRoot().getChild(Fqn.fromString("/parent")));
   }

   /**
    * Put first before settting the relationship
    *
    * @throws Exception
    */
   public void testCircularReference5() throws Exception
   {
      log.info("testCircularReference5() ...");
      Link parent = new Link("parent");
      Link child = new Link("child");
      cache_.attach("/link/parent", parent);
      cache_.attach("/link/child", child);
      parent.setLink(child);
      child.setLink(parent);
      assertEquals("parent", ((Link) cache_.find("/link/parent")).getName());
      assertEquals("child", ((Link) cache_.find("/link/parent")).getLink().getName());
      assertEquals("child", ((Link) cache_.find("/link/child")).getName());
      assertEquals("parent", ((Link) cache_.find("/link/child")).getLink().getName());
      Link link = (Link) cache_.detach("/link/parent");
      assertEquals("child", link.getLink().getName());
      assertNull("Cache should be null ", cache_.getCache().getRoot().getChild(Fqn.fromString("/parent")));
   }

   /**
    * Put first before settting the relationship
    *
    * @throws Exception
    */
   public void testCircularReference6() throws Exception
   {
      log.info("testCircularReference6() ...");
      Link parent = new Link("parent");
      Link child = new Link("child");
      cache_.attach("/link/parent", parent);
      cache_.attach("/link/child", child);
      parent.setLink(child);
      child.setLink(parent);
      assertEquals("parent", ((Link) cache_.find("/link/parent")).getName());
      assertEquals("child", ((Link) cache_.find("/link/parent")).getLink().getName());
      assertEquals("child", ((Link) cache_.find("/link/child")).getName());
      assertEquals("parent", ((Link) cache_.find("/link/child")).getLink().getName());
      Link link = (Link) cache_.detach("/link/parent");
      assertEquals("child", link.getLink().getName());
      assertNull("Cache should be null ", (cache_.getCache().getRoot().getChild(Fqn.fromString("/parent"))));

      // re-attach
      cache_.attach("/link/parent", parent);
   }

   /**
    * Setting the circular relationship and also as a shared object.
    *
    * @throws Exception
    */
   
   public void testCircularReference7() throws Exception
   {
      log.info("testCircularReference7() ...");
      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);

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

      @SuppressWarnings("unchecked")
      List<Link> list1 = (List<Link>) cache_.find("/list");
      
      @SuppressWarnings("unchecked")
      List<Link> list2 = (List<Link>) cache_.find("/alias");

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

   /**
    * @throws Exception
    */
   public void testCircularReference8() throws Exception
   {
      log.info("testCircularReference8() ...");
      Link parent = new Link("parent");
      Link child = new Link("child");
      parent.setLink(child);
      child.setLink(parent);

      cache_.attach("parent", parent);
      parent.setLink(child);// again
      child.setLink(parent);
   }

   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");

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

      List<?> list = pm_.findNode("root").getChildren();
      assertEquals("Root should have children of ", 1, list.size());
      pm_.printNodes();
   }
}
