/*
 * JBoss, Home of Professional Open Source
 *
 * Distributable under LGPL license.
 * See terms of license at gnu.org.
 */
package org.jboss.cache.loader;

import org.jboss.cache.TreeCache;
import org.jboss.cache.Fqn;

import java.util.*;

/**
 * Dummy cache loader that captures the number of times each method is called.
 *
 * @author <a href="mailto:manik@jboss.org">Manik Surtani (manik@jboss.org)</a>
 */
public class DummyCacheLoader implements CacheLoader
{
    private int getChildrenNamesCount = 0, getCount = 0, putCount = 0, existsCount = 0, removeCount = 0;
    private Map m_transactions = new HashMap();

    public int getGetChildrenNamesCount()
    {
        return getChildrenNamesCount;
    }

    public int getGetCount()
    {
        return getCount;
    }

    public int getPutCount()
    {
        return putCount;
    }

    public int getExistsCount()
    {
        return existsCount;
    }

    public int getRemoveCount()
    {
        return removeCount;
    }


    /**
     * Sets the configuration. Will be called before {@link #create()} and {@link #start()}
     *
     * @param url A list of properties, defined in the XML file
     */
    public void setConfig(Properties url)
    {
    }

    /**
     * This method allows the CacheLoader to set the TreeCache, therefore allowing the CacheLoader to invoke
     * methods of the TreeCache. It can also use the TreeCache to fetch configuration information. Alternatively,
     * the CacheLoader could maintain its own configuration<br/>
     * This method will be called directly after the CacheLoader instance has been created
     *
     * @param c The cache on which this loader works
     */
    public void setCache(TreeCache c)
    {
    }

    /**
     * Returns a list of children names, all names are <em>relative</em>. Returns null if the parent node is not found.
     * The returned set must not be modified, e.g. use Collections.unmodifiableSet(s) to return the result
     *
     * @param fqn The FQN of the parent
     * @return Set<String>. A list of children. Returns null if no children nodes are present, or the parent is
     *         not present
     */
    public Set getChildrenNames(Fqn fqn) throws Exception
    {
        getChildrenNamesCount++;
        return null;
    }

    /**
     * Returns the value for a given key. Returns null if the node doesn't exist, or the value is not bound
     *
     * @param name
     * @return
     * @throws Exception
     */
    public Object get(Fqn name, Object key) throws Exception
    {
        getCount++;
        return null;
    }

    /**
     * Returns all keys and values from the persistent store, given a fully qualified name
     *
     * @param name
     * @return Map<Object,Object> of keys and values for the given node. Returns null if the node was not found, or
     *         if the node has no attributes
     * @throws Exception
     */
    public Map get(Fqn name) throws Exception
    {
        getCount++;
        return null;
    }

    /**
     * Checks whether the CacheLoader has a node with Fqn
     *
     * @param name
     * @return True if node exists, false otherwise
     */
    public boolean exists(Fqn name) throws Exception
    {
        existsCount++;
        return false;
    }

    /**
     * Inserts key and value into the attributes hashmap of the given node. If the node does not exist, all
     * parent nodes from the root down are created automatically. Returns the old value
     */
    public Object put(Fqn name, Object key, Object value) throws Exception
    {
        putCount++;
        return null;
    }

    /**
     * Inserts all elements of attributes into the attributes hashmap of the given node, overwriting existing
     * attributes, but not clearing the existing hashmap before insertion (making it a union of existing and
     * new attributes)
     * If the node does not exist, all parent nodes from the root down are created automatically
     *
     * @param name       The fully qualified name of the node
     * @param attributes A Map of attributes. Can be null
     */
    public void put(Fqn name, Map attributes) throws Exception
    {
        putCount++;
    }

    /**
     * Inserts all modifications to the backend store. Overwrite whatever is already in
     * the datastore.
     *
     * @param modifications A List<Modification> of modifications
     * @throws Exception
     */
    public void put(List modifications) throws Exception
    {
        putCount++;
    }

    /**
     * Removes the given key and value from the attributes of the given node. No-op if node doesn't exist
     */
    public Object remove(Fqn name, Object key) throws Exception
    {
        removeCount++;
        return null;
    }

    /**
     * Removes the given node. If the node is the root of a subtree, this will recursively remove all subnodes,
     * depth-first
     */
    public void remove(Fqn name) throws Exception
    {
        removeCount++;
    }

    /**
     * Removes all attributes from a given node, but doesn't delete the node itself
     *
     * @param name
     * @throws Exception
     */
    public void removeData(Fqn name) throws Exception
    {
        removeCount++;
    }

    /**
     * Prepare the modifications. For example, for a DB-based CacheLoader:
     * <ol>
     * <li>Create a local (JDBC) transaction
     * <li>Associate the local transaction with <code>tx</code> (tx is the key)
     * <li>Execute the coresponding SQL statements against the DB (statements derived from modifications)
     * </ol>
     * For non-transactional CacheLoader (e.g. file-based), this could be a null operation
     *
     * @param tx            The transaction, just used as a hashmap key
     * @param modifications List<Modification>, a list of all modifications within the given transaction
     * @param one_phase     Persist immediately and (for example) commit the local JDBC transaction as well. When true,
     *                      we won't get a {@link #commit(Object)} or {@link #rollback(Object)} method call later
     * @throws Exception
     */
    public void prepare(Object tx, List modifications, boolean one_phase) throws Exception
    {
        if (one_phase) {
            put(modifications);
        } else {
            m_transactions.put(tx, modifications);
        }
    }

    /**
     * Commit the transaction. A DB-based CacheLoader would look up the local JDBC transaction asociated
     * with <code>tx</code> and commit that transaction<br/>
     * Non-transactional CacheLoaders could simply write the data that was previously saved transiently under the
     * given <code>tx</code> key, to (for example) a file system (note this only holds if the previous prepare() did
     * not define one_phase=true
     *
     * @param tx
     */
    public void commit(Object tx) throws Exception
    {
        List modifications = (List) m_transactions.get(tx);
        if (modifications == null) {
            return;
        }
        put(modifications);
        m_transactions.remove(tx);
    }

    /**
     * Roll the transaction back. A DB-based CacheLoader would look up the local JDBC transaction asociated
     * with <code>tx</code> and roll back that transaction
     *
     * @param tx
     */
    public void rollback(Object tx)
    {
         List modifications = (List) m_transactions.get(tx);
        if (modifications == null) {
            return;
        }
        m_transactions.remove(tx);
    }

    /**
     * Fetch the entire state for this cache from secondary storage (disk, DB) and return it as a byte buffer.
     * This is for initialization of a new cache from a remote cache. The new cache would then call
     * storeEntireState().<br/>
     * todo: define binary format for exchanging state
     */
    public byte[] loadEntireState() throws Exception
    {
        return new byte[0];
    }

    /**
     * Store the given state in secondary storage. Overwrite whatever is currently in seconday storage.
     */
    public void storeEntireState(byte[] state) throws Exception
    {
    }

    public void create() throws Exception
    {
    }

    public void start() throws Exception
    {
    }

    public void stop()
    {
    }

    public void destroy()
    {
        getChildrenNamesCount = 0;
        getCount = 0;
        putCount = 0;
        existsCount = 0;
        removeCount = 0;
    }
}