package org.jboss.cache.search;

import org.apache.lucene.search.Query;
import org.hibernate.search.impl.SearchFactoryImpl;
import org.hibernate.search.engine.SearchFactoryImplementor;
import org.jboss.cache.Cache;
import org.jboss.cache.CacheException;
import org.jboss.cache.CacheStatus;
import org.jboss.cache.Fqn;
import org.jboss.cache.InvocationContext;
import org.jboss.cache.Node;
import org.jboss.cache.NodeNotExistsException;
import org.jboss.cache.Region;
import org.jboss.cache.config.Configuration;
import org.jgroups.Address;

import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @author Navin Surtani  - navin@surtani.org
 *         <p/>
 *         Implementation class for the SearchableCache interface.
 */
public class SearchableCacheImpl<K, V> implements SearchableCache<K, V>
{
   // this is the ACTUAL cache. that does all the work.
   private Cache<K, V> cache;


   private SearchFactoryImplementor searchFactory;

   public SearchableCacheImpl(Cache<K, V> cache, SearchFactoryImplementor searchFactory)
   {
      if (cache == null) throw new NullPointerException("Cache is null");
      if (searchFactory == null) throw new NullPointerException("searchFactory is null");
      this.cache = cache;
      this.searchFactory = searchFactory;
   }

   /**
    * Creates a CacheQuery object from a given Lucene Query.
    *
    * @param luceneQuery - for lucene
    * @return CacheQuery object
    */

   public CacheQuery createQuery(Query luceneQuery)
   {
      return new CacheQueryImpl(luceneQuery, searchFactory, cache);
   }

   /**
    * Creates a CacheQuery object from a Lucene Query and a class array.
    *
    * @param luceneQuery - for lucene
    * @param classes array
    * @return CacheQuery object.
    */


   public CacheQuery createQuery(Query luceneQuery, Class... classes)
   {
      return new CacheQueryImpl(luceneQuery, searchFactory, cache, classes);

   }

   /**
    * Returns an org.jboss.cache.config.Configuration instance.
    *
    * @return org.jboss.cache.config.Configuration
    */

   public Configuration getConfiguration()
   {
      return cache.getConfiguration();
   }

   /**
    * Returns an org.jboss.cache.Node instance.
    *
    * @return org.jboss.cache.Node<K,V>
    */

   public Node getRoot()
   {
      return cache.getRoot();
   }

   /**
    * Adds a listener to the cache.
    *
    * @param listener
    */

   //TODO: Does this have to be a CacheListener?
   public void addCacheListener(Object listener)
   {
      cache.addCacheListener(listener);
   }

   /**
    * Removes a listener from the cache.
    *
    * @param listener
    */


   public void removeCacheListener(Object listener)
   {
      cache.removeCacheListener(listener);
   }

   /**
    * Returns a set of listeners that the cache has.
    *
    * @return A set of listeners.
    */

   public Set getCacheListeners()
   {
      return cache.getCacheListeners();
   }


   /**
    * Puts something into the cache with a given Fqn, key and value.
    *
    * @param fqn
    * @param key
    * @param value
    * @return
    */

   public V put(String fqn, K key, V value)
   {
      return cache.put(fqn, key, value);
   }

   /**
    * Puts something into the cache with a given Fqn and Map instance.
    *
    * @param fqn
    * @param data
    */


   public void put(String fqn, Map<K, V> data)
   {
      cache.put(fqn, data);
   }

   /**
    * Removes something from the cache with a given Fqn and key.
    *
    * @param fqn
    * @param key
    * @return
    */


   public V remove(String fqn, K key)
   {
      return cache.remove(fqn, key);
   }

   /**
    * Convenience method that takes a string representation of an Fqn. Otherwise identical to removeNode(Fqn)
    *
    * @param fqn
    * @return True if the node was removed, false if the node wasn't
    */

   public boolean removeNode(String fqn)
   {
      return cache.removeNode(fqn);
   }

   /**
    * Gets a node from a String representation of a Fqn
    *
    * @param fqn
    * @return
    */

   public Node getNode(String fqn)
   {
      return cache.getNode(fqn);
   }

   /**
    * Convenience method that allows for direct access to the data in a Node.
    *
    * @param fqn
    * @param key
    * @return
    */
   public V get(String fqn, K key)
   {
      return cache.get(fqn, key);
   }

   /**
    * Lifecycle method that initializes configuration state, the root node, etc.
    *
    * @throws CacheException
    */

   public void create() throws CacheException
   {
      cache.create();
   }

   /**
    * Lifecycle method that starts the cache loader, starts cache replication,
    * starts the region manager, etc., and (if configured) warms the cache using a
    * state transfer or cache loader preload.
    *
    * @throws CacheException
    */

   public void start() throws CacheException
   {
      cache.start();
   }

   /**
    * Lifecycle method that stops the cache, including replication, clustering, cache loading, notifications, etc.,
    * and clears all cache in-memory state.
    */

   public void stop()
   {
      cache.stop();
   }

   /**
    * Lifecycle method that destroys the cache and removes any interceptors/configuration elements.
    */

   public void destroy()
   {
      cache.destroy();
   }

   /**
    * Gets where the cache currently is its lifecycle transitions.
    * @return the CacheStatus. Will not return null.
    */

   public CacheStatus getCacheStatus()
   {
      return cache.getCacheStatus();
   }

   /**
    * The current invocation context for the current invocation and cache instance.
    * 
    * @return the current invocation context for the current invocation and cache instance
    */
   public InvocationContext getInvocationContext()
   {
      return cache.getInvocationContext();
   }

