package com.googlecode.objectify.cache;

import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;


/**
 * A Future which merges some previously loaded values with the results of another
 * Future that is in progress.  It can apply to any key/value pair type; typically
 * it will be for Key/Entity or Key<T>/T
 * 
 * @author Jeff Schnitzer <jeff@infohazard.org>
 */
public class MergeFuture<K, V> implements Future<Map<K, V>>
{
	/**
	 * Values that we have obtained.  Starts with just the preloaded values,
	 * then when the pending is complete the values get appended to this collection.
	 */
	Map<K, V> loaded;
	
	/** Pending requests - if null, this Future is complete and all values are in loaded */
	Future<Map<K, V>> pending;
	
	/**
	 * @param preloaded is a collection of entities that have already been obtained, say
	 * from the memcache.  TAKES OWNERSHIP OF THE MAP OBJECT - it will be modified later.
	 * @param pending is a future of entities that will be obtained sometime later, or
	 * null if merging is unnecessary and the preloaded values complete the result.
	 */
	public MergeFuture(Map<K, V> preloaded, Future<Map<K, V>> pending)
	{
		assert preloaded != null;
		this.loaded = preloaded;
		this.pending = pending;
	}

	/* (non-Javadoc)
	 * @see java.util.concurrent.Future#cancel(boolean)
	 */
	@Override
	public boolean cancel(boolean mayInterruptIfRunning)
	{
		//return this.raw.cancel(mayInterruptIfRunning);
		throw new UnsupportedOperationException("This makes my head spin. Don't do it.");
	}

	/* (non-Javadoc)
	 * @see java.util.concurrent.Future#isCancelled()
	 */
	@Override
	public boolean isCancelled()
	{
		return false;
	}

	/**
	 * 
	 * @see java.util.concurrent.Future#isDone()
	 */
	@Override
	public boolean isDone()
	{
		if (this.pending == null)
			return true;
		else
			return this.pending.isDone();
	}

	/* (non-Javadoc)
	 * @see java.util.concurrent.Future#get()
	 */
	@Override
	public Map<K, V> get() throws InterruptedException, ExecutionException
	{
		if (this.pending != null)
		{
			this.loaded.putAll(this.pending.get());
			this.pending = null;
		}
		
		return this.loaded;
	}

	/* (non-Javadoc)
	 * @see java.util.concurrent.Future#get(long, java.util.concurrent.TimeUnit)
	 */
	@Override
	public Map<K, V> get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException
	{
		if (this.pending != null)
		{
			this.loaded.putAll(this.pending.get(timeout, unit));
			this.pending = null;
		}
		
		return this.loaded;
	}
}