package org.jboss.cache.pojo;

import static org.testng.AssertJUnit.assertEquals;
import static org.testng.AssertJUnit.assertFalse;
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.Fqn;
import org.jboss.cache.pojo.impl.PojoReference;
import org.jboss.cache.pojo.test.Address;
import org.jboss.cache.pojo.test.DoubleRef;
import org.jboss.cache.pojo.test.Person;
import org.jboss.cache.pojo.test.Student;
import org.testng.AssertJUnit;
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(LocalTest.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() {}

   private Person createPerson(String id, String name, int age)
   {
      return createPerson(id, name, age, null);
   }
   
   private Person createPerson(String id, String name, int age, Map<String, String> finalMap)
   {
      Person p = new Person(finalMap);
      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());
   }

   public void testSimpleType() throws Exception
   {
      log.info("testSimpleType() ....");
      String test = "This is a test";
      cache_.attach("test", test);

      assertEquals("String ", test, cache_.find("test"));
   }

   public void testSimpleTypeFqn() throws Exception
   {
      log.info("testSimpleType() ....");
      String test = "This is a test";
      cache_.attach(Fqn.fromString("/test"), test);

      assertEquals("String ", test, cache_.find(Fqn.fromString("/test")));
   }


   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 testReplaceSubPojo() throws Exception
   {
      Person joe = createPerson("/person/test3", "Joe", 32);

      Address addr = new Address();
      addr.setCity("Taipei");
      addr.setZip(106);
      joe.setAddress(addr);
      assertEquals("City ", "Taipei", joe.getAddress().getCity());
   }

   public void testRemove() throws Exception
   {
      createPerson("/person/test3", "Joe", 32);
      cache_.detach("/person/test3");
      //      assertNull("Underlying cache content " + cache_.getCache().printDetails(),
      //              cache_.getCache().get("/person/test3"));
   }

   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 ", "Lipitor", 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 testFinalMap() throws Exception
   {
      log.info("testMap() ....");
      Person ben = createPerson("/person/test1", "Ben Wang", 40, new HashMap<String, String>());
      assertEquals((Object) "Ben Wang", ben.getName());
      Map<String, String> map = ben.getFinalMap();
      if (!(map instanceof ClassProxy))
      {
         fail("Final map is not an instance of ClassProxy");
      }
      
      map.put("test1", "testa");
      map.put("test2", "testb");

      assertEquals("Hobbies size", 2, map.size());
      ben = (Person) cache_.detach("/person/test1");
      map = ben.getFinalMap();
      if (!(map instanceof HashMap))
      {
         fail("Final map is not an instance of HashMap");
      }
   }

   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");
      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 = 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);
      //      TreeCacheProxyImpl impl = (TreeCacheProxyImpl)cache_.getCache();
      //      System.out.println("*** Here I ");
      //      System.out.println(impl.printDetails());

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

   public void testExists() throws Exception
   {
      Fqn<String> fqn = Fqn.fromString("/person/test1");
      createPerson(fqn.toString(), "Joe Black", 32);
      assertTrue(cache_.exists(fqn));
      assertFalse(cache_.exists(Fqn.fromString("/blah")));

      PojoReference ref = (PojoReference) cache_.getCache().get(fqn,  PojoReference.KEY);
      assertTrue(cache_.exists(ref.getFqn()));
   }
   
   public void testDoubleRef() throws Exception
   {
      DoubleRef ref = new DoubleRef();
      cache_.attach("/test", ref);

      AssertJUnit.assertSame(ref.getOne(), ref.getTwo());
      
      ref.setOne(null);
      ref.setTwo(null);
   }
}