/**
 *
 */
package org.jboss.cache;

import static org.jboss.cache.AbstractNode.NodeFlags.DELETED;
import static org.jboss.cache.AbstractNode.NodeFlags.RESIDENT;

import java.util.Map;

/**
 * Base class for {@link UnversionedNode}.
 *
 * @author manik
 */
public abstract class AbstractNode<K, V>// implements Node<K, V>
{
   protected Map<Object, Node<K, V>> children;
   protected Fqn<?> fqn;
   /**
    * Flags placed on the node.  Replaces older 'boolean' flags.
    */
   // NOTE: this is a lot more efficient than an EnumSet, expecially when initialising and copying.
   protected short flags = 0;

   /**
    * These flags were originally stored as booleans on the UnversionedNode class.  They have been replaced with an enum
    * and an EnumSet, which is much more space-efficient for very little cost in lookups.
    */
   public static enum NodeFlags
   {
      /**
       * All children are loaded from the cache loader if this flag is present.
       */
      CHILDREN_LOADED(0x1),
      /**
       * Data is loaded from the cache loader if this flag is present.
       */
      DATA_LOADED(0x2),
      /**
       * Node is write-locked when children are added or removed if this flag is enabled.
       */
      LOCK_FOR_CHILD_INSERT_REMOVE(0x4),
      /**
       * Node is valid if this flag is present.
       */
      VALID(0x8),
      /**
       * Node has been deleted.
       */
      DELETED(0x10),
      /**
       * NOde is resident and excluded from evictions
       */
      RESIDENT(0x20),
      /**
       * Specific to Optimistic Locking Workspace nodes - set if a node has been modified in a workspace.
       */
      MODIFIED_IN_WORKSPACE(0x40),
      /**
       * Specific to Optimistic Locking Workspace nodes - set if a node has been created in a workspace.
       */
      CREATED_IN_WORKSPACE(0x80),
      /**
       * Specific to Optimistic Locking Workspace nodes - set if a node has added or removed children in a workspace.
       */
      CHILDREN_MODIFIED_IN_WORKSPACE(0x100),
      /**
       * Specific to Optimistic Locking Workspace nodes - set if an implicit version is associated with this node.
       */
      VERSIONING_IMPLICIT(0x200),
      /**
       * Specific to Optimistic Locking Workspace nodes - set if a node has been resurrected in a workspace.
       */
      RESURRECTED_IN_WORKSPACE(0x400);

      protected final short mask;

      NodeFlags(int mask)
      {
         this.mask = (short) mask;
      }
   }

   /**
    * Tests whether a flag is set.
    *
    * @param flag flag to test
    * @return true if set, false otherwise.
    */
   protected final boolean isFlagSet(NodeFlags flag)
   {
      return (flags & flag.mask) != 0;
   }

   /**
    * Utility method for setting or unsetting a flag.  If status is true, the NodeFlag specified is added to the {@link #flags}
    * encoded short.  If status is false, the NodeFlag is removed from the encoded short.
    *
    * @param flag  flag to set or unset
    * @param value true to set the flag, false to unset the flag.
    */
   protected final void setFlag(NodeFlags flag, boolean value)
   {
      if (value)
         setFlag(flag);
      else
         unsetFlag(flag);
   }

   /**
    * Unility method that sets the value of the given flag to true.
    *
    * @param flag flag to set
    */
   protected final void setFlag(NodeFlags flag)
   {
      flags |= flag.mask;
   }

   /**
    * Utility method that sets the value of the flag to false.
    *
    * @param flag flag to unset
    */
   protected final void unsetFlag(NodeFlags flag)
   {
      flags &= ~flag.mask;
   }

   public boolean isDeleted()
   {
      return isFlagSet(DELETED);
   }

   public void markAsDeleted(boolean marker)
   {
      markAsDeleted(marker, false);
   }

   public void markAsDeleted(boolean marker, boolean recursive)
   {
      setFlag(DELETED, marker);

      if (recursive && children != null)
      {
         synchronized (this)
         {
            for (Node<?, ?> child : children.values())
            {
               ((NodeSPI) child).markAsDeleted(marker, true);
            }
         }
      }
   }

   public void setResident(boolean resident)
   {
      setFlag(RESIDENT, resident);
   }


   public boolean isResident()
   {
      return isFlagSet(RESIDENT);
   }

   @Override
   public boolean equals(Object another)
   {
      if (another instanceof AbstractNode)
      {
         AbstractNode<?, ?> anotherNode = (AbstractNode) another;
         return fqn == null && anotherNode.fqn == null || !(fqn == null || anotherNode.fqn == null) && fqn.equals(anotherNode.fqn);
      }
      return false;
   }

   @Override
   public int hashCode()
   {
      return fqn.hashCode();
   }
}
