package org.infinispan.commons.api.functional;

import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentMap;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;

import org.infinispan.commons.api.functional.EntryView.ReadEntryView;
import org.infinispan.commons.api.functional.EntryView.ReadWriteEntryView;
import org.infinispan.commons.api.functional.EntryView.WriteEntryView;
import org.infinispan.commons.api.functional.Listeners.ReadWriteListeners;
import org.infinispan.commons.api.functional.Listeners.WriteListeners;
import org.infinispan.commons.marshall.MarshallableFunctions;
import org.infinispan.commons.util.Experimental;

/**
 * Top level functional map interface offering common functionality for the
 * read-only, read-write, and write-only operations that can be run against a
 * functional map asynchronously.
 *
 * <p>Lambdas passed in as parameters to functional map methods define the
 * type of operation that is executed, but since lambdas are transparent to
 * the internal logic, it was decided to separate the API into three types
 * of operation: read-only, write-only, and read-write. This separation helps
 * the user understand the group of functions and their possibilities.
 *
 * <p>This conscious decision to separate read-only, write-only and
 * read-write interfaces helps type safety. So, if a user gets a read-only
 * map, it can't write to it by mistake since no such APIs are exposed.
 * The same happens with write-only maps, the user can only write and cannot
 * make the mistake of reading from the entry view because read operations
 * are not exposed.
 *
 * <p>Lambdas passed in to read-write and write-only operations, when
 * running in a cluster, must be marshallable. One option to do so is to
 * mark them as being {@link java.io.Serializable} but this is expensive
 * in terms of payload size. Alternatively, you can provide an Infinispan
 * {@link org.infinispan.commons.marshall.Externalizer} for it which
 * drastically reduces the payload size. Marshallable lambdas for some of
 * the most popular lambda functions used by {@link ConcurrentMap} and
 * javax.cache.Cache are available via the
 * {@link MarshallableFunctions} helper class.
 *
 * <p>Being an asynchronous API, all methods that return a single result,
 * return a {@link CompletableFuture} which wraps the result. To avoid
 * blocking, it offers the possibility to receive callbacks when the
 * {@link CompletableFuture} has completed, or it can be chained or composes
 * with other {@link CompletableFuture} instances.
 *
 * <p>For those operations that return multiple results, the API returns
 * instances of a {@link Traversable} interface which offers a lazy pull­style
 * API for working with multiple results. Although push­style interfaces for
 * handling multiple results, such as RxJava, are fully asynchronous, they're
 * harder to use from a user’s perspective. {@link Traversable},​ being a lazy
 * pull­style API, can still be asynchronous underneath since the user can
 * decide to work on the {@link Traversable} at a later stage, and the
 * implementation itself can decide when to compute those results.
 *
 * @since 8.0
 */
@Experimental
public interface FunctionalMap<K, V> extends AutoCloseable {

   /**
    * Tweak functional map executions providing {@link Param} instances.
    */
   FunctionalMap<K, V> withParams(Param<?>... ps);

   /**
    * Functional map's name.
    */
   String getName();

   /**
    * Functional map's status.
    */
   Status getStatus();

   /**
    * Exposes read-only operations that can be executed against the functional map.
    * The information that can be read per entry in the functional map is
    * exposed by {@link ReadEntryView}.
    *
    * <p>Read-only operations have the advantage that no locks are acquired
    * for the duration of the operation and so it makes sense to have them
    * a top-level interface dedicated to them.
    *
    * <p>Browsing methods that provide a read-only view of the cached data
    * are available via {@link #keys()} and {@link #entries()}.
    * Having {@link #keys()} makes sense since that way keys can be traversed
    * without having to bring values. Having {@link #entries()} makes sense
    * since it allows traversing both keys, values and any meta parameters
    * associated with them, but this is no extra cost to exposing just values
    * since keys are the main index and hence will always be available.
    * Hence, adding a method to only browse values offers nothing extra to
    * the API.
    *
    * @since 8.0
    */
   @Experimental
   interface ReadOnlyMap<K, V> extends FunctionalMap<K, V> {
      /**
       * Tweak read-only functional map executions providing {@link Param} instances.
       */
      ReadOnlyMap<K, V> withParams(Param<?>... ps);

      /**
       * Evaluate a read-only function on the value associated with the key
       * and return a {@link CompletableFuture} with the return type of the function.
       * If the user is not sure if the key is present, {@link ReadEntryView#find()}
       * can be used to find out for sure. Typically, function implementations
       * would return value or {@link MetaParam} information from the cache
       * entry in the functional map.
       *
       * <p>By returning {@link CompletableFuture} instead of the function's
       * return type directly, the method hints at the possibility that to
       * execute the function might require to go remote to retrieve data in
       * persistent store or another clustered node.
       *
       * <p>This method can be used to implement read-only single-key based
       * operations in {@link ConcurrentMap} and javax.cache.Cache
       * such as:
       *
       * <ul>
       *    <li>{@link ConcurrentMap#get(Object)}</li>
       *    <li>{@link ConcurrentMap#containsKey(Object)}</li>
       *    <li>{@code javax.cache.Cache#get(Object)}</li>
       *    <li>{@code javax.cache.Cache#containsKey(Object)}</li>
       * </ul>
       *
       * @param key the key associated with the {@link ReadEntryView} to be
       *            passed to the function.
       * @param f function that takes a {@link ReadEntryView} associated with
       *          the key, and returns a value.
       * @param <R> function return type
       * @return a {@link CompletableFuture} which will be completed with the
       *         returned value from the function
       */
      <R> CompletableFuture<R> eval(K key, Function<ReadEntryView<K, V>, R> f);

      /**
       * Evaluate a read-only function on a key and potential value associated in
       * the functional map, for each of the keys in the set passed in, and
       * returns an {@link Traversable} to work on each computed function's result.
       *
       * <p>The function passed in will be executed for as many keys
       * present in keys collection set. Similar to {@link #eval(Object, Function)},
       * if the user is not sure whether a particular key is present,
       * {@link ReadEntryView#find()} can be used to find out for sure.
       *
       * <p>This method can be used to implement operations such as
       * {@code javax.cache.Cache#getAll(Set)}.
       *
       * DESIGN RATIONALE:
       * <ul>
       *    <li>It makes sense to expose global operation like this instead of
       *    forcing users to iterate over the keys to lookup and call get
       *    individually since Infinispan can do things more efficiently.
       *    </li>
       * </ul>
       *
       * @param keys the keys associated with each of the {@link ReadEntryView}
       *             passed in the function callbacks
       * @param f function that takes a {@link ReadEntryView} associated with
       *          the key, and returns a value. It'll be invoked once for each key
       *          passed in
       * @param <R> function return type
       * @return a sequential {@link Traversable} that can be navigated to
       *         retrieve each function return value
       */
      <R> Traversable<R> evalMany(Set<? extends K> keys, Function<ReadEntryView<K, V>, R> f);

      /**
       * Provides a {@link Traversable} that allows clients to navigate all cached keys.
       *
       * <p>This method can be used to implement operations such as:
       * <ul>
       *    <li>{@link ConcurrentMap#size()}</li>
       *    <li>{@link ConcurrentMap#keySet()}</li>
       *    <li>{@link ConcurrentMap#isEmpty()}</li>
       * </ul>
       *
       * @return a sequential {@link Traversable} to navigate each cached key
       */
      Traversable<K> keys();

      /**
       * Provides a {@link Traversable} that allows clients to navigate all cached entries.
       *
       * <p>This method can be used to implement operations such as:
       * <ul>
       *    <li>{@link ConcurrentMap#containsValue(Object)}</li>
       *    <li>{@link ConcurrentMap#values()}</li>
       *    <li>{@link ConcurrentMap#entrySet()}</li>
       *    <li>{@code javax.cache.Cache#iterator()}</li>
       * </ul>
       *
       * @return a sequential {@link Traversable} to navigate each cached entry
       */
      Traversable<ReadEntryView<K, V>> entries();
   }

   /**
    * Exposes write-only operations that can be executed against the functional map.
    * The write operations that can be applied per entry are exposed by
    * {@link WriteEntryView}.
    *
    * <p>Write-only operations require locks to be acquired but crucially
    * they do not require reading previous value or metadata parameter
    * information associated with the cached entry, which sometimes can be
    * expensive since they involve talking to a remote node in the cluster
    * or the persistence layer So, exposing write-only operations makes it
    * easy to take advantage of this important optimisation.
    *
    * <p>Method parameters for write-only operations, including lambdas,
    * must be marshallable when running in a cluster.
    *
    * @since 8.0
    */
   @Experimental
   interface WriteOnlyMap<K, V> extends FunctionalMap<K, V> {
      /**
       * Tweak write-only functional map executions providing {@link Param} instances.
       */
      WriteOnlyMap<K, V> withParams(Param<?>... ps);

      /**
       * Evaluate a write-only {@link BiConsumer} operation, with a value
       * passed in and a {@link WriteEntryView} of the value associated with
       * the key, and return a {@link CompletableFuture} which will be
       * completed when the operation completes.
       *
       * <p>Since this is a write-only operation, no entry attributes can be
       * queried, hence the only reasonable thing can be returned is Void.
       *
       * <p>This method can be used to implement single-key write-only operations
       * which do not need to query previous value, such as
       * {@code javax.cache.Cache#put(Object, Object)}
       *
       * <p>This operation is very similar to {@link #eval(Object, Consumer)}
       * and in fact, the functionality provided by this function could indeed
       * be implemented with {@link #eval(Object, Consumer)}, but there's a
       * crucial difference. If you want to store a value and reference the
       * value to be stored from the passed in operation,
       * {@link #eval(Object, Consumer)} needs to capture that value.
       * Capturing means that each time the operation is called, a new lambda
       * needs to be instantiated. By offering a {@link BiConsumer} that
       * takes user provided value as first parameter, the operation does not
       * capture any external objects when implementing simple operations
       * such as {@code javax.cache.Cache#put(Object, Object)}, and hence, the
       * {@link BiConsumer} could be cached and reused each time it's invoked.
       *
       * @param key the key associated with the {@link WriteEntryView} to be
       *            passed to the operation
       * @param value value to write, passed in as first parameter to the
       *              {@link BiConsumer} operation.
       * @param f operation that takes a user defined value, and a
       *          {@link WriteEntryView} associated with the key, and writes
       *          to the {@link WriteEntryView} passed in without returning anything
       * @return a {@link CompletableFuture} which will be completed when the
       *         operation completes
       */
      CompletableFuture<Void> eval(K key, V value, BiConsumer<V, WriteEntryView<V>> f);

      /**
       * Evaluate a write-only {@link Consumer} operation with a
       * {@link WriteEntryView} of the value associated with the key,
       * and return a {@link CompletableFuture} which will be
       * completed with the object returned by the operation.
       *
       * <p>Since this is a write-only operation, no entry attributes can be
       * queried, hence the only reasonable thing can be returned is Void.
       *
       * <p>This operation can be used to either remove a cached entry,
       * or to write a constant value along with optional metadata parameters.
       *
       * @param key the key associated with the {@link WriteEntryView} to be
       *            passed to the operation
       * @param f operation that takes a {@link WriteEntryView} associated with
       *          the key and writes to the it without returning anything
       * @return a {@link CompletableFuture} which will be completed when the
       *         operation completes
       */
      CompletableFuture<Void> eval(K key, Consumer<WriteEntryView<V>> f);

      /**
       * Evaluate a write-only {@link BiConsumer} operation, with a value
       * passed in and a {@link WriteEntryView} of the value associated with
       * the key, for each of the keys in the set passed in, and returns a
       * {@link CompletableFuture} that will be completed when the write-only
       * operation has been executed against all the entries.
       *
       * This method can be used to implement operations such as:
       *
       * <ul>
       *    <li>{@link ConcurrentMap#putAll(Map)}</li>
       *    <li>{@code javax.cache.Cache#putAll(Map)}</li>
       * </ul>
       *
       * <p>These kind of operations are preferred to traditional end user
       * iterations because the internal logic can often iterate more
       * efficiently since it knows more about the system.
       *
       * <p>Since this is a write-only operation, no entry attributes can be
       * queried, hence the only reasonable thing can be returned is Void.
       *
       * @param entries the key/value pairs associated with each of the
       *             {@link WriteEntryView} passed in the function callbacks
       * @param f operation that consumes a value associated with a key in the
       *          entries collection and the {@link WriteEntryView} associated
       *          with that key in the cache
       * @return a {@link CompletableFuture} which will be completed when
       *         the {@link BiConsumer} operation  has been executed against
       *         all entries
       */
      CompletableFuture<Void> evalMany(Map<? extends K, ? extends V> entries, BiConsumer<V, WriteEntryView<V>> f);

      /**
       * Evaluate a write-only {@link Consumer} operation with the
       * {@link WriteEntryView} of the value associated with the key, for each
       * of the keys in the set passed in, and returns a
       * {@link CompletableFuture} that will be completed when the write-only
       * operation has been executed against all the entries.
       *
       * <p>This method can be used to implement operations such as
       * {@code javax.cache.Cache#removeAll(Set)}.
       *
       * <p>These kind of operations are preferred to traditional end user
       * iterations because the internal logic can often iterate more
       * efficiently since it knows more about the system.
       *
       * <p>Since this is a write-only operation, no entry attributes can be
       * queried, hence the only reasonable thing can be returned is Void.
       *
       * @param keys the keys associated with each of the {@link WriteEntryView}
       *             passed in the function callbacks
       * @param f operation that the {@link WriteEntryView} associated with
       *          one of the keys passed in
       * @return a {@link CompletableFuture} which will be completed when
       *         the {@link Consumer} operation has been executed against all
       *         entries
       */
      CompletableFuture<Void> evalMany(Set<? extends K> keys, Consumer<WriteEntryView<V>> f);

      /**
       * Evaluate a write-only {@link Consumer} operation with the
       * {@link WriteEntryView} of the value associated with the key, for all
       * existing keys in functional map, and returns a {@link CompletableFuture}
       * that will be completed when the write-only operation has been executed
       * against all the entries.
       *
       * <p>This method can be used to implement operations such as
       * {@code javax.cache.Cache#removeAll()}.
       *
       * @param f operation that the {@link WriteEntryView} associated with
       *          one of the keys passed in
       * @return a {@link CompletableFuture} which will be completed when
       *         the {@link Consumer} operation has been executed against all
       *         entries
       */
      CompletableFuture<Void> evalAll(Consumer<WriteEntryView<V>> f);

      /**
       * Truncate the contents of the cache, returning a {@link CompletableFuture}
       * that will be completed when the truncate process completes.
       *
       * This method can be used to implement:
       *
       * <ul>
       *    <li>{@link ConcurrentMap#clear()}</li>
       *    <li>{@code javax.cache.Cache#clear()}</li>
       * </ul>
       *
       * @return a {@link CompletableFuture} that completes when the truncat
       *         has finished
       */
      CompletableFuture<Void> truncate();

      /**
       * Allows to write-only listeners to be registered.
       */
      WriteListeners<K, V> listeners();
   }

   /**
    * Exposes read-write operations that can be executed against the functional map.
    * The read-write operations that can be applied per entry are exposed by
    * {@link ReadWriteEntryView}.
    *
    * <p>Read-write operations offer the possibility of writing values or
    * metadata parameters, and returning previously stored information.
    * Read-write operations are also crucial for implementing conditional,
    * compare-and-swap (CAS) like operations.
    *
    * <p>Locks are acquired before executing the read-write lambda.
    *
    * <p>Method parameters for read-write operations, including lambdas,
    * must be marshallable when running in a cluster.
    *
    * @since 8.0
    */
   @Experimental
   interface ReadWriteMap<K, V> extends FunctionalMap<K, V> {
      /**
       * Tweak read-write functional map executions providing {@link Param} instances.
       */
      ReadWriteMap<K, V> withParams(Param<?>... ps);

      /**
       * Evaluate a read-write function on the value and metadata associated
       * with the key and return a {@link CompletableFuture} with the return
       * type of the function. If the user is not sure if the key is present,
       * {@link ReadWriteEntryView#find()} can be used to find out for sure.
       *
       * This method can be used to implement single-key read-write operations
       * in {@link ConcurrentMap} and {@code javax.cache.Cache} that do not
       * depend on value information given by the user such as:
       *
       * <ul>
       *    <li>{@link ConcurrentMap#remove(Object)}</li>
       *    <li>{@code javax.cache.Cache#remove(Object)}</li>
       *    <li>{@code javax.cache.Cache#getAndRemove(Object)}</li>
       *    <li>{@code javax.cache.Cache#invoke(Object, EntryProcessor, Object...)}</li>
       * </ul>
       *
       * @param key the key associated with the {@link ReadWriteEntryView} to be
       *            passed to the function.
       * @param f function that takes a {@link ReadWriteEntryView} associated with
       *          the key, and returns a value.
       * @param <R> function return type
       * @return a {@link CompletableFuture} which will be completed with the
       *         returned value from the function
       */
      <R> CompletableFuture<R> eval(K key, Function<ReadWriteEntryView<K, V>, R> f);

      /**
       * Evaluate a read-write function, with a value passed in and a
       * {@link WriteEntryView} of the value associated with the key, and
       * return a {@link CompletableFuture} which will be completed with the
       * returned value by the function.
       *
       * <p>This method provides the the capability to both update the value and
       * metadata associated with that key, and return previous value or metadata.
       *
       * <p>This method can be used to implement the vast majority of single-key
       * read-write operations in {@link ConcurrentMap} and {@code javax.cache.Cache}
       * such as:
       *
       * <ul>
       * <li>{@link ConcurrentMap#put(Object, Object)}</li>
       * <li>{@link ConcurrentMap#putIfAbsent(Object, Object)}</li>
       * <li>{@link ConcurrentMap#replace(Object, Object)}</li>
       * <li>{@link ConcurrentMap#replace(Object, Object, Object)}</li>
       * <li>{@link ConcurrentMap#remove(Object, Object)}</li>
       * <li>{@code javax.cache.Cache#getAndPut(Object, Object)}</li>
       * <li>{@code javax.cache.Cache#putIfAbsent(Object, Object)}</li>
       * <li>{@code javax.cache.Cache#remove(Object, Object)}</li>
       * <li>{@code javax.cache.Cache#replace(Object, Object, Object)}</li>
       * <li>{@code javax.cache.Cache#replace(Object, Object)}</li>
       * <li>{@code javax.cache.Cache#getAndReplace(Object, Object)}</li>
       * </ul>
       *
       * <p> The functionality provided by this function could indeed be
       * implemented with {@link #eval(Object, Function)}, but there's a
       * crucial difference. If you want to store a value and reference the
       * value to be stored from the passed in operation,
       * {@link #eval(Object, Function)} needs to capture that value.
       * Capturing means that each time the operation is called, a new lambda
       * needs to be instantiated. By offering a {@link BiFunction} that
       * takes user provided value as first parameter, the operation does
       * not capture any external objects when implementing
       * simple operations such as {@code javax.cache.Cache#getAndPut(Object, Object)},
       * and hence, the {@link BiFunction} could be cached and reused each
       * time it's invoked.
       *
       * @param key the key associated with the {@link ReadWriteEntryView} to be
       *            passed to the operation
       * @param value value to write, passed in as first parameter to the {@link BiFunction}.
       * @param f operation that takes a user defined value, and a
       *          {@link ReadWriteEntryView} associated with the key, and writes
       *          to the {@link ReadWriteEntryView} passed in, possibly
       *          returning previously stored value or metadata information
       * @param <R> type of the function's return
       * @return a {@link CompletableFuture} which will be completed with the
       *         returned value from the function
       */
      <R> CompletableFuture<R> eval(K key, V value, BiFunction<V, ReadWriteEntryView<K, V>, R> f);

      /**
       * Evaluate a read-write {@link BiFunction}, with a value passed in and
       * a {@link ReadWriteEntryView} of the value associated with
       * the key, for each of the keys in the set passed in, and
       * returns an {@link Traversable} to navigate each of the
       * {@link BiFunction} invocation returns.
       *
       * <p>This method can be used to implement operations that store a set of
       * keys and return previous values or metadata parameters.
       *
       * <p>These kind of operations are preferred to traditional end user
       * iterations because the internal logic can often iterate more
       * efficiently since it knows more about the system.
       *
       * @param entries the key/value pairs associated with each of the
       *             {@link ReadWriteEntryView} passed in the function callbacks
       * @param f function that takes in a value associated with a key in the
       *          entries collection and the {@link ReadWriteEntryView} associated
       *          with that key in the cache
       * @return a {@link Traversable} to navigate each {@link BiFunction} return
       */
      <R> Traversable<R> evalMany(Map<? extends K, ? extends V> entries, BiFunction<V, ReadWriteEntryView<K, V>, R> f);

      /**
       * Evaluate a read-write {@link Function} operation with the
       * {@link ReadWriteEntryView} of the value associated with the key, for each
       * of the keys in the set passed in, and returns a {@link Traversable}
       * to navigate each of the {@link Function} invocation returns.
       *
       * <p>This method can be used to implement operations such as
       * {@code javax.cache.Cache#invokeAll(Set, EntryProcessor, Object...)},
       * or a remove a set of keys returning previous values or metadata
       * parameters.
       *
       * @param keys the keys associated with each of the {@link ReadWriteEntryView}
       *             passed in the function callbacks
       * @param f function that the {@link ReadWriteEntryView} associated with
       *          one of the keys passed in, and returns a value
       * @return a {@link Traversable} to navigate each {@link Function} return
       */
      <R> Traversable<R> evalMany(Set<? extends K> keys, Function<ReadWriteEntryView<K, V>, R> f);

      /**
       * Evaluate a read-write {@link Function} operation with the
       * {@link ReadWriteEntryView} of the value associated with the key, for all
       * existing keys, and returns a {@link Traversable} to navigate each of
       * the {@link Function} invocation returns.
       *
       * <p>This method can be used to an operation that removes all cached
       * entries individually, and returns previous value and/or metadata
       * parameters.
       *
       * @return a {@link Traversable} to navigate each {@link Function} return
       */
      <R> Traversable<R> evalAll(Function<ReadWriteEntryView<K, V>, R> f);

      /**
       * Allows to read-write listeners to be registered.
       */
      ReadWriteListeners<K, V> listeners();
   }

}
