package org.jboss.cache.pojo.collection;

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

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
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.pojo.PojoCache;
import org.jboss.cache.pojo.PojoCacheFactory;
import org.jboss.cache.pojo.test.Address;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

/**
 * Set interface testing.
 *
 * @author Scott Marlow
 */

@Test(groups = {"functional"})
public class CachedMapNullTest 
{
   Log log = LogFactory.getLog(CachedMapNullTest.class);
   PojoCache cache_;
   Map<String, String> hobbies;


   @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();

      stage();
   }

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

   static final int NUMBER_OF_STAGED_HOBBIES = 5;

   @SuppressWarnings("unchecked")
   protected void stage() throws Exception
   {
      hobbies = new HashMap<String, String>();
      hobbies.put("1", "golf");
      hobbies.put("2", "tennis");
      hobbies.put("3", "polo");
      hobbies.put(null, "Non-null value but the key is null");
      hobbies.put("key is non-null but value is null", null);

      cache_.attach("/person/test7", hobbies);
      hobbies = (Map<String, String>) cache_.find("/person/test7");
      assertEquals("Map size", NUMBER_OF_STAGED_HOBBIES, hobbies.size());

      if (!(hobbies instanceof ClassProxy || hobbies instanceof Map))
      {
         fail("testPut(): hobbies is not instance of ClassProxy nor Map");
      }
   }

   /**
    * Test simple put
    *
    * @throws Throwable
    */
   public void testPut() throws Throwable
   {
      int size = hobbies.size();
      assertEquals("Size is ", NUMBER_OF_STAGED_HOBBIES, size);

      hobbies.put("6", "baseball");
      size = hobbies.size();
      assertEquals("Size is ", NUMBER_OF_STAGED_HOBBIES + 1, size);

   }

   public void testAddAndRemoveIndex() throws Throwable
   {
      hobbies.put("4", "baseball");
      int size = hobbies.size();
      assertEquals("Size is ", NUMBER_OF_STAGED_HOBBIES + 1, size);

      assertTrue("Skill contain Golf ", hobbies.containsKey("3"));

      hobbies.remove("3");
      size = hobbies.size();
      assertEquals("Size is ", NUMBER_OF_STAGED_HOBBIES, size);
      assertFalse("Skill does not contain " + NUMBER_OF_STAGED_HOBBIES + " anymore ", hobbies.containsKey("3"));

      assertTrue("search for null key returned non-null value " + hobbies.get(null), hobbies.get(null) != null);

      hobbies.remove(null);
      size = hobbies.size();
      assertEquals("Size is ", NUMBER_OF_STAGED_HOBBIES - 1, size);
      assertFalse("Skill does not contain " + (NUMBER_OF_STAGED_HOBBIES - 1) + " ", hobbies.containsKey(null));

      hobbies.clear();
      size = hobbies.size();
      assertEquals("Size is ", 0, size);

      assertTrue("Should be empty", hobbies.isEmpty());
   }

   public void testPutAllEtc() throws Throwable
   {
      Map<String, String> map = new HashMap<String, String>();
      map.put("4", "pingpong");
      map.put("5", "handball");

      hobbies.putAll(map);
      int size = hobbies.size();
      assertEquals("Size is ", NUMBER_OF_STAGED_HOBBIES + 2, size);

      assertTrue("Key is ", hobbies.containsKey("4"));

      Set<String> keys = hobbies.keySet();
      assertEquals("Key size ", NUMBER_OF_STAGED_HOBBIES + 2, keys.size());

      Set<Map.Entry<String, String>> entries = hobbies.entrySet();
      assertEquals("Entry size ", NUMBER_OF_STAGED_HOBBIES + 2, entries.size());

   }

   public void testEntrySet() throws Throwable
   {
      System.out.println("Map " + hobbies.toString());
      for (Iterator<Map.Entry<String, String>> i = hobbies.entrySet().iterator(); i.hasNext();)
      {
         Map.Entry<String, String> entry = i.next();
         System.out.println("Entry key and value " + entry.getKey() + " " + entry.getValue());
      }
   }

   public void testValues() throws Throwable
   {
      System.out.println("Map " + hobbies.toString());

      Set<String> correct = new HashSet<String>();
      correct.add("golf");
      correct.add("tennis");
      correct.add("polo");
      correct.add("Non-null value but the key is null");
      correct.add(null);

      Collection<String> values = hobbies.values();
      assertEquals("Correct number of elements in value collection",
              correct.size(), values.size());

      Iterator<String> iter = null;
      for (iter = correct.iterator(); iter.hasNext();)
         assertTrue(values.contains(iter.next()));

      for (iter = values.iterator(); iter.hasNext();)
      {
         Object value = iter.next();
         assertTrue(value + " expected", correct.remove(value));
      }
      assertTrue("No missing elements from iterator", correct.size() == 0);

      iter.remove();
      assertTrue("2 elements left after remove via iter", values.size() == NUMBER_OF_STAGED_HOBBIES - 1);
      assertTrue("Iter removal reflected in map", hobbies.size() == NUMBER_OF_STAGED_HOBBIES - 1);

      Object[] data = values.toArray();
      assertTrue("2 elements in values array", data.length == NUMBER_OF_STAGED_HOBBIES - 1);

      values.remove(data[0]);
      assertTrue("1 element left after remove", values.size() == NUMBER_OF_STAGED_HOBBIES - 2);
      assertTrue("Removal reflected in map", hobbies.size() == NUMBER_OF_STAGED_HOBBIES - 2);

      values.clear();
      assertTrue("0 elements left after clear", values.size() == 0);
      assertTrue("Clear reflected in map", hobbies.size() == 0);
   }

   public void testContainsValue() throws Throwable
   {
      System.out.println("Map " + hobbies.toString());
      assertTrue("contains golf", hobbies.containsValue("golf"));
      assertTrue("contains tennis", hobbies.containsValue("tennis"));
      assertTrue("contains polo", hobbies.containsValue("polo"));
      assertFalse("does not contain squash", hobbies.containsValue("squash"));
   }

   public void testEquals() throws Throwable
   {
      Map<String, String> map = new HashMap<String, String>();
      map.put("1", "test");
      map.put("4", "test");
      map.put("2", "tennis");
      assertFalse("Map should not be the same ", map.equals(hobbies));

      map.clear();
      map.put("1", "golf");
      map.put("2", "tennis");
      map.put("3", "polo");
      map.put(null, "Non-null value but the key is null");
      map.put("key is non-null but value is null", null);
      assertTrue("Map should be the same ", map.equals(hobbies));
      assertTrue("Map should be the same, hobbies=" + hobbies.toString() + ", map=" + map.toString(), hobbies.equals(map));
      assertTrue("Map should be the same ", hobbies.equals(hobbies));
   }

   @SuppressWarnings("unchecked")
   public void testAttachAndDetach() throws Exception
   {
      Map<String, String> map = new HashMap<String, String>();
      map.put("1", "English");
      map.put("2", "French");
      map.put("3", "Taiwanese");

      cache_.attach("/test", map); // attach
      map = (Map<String, String>) cache_.find("/test");
      assertEquals("Size ", 3, map.size());

      map = (Map<String, String>) cache_.detach("/test");  // detach
      assertEquals("Size ", 3, map.size());

      System.out.println("**** End of cache content **** ");
      map.remove("2");
      map.put("2", "Hoklo");
      assertEquals("Size ", 3, map.size());
      assertEquals("Content ", "Hoklo", map.get("2"));

      // Try to re-attach
      cache_.attach("/test", map);
      map.remove("3");
      assertEquals("Size ", 2, map.size());
   }

   @SuppressWarnings("unchecked")
   public void testPojoAttachAndDetach() throws Exception
   {
      Address add1 = new Address();
      add1.setCity("San Jose");
      add1.setZip(95123);

      Address add2 = new Address();
      add1.setCity("Sunnyvale");
      add1.setZip(94086);

      Address add3 = new Address();
      add1.setCity("Santa Clara");
      add1.setZip(951131);

      Map<String, Address> map = new HashMap<String, Address>();
      map.put("1", add1);
      map.put("2", add2);
      map.put("3", add3);

      cache_.attach("/test", map); // attach
      map = (Map<String, Address>) cache_.find("/test");
      assertEquals("Size ", 3, map.size());

      map = (Map<String, Address>) cache_.detach("/test");
      assertEquals("Size ", 3, map.size());

      System.out.println("**** End of cache content **** ");
      map.remove("2");
      map.put("2", add2);
      assertEquals("Size ", 3, map.size());
      assertEquals("Content ", add2, map.get("2"));

      // Try to re-attach
      cache_.attach("/test", map);
      map.remove("2");
      assertEquals("Size ", 2, map.size());
   }


}

