package org.jboss.cache.commands.write;

import org.jboss.cache.DataContainer;
import org.jboss.cache.Fqn;
import org.jboss.cache.InvocationContext;
import org.jboss.cache.NodeSPI;
import org.jboss.cache.commands.Visitor;
import org.jboss.cache.commands.read.AbstractDataCommand;
import org.jboss.cache.notifications.Notifier;

import java.util.List;

/**
 * Implements functionality defined by {@link org.jboss.cache.Cache#evict(org.jboss.cache.Fqn)}
 *
 * @author Mircea.Markus@jboss.com
 * @since 2.2
 */
public class EvictCommand extends AbstractDataCommand
{
   public static final int METHOD_ID = 8;
   public static final int VERSIONED_METHOD_ID = 9;

   private boolean recursive = false;

   protected Notifier notifier;

   public EvictCommand(Fqn fqn)
   {
      this.fqn = fqn;
   }

   public EvictCommand()
   {
   }

   public void initialize(Notifier notifier, DataContainer dataContainer)
   {
      super.initialize(dataContainer);
      this.notifier = notifier;
   }

   /**
    * Evicts a node.
    * <p/>
    * See {@link org.jboss.cache.interceptors.EvictionInterceptor#visitEvictFqnCommand(org.jboss.cache.InvocationContext, EvictCommand)}
    * which is where the return value is used
    *
    * @return true if the node was removed from the tree or if it is resident.  Returns false if the node still exists; i.e. was only data removed because it still has children.
    */
   public Object perform(InvocationContext ctx)
   {
      NodeSPI node = dataContainer.peek(fqn, false, true);
      if (node == null)
      {
         return false;
      }
      else if (node.isResident())
      {
         return true;
      }
      else if (recursive)
      {
         List<Fqn> nodesToEvict = dataContainer.getNodesForEviction(fqn, true);
         for (Fqn aFqn : nodesToEvict)
         {
            evictNode(aFqn, ctx);
         }
         return !nodesToEvict.isEmpty();
      }
      else
      {
         return evictNode(fqn, ctx);
      }
   }

   boolean evictNode(Fqn fqn, InvocationContext ctx)
   {
      notifier.notifyNodeEvicted(fqn, true, ctx);
      try
      {
         return dataContainer.evict(fqn);
      }
      finally
      {
         notifier.notifyNodeEvicted(fqn, false, ctx);
      }
   }

   public Object acceptVisitor(InvocationContext ctx, Visitor visitor) throws Throwable
   {
      return visitor.visitEvictFqnCommand(ctx, this);
   }

   public int getCommandId()
   {
      return METHOD_ID;
   }

   public boolean isRecursive()
   {
      return recursive;
   }

   public void setRecursive(boolean recursive)
   {
      this.recursive = recursive;
   }

   @Override
   public Object[] getParameters()
   {
      throw new UnsupportedOperationException(getClass().getSimpleName() + " is not meant to be marshalled and replicated!");
   }

   @Override
   public void setParameters(int commandId, Object[] args)
   {
      throw new UnsupportedOperationException(getClass().getSimpleName() + " is not meant to be marshalled and replicated!");
   }


   @Override
   public String toString()
   {
      return "EvictCommand{" +
            "fqn=" + fqn +
            ", recursive=" + recursive +
            "}";
   }

   public void rollback()
   {
      // this is a no-op.
   }
}
