/*******************************************************************************
 * Copyright (c) 2016 Bruno Medeiros and other Contributors.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Bruno Medeiros - initial API and implementation
 *******************************************************************************/
package melnorme.utilbox.concurrency;

import static melnorme.utilbox.core.Assert.AssertNamespace.assertTrue;

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

import melnorme.utilbox.core.fntypes.Result;

/**
 * Variant of {@link Future} with a safer and more precise API, with regards to exception throwing:
 * 
 * - Does not throw {@link ExecutionException}. If you want an Exception as a possible result, use {@link Result}.
 * - If cancellation occurs, this throws a checked exception instead of an unchecked exception. 
 *
 * @param <RESULT> The result type returned by the result methods.
 * @param <EXCEPTION> The exception thrown by the result methods. 
 * It should not be {@link InterruptedException} or {@link OperationCancellation} !
 * 
 * @see Future2
 * 
 */
public interface BasicFuture<RESULT> extends AsyncSupplier<RESULT> {
	
    /** @return true if execution of this future has terminated already. */
    boolean isTerminated();
    
    /** @return true if execution of this future has terminated with a cancellation. */
    boolean isCancelled();
    
    /** @return true if terminated with a result, without being cancelled. */
	default boolean isCompletedSuccessfully() {
		return isTerminated() && !isCancelled();
	}
	
	
    /* -----------------  ----------------- */
	
	void awaitTermination() throws InterruptedException;
    
	void awaitTermination(long timeout, TimeUnit unit) throws InterruptedException, TimeoutException;
	
	/**
	 * It is only legal to call this method if the future has terminated already.
	 * @return the result or throw OperationCancellation if cancelled. 
	 */
	default RESULT getResult_forTerminated() throws OperationCancellation {
		assertTrue(isTerminated());
		
		if(isCancelled()) {
			throw new OperationCancellation();
		}
		return getResult_forSuccessfulyCompleted();
	}
	
	/**
	 * It is only legal to call this method if the future has completed successfully.
	 * ({@link #isCompletedSuccessfully()} == true)
	 * @return the result or throw OperationCancellation if cancelled. 
	 */
	RESULT getResult_forSuccessfulyCompleted();
	
	/* -----------------  ----------------- */
	
	@Override
	default RESULT awaitResult() 
			throws OperationCancellation, InterruptedException {
		awaitTermination();
		return getResult_forTerminated();
	}
	
	@Override
	default RESULT awaitResult(long timeout, TimeUnit unit) 
			throws OperationCancellation, InterruptedException, TimeoutException {
		awaitTermination(timeout, unit);
		return getResult_forTerminated();
	}
	
}