package org.jboss.cache.pojo;

import static org.testng.AssertJUnit.assertEquals;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.cache.pojo.test.Address;
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 ObjectGraphTest 
{
   Log log = LogFactory.getLog(ObjectGraphTest.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;
   }

   /**
    * Test shared sub-object. In diagram, it is a forest
    * where the sub-object is connected by two tree parents.
    */
   public void testMultipleReference() throws Exception
   {
      log.info("testMultipleReference() ...");
      cache_.attach("/person/joe", createPerson("Joe Black", 31));
      Person joe = (Person) cache_.find("/person/joe");
      cache_.attach("/person/ben", createPerson("Ben Hogan", 51));
      Person ben = (Person) cache_.find("/person/ben");

      Address addr = new Address();
      addr.setStreet("123 Albert Ave.");
      addr.setCity("Sunnyvale");
      addr.setZip(94087);

      // They share the sub-object: address
      log.info("testMultipleReference(): set Joe address");
      joe.setAddress(addr);
      log.info("testMultipleReference(): set Ben address");
      ben.setAddress(addr);

      log.info("testMultipleReference(): verify");
      Address add1 = ((Person) cache_.find("/person/joe")).getAddress();
      Address add2 = ((Person) cache_.find("/person/ben")).getAddress();
      assertEquals(add1.getCity(), add2.getCity());
      addr.setCity("Santa Clara");
      assertEquals(add1.getCity(), add2.getCity());
   }

   public void testReAttach() throws Exception
   {
      log.info("testMultipleReference() ...");
      cache_.attach("/person/joe", createPerson("Joe Black", 31));
      Person joe = (Person) cache_.find("/person/joe");
      cache_.attach("/person/ben", createPerson("Ben Hogan", 51));
      Person ben = (Person) cache_.find("/person/ben");

      Address addr = new Address();
      addr.setStreet("123 Albert Ave.");
      addr.setCity("Sunnyvale");
      addr.setZip(94087);

      // They share the sub-object: address
      log.info("testMultipleReference(): set Joe address");
      joe.setAddress(addr);
      log.info("testMultipleReference(): set Ben address");
      ben.setAddress(addr);

      log.info("testMultipleReference(): verify");
      Address add1 = ((Person) cache_.find("/person/joe")).getAddress();
      Address add2 = ((Person) cache_.find("/person/ben")).getAddress();
      assertEquals(add1.getCity(), add2.getCity());
      addr.setCity("Santa Clara");
      assertEquals(add1.getCity(), add2.getCity());

      cache_.detach("/person/joe");

      cache_.attach("/person/joe", joe);
   }

   /**
    * Reattach in reverse order
    *
    * @throws Exception
    */
   public void testReAttach1() throws Exception
   {
      log.info("testMultipleReference() ...");
      cache_.attach("/person/joe", createPerson("Joe Black", 31));
      Person joe = (Person) cache_.find("/person/joe");
      cache_.attach("/person/ben", createPerson("Ben Hogan", 51));
      Person ben = (Person) cache_.find("/person/ben");

      Address addr = new Address();
      addr.setStreet("123 Albert Ave.");
      addr.setCity("Sunnyvale");
      addr.setZip(94087);

      // They share the sub-object: address
      log.info("testMultipleReference(): set Joe address");
      joe.setAddress(addr);
      log.info("testMultipleReference(): set Ben address");
      ben.setAddress(addr);

      log.info("testMultipleReference(): verify");
      Address add1 = ((Person) cache_.find("/person/joe")).getAddress();
      Address add2 = ((Person) cache_.find("/person/ben")).getAddress();
      assertEquals(add1.getCity(), add2.getCity());
      addr.setCity("Santa Clara");
      assertEquals(add1.getCity(), add2.getCity());

      cache_.detach("/person/ben");
      cache_.detach("/person/joe");

      cache_.attach("/person/ben", ben);
      cache_.attach("/person/joe", joe);
   }

   public void testRefCount() throws Exception
   {
      log.info("testRefCount() ...");
      Person joe = createPerson("Joe Black", 31);
      Person ben = createPerson("Ben Hogan", 51);
      cache_.attach("/person/joe", joe);
      cache_.attach("/person/ben", ben);

      Address addr = new Address();
      addr.setStreet("123 Albert Ave.");
      addr.setCity("Sunnyvale");
      addr.setZip(94087);

      joe.setAddress(addr);
      ben.setAddress(addr);
      // They share the sub-object: address
      Address add1 = ((Person) cache_.find("/person/joe")).getAddress();
      Address add2 = ((Person) cache_.find("/person/ben")).getAddress();
      assertEquals("Joe's address should still be valid ", "Sunnyvale", add1.getCity());
      assertEquals("Ben's address should still be valid ", "Sunnyvale", add2.getCity());
      assertEquals(add1.getCity(), add2.getCity());
      addr.setCity("Santa Clara");
      assertEquals(add1.getCity(), add2.getCity());

      cache_.detach("/person/joe");
      ben = (Person) cache_.find("/person/ben");
      addr = ben.getAddress();
      addr.setCity("Santa Clara");
      assertEquals("Ben's address should be changed ", "Santa Clara", addr.getCity());

      cache_.detach("/person/ben");
   }

   public void testRemoveObject1() throws Exception
   {
      log.info("testRemoveObject1() ...");
      cache_.attach("/person/joe", createPerson("Joe Black", 31));
      Person joe = (Person) cache_.find("/person/joe");
      cache_.attach("/person/ben", createPerson("Ben Hogan", 51));
      Person ben = (Person) cache_.find("/person/ben");

      Address addr = new Address();
      addr.setStreet("123 Albert Ave.");
      addr.setCity("Sunnyvale");
      addr.setZip(94087);

      // They share the sub-object: address
      log.info("testMultipleReference(): set Joe address");
      joe.setAddress(addr);
      log.info("testMultipleReference(): set Ben address");
      ben.setAddress(addr);

      Address add1 = ((Person) cache_.find("/person/joe")).getAddress();
      Address add2 = ((Person) cache_.find("/person/ben")).getAddress();
      assertEquals(add1.getCity(), add2.getCity());
      addr.setCity("Santa Clara");
      assertEquals(add1.getCity(), add2.getCity());

      // Remove pojo joe will relocate the address field to ben's
      cache_.detach("/person/joe");
      add2 = ((Person) cache_.find("/person/ben")).getAddress();
      assertEquals("City ", "Santa Clara", add2.getCity());
   }

   public void testRemoveObject2() throws Exception
   {
      log.info("testRemoveObject2() ...");
      cache_.attach("/person/joe", createPerson("Joe Black", 31));
      Person joe = (Person) cache_.find("/person/joe");
      cache_.attach("/person/ben", createPerson("Ben Hogan", 51));
      Person ben = (Person) cache_.find("/person/ben");
      cache_.attach("/person/john", createPerson("John Daly", 41));
      Person john = (Person) cache_.find("/person/john");

      Address addr = new Address();
      addr.setStreet("123 Albert Ave.");
      addr.setCity("Sunnyvale");
      addr.setZip(94087);

      Address addr1 = new Address();
      addr1.setStreet("123 Albert Ave.");
      addr1.setCity("San Jose");
      addr1.setZip(94087);

      // They share the sub-object: address
      log.info("testMultipleReference(): set Joe address");
      joe.setAddress(addr);
      log.info("testMultipleReference(): set Ben address");
      ben.setAddress(addr);
      log.info("testMultipleReference(): set John address");
      john.setAddress(addr);

      Address add1 = ((Person) cache_.find("/person/joe")).getAddress();
      Address add2 = ((Person) cache_.find("/person/ben")).getAddress();
      assertEquals(add1.getCity(), add2.getCity());
      addr.setCity("Santa Clara");
      assertEquals(add1.getCity(), add2.getCity());

      // Remove pojo joe will relocate the address field to ben's
      joe.setAddress(addr1);
      add2 = ((Person) cache_.find("/person/joe")).getAddress();
      assertEquals("City ", "San Jose", add2.getCity());
      add2 = ((Person) cache_.find("/person/ben")).getAddress();
      assertEquals("City ", "Santa Clara", add2.getCity());
      add2 = ((Person) cache_.find("/person/john")).getAddress();
      assertEquals("City ", "Santa Clara", add2.getCity());
   }

   public void XtestObjectIdentity() throws Exception
   {
   }



}

