package org.infinispan.test.concurrent;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.TimeoutException;

import org.infinispan.Cache;
import org.infinispan.commands.ReplicableCommand;
import org.infinispan.interceptors.AsyncInterceptor;
import org.infinispan.manager.EmbeddedCacheManager;


/**
 * Various helper methods for working with {@link StateSequencer}s.
 *
 * @author Dan Berindei
 * @since 7.0
 */
public class StateSequencerUtil {
   /**
    * Start decorating interceptor {@code interceptorClass} on {@code cache} to interact with a {@code StateSequencer}.
    */
   public static InterceptorSequencerAction advanceOnInterceptor(StateSequencer stateSequencer, Cache<?, ?> cache,
         Class<? extends AsyncInterceptor> interceptorClass, CommandMatcher matcher) {
      return new InterceptorSequencerAction(stateSequencer, cache, interceptorClass, matcher);
   }

   /**
    * Start decorating the {@code InboundInvocationHandler} on {@code cacheManager} to interact with a {@code StateSequencer}
    * when a {@code CacheRpcCommand} is received.
    */
   public static InboundRpcSequencerAction advanceOnInboundRpc(StateSequencer stateSequencer, Cache cache,
         CommandMatcher matcher) {
      return new InboundRpcSequencerAction(stateSequencer, cache, matcher);
   }

   /**
    * Start decorating the {@code RpcManager} on {@code cacheManager} to interact with a {@code StateSequencer} when a
    * command is sent.
    */
   public static OutboundRpcSequencerAction advanceOnOutboundRpc(StateSequencer stateSequencer, Cache<?, ?> cache,
         CommandMatcher matcher) {
      return new OutboundRpcSequencerAction(stateSequencer, cache, matcher);
   }

   /**
    * Start decorating the component {@code componentClass} on {@code cache} to interact with a {@code StateSequencer} when a
    * method is called.
    */
   public static CacheComponentSequencerAction advanceOnComponentMethod(StateSequencer stateSequencer, Cache<?, ?> cache, Class<?> componentClass,
         InvocationMatcher matcher) {
      return new CacheComponentSequencerAction(stateSequencer, cache, componentClass, matcher);
   }

   /**
    * Start decorating the component {@code componentClass} on {@code cacheManager} to interact with a {@code StateSequencer}
    * when a method is called.
    */
   public static <T> GlobalComponentSequencerAction advanceOnGlobalComponentMethod(StateSequencer stateSequencer,
         EmbeddedCacheManager cacheManager, Class<T> componentClass, InvocationMatcher matcher) {
      return new GlobalComponentSequencerAction(stateSequencer, cacheManager, componentClass, matcher);
   }

   /**
    * Start building a {@link CommandMatcher}.
    */
   public static CommandMatcherBuilder matchCommand(Class<? extends ReplicableCommand> commandClass) {
      return new CommandMatcherBuilder(commandClass);
   }

   /**
    * Start building a {@link InvocationMatcher}.
    */
   public static InvocationMatcherBuilder matchMethodCall(String methodName) {
      return new InvocationMatcherBuilder(methodName);
   }

   public static List<String> listCopy(List<String> statesUp) {
      return statesUp != null ? Collections.unmodifiableList(new LinkedList<String>(statesUp)) : null;
   }

   public static List<String> concat(String state1, String... additionalStates) {
      List<String> states = new ArrayList<String>();
      states.add(state1);
      if (additionalStates != null) {
         states.addAll(Arrays.asList(additionalStates));
      }
      return states;
   }

   /**
    * Advance to the every state in the {@code states} list, in the given order, but only if {@code condition} is true.
    * <p/>
    * Does nothing if {@code states} is {@code null} or empty.
    */
   public static void advanceMultiple(StateSequencer stateSequencer, boolean condition, List<String> states)
         throws TimeoutException, InterruptedException {
      if (condition && states != null) {
         for (String state : states) {
            stateSequencer.advance(state);
         }
      }
   }
}
