package com.hammurapi.common.concurrent;

import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

import com.hammurapi.common.Adaptable;

/**
 * <a href="http://doc.hammurapi.com/dokuwiki/doku.php?id=products:common:concurrent:property_set">Online documentation</a>
 * @author Pavel Vlasov
 * 
 * @param <KP>
 *            Key path.
 */
public interface PropertySet<KP> extends Adaptable, Versionable<PropertySet<KP>> {
	/**
	 * @return Names of properties in the set
	 */
	Set<KP> keySet();

	/**
	 * @param prefix
	 * @return Property set which operates on properties starting with given
	 *         prefix.
	 */
	PropertySet<KP> subset(KP prefix);

	/**
	 * Removes property from the set. Chained properties are shadowed (appear to
	 * be removed).
	 * 
	 * @param name
	 */
	void remove(KP key);

	/**
	 * Removes all entries from the property set
	 */
	void clear();

	/**
	 * Mounts source property set at specified prefix. E.g. if source property
	 * set has entry 'a' and prefix is 'b/' then that entry will be available as
	 * 'b/a'. Mounted entries shadow own entries.
	 * 
	 * @param prefix
	 * @param source
	 */
	void mount(KP prefix, PropertySet<KP> source);

	public void unmount(KP prefix);
	
	/**
	 * Copies all entries from the source property set to self.
	 * 
	 * @param source
	 */
	void setAll(PropertySet<KP> source);

//	/**
//	 * @param otherSet
//	 * @return true if all properties in this set are equal to properties in the
//	 *         other set.
//	 */
//	boolean compareProperties(PropertySet<KP> otherSet);
		
	/**
	 * Retrieves property without conversion.
	 */
	Object get(KP key);		

	/**
	 * @param key
	 *            Property key.
	 * @param defaultValue
	 *            Default value.
	 * @return Property value or default value if property doesn't exist in the
	 *         property set.
	 */
	Object get(KP key, Object defaultValue);

	/**
	 * Retrieves property with conversion to target type.
	 */
	<T> T get(KP key, Class<T> type);

	/**
	 * @param key
	 *            Property key.
	 * @param type
	 *            target type.
	 * @param defaultValue
	 *            Default value.
	 * @return Property value or default value if property doesn't exist in the
	 *         property set.
	 */
	<T> T get(KP key, Class<T> type, T defaultValue);

	/**
	 * Retrieves property without conversion.
	 */
	Object get(KP key, long timeout, TimeUnit unit);		

	/**
	 * @param key
	 *            Property key.
	 * @param defaultValue
	 *            Default value.
	 * @return Property value or default value if property doesn't exist in the
	 *         property set.
	 */
	Object get(KP key, Object defaultValue, long timeout, TimeUnit unit);

	/**
	 * Retrieves property with conversion to target type.
	 */
	<T> T get(KP key, Class<T> type, long timeout, TimeUnit unit);

	/**
	 * @param key
	 *            Property key.
	 * @param type
	 *            target type.
	 * @param defaultValue
	 *            Default value.
	 * @return Property value or default value if property doesn't exist in the
	 *         property set.
	 */
	<T> T get(KP key, Class<T> type, T defaultValue, long timeout, TimeUnit unit);

	/**
	 * Sets property.
	 * 
	 * @param key
	 * @param value
	 */
	void set(KP key, Object value);

	/**
	 * Convenience method to set invocable.
	 * 
	 * @param key
	 * @param invocable
	 */
	void setInvocable(KP key, Invocable<PropertySet<KP>, ?, KP, ?> invocable);

	/**
	 * Puts callable to the property set for asynchronous invocation to retrieve
	 * value at some point in the future.
	 * 
	 * @param name
	 * @param callable
	 */
	void cook(KP key, Callable<?> callable);

	/**
	 * Puts callable to the property set for asynchronous invocation to retrieve
	 * value at some point in the future after specified delay.
	 * 
	 * @param name
	 * @param callable
	 * @param delay
	 */
	void cook(KP key, Callable<?> callable, long delay, TimeUnit timeUnit);

	/**
	 * Puts value which is not ready yet but "is being cooked by another thread"
	 * to the property set to be used at some point of time in the future.
	 * 
	 * @param name
	 * @param future
	 *            Value being cooked.
	 */
	void cooking(KP key, Future<?> future);

	/**
	 * Puts callable to the property set. Value from callable is retrieved when
	 * <code>get()</code> method for the given name is invoked.
	 * 
	 * @param name
	 * @param callable
	 */
	void lazy(KP key, Callable<?> callable);

	/**
	 * Removes callable to be cooked from the cooking queue, converting it to
	 * lazy, if it is not already cooked.
	 * 
	 * @param name
	 */
	void cancel(KP key);

	/**
	 * Cancels all unfinished background computations.
	 */
	void cancelAll();
	
	// adapt - extend adaptable
	
	/**
	 * Invokes invocable at given key. Converts arguments to proper types if needed.
	 * Invocation is performed under the write lock, so the invocable can safely modify
	 * the property set. 
	 */
	Object invoke(KP key, Object... args);
	
	// Access to shadow property set(s)
	
	/**
	 * Invokes invocable in the shadow property set at given key. Converts arguments to proper types if needed.
	 * The shadow invocable is provided a read-only property set as a context and thus any attempt to modify 
	 * the state of shadow property set(s) will result in exception. 
	 */
	Object shadowInvoke(KP key, Object... args);
	
	/**
	 * Retrieves shadow property without conversion.
	 */
	Object shadowGet(KP key);

	/**
	 * @param key
	 *            Property key.
	 * @param defaultValue
	 *            Default value.
	 * @return Property value or default value if property doesn't exist in the
	 *         property set.
	 */
	Object shadowGet(KP key, Object defaultValue);

	/**
	 * Retrieves property with conversion to target type.
	 */
	<T> T shadowGet(KP key, Class<T> type);

	/**
	 * @param key
	 *            Property key.
	 * @param type
	 *            target type.
	 * @param defaultValue
	 *            Default value.
	 * @return Property value or default value if property doesn't exist in the
	 *         property set.
	 */
	<T> T shadowGet(KP key, Class<T> type, T defaultValue);

	/**
	 * Retrieves shadow property without conversion.
	 */
	Object shadowGet(KP key, long timeout, TimeUnit unit);

	/**
	 * @param key
	 *            Property key.
	 * @param defaultValue
	 *            Default value.
	 * @return Property value or default value if property doesn't exist in the
	 *         property set.
	 */
	Object shadowGet(KP key, Object defaultValue, long timeout, TimeUnit unit);

	/**
	 * Retrieves property with conversion to target type.
	 */
	<T> T shadowGet(KP key, Class<T> type, long timeout, TimeUnit unit);

	/**
	 * @param key
	 *            Property key.
	 * @param type
	 *            target type.
	 * @param defaultValue
	 *            Default value.
	 * @return Property value or default value if property doesn't exist in the
	 *         property set.
	 */
	<T> T shadowGet(KP key, Class<T> type, T defaultValue, long timeout, TimeUnit unit);

}
