package com.linkedin.parseq.batching;

import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;

import com.linkedin.parseq.batching.BatchImpl.BatchEntry;
import com.linkedin.parseq.promise.Promise;
import com.linkedin.parseq.promise.PromiseResolvedException;
import com.linkedin.parseq.promise.SettablePromise;

/**
 * Batch represents a collection of keys related to each other in such a way that
 * it is more efficient to compute values for those keys in bulk than computing value
 * for each key individually.
 * This class contains methods helpful in implementing bulk operation that completes
 * Promises associated with keys.
 *
 * @author Jaroslaw Odzga (jodzga@linkedin.com)
 *
 * @param <K> Type of a Key
 * @param <T> Type of a Value
 */
public interface Batch<K, T> {

  /**
   * Performs the given action for each element of the batch
   * until all elements have been processed or the action throws an
   * exception. Order in which elements are processed is unspecified.
   * Exceptions thrown by the action are relayed to the caller.
   * @param consumer action to be performed in each element of the batch
   */
  void foreach(BiConsumer<K, SettablePromise<T>> consumer);

  /**
   * Returns set of keys belonging to this batch.
   * @return set of keys belonging to this batch
   */
  Set<K> keys();

  /**
   * Returns number of keys belonging to this batch.
   * @return number of keys belonging to this batch.
   */
  int keySize();

  /**
   * Returns size of this batch. This number is not necessarily equal to the number of keys belonging to this batch.
   * This number might be different than number of keys if {@link BatchingStrategy#keySize(Object, Object)} is
   * defined.
   * @return size of this batch.
   */
  int batchSize();

  /**
   * Completes a {@link Promise} associated with given key with
   * a value.
   * Throws PromiseResolvedException if Promise associated with given key has already been resolved.
   * @param key key that identifies a Promise to be completed
   * @param value value to complete Promise with
   * @throws PromiseResolvedException if Promise associated with given key has already been resolved
   */
  void done(K key, T value) throws PromiseResolvedException;


  /**
   * Fails a {@link Promise} associated with given key with
   * an error.
   * Throws PromiseResolvedException if Promise associated with given key has already been resolved.
   * @param key key that identifies a Promise to be completed
   * @param error error to fail Promise with
   * @throws PromiseResolvedException if Promise associated with given key has already been resolved
   */
  void fail(K key, Throwable error) throws PromiseResolvedException;

  /**
   * Fails all promises belonging to this batch with given error.
   * If a promise belonging to this batch has already been completed then it is
   * ignored by this method.
   * This method guarantees that after it returns each Promise in this batch is either completed or failed.
   * @param error error that all promises belonging to this batch will be failed with
   * @return number of promises that were not failed because the promise has already been resolved; if this
   * method returns {@code 0} it means that it successfully failed all promises belonging to this batch with
   * specified error
   */
  int failAll(Throwable error);

  Collection<BatchEntry<T>> values();

  Set<Map.Entry<K, BatchEntry<T>>> entries();

}
