package org.jxmpp.util.cache;

import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

public class ExpirationCache<K, V> implements Cache<K, V>, Map<K, V> {
   private final LruCache<K, ExpirationCache.ExpireElement<V>> cache;
   private long defaultExpirationTime;

   public ExpirationCache(int maxSize, long defaultExpirationTime) {
      this.cache = new LruCache(maxSize);
      this.setDefaultExpirationTime(defaultExpirationTime);
   }

   public void setDefaultExpirationTime(long defaultExpirationTime) {
      if (defaultExpirationTime <= 0L) {
         throw new IllegalArgumentException();
      } else {
         this.defaultExpirationTime = defaultExpirationTime;
      }
   }

   public V put(K key, V value) {
      return this.put(key, value, this.defaultExpirationTime);
   }

   public V put(K key, V value, long expirationTime) {
      ExpirationCache.ExpireElement<V> eOld = (ExpirationCache.ExpireElement)this.cache.put(key, new ExpirationCache.ExpireElement(value, expirationTime));
      return eOld == null ? null : eOld.element;
   }

   public V get(Object key) {
      ExpirationCache.ExpireElement<V> v = (ExpirationCache.ExpireElement)this.cache.get(key);
      if (v == null) {
         return null;
      } else if (v.isExpired()) {
         this.remove(key);
         return null;
      } else {
         return v.element;
      }
   }

   public V remove(Object key) {
      ExpirationCache.ExpireElement<V> e = (ExpirationCache.ExpireElement)this.cache.remove(key);
      return e == null ? null : e.element;
   }

   public int getMaxCacheSize() {
      return this.cache.getMaxCacheSize();
   }

   public void setMaxCacheSize(int maxCacheSize) {
      this.cache.setMaxCacheSize(maxCacheSize);
   }

   public int size() {
      return this.cache.size();
   }

   public boolean isEmpty() {
      return this.cache.isEmpty();
   }

   public boolean containsKey(Object key) {
      return this.cache.containsKey(key);
   }

   public boolean containsValue(Object value) {
      return this.cache.containsValue(value);
   }

   public void putAll(Map<? extends K, ? extends V> m) {
      Iterator i$ = m.entrySet().iterator();

      while(i$.hasNext()) {
         Entry<? extends K, ? extends V> entry = (Entry)i$.next();
         this.put(entry.getKey(), entry.getValue());
      }

   }

   public void clear() {
      this.cache.clear();
   }

   public Set<K> keySet() {
      return this.cache.keySet();
   }

   public Collection<V> values() {
      Set<V> res = new HashSet();
      Iterator i$ = this.cache.values().iterator();

      while(i$.hasNext()) {
         ExpirationCache.ExpireElement<V> value = (ExpirationCache.ExpireElement)i$.next();
         res.add(value.element);
      }

      return res;
   }

   public Set<Entry<K, V>> entrySet() {
      Set<Entry<K, V>> res = new HashSet();
      Iterator i$ = this.cache.entrySet().iterator();

      while(i$.hasNext()) {
         Entry<K, ExpirationCache.ExpireElement<V>> entry = (Entry)i$.next();
         res.add(new ExpirationCache.EntryImpl(entry.getKey(), ((ExpirationCache.ExpireElement)entry.getValue()).element));
      }

      return res;
   }

   private static class EntryImpl<K, V> implements Entry<K, V> {
      private final K key;
      private V value;

      public EntryImpl(K key, V value) {
         this.key = key;
         this.value = value;
      }

      public K getKey() {
         return this.key;
      }

      public V getValue() {
         return this.value;
      }

      public V setValue(V value) {
         V oldValue = this.value;
         this.value = value;
         return oldValue;
      }
   }

   private static class ExpireElement<V> {
      public final V element;
      public final long expirationTimestamp;

      public ExpireElement(V element, long expirationTime) {
         this.element = element;
         this.expirationTimestamp = System.currentTimeMillis() + expirationTime;
      }

      public boolean isExpired() {
         return System.currentTimeMillis() > this.expirationTimestamp;
      }

      public int hashCode() {
         return this.element.hashCode();
      }

      public boolean equals(Object other) {
         if (!(other instanceof ExpirationCache.ExpireElement)) {
            return false;
         } else {
            ExpirationCache.ExpireElement<?> otherElement = (ExpirationCache.ExpireElement)other;
            return this.element.equals(otherElement.element);
         }
      }
   }
}
