package org.jboss.cache;

import org.jboss.cache.marshall.NodeData;
import org.jboss.cache.optimistic.DataVersion;
import org.jboss.cache.transaction.GlobalTransaction;

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

/**
 * This class defines the functionality needed for node manipulation.
 *
 * @author Mircea.Markus@jboss.com
 * @see DataContainerImpl
 * @since 2.2
 */
public interface DataContainer
{
   /**
    * Retrieves the root node.
    *
    * @return the root node
    */
   NodeSPI getRoot();

   /**
    * Adds the specified Fqn to the list of Fqns to be considered "internal".
    *
    * @param fqn fqn to add to list
    */
   void registerInternalFqn(Fqn fqn);

   /**
    * Finds a node given a fully qualified name, directly off the interceptor chain.  In the event of an exception,
    * returns null.  Does not include invalid or deleted nodes.
    *
    * @param fqn Fully qualified name for the corresponding node.
    * @return Node referenced by the given Fqn, or null if the node cannot be found or if there is an exception.
    */
   NodeSPI peek(Fqn fqn);

   /**
    * Finds a node given a fully qualified name and DataVersion.  Does not include invalid or deleted nodes.  If the data
    * version passed in is null, then data version checking is skipped.  Data version checking is also skipped if optimistic
    * locking is not used.
    *
    * @param fqn     fqn to find
    * @param version version of the node to find
    * @return a node, if found, or null if not.
    */
   NodeSPI peekVersioned(Fqn fqn, DataVersion version);

   /**
    * Similar to {@link #peekVersioned(Fqn, org.jboss.cache.optimistic.DataVersion)} except that it throws a {@link org.jboss.cache.NodeNotExistsException}
    * if the node cannot be found.
    *
    * @param gtx            global transaction
    * @param fqn            fqn to find
    * @param includeInvalid if true, invalid nodes are considered as well.
    * @return the node
    */
   NodeSPI peekStrict(GlobalTransaction gtx, Fqn fqn, boolean includeInvalid);

   /**
    * Searches for a specific node, with a specific data version and, optionally, invalid nodes as well, but not
    * deleted nodes.  If the data version passed in is null, then data version checking is skipped.  Data version
    * checking is also skipped if optimistic locking is not used.
    *
    * @param fqn                 Fqn to find
    * @param version             version of the node to find
    * @param includeInvalidNodes if true, invalid nodes are considered
    * @return the node, if found, or null otherwise.
    */
   NodeSPI peekVersioned(Fqn fqn, DataVersion version, boolean includeInvalidNodes);

   /**
    * Same as calling <tt>peek(fqn, includeDeletedNodes, false)</tt>.
    *
    * @param fqn                 Fqn to find
    * @param includeDeletedNodes if true, deleted nodes are considered
    * @return the node, if found, or null otherwise.
    */
   NodeSPI peek(Fqn<?> fqn, boolean includeDeletedNodes);

   /**
    * Peeks for a specified node.  This involves a direct walk of the tree, starting at the root, until the required node
    * is found.  If the node is not found, a null is returned.
    *
    * @param fqn                 Fqn of the node to find
    * @param includeDeletedNodes if true, deleted nodes are also considered
    * @param includeInvalidNodes if true, invalid nodes are also considered
    * @return the node, if found, or null otherwise.
    */
   NodeSPI peek(Fqn<?> fqn, boolean includeDeletedNodes, boolean includeInvalidNodes);

   /**
    * Tests if an Fqn exists and is valid and not deleted.
    *
    * @param fqn the fqn representing the node to test
    * @return true if the node exists, false otherwise.
    */
   boolean exists(Fqn fqn);

   /**
    * Returns true if the Fqn exists, is valid and is not deleted, and the node has children.
    *
    * @param fqn the fqn to test
    * @return true if the Fqn exists, is valid and is not deleted, and the node has children.
    */
   boolean hasChildren(Fqn fqn);

   /**
    * Prepares a list of {@link NodeData} objects for a specified node and all its children.
    *
    * @param list List of NodeData objects, which will be added to.
    * @param node node to recursively add to the list
    * @return the same list passed in
    */
   List<NodeData> buildNodeData(List<NodeData> list, NodeSPI node);

   /**
    * Generates a list of nodes for eviction.  This filters out nodes that cannot be evicted, such as those which are
    * marked as resident.  See {@link NodeSPI#setResident(boolean)}.
    *
    * @param fqn       the node to consider for eviction
    * @param recursive if recursive, child nodes are also considered
    * @return a list of Fqns that can be considered for eviction
    */
   List<Fqn> getNodesForEviction(Fqn fqn, boolean recursive);

   /**
    * Returns a Set<Fqn> of Fqns of the topmost node of internal regions that
    * should not included in standard state transfers. Will include
    * {@link org.jboss.cache.buddyreplication.BuddyManager#BUDDY_BACKUP_SUBTREE} if buddy replication is
    * enabled.
    *
    * @return an unmodifiable Set<Fqn>.  Will not return <code>null</code>.
    */
   Set<Fqn> getInternalFqns();

   /**
    * Returns the number of read or write locks held across the entire cache.
    */
   int getNumberOfLocksHeld();

   /**
    * Returns an <em>approximation</em> of the total number of nodes in the
    * cache. Since this method doesn't acquire any locks, the number might be
    * incorrect, or the method might even throw a
    * ConcurrentModificationException
    */
   int getNumberOfNodes();

   /**
    * Returns an <em>approximation</em> of the total number of attributes in
    * this sub cache.
    */
   int getNumberOfAttributes(Fqn fqn);

   /**
    * Returns an <em>approximation</em> of the total number of attributes in
    * the cache. Since this method doesn't acquire any locks, the number might
    * be incorrect, or the method might even throw a
    * ConcurrentModificationException
    *
    * @return number of attribs
    */
   int getNumberOfAttributes();

   /**
    * Removes the actual node from the tree data structure.
    *
    * @param f               the Fqn of the node to remove
    * @param skipMarkerCheck if true, skips checking the boolean {@link org.jboss.cache.NodeSPI#isDeleted()} flag and deletes the node anyway.
    * @return Returns true if the node was found and removed, false if not.
    */
   boolean removeFromDataStructure(Fqn f, boolean skipMarkerCheck);

   /**
    * Evicts the given node. If recursive is set to true then all child nodes are recusively evicted.
    */
   void evict(Fqn fqn, boolean recursive);

   /**
    * <pre>
    * Following scenarios define how eviction works.
    * 1. If the given node is a leaf then it is entirely removed from the data structure. The node is marked as invalid.</li>
    * 2. If the given node is a leaf then only the data map is cleared.
    * 3. If the given node is the root node then the cildren nodes are evicted. For each child node 1. or 2. applies
    * </pre>
    *
    * @return true if the FQN is leaf and was removed; false if is an intermediate FQN and only contained data
    *         is droped.
    */
   boolean evict(Fqn fqn);

   /**
    * Traverses the tree to the given Fqn, creating nodes if needed.  Returns a list of nodes created, as well as a reference to the last node.
    * <p/>
    * E.g.,
    * <code>
    * Object[] results = createNode(myFqn);
    * results[0] // this is a List&lt;NodeSPI&gt; of nodes <i>created</i> in getting to the target node.
    * results[1] // is a NodeSPI reference to the target node, regardless of whether it was <i>created</i> or just <i>found</i>.
    * </code>
    *
    * @param fqn fqn to find
    * @return see above.
    */
   Object[] createNodes(Fqn fqn);
}
