package com.hammurapi.store;

import java.util.Comparator;
import java.util.Iterator;
import java.util.concurrent.locks.ReadWriteLock;

import com.hammurapi.extract.Extractor;
import com.hammurapi.extract.Predicate;

/**
 * Abstraction of object store.
 * @author Pavel Vlasov.
 *
 * @param <T> Object type.
 * @param <PK> Primary key type.
 * @param <S> Self-type - sub-type of store. Generic parameter for predicates, extractors, update tasks and views. 
 */
public interface Store<T, PK, S extends Store<T,PK,S>> extends ReadWriteLock, Iterable<T> {
	
	enum ViewType {
		/**
		 * Live perform all operations on the master data, they
		 * do not create an intermediary store for view data.
		 */
		LIVE,
		/**
		 * Lazy views create intermediary store which contains only 
		 * data visible by the view. Updates from the main store
		 * are queued up and get applied to the intermediary store when 
		 * one of view data retrieval methods is invoked. 
		 */
		LAZY,
		/**
		 * Asynchronous views create intermediary store which contains only 
		 * data visible by the view. Updates from the main store
		 * are queued up and get applied to the intermediary store in a 
		 * background task, or when 
		 * one of view data retrieval methods is invoked. 
		 */
		ASYNCHRONOUS,
		/**
		 * Synchronous views create intermediary store which contains only 
		 * data visible by the view. Updates from the main store
		 * get applied to the view store as part of update operation. 
		 */		
		SYNCHRONOUS
	}
	
	/**
	 * Object handle.
	 * @author Pavel Vlasov
	 *
	 * @param <T>
	 */
	interface Handle<T, PK, S extends Store<T,PK,S>> {
		
		/**
		 * Updates object referenced by this handle. Clears cache and updates
		 * indexes. The new object shall have the same primary key if the store 
		 * has primary key extractor.
		 * @param obj New object for the handle (for immutable objects).
		 */
		void update(T obj);
		
		/**
		 * Clears cache and updates
		 * indexes. Updated object shall have the same primary key if the store 
		 * has primary key extractor.
		 */
		void update();
		
		/**
		 * Removes object referenced by this handle.
		 */
		void remove();
		
		/**
		 * @return Object referenced by this handle.
		 */
		T get();
		
		/**
		 * @return Primary key if object store is configured with primary
		 * key extractor.
		 */
		PK getPrimaryKey();
		
		/**
		 * @return True if object referenced by this handle is present in the store.
		 */
		boolean isValid();
		
		/**
		 * Extracts value from handle's object using given extractor
		 * and internal handle's cache.
		 * @param <V> Value type
		 * @param extractor Extractor
		 * @return extracted value.
		 */
		<V> V extract(Extractor<T, V, S> extractor);
	}
	
	// CRUDI
	
	/**
	 * Puts object to the store.
	 * If object is already present in the store, it gets
	 * updated in the store.
	 * @param obj Object to put to the store.
	 * @param validators Predicate(s) which are checked when object is retrieved from the store. 
	 * If there is more than one predicate, predicates are connected with AND. If validator(s) evaluate to
	 * false, the object handle is considered invalid, i.e. object is considered removed from the store. 
	 * Once validator(s) evaluate to false, they shall always evaluate to false. One of use of validators
	 * can be to expire object in the store, i.e. after some time validator returns false and object is cleared
	 * from the database. If validators evaluate to false at put time, object is not put to the store and 
	 * put() returns null. Validators evaluation result is not cached.
	 * @return Object handle or null if validator(s) evaluate to false. 
	 */
	Handle<T, PK, S> put(T obj, Predicate<T, S>... validators);
	
	/**
	 * Retrieves all objects from the store.
	 * @return
	 */
	Iterable<T> getAll();
	
	/**
	 * Processes all objects in the store.
	 * @param processor
	 * @return number of object queried.
	 */
	int queryAll(QueryTask<T, PK, S> processor);
	
	/**
	 * Retrieves object with given primary key.
	 * @param primaryKey
	 * @return 
	 * @throws IllegalStateException if object store is configured without
	 * primary key.
	 */
	T getByPrimaryKey(PK primaryKey);
	
	/**
	 * Some stores may return StoreIterator, which
	 * needs to be closed if iterator operation is
	 * terminated before hasNext() returns false.
	 * @author Pavel Vlasov.
	 *
	 * @param <T>
	 */
	interface StoreIterator<T> extends Iterator<T> {
		void close();
	}
	
	/**
	 * Retrieves objects matching the predicate.
	 * @param selector
	 * @return
	 */
	Iterable<T> get(Predicate<T, S> selector);
	
	/**
	 * Processes objects matching the predicate.
	 * @param selector
	 * @return Number of objects queried.
	 */
	int query(Predicate<T, S> selector, QueryTask<T, PK, S> processor);
	
	/**
	 * Processes values extracted from objects matching the predicate.
	 * @param selector
	 * @param extractor Extractor.
	 * @param processor Processor of query results.
	 * @return Number of objects queried.
	 */
	<V> int query(Predicate<T, S> selector, Extractor<T, V, S> extractor, QueryTask<V, PK, S> processor);
	
	/**
	 * Processes multiple values extracted from objects matching the predicate.
	 * @param selector Objects shall match this predicate. If null, all objects match.
	 * @param extractor Extractor
	 * @param valueSelector Values from matched objects shall match this predicate. If null, all values match.
	 * Value selectors are executed in separate tasks from extractor.
	 * Separation of value selection logic from value extraction logic makes
	 * sense if value selection logic is relatively heavy and parallelization of its
	 * execution will reduce overall query execution time.
	 * @param processor Processor of query results.
	 * @return Number of objects queried.
	 */
	<V> int queryMultiple(Predicate<T, S> selector, Extractor<T, Iterable<V>, S> extractor, Predicate<V, S> valueSelector, QueryTask<V, PK, S> processor);
	
	/**
	 * Retrieves values from objects matching the predicate.
	 * @param <V>
	 * @param selector Object selector.
	 * @param extractor Value extractor. If null, then object itself is returned.
	 * @param ordered If true, then returned result is ordered by comparator or by natural order if
	 * comparator is null.
	 * @param comparator Comparator to use for ordering.
	 * @return
	 */
	<V> Iterable<V> get(Predicate<T, S> selector, Extractor<T, V, S> extractor, boolean ordered, Comparator<V> comparator);
	
	/**
	 * Retrieves values from objects matching the predicate. Multiple values are retrieved from each object.
	 * @param <V>
	 * @param selector Objects shall match this predicate. If null, all objects match.
	 * @param extractor Value extractor.   
	 * @param valueSelector Values from matched objects shall match this predicate. If null, all values match.
	 * Value selectors are executed in separate tasks from extractor.
	 * Separation of value selection logic from value extraction logic makes
	 * sense if value selection logic is relatively heavy and parallelization of its
	 * execution will reduce overall query execution time.
	 * @param ordered If true, then returned result is ordered by comparator or by natural order if
	 * comparator is null.
	 * @param comparator Comparator to use for ordering.
	 * @return
	 */
	<V> Iterable<V> getMultiple(Predicate<T, S> selector, Extractor<T, Iterable<V>, S> extractor, Predicate<V, S> valueSelector, boolean ordered, Comparator<V> comparator);
	
	/**
	 * Removes all entries from the store.
	 */
	void clear();
	
	/**
	 * Removes specified object from the store.
	 * @param obj Object to remove.
	 * @return true if object was removed.
	 */
	boolean remove(T obj);
	
	/**
	 * Removes object by primary key.
	 * @param primaryKey
	 * @return
	 */
	boolean removeByPrimaryKey(PK primaryKey);

	/**
	 * Removes objects matching the predicate.
	 * @param predicate
	 * @return number of returned objects or -1 if number is unknown.
	 */
	int remove(Predicate<T, S> selector);
	

	/**
	 * Task to update objects.
	 * @author Pavel Vlasov.
	 *
	 * @param <T> Object type.
	 */
	interface UpdateTask<T, PK, S extends Store<T,PK,S>> {
	
		/**
		 * Executes update.
		 * @param store Store to perform joins, sub-queries, etc.
		 * @param obj
		 * @param primaryKey
		 * @return Undo task to rollback changes done by this task or null if there are no changes. 
		 */
		Runnable execute(S store, Handle<T,PK,S> handle);
	}
	
	/**
	 * Task to perform processing on objects (without updating).
	 * @author Pavel Vlasov.
	 *
	 * @param <T> Object type.
	 */
	interface QueryTask<T, PK, S extends Store<?,PK,S>> {
	
		/**
		 * Performs querying.
		 * @param store Store to perform joins, sub-queries, etc.
		 * @param obj
		 * @param primaryKey
		 */
		void execute(S store, T obj, PK primaryKey);
	}	
	
	/**
	 * Updates objects matching the predicate.
	 * @param selector
	 * @param updater
	 * @return
	 */
	int update(Predicate<T, S> selector, UpdateTask<T, PK, S> updater);
	
	/**
	 * Adds index to the store to speed-up extraction operations.
	 * @param predicate Only store items matching the predicate are indexed.
	 * @param extractor Extractor of the index key.
	 * @param Index type.
	 * @param ordered If true index is ordered.
	 * @param comparator Comparator to use for ordering. If null, then natural order is used.
	 * @return A "mix" of Index sub-interfaces corresponding to the 
	 * @param <V> Value type extracted by extractor for ordering.
	 * @param <ST> Type of index elements, which can be a sub-type of store element type. 
	 * index creation parameters.
	 */
	<V, ST extends T> Index<T,ST,PK,V,S> addIndex(
			Predicate<T, S> predicate,
			Extractor<ST, V, S> extractor, 
			Index.Type type, 
			boolean ordered, 
			Comparator<V> comparator);

	/**
 	 * If primary key is set, then the store shall use equals on the 
 	 * value returned from the primaryKeyExtractor 
	 * instead of equals on the object to compare objects for removal and update (put).
	 * @param primaryKeyExtractor Primary key extractor.
	 * @throws IllegalStateException If primary key is already set.
	 */
	Extractor<T, PK, S> getPrimaryKeyExtractor();
		
	/**
	 * Creates a view - a store which sees only objects matching the given selector.
	 * @param selector View selector.
	 * @param materialized If true, view elements are calculated at modify time instead of query time.
	 * @return Store which sees only objects matching give selector. The returned store is backed by the master store.
	 */
	S createView(Predicate<T, S> selector, ViewType viewType);
	
	/**
	 * Creates unmodifiable facade for this store.
	 * @return
	 */
	S createUnmodifiableFacade();
	
}