/*
 * JBoss, the OpenSource J2EE webOS
 *
 * Distributable under LGPL license.
 * See terms of license at gnu.org.
 */
package org.jboss.cache.aop;

import org.jboss.cache.Fqn;
import org.jboss.cache.Node;
import org.jboss.util.NestedRuntimeException;

import java.util.*;

/**
 * Currently not used.
 */
public class CachedMap extends AbstractMap implements Map
{

   protected class Entry implements Map.Entry
   {

      Object key;

      public Entry(Object key)
      {
         this.key = key;
      }

      public Object getKey()
      {
         return key;
      }

      public Object getValue()
      {
         try {
            return cache.getObject(new Fqn(fqn, key));
         } catch (Exception e) {
            throw new NestedRuntimeException(e);
         }
      }

      public Object setValue(Object value)
      {
         try {
            return cache.putObject(new Fqn(fqn, key), value);
         } catch (Exception e) {
            throw new NestedRuntimeException(e);
         }
      }

      public int hashCode()
      {
         Object value = getValue();
         return ((key == null) ? 0 : key.hashCode())
               ^ ((value == null) ? 0 : value.hashCode());
      }

      public boolean equals(Object obj)
      {
         if (!(obj instanceof Map.Entry))
            return false;
         Map.Entry entry = (Map.Entry) obj;
         Object value = getValue();
         return (
               key == null
               ? entry.getKey() == null
               : key.equals(entry.getKey()))
               && (value == null
               ? entry.getValue() == null
               : value.equals(entry.getValue()));
      }
   }

   protected TreeCacheAop cache;
   protected Fqn fqn;

   protected Node getNode()
   {
      try {
         return cache.get(fqn);
      } catch (Exception e) {
         throw new NestedRuntimeException(e);
      }
   }

   protected CachedMap(TreeCacheAop cache, Fqn fqn)
   {
      this.cache = cache;
      this.fqn = fqn;
   }

   public Object get(Object key)
   {
      try {
         return cache.getObject(new Fqn(fqn, key));
      } catch (Exception e) {
         throw new NestedRuntimeException(e);
      }
   }

   public Object put(Object key, Object value)
   {
      try {
         return cache.putObject(new Fqn(fqn, key), value);
      } catch (Exception e) {
         throw new NestedRuntimeException(e);
      }
   }

   public void putAll(Map map)
   {
      for (Iterator i = map.entrySet().iterator(); i.hasNext();) {
         Map.Entry entry = (Map.Entry) i.next();
         put(entry.getKey(), entry.getValue());
      }
   }

   public Object remove(Object key)
   {
      try {
         return cache.removeObject(new Fqn(fqn, key));
      } catch (Exception e) {
         throw new NestedRuntimeException(e);
      }
   }

   public void clear()
   {
      for (Iterator i = keySet().iterator(); i.hasNext();) {
         remove(i.next());
      }
   }

   public int size()
   {
      Map children = getNode().getChildren();
      return children == null ? 0 : children.size();
   }

   public boolean isEmpty()
   {
      return size() == 0;
   }

   public boolean containsKey(Object object)
   {
      Map children = getNode().getChildren();
      return children == null ? false : children.containsKey(object);
   }

   public boolean containsValue(Object object)
   {
      return false;
   }

   public Set entrySet()
   {
      final CachedMap map = this;

      return new AbstractSet()
      {

         public int size()
         {
            Map children = getNode().getChildren();
            return children == null ? 0 : children.size();
         }

         public Iterator iterator()
         {
            Map children = getNode().getChildren();
            final Iterator i =
                  children == null
                  ? Collections.EMPTY_LIST.iterator()
                  : children.keySet().iterator();

            return new Iterator()
            {
               Object lastKey; // for remove

               public boolean hasNext()
               {
                  return i.hasNext();
               }

               public Object next()
               {
                  return new Entry(lastKey = i.next());
               }

               public void remove()
               {
                  map.remove(lastKey);
               }
            };
         }
      };
   }

   public Collection values()
   {
      return null; //TODO
   }

   public Set keySet()
   {
      Map children = getNode().getChildren();
      return children == null ? Collections.EMPTY_SET : children.keySet();
   }

   public int hashCode()
   {
      int result = 0;
      for (Iterator i = entrySet().iterator(); i.hasNext();) {
         result += i.next().hashCode();
      }
      return result;
   }

   public boolean equals(Object object)
   {
      if (object == this)
         return true;
      if (!(object instanceof Map))
         return false;
      Map map = (Map) object;
      if (size() != map.size())
         return false;
      for (Iterator i = entrySet().iterator(); i.hasNext();) {
         Map.Entry entry = (Map.Entry) i.next();
         Object value = entry.getValue();
         if (value != null) {
            if (map.get(entry.getKey()) != null
                  || !map.containsKey(entry.getKey()))
               return false;
         } else {
            if (!value.equals(map.get(entry.getKey())))
               return false;
         }
      }
      return true;
   }

}