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

package org.jboss.cache.pojo.region;

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

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.aop.proxy.ClassProxy;
import org.jboss.cache.CacheImpl;
import org.jboss.cache.Fqn;
import org.jboss.cache.Node;
import org.jboss.cache.pojo.PojoCache;
import org.jboss.cache.pojo.PojoCacheFactory;
import org.jboss.cache.pojo.impl.InternalConstant;
import org.jboss.cache.pojo.test.Address;
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;

/**
 * Basic PojoCache test case.
 *
 * @author Ben Wang
 */

@Test(groups = {"functional"})
public class LocalTest
{
   Log log = LogFactory.getLog(org.jboss.cache.pojo.region.LocalTest.class);
   PojoCache cache_;
   static final String REGION = "person";


   @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();
      cache_.getCache().getRegion(Fqn.fromString(REGION), true);
      cache_.getCache().getRegion(Fqn.fromString("RANDOM"), true);
   }

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

//   public void testDummy() {}

   private Person createPerson(String id, String name, int age)
   {
      Person p = new Person();
      p.setName(name);
      p.setAge(age);
      Address add = new Address();
      add.setZip(95123);
      add.setCity("San Jose");
      p.setAddress(add);
      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);
      Address add = new Address();
      add.setZip(95123);
      add.setCity("San Jose");
      p.setAddress(add);
      cache_.attach(id, p);
      return p;
   }

   public void testSimple() throws Exception
   {
      log.info("testSimple() ....");
      Person p = createPerson("person/test1", "Joe Black", 32);
      assertEquals((Object) "Joe Black", p.getName());

      assertTrue("Region node should exist ",
              cache_.getCache().getRoot().hasChild(new Fqn<String>(REGION)));
      Fqn<String> fqn = new Fqn<String>(Fqn.fromString(REGION), InternalConstant.JBOSS_INTERNAL);
      assertTrue("Internal region node should exist ",
              cache_.getCache().getRoot().hasChild(fqn));
      //System.out.println("Cache content: " +((org.jboss.cache.CacheImpl<Object, Object>)cache_.getCache()).printDetails());
   }

   public void testModification() throws Exception
   {
      Person joe = createPerson("person/test2", "Joe", 32);
      joe.setName("Joe Black");
      assertEquals(joe.getName(), "Joe Black");
      cache_.detach("person/test2");
   }

   public void testRemove() throws Exception
   {
      createPerson("person/test3", "Joe", 32);
      cache_.detach("person/test3");

      //String str = ((CacheImpl<Object, Object>) cache_.getCache()).printDetails();
      //System.out.println("**** Details ***/n" + str);

      Fqn<String> fqn = new Fqn<String>(Fqn.fromString(REGION), InternalConstant.JBOSS_INTERNAL);
      Node<Object, Object> n = cache_.getCache().getRoot().getChild(fqn);
      assertTrue("Internal region node should not exist ",
              n.getChildren() != null);
   }

   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(60);
         med = person.getMedication();
         assertEquals("Medication ", (Object) "Lipitor", (Object) med.get(0));
      }
      catch (Exception e)
      {
         // should be thrown
      }
   }

   public void testMap() throws Exception
   {
      log.info("testMap() ....");
      Person ben = createPerson("person/test1", "Ben Wang", 40);
      assertEquals((Object) "Ben Wang", ben.getName());
      Map<String, String> hobbies = ben.getHobbies();
      if (hobbies == null)
      {
         hobbies = new HashMap<String, String>();
         ben.setHobbies(hobbies);
         // NB: it is neccessary to get hobbies again to get advised version
         hobbies = ben.getHobbies();
      }
      hobbies.put("1", "English");
      hobbies.put("2", "French");
      if (!(hobbies instanceof ClassProxy))
      {
         fail("Hobbies is not an instance of ClassProxy");
      }

      hobbies = ben.getHobbies();
      assertEquals("Hobbies size", 2, hobbies.size());
      log.debug("Hobbies is " + hobbies.toString());
   }

   public void testMapDetachAttach() throws Exception
   {
      log.info("testMapDetachATtach() ....");
      Person ben = createPerson("person/test1", "Ben Wang", 40);
      assertEquals((Object) "Ben Wang", ben.getName());
      Map<String, String> hobbies = ben.getHobbies();
      if (hobbies == null)
      {
         hobbies = new HashMap<String, String>();
         ben.setHobbies(hobbies);
         // NB: it is neccessary to get hobbies again to get advised version
         hobbies = ben.getHobbies();
      }
      hobbies.put("1", "English");
      hobbies.put("2", "French");
      if (!(hobbies instanceof ClassProxy))
      {
         fail("Hobbies is not an instance of ClassProxy");
      }

      hobbies = ben.getHobbies();
      assertEquals("Hobbies size", 2, hobbies.size());
      log.debug("Hobbies is " + hobbies.toString());

      cache_.detach("person/test1");

      Fqn<String> fqn = new Fqn<String>(Fqn.fromString(REGION), InternalConstant.JBOSS_INTERNAL);
      Node<Object, Object> n = cache_.getCache().getRoot().getChild(fqn);
      assertTrue("Internal region node should not exist ",
              n.getChildren() != null);

      hobbies = ben.getHobbies();
      if ((hobbies instanceof ClassProxy))
      {
         fail("Hobbies should not be an instance of ClassProxy");
      }

      cache_.attach("person/1", ben);

   }

   public void testMap2() throws Throwable
   {
      Person joe = createPerson("person/test5", "Joe Black", 32);
      Map<String, String> hobby = new HashMap<String, String>();
      hobby.put("music", "guitar");
      joe.setHobbies(hobby);
      Object val = joe.getHobbies().get("music");
      assertEquals("guitar", val);
      hobby = joe.getHobbies();
      hobby.put("novel", "English");
      assertEquals("Size of map ", 2, joe.getHobbies().size());
   }

   public void testList() throws Throwable
   {
      Person joe = createPerson("person/test6", "Joe", 50);
      List<String> language = new ArrayList<String>();
      language.add("German");
      language.add("English");
      language.add("French");
      joe.setLanguages(language);

      assertEquals("Size of language ", 3, joe.getLanguages().size());
      language = joe.getLanguages();
      language.add("Mandarin");
      language.add("Taiwanese");
      language.add("Haka");
      assertEquals("Size of language ", 6, joe.getLanguages().size());

      String English = (String) language.get(1);
      assertEquals((Object) "English", English);
      cache_.detach("person/test6");
   }

   public void testListDetachAndAttach() throws Throwable
   {
      String id = "person/test6";
      Person joe = new Person();
      List<String> language = new ArrayList<String>();
      language.add("German");
      language.add("English");
      language.add("French");
      joe.setLanguages(language);

      cache_.attach(id, joe);

      cache_.detach(id);
      joe.getAge();
      cache_.attach(id, joe);
   }

   public void testListDetachAndAttach2() throws Throwable
   {
      String id = "person/test6";
      Person joe = createPerson(id, "Joe", 50);
      List<String> language = new ArrayList<String>();
      language.add("German");
      language.add("English");
      language.add("French");
      joe.setLanguages(language);

      assertEquals("Size of language ", 3, joe.getLanguages().size());
      language = joe.getLanguages();
      language.add("Mandarin");
      language.add("Taiwanese");
      language.add("Haka");
      assertEquals("Size of language ", 6, joe.getLanguages().size());

      String English = (String) language.get(1);
      assertEquals((Object) "English", English);

      if (!(language instanceof ClassProxy))
      {
         fail("Language is not an instance of ClassProxy");
      }

      cache_.detach(id);
      joe.getAge();
      language = joe.getLanguages();
      if ((language instanceof ClassProxy))
      {
         fail("Language is an instance of ClassProxy");
      }

      cache_.attach(id, joe);
   }

   public void testSet() throws Throwable
   {
      Person joe = createPerson("person/test7", "Joe", 27);
      Set<String> skill = new HashSet<String>();
      skill.add("Java");
      skill.add("Java");
      skill.add("Java");
      joe.setSkills(skill);
      skill = joe.getSkills();
      assertEquals("Size of skill ", 1, skill.size());

      skill.remove("Java");
      assertTrue(skill.isEmpty());
      skill.add("Java");
      skill.add("J2EE");
      skill.add("JBoss");
      assertEquals(new Integer(3), new Integer(skill.size()));
   }

   public void testSetDetachAttach() throws Throwable
   {
      String id = "person/test7";
      Person joe = createPerson(id, "Joe", 27);
      Set<String> skill = new HashSet<String>();
      skill.add("Java");
      skill.add("Java");
      skill.add("Java");
      joe.setSkills(skill);
      skill = joe.getSkills();
      assertEquals("Size of skill ", 1, skill.size());

      skill.remove("Java");
      assertTrue(skill.isEmpty());
      skill.add("Java");
      skill.add("J2EE");
      skill.add("JBoss");
      assertEquals(new Integer(3), new Integer(skill.size()));

      if (!(skill instanceof ClassProxy))
      {
         fail("Skill is not an instance of ClassProxy");
      }

      cache_.detach(id);
      joe.getAge();
      skill = joe.getSkills();
      if ((skill instanceof ClassProxy))
      {
         fail("Skill is an instance of ClassProxy");
      }

      cache_.attach(id, joe);
   }

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





}
