/** @file future.h
*	future operator implementation for EABaseTypes
*	@author Chris Burns (cburns@ea.com)
*
*	CTUI
*	(c) 2013 Electronic Arts Inc.
*/

#ifndef EABASETYPESFUTURE_FUTURE_H
#define EABASETYPESFUTURE_FUTURE_H

#include "EABaseTypes/datatypes.h"
#include "EABaseTypes/weakref.h"

namespace EA
{
    namespace Types
    {
        EABASETYPES_FORWARD_DECLARATION(class, FutureFunction)
        
        /*! @class FutureFunction future.cpp
		*	This is the actual future implementation
		*/
        class FutureFunction : public Function
        {
        public:
        
            FutureFunction(Factory* factory);
            virtual ~FutureFunction();
            
            virtual BaseTypeReturn Clone();
            
            virtual BaseTypeReturn Call(ArrayIn parameters);
            
            virtual bool Equals(FunctionIn pCompare);

			virtual EA::Type::TypeID GetFunctionType();
            
            FunctionReturn GetResolver();
            
            void AddCallback(FunctionIn callback);
            
            void AddDependency(FutureFunctionIn dependency);
            
            void Resolve(BaseTypeIn value, StringIn error);
            
            WeakRefRoot* GetWeakRefRoot();
            
        private:
        
            bool            mResolved;
            BaseTypeRef     mValue;
            StringRef       mError;
            ArrayRef        mCallbacks;
            ArrayRef        mDependencies;
            WeakRefRoot     mWeakRefRoot;
        };
        
        /*!
        *   Create an unbound future from a factory
        */
        FutureFunctionReturn Future(Factory* factory);
        
        /*!
        *	Create a new future
        *	@param	parameters  An array containing:
        *       - Nothing - an unbound future will be created that you can resolve yourself
        *           OR
        *       - A function, and some arguments (which may themselves be futures or CONTAIN futures), whose eventual invocation will resolve 
        *           the returned future and which will only be invoked once the futures in the arguments have been resolved
        *           - If the arguments contain no futures, the invocation will occur immediately
        *
        *   @return BaseTypeReturn A new future
        *
        *   @note It is very unlikely you will want to use this function directly - you should wrap it up with a FunctorRaw and use the
        *        ->Call() syntax to invoke it
        *
        *   @note   The returned future is in fact a Function, which can be invoked in the following ways
        *
        *       Retrieve the resolution function for the future
        *           FunctionRef resolve = future->Call<FunctionReturn>()
        *               - ONLY do this with an unbound future
        *
        *               - When called, the resolution function will resolve the future with the value specified and an error
        *                   resolve->Call<void>(value);
        *
        *               (if the error is a non-NULL, non-empty string it indicates the future has been failed)
        *                   resolve->Call<void>(BaseTypeIn(NULL), "This future has failed!");
        *
        *       
        *
        *       Register a callback with the future
        *           FunctionRef callback = (your callback);
        *           future->Call<void>(callback);
        *
        *               - The callback will be called once the future is resolved
        *               - Multiple callbacks can listen to a future at once
        *               - If the future is already resolved, the callback will be called immediately!
        *
        *
        *       Index the future as an Array
        *
        *           FunctionRef elementFuture = future->Call<FunctionReturn>(1);
        *               OR
        *           FunctionRef elementFuture = (*future)[1]->AsFunction();
        *
        *               - The new future will be resolved once the original future is resolved
        *               - The new future will fail if future doesn't actually turn out to be an array
        *
        *       
        *       Index the future as an Object
        *
        *           FunctionRef valueFuture = future->Call<FunctionReturn>("mykey");
        *               OR
        *           FunctionRef valueFuture = (*future)["mykey"]->AsFunction();
        *
        *               - The new future will be resolved once the original future is resolved
        *               - The new future will fail if future doesn't actually turn out to be an object
        */
        BaseTypeReturn Future(ArrayIn parameters);

		/*!
		*	Return the Future function above as a Function
		*/
		FunctionReturn FutureAsFunction(Factory* factory);
        
        /*!
        *	Determine if a BaseType is a future
        *	@param	baseType    A BaseType
        *   @return bool        true if the argument is a future, false otherwise
        */
        bool IsFuture(BaseTypeIn baseType);
        
        /*!
        *   Turn a BaseType into a future
		*	@param factory					The factory to use for creating the future
        *   @param baseType                 A BaseType
        *   @return FutureFunctionReturn    A future for that baseType
        */
        FutureFunctionReturn AsFuture(Factory* factory, BaseTypeIn baseType);

		/*!
		*	Wrap a function such that it receives an array consisting of the parameters passed to the wrapping function
		*	@note This should probably be moved to the EABaseTypes package
		*/
		FunctionReturn PackParameters(FunctionIn function);
    }
}

#endif /* EABASETYPESFUTURE_FUTURE_H */
