package org.jboss.cache.pojo;

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

import java.io.Serializable;
import java.util.Properties;

import javax.naming.Context;

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


/**
 * Replication PojoCache test. This is the real beef as local mode for PojoCache really does
 * not do anything.
 *
 * @author Ben Wang
 */

@Test(groups = {"functional"})
public class NewReplicatedTest 
{
   Log log_ = LogFactory.getLog(NewReplicatedTest.class);
   PojoCache cache_;
   PojoCache cache1_;


   @BeforeMethod(alwaysRun = true)
   protected void setUp() throws Exception
   {
      Properties prop = new Properties();
      prop.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.cache.transaction.DummyContextFactory");
      boolean toStart = false;
      cache_ = PojoCacheFactory.createCache(UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC), toStart);
      cache1_ = PojoCacheFactory.createCache(UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC), toStart);
      cache_.start();
      cache1_.start();
   }

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

   /*
   public void testObjectSizePut() throws Exception
   {
      org.jboss.cache.data.Student student = (org.jboss.cache.data.Student)constructObject();
      cache_.putObject("/joe", student);
   }

   static Object constructObject()
   {
      org.jboss.cache.data.Student joe = new org.jboss.cache.data.Student();
      joe.setName("Joe");

      Address add = new Address();
      add.setZip(94086);
      add.setCity("Sunnyvale)");
      add.setStreet("Albertson");
      joe.setAddress(add);

      String str;
      for(int i=0; i < 100; i++)
      {
         Course course = new Course();
         str = RandomString.randomstring(10,20);
         course.setInstructor(str);
         str = RandomString.randomstring(10,20);
         course.setTitle(str);
         str = RandomString.randomstring(10,20);
         course.setRoom(str);
         joe.addCourse(course);
      }

      return joe;
   }
   */


   public void testSubClass() throws Exception
   {
      log_.info("testsubClass() ....");
      Student test = new Student();
      test.setName("Ben");
      test.setAge(10);
      Address addr = new Address();
      addr.setCity("Taipei");
      addr.setZip(106);
      test.setAddress(addr);
      cache_.attach("/a", test);
      Student result = (Student) cache_.find("/a");
      assertEquals(" ", test, result);

      Student remote = (Student) cache1_.find("/a");
      System.out.println("Output on cache1: " + result);
      System.out.println("Output on cache2: " + remote);
      assertEquals("Age should be ", 10, remote.getAge());
   }

   public void testRemoteRemove() throws Exception
   {
      log_.info("testRemoteRemove() ....");
      Person test = new Person();
      test.setName("Ben");
      test.setAge(10);
      cache_.attach("/a", test);
      Person result = (Person) cache_.find("/a");
      assertEquals(" ", test, result);

      Person remote = (Person) cache1_.find("/a");
      assertEquals("Age should be ", 10, remote.getAge());

      // Remote remove
      cache1_.detach("/a");
      assertNull("Object should be null ", cache_.find("/a"));

      assertNull("Object should be null ", cache1_.find("/a"));
      // It should still be 10
      assertEquals("Age should be ", 10, remote.getAge());
   }

   public void testRemoteRemove2() throws Exception
   {
      log_.info("testRemoteRemove() ....");
      Person test = new Person();
      test.setName("Ben");
      test.setAge(10);
      cache_.attach("/a", test);
      Person result = (Person) cache_.find("/a");
      assertEquals(" ", test, result);

      Person remote = (Person) cache1_.find("/a");
      assertEquals("Age should be ", 10, remote.getAge());

      // Remote remove
      cache_.detach("/a");
      assertNull("Object should be null ", cache_.find("/a"));

      assertNull("Object should be null ", cache1_.find("/a"));
      // this will trigger the PojoCacheAlreadyDetachedException
      try
      {
         remote.getAge();
         fail("Should throw out exception here.");
      }
      catch (PojoCacheAlreadyDetachedException pe)
      {
      }
   }

   /**
    * Test for pojo detachment and then serialization.
    *
    * @throws Exception
    */
   public void testRemoteDetach() throws Exception
   {
      log_.info("testRemoteDetach() ....");
      SpecialSerializedAddress addr = new SpecialSerializedAddress();
      addr.setZip(95123);
      addr.addResidents("Ben");
      addr.addResidents("Joe");
      // Test serialization first
      Fqn<String> fqn = Fqn.fromString("/plain");
      cache_.getCache().put(fqn, "test", addr);
      cache_.getCache().remove(fqn, "test");

      cache_.attach("/a", addr);
      SpecialSerializedAddress result = (SpecialSerializedAddress) cache_.find("/a");
      assertEquals(" ", addr, result);

      // Remote remove
      cache_.detach("/a");
      assertNull("Object should be null ", cache_.find("/a"));

      // Test serialization after detach
      cache_.getCache().put(fqn, "test", addr);

      SpecialSerializedAddress remote = (SpecialSerializedAddress)
            cache1_.getCache().get(fqn, "test");
      assertEquals("Name should be ", 95123, remote.getZip());
   }

   public void testPutArray1() throws Exception
   {
      log_.info("testPutArray1() ....");
      long[] arr = new long[]{1, 2};
      cache_.attach("array", arr);

      long[] a2 = (long[]) cache1_.find("array");
      assertEquals("arr 0", 1, a2[0]);
   }

   public static class PersonSerial implements Serializable
   {
      private static final long serialVersionUID = 1L;
      public String name;
      public int age;
      
      public PersonSerial(String name, int age)
      {
         this.name = name;
         this.age = age;
      }
   }

   public void testPutArray2() throws Exception
   {
      log_.info("testPutArray2() ....");
      PersonSerial p1 = new PersonSerial("Ben", 10);

      PersonSerial p2 = new PersonSerial("Joe", 20);
      PersonSerial[] arr = new PersonSerial[]{p1, p2};

      cache_.attach("array", arr);

      PersonSerial[] a2 = (PersonSerial[]) cache1_.find("array");
      assertEquals("arr 0", "Ben", a2[0].name);
   }

   /**
    * JBCACHE-200.
    *
    * @throws Exception
    */
   public void testStateTransfer() throws Exception
   {
      log_.info("testStateTransfer() ....");
      Person test = new Person();
      test.setName("Ben");
      test.setAge(10);
      cache_.attach("/a", test);
      Person result = (Person) cache_.find("/a");
      assertEquals(" ", test, result);

      // restart cache1_
      cache1_.stop();
      cache1_.getCache().removeNode(Fqn.fromString("/a"));
      cache1_.start();
      // Start from scratch for initial state transfer
      Person remote = (Person) cache1_.find("/a");
      assertEquals("Age should be ", 10, remote.getAge());
   }




}