   /**
    * Sets the passed in InvocationContext as current.
    *
    * @param ctx
    */

   public void setInvocationContext(InvocationContext ctx)
   {
      cache.setInvocationContext(ctx);
   }

   /**
    * Returns the local address of this cache in a cluster, or null  if running in local mode.
    * 
    * @return Returns the local address of this cache in a cluster, or null  if running in local mode.
    */
   public Address getLocalAddress()
   {
      return cache.getLocalAddress();
   }

   /**
    * Returns a list of members in the cluster, or null  if running in local mode.
    * 
    * @return Returns a list of members in the cluster, or null  if running in local mode.
    */


   public List getMembers()
   {
      return cache.getMembers();
   }

   /**
    * Moves a part of the cache to a different subtree. Takes Strings for convenience.
    * 
    * @param nodeToMove
    * @param newParent
    * @throws NodeNotExistsException
    */
   public void move(String nodeToMove, String newParent) throws NodeNotExistsException
   {
      cache.move(nodeToMove, newParent);
   }

   /**
    * Returns the version of the cache as a string.
    * 
    * @return Returns the version of the cache as a string.
    */
   public String getVersion()
   {
      return cache.getVersion();
   }

   /**
    * Returns a set of attribute keys for the Fqn. Takes Strings for convenience.
    *
    * @param fqn
    * @return Returns a set of attribute keys for the Fqn.
    */

   public Set<K> getKeys(String fqn)
   {
      return cache.getKeys(fqn);
   }

   /**
    * Removes the keys and properties from a named node. Takes Strings for convenience.
    *
    * @param fqn
    */

   public void clearData(String fqn)
   {
      cache.clearData(fqn);
   }

   /**
    * Removes the keys and properties from a named node.
    * @param fqn
    */

   public void clearData(Fqn<?> fqn)
   {
      cache.clearData(fqn);
   }

   /**
    * Returns a set of attribute keys for the Fqn.
    *
    * @param fqn
    * @return
    */
   public Set getKeys(Fqn<?> fqn)
   {
      return cache.getKeys(fqn);
   }

   /**
    * Retrieves a defensively copied data map of the underlying node.
    *
    * @param fqn
    * @return a defensively copied data map of the underlying node.
    */
   public Map getData(Fqn<?> fqn)
   {
      return cache.getData(fqn);
   }

   /**
    * Moves a part of the cache to a different subtree.
    *
    * @param nodeToMove
    * @param newParent
    * @throws NodeNotExistsException
    */
   public void move(Fqn<?> nodeToMove, Fqn<?> newParent) throws NodeNotExistsException
   {
      cache.move(nodeToMove, newParent);
   }

   /**
    * Removes a region denoted by the Fqn passed in.
    *
    * @param fqn
    * @return True if the region did exist and was removed, false otherwise.
    */

   public boolean removeRegion(Fqn<?> fqn)
   {
      return cache.removeRegion(fqn);
   }

   /**
    * Retrieves a Region for a given Fqn.
    *
    * @param fqn
    * @param createIfAbsent
    * @return a MarshRegion. Null if none is found.
    */
   public Region getRegion(Fqn<?> fqn, boolean createIfAbsent)
   {
      return cache.getRegion(fqn, createIfAbsent);
   }

   /**
    * Eviction call that evicts the specified Node from memory.
    * 
    * @param fqn
    */

   public void evict(Fqn<?> fqn)
   {
      cache.evict(fqn);
   }

   /**
    * Eviction call that evicts the specified Node from memory.
    *
    * @param fqn
    * @param recursive
    */


   public void evict(Fqn<?> fqn, boolean recursive)
   {
      cache.evict(fqn, recursive);
   }

   /**
    * Convenience method that allows for direct access to the data in a Node.
    * 
    * @param fqn
    * @param key
    * @return
    */


   public V get(Fqn<?> fqn, K key)
   {
      return cache.get(fqn, key);
   }

   /**
    * A convenience method to retrieve a node directly from the cache.
    *
    * @param fqn
    * @return
    */

   public Node getNode(Fqn<?> fqn)
   {
      return cache.getNode(fqn);
   }

   /**
    * Removes a Node indicated by absolute Fqn.
    *
    * @param fqn
    * @return True if the node was removed, false if the node was not found.
    */
   public boolean removeNode(Fqn<?> fqn)
   {
      return cache.removeNode(fqn);
   }

   /**
    * Moves a part of the cache to a different subtree.
    *
    * @param fqn
    * @param key
    * @return
    */
   public V remove(Fqn<?> fqn, K key)
   {
      return cache.remove(fqn, key);
   }

   /**
    * Copies all of the mappings from the specified map to a Node.
    * 
    * @param fqn
    * @param data
    */


   public void put(Fqn<?> fqn, Map<K, V> data)
   {
      cache.put(fqn, data);
   }

   /**
    * Under special operating behavior, associates the value with the specified key for a node identified
    *  by the Fqn passed in.
    * @param fqn
    * @param key
    * @param value
    */

   public void putForExternalRead(Fqn<?> fqn, K key, V value)
   {
      cache.putForExternalRead(fqn, key, value);
   }

   /**
    * Associates the specified value with the specified key for a Node in this cache.
    *
    *
    * @param fqn
    * @param key
    * @param value
    * @return
    */


   public V put(Fqn<?> fqn, K key, V value)
   {
      return cache.put(fqn, key, value);
   }
}
