package org.jboss.cache.pojo;

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

import java.util.List;

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.pojo.test.Person;
import org.jboss.cache.pojo.test.Student;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

/**
 * Replicated test that use a tester wrapper. Future new test should use NewReplicatedAopTest
 *
 * @author Ben Wang
 */
@Test(groups = {"functional"})
public class ReplicatedTest 
{
   Log log = LogFactory.getLog(ReplicatedTest.class);
   PojoCache cache, cache1;

   @BeforeMethod(alwaysRun = true)
   protected void setUp() throws Exception
   {
      log.info("setUp() ....");
      boolean toStart = false;
      cache = PojoCacheFactory.createCache(UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC), toStart);
      cache.start();
      cache1 = PojoCacheFactory.createCache(UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC), toStart);
      cache1.start();
   }

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

//   public void testDummy() {}

   private Person createPerson(String id, String name, int age)
   {
      Person p = new Person();
      p.setName(name);
      p.setAge(age);
      cache.attach(id, p);
      return p;
   }

   private Student createStudent(String id, String name, int age, String grade)
   {
      Student p = new Student();
      p.setName(name);
      p.setAge(age);
      p.setYear(grade);
      cache.attach(id, p);
      return p;
   }

   public void testSimple() throws Exception
   {
      log.info("testSimple() ....");
      Person ben = createPerson("/person/test1", "Ben Wang", 40);
      assertEquals("Ben Wang", ben.getName());
      assertEquals("Ben Wang", ((Person) cache1.find("/person/test1")).getName());
      cache.detach("/person/test1");
   }


   public void testDynamicRefSwapping() throws Exception
   {
      Person person = createPerson("/person/test3", "Joe", 32);
      try
      {
         person.setAge(30);
         List<String> med = person.getMedication();
         assertNull("Medication should be null ", med);
         person.setAge(61);
         med = person.getMedication();
         assertEquals("Medication ", (Object) "Lipitor", (Object) med.get(0));
         assertEquals("Medication on cache1 ", "Lipitor",
                 person.getMedication().get(0));

         person.setAge(71);
         assertEquals("Medication ", "Vioxx", med.get(1));
         assertEquals("Medication on cache1 ", "Vioxx",
                 ((Person) cache1.find("/person/test3")).getMedication().get(1));
         cache.detach("/person/test3");

      } catch (Exception e)
      {
         // should be thrown
      }
   }

   public void testTransient() throws Exception
   {
      log.info("testTransient() ....");
      Person ben = createPerson("/person/test1", "Ben Wang", 40);
      ben.setCurrentStatus("Idle");
      assertEquals("Cache 1 ", "Idle", ben.getCurrentStatus());
      assertEquals("Cache 2 ", "Active",
              ((Person) cache1.find("/person/test1")).getCurrentStatus());
      cache.detach("/person/test1");
   }

   public void testModification() throws Exception
   {
      Person ben = createPerson("/person/test2", "Ben Wang", 40);
      ben.setName("Harald Gliebe");
      assertEquals(ben.getName(), "Harald Gliebe");
      assertEquals(((Person) cache1.find("/person/test2")).getName(), "Harald Gliebe");
      cache.detach("/person/test2");
   }

   public void testPostDetachModification() throws Exception
   {
      Person ben = createPerson("/person/test2", "Ben Wang", 40);
      ben = (Person) cache1.find("/person/test2");

      // Detach on remote node
      cache.detach("/person/test2");

      boolean passGet = false;
      try
      {
         ben.getAge();
      }
      catch (PojoCacheAlreadyDetachedException e)
      {
         passGet = true;
      }

      assertTrue("Expected PojoCacheAlreadyDetachedException!", passGet);

      boolean passSet = false;
      try
      {
         ben.setAge(10);
      }
      catch (PojoCacheAlreadyDetachedException e)
      {
         passSet = true;
      }

      assertTrue("Expected PojoCacheAlreadyDetachedException!", passSet);
   }

   public void testStaleFieldDetach() throws Exception
   {
      Person ben = createPerson("/person/test2", "Ben Wang", 40);
      ben = (Person) cache1.find("/person/test2");

      // Update on remote node
      ben.setAge(99);

      // Detach before a field read on local node
      ben = (Person) cache.detach("/person/test2");

      assertEquals("Detach did not refresh field", 99, ben.getAge());
   }

   public void testInheritance() throws Exception
   {
      Student joe = createStudent("/person/joe", "Joe", 32, "Senior");
      joe.setName("Joe Black");
      assertEquals(joe.getName(), "Joe Black");
      Student joe1 = (Student) cache1.find("/person/joe");
      assertEquals(joe1.getName(), "Joe Black");
      joe1.setYear("Junior");
      assertEquals(joe.getYear(), "Junior");
      assertEquals(joe1.getYear(), "Junior");
      cache.detach("/person/joe");
      cache.detach("/person/joe");
   }





}

