/** @file future.cpp
*	future operator implementation for EABaseTypes
 * 	@headerfile future.h "EABaseTypesFuture/future.h"
 *	@author Chris Burns (cburns@ea.com)
 *
 *	CTUI
 *	(c) 2013 Electronic Arts Inc. 
 */
 
#include "EABaseTypesFuture/future.h"
 
#include "EABaseTypes/datatypes.h"
#include "EABaseTypes/functor.h"
#include "EABaseTypes/functioncallwrappers.h"
#include "EABaseTypes/weakrefuserdata.h"
 
namespace EA
{
    namespace Types
    {    
        /*!
        *   Helper function for indexing an array
        *       Fails gracefully if the value is not an array
        */
        static BaseTypeReturn IndexArray(ArrayIn parameters)
        {
            FunctionRef callback = (*parameters)[0]->AsFunction();
            BaseTypeRef baseType = (*parameters)[1];
            
            unsigned int number = (*parameters)[2]->AsUnsignedInt();
            if(baseType->GetType() == ARRAY_TYPE)
            {
                ArrayRef a = baseType->AsArray();
                
                if(a->size() > number)
                {
                    callback->Call<void>(a->at(number), StringIn(NULL));
                }
                else
                {
                    Factory* factory = callback->GetFactory();
                    EA::RawString error(factory->GetAllocator());
                    error.sprintf("Array did not contain index %u", number);
                    callback->Call<void>(BaseTypeIn(NULL), factory->String(error.c_str()));
                }
            }
            else
            {
                Factory* factory = callback->GetFactory();
                EA::RawString error(factory->GetAllocator());
                error.sprintf("Non-array could not be indexed with %u", number);
                callback->Call<void>(BaseTypeIn(NULL), factory->String(error.c_str()));
            }
            
            return NULL;
        }
        
        /*!
        *   Helper function for indexing an object
        *       Fails gracefully if the value is not an object
        */
        static BaseTypeReturn IndexObject(ArrayIn parameters)
        {
            FunctionRef callback = (*parameters)[0]->AsFunction();
            BaseTypeRef baseType = (*parameters)[1];
            
            const EA::String& key = (*parameters)[2]->AsEAString();
            if(baseType->GetType() == OBJECT_TYPE)
            {
                ObjectRef o = baseType->AsObject();
                
                if(o->contains(key))
                {
                    callback->Call<void>(o->get(key), StringIn(NULL));
                }
                else
                {
                    Factory* factory = callback->GetFactory();
                    EA::RawString error(factory->GetAllocator());
                    error.sprintf("Object did not contain key %s", key.c_str());
                    callback->Call<void>(BaseTypeIn(NULL), factory->String(error.c_str()));
                }
            }
            else
            {
                Factory* factory = callback->GetFactory();
                EA::RawString error(factory->GetAllocator());
                error.sprintf("Non-object could not be indexed with %s", key.c_str());
                callback->Call<void>(BaseTypeIn(NULL), factory->String(error.c_str()));
            }
            
            return NULL;
        }
        
        FutureFunctionReturn Future(Factory* factory)
        {
            return new (factory) FutureFunction(factory);
        }
        
        /*! @class FutureFunctionResolver future.cpp
        *	This is a helper function that resolves the future when called
        *   It is the future resolution interface
        */
        class FutureFunctionResolver : public Functor2<void, BaseTypeIn, StringIn>
        {
        public:
        
            FutureFunctionResolver(Factory* factory, FutureFunctionIn future):
                Functor2<void, BaseTypeIn, StringIn>(factory, static_cast<FutureFunctionResolver*>(NULL), &FutureFunctionResolver::Do),
                mFuture(future)
            {
                SetObject(this);
            }
            
            virtual EA::Type::TypeID GetFunctionType()
            {
                // return a unique type for the future resolver so that it can be recognized when composing futures
                return EA::Type::TypeOf(*this);
            }
            
            void Do(BaseTypeIn value, StringIn error)
            {
                mFuture->Resolve(value, error);
            }
            
            FutureFunctionReturn GetFuture()
            {
                return mFuture;
            }
            
        private:
        
            FutureFunctionRef mFuture;
        };
        
        FutureFunction::FutureFunction(Factory* factory):
            Function(factory),
            mResolved(false),
            mValue(),
            mError(),
            mCallbacks(),
            mDependencies(),
            mWeakRefRoot()
        {
        }
            
        /* virtual */ FutureFunction::~FutureFunction()
        {
            if(!mResolved)
            {
                Resolve(BaseTypeIn(NULL), mFactory->String("Future was destroyed without ever being resolved"));
            }
        }
            
        /* virtual */ BaseTypeReturn FutureFunction::Clone()
        {
            AddRef();
            
            return this;
        }
            
        /* virtual */ BaseTypeReturn FutureFunction::Call(ArrayIn parameters)
        {
            BaseTypeRef result;
            
            EA_ASSERT(parameters->size() == 0 || parameters->size() == 1);
            if(parameters->size() == 0)
            {
                result = GetResolver();
            }
            else if(parameters->size() == 1)
            {
                BaseTypeRef& parameter1 = (*parameters)[0];
                
                switch(parameter1->GetType())
                {
                    // create a future that indexes the eventual value of this future as an array
                    case NUMBER_TYPE:
                        {
                            ArrayRef indexParameters = mFactory->Array();
                            
                            FunctionRef indexArray = new (mFactory) FunctorRaw(mFactory, &IndexArray);
                            indexParameters->push_back(indexArray);
                            
                            FutureFunctionRef future = Future(mFactory);
                            indexParameters->push_back(future->GetResolver());
                            indexParameters->push_back(FunctionIn(this));
                            indexParameters->push_back(parameter1);
                            
                            Future(indexParameters);
                            
                            result = future;
                        }
                        break;
                    // create a future that indexes the eventual value of this future as an object
                    case STRING_TYPE:
                        {
                            ArrayRef indexParameters = mFactory->Array();

                            FunctionRef indexObject = new (mFactory) FunctorRaw(mFactory, &IndexObject);
                            indexParameters->push_back(indexObject);
                            
                            FutureFunctionRef future = Future(mFactory);
                            indexParameters->push_back(future->GetResolver());
                            indexParameters->push_back(FunctionIn(this));
                            indexParameters->push_back(parameter1);
                            
                            Future(indexParameters);
                            
                            result = future;
                        }
                        break;
                    case FUNCTION_TYPE:
                        AddCallback(parameter1->AsFunction());
                        break;
                    default:
                        EA_FAIL_MESSAGE("A future can only be called with no arguments, a string, a number, or a function - see EABaseTypesFuture/future.h");
                        break;
                }
            }
            
            return result;
        }
        
        /* virtual */ bool FutureFunction::Equals(FunctionIn pCompare)
        {
            return pCompare.GetUnsafePointer() == this;
        }

        /* virtual */ EA::Type::TypeID FutureFunction::GetFunctionType()
        {
            // return a unique type for the future so that it can be recognized when composing futures
            return EA::Type::TypeOf(*this);
        }
        
        FunctionReturn FutureFunction::GetResolver()
        {
            // return the resolution function for this future
            // we should probably make it impossible to retrieve this more than once
            return new (mFactory) FutureFunctionResolver(mFactory, FutureFunctionIn(this));
        }
        
        void FutureFunction::AddCallback(FunctionIn callback)
        {
            if(mResolved)
            {
                // call the passed in callback re-entrantly with the value and error
                callback->AsFunction()->Call<void>(mValue, mError);
            }
            else
            {
                // store the passed in callback to be called once this future is resolved
                if(mCallbacks == NULL) mCallbacks = mFactory->Array();
                mCallbacks->push_back(callback);
            }
        }
        
        void FutureFunction::AddDependency(FutureFunctionIn dependency)
        {
            if(mDependencies == NULL) mDependencies = mFactory->Array();
            
            mDependencies->push_back(WeakRefUserData<FutureFunction>::Create(mFactory, dependency));
        }
            
        void FutureFunction::Resolve(BaseTypeIn value, StringIn error)
        {
            // we should probably have better error handling here in situations where someone makes a horrible mistake
            // and resolves the future twice
            EA_ASSERT(!mResolved);
            
            mResolved = true;
            
            mValue = value;
            mError = error;
            
            if(mCallbacks != NULL)
            {
                Array::iterator it = mCallbacks->begin();
                Array::iterator end = mCallbacks->end();
                
                for(; it != end; ++it)
                {
                    (*it)->AsFunction()->Call<void>(mValue, mError);
                }
                
                mCallbacks = NULL;
            }
        }
        
        WeakRefRoot* FutureFunction::GetWeakRefRoot()
        {
            return &mWeakRefRoot;
        }
        
        /*!
        *   Helper function for creating an array
        *       We can just return the parameter list
        */
        static BaseTypeReturn CreateArray(ArrayIn parameters)
        {
            return parameters;
        }
        
        /*!
        *   Helper function for creating an object
        *       Here we turn the parameter list into an object
        */
        static BaseTypeReturn CreateObject(ArrayIn parameters)
        {
            EA::Types::ObjectRef o = parameters->GetFactory()->Object();
        
            Array::iterator it = parameters->begin();
            Array::iterator end = parameters->end();
            
            for(; it != end;)
            {
                const EA::String& key = (*it)->AsEAString();
                ++it;
                o->insert(key, (*it));
                ++it;
            }
            
            return o;
        }
        
        /*!
        *   This is function that transforms data structures containing futures
        *   into futures for those data structures with the resolved values of the futures
        *   in place of the futures.
        *
        *   Essentially, any data structure that contains futures will be turned into a future
        *   for that data structure but with the resolved future values, while a data structure
        *   that contains no futures will simply be returned
        */
        static BaseTypeReturn Transform(BaseTypeIn baseType)
        {
            BaseTypeRef result;
            
            if(baseType != NULL)
            {
                switch(baseType->GetType())
                {
                    case UNKNOWN_TYPE:
                        EA_FAIL();
                        break;
                    case BOOLEAN_TYPE:
                    case NUMBER_TYPE:
                    case STRING_TYPE:
                    case FUNCTION_TYPE:
                    case USERDATA_TYPE:
                        result = baseType;
                        break;
                    case ARRAY_TYPE:
                        {
                            Factory* factory = baseType->GetFactory();
                            
                            ArrayRef parameters = factory->Array();
                            parameters->push_back(new (factory) FunctorRaw(factory, &CreateArray));
                            
                            ArrayRef a = baseType->AsArray();
                            Array::iterator it = a->begin();
                            Array::iterator end = a->end();
                            
                            bool containsFuture = false;
                            
                            for(; it != end; ++it)
                            {
                                BaseTypeRef value = Transform(*it);
                                containsFuture = containsFuture || IsFuture(value);
                                
                                parameters->push_back(value);
                            }
                            
                            if(containsFuture)
                            {
                                // if the array has a future in it, we have to make a future for the array
                                result = Future(parameters);
                            }
                            else
                            {
                                // otherwise, we can just return the array
                                return a;
                            }
                        }
                        break;
                    case OBJECT_TYPE:
                        {
                            Factory* factory = baseType->GetFactory();
                            
                            ArrayRef parameters = factory->Array();
                            parameters->push_back(new (factory) FunctorRaw(factory, &CreateObject));
                            
                            ObjectRef o = baseType->AsObject();
                            Object::iterator it = o->begin();
                            Object::iterator end = o->end();
                            
                            bool containsFuture = false;
                            
                            for(; it != end; ++it)
                            {
                                BaseTypeRef value = Transform((*it).second);
                                containsFuture = containsFuture || IsFuture(value);
                                
                                parameters->push_back((*it).first);
                                parameters->push_back(value);
                            }
                            
                            if(containsFuture)
                            {
                                // if the object has a future in it, we have to make a future for the object
                                result = Future(parameters);
                            }
                            else
                            {
                                // otherwise, we can just return the object
                                return o;
                            }
                        }
                        break;
                }
            }
            
            return result;
        }
        
        EABASETYPES_FORWARD_DECLARATION(class, FunctionResolver)
        
        /*! @class FunctionResolver future.cpp
		*	This is a helper class that implements a resolver for futures given a function and some arguments (any of which may be futures)
		*/
        class FunctionResolver : public UserData
        {
        public:
        
            FunctionResolver(Factory* factory, FunctionIn resolver, size_t unresolved):
                UserData(factory),
                mResolver(resolver),
                mUnresolved(unresolved),
                mFunction(),
                mParameters(),
                mError(factory->GetAllocator())
            {
                if(unresolved > 1)
                {
                    // create an array to hold all our parameters
                    // fill it with NULLs so that we can insert into it at random as the
                    // futures for the parameters are resolved
                    mParameters = factory->Array();
                    size_t numParameters = unresolved - 1;
                    for(size_t i = 0; i < numParameters; ++i)
                    {
                        mParameters->push_back(BaseTypeIn(NULL));
                    }
                }
            }
            
            virtual BaseTypeReturn Clone() { return this; }

			virtual bool Equals(UserDataIn pCompare)
			{
				return this == pCompare.GetUnsafePointer();
			}
            
            virtual EA::Type::TypeID GetUserDataType()
            {
                return EA::Type::TypeOf(*this);
            }
            
            void Resolve(size_t index, BaseTypeIn value, StringRef error)
            {
                if(index == 0)
                {
                    // the first parameter is the function - store it separately for efficiency
                    if(value != NULL && value->GetType() == FUNCTION_TYPE)
                    {
                        mFunction = value->AsFunction();
                    }
                    else if(error == NULL || error->AsEAString().empty())
                    {
                        // if the future for the function succeeded, but was not actually a function, that is an error
                        mError.append("The function to call was not a function");
                    }
                }
                else
                {
                    // all other parameters will be the parameters for the function
                    (*mParameters)[index - 1] = value;
                }
                
                if(error != NULL && !error->AsEAString().empty())
                {
                    // if there was an error, aggregate it
                    mError.append(error->AsCStr());
                    mError.append("\n");
                }
                
                if(--mUnresolved <= 0)
                {
                    // once our function and all its parameters are resolved
                    BaseTypeRef v;
                    StringRef e;
                    
                    if(mError.empty())
                    {
                        // call it if there's no error
                        v = mFunction->Call(mParameters);
                    }
                    else
                    {
                        // get the error
                        e = mFactory->String(mError);
                    }
                    
                    // resolve our target future with either the value and NULL, or NULL and the error
                    mResolver->Call<void>(v, e);
                }
            }
            
            FunctionReturn GetResolver(size_t index)
            {
                return new (mFactory) Resolver(mFactory, FunctionResolverIn(this), index);
            }
            
        private:
        
            /*! @class FunctionResolver::Resolver future.cpp
            *	This is a helper class that implements a closure for resolving part of a function resolver
            */
            class Resolver : public Functor2<void, BaseTypeIn, StringIn>
            {
            public:
            
                Resolver(Factory* factory, FunctionResolverIn functionResolver, size_t index):
                    Functor2<void, BaseTypeIn, StringIn>(factory, static_cast<Resolver*>(NULL), &Resolver::Do),
                    mFunctionResolver(functionResolver),
                    mIndex(index)
                {
                    SetObject(this);
                }
                
                void Do(BaseTypeIn value, StringIn error)
                {
                    mFunctionResolver->Resolve(mIndex, value, error);
                }
                
            private:
            
                FunctionResolverRef mFunctionResolver;
                size_t              mIndex;
            };
        
            size_t          mUnresolved;
            FunctionRef     mResolver;
            FunctionRef     mFunction;
            ArrayRef        mParameters;
            EA::RawString   mError;
        };
        
        static void AddDependencies(FutureFunctionIn future, ArrayIn transformed)
        {
            Array::iterator it = transformed->begin();
            Array::iterator end = transformed->end();
            for(; it != end; ++it)
            {
                if(IsFuture(*it))
                {
                    future->AddDependency(FutureFunctionIn(static_cast<FutureFunction*>((*it).GetUnsafePointer())));
                }
            }
        }
        
        static bool IsFutureResolver(BaseTypeIn baseType)
        {
            return baseType != NULL && baseType->GetType() == FUNCTION_TYPE && baseType->AsFunction()->GetFunctionType() == EA::Type::TypeOf<FutureFunctionResolver>();
        }
        
        BaseTypeReturn Future(ArrayIn parameters)
        {
            Factory* factory = parameters->GetFactory();
            
            // create the future
            FutureFunctionRef result = Future(factory);
            
            // if there's at least one parameter, create a function resolver to resolve the future with the result of the function call
            if(parameters->size() > 0)
            {
                FunctionResolverRef functionResolver = new (factory) FunctionResolver(factory, result->GetResolver(), parameters->size());
                
                Array::iterator it = parameters->begin();
                Array::iterator end = parameters->end();
                
                ArrayRef transformed = factory->Array();
                
                for(; it != end; ++it)
                {
                    // transform the baseType so that it's either a non-future data structure or a future
                    transformed->push_back(Transform(*it));
                }
                
                it = transformed->begin();
                end = transformed->end();
                
                for(size_t i = 0; it != end; ++it, ++i)
                {   
                    if(IsFuture(*it))
                    {
                        FutureFunction* future = static_cast<FutureFunction*>((*it).GetUnsafePointer());
                        // the result future depends on the passed in futures
                        result->AddDependency(FutureFunctionIn(future));
                    
                        // if the parameter is a future, have our function resolver part resolver listen for it to be resolved
                        future->AddCallback(functionResolver->GetResolver(i));
                    }
                    else
                    {
                        // if this is a future resolver, its future depends on the passed in futures
                        if(IsFutureResolver(*it))
                        {
                            FutureFunctionRef future = static_cast<FutureFunctionResolver*>((*it).GetUnsafePointer())->GetFuture();
                            
                            AddDependencies(future, transformed);
                        }
                        
                        // otherwise, just resolve this part of the function resolver
                        functionResolver->Resolve(i, (*it), StringIn(NULL));
                    }
                }
            }
            
            return result;
        }

		FunctionReturn FutureAsFunction(EA::Types::Factory* factory)
		{
			return new (factory) FunctorRaw(factory, &Future);
		}
        
        bool IsFuture(BaseTypeIn baseType)
        {
            return baseType != NULL && baseType->GetType() == FUNCTION_TYPE && baseType->AsFunction()->GetFunctionType() == EA::Type::TypeOf<FutureFunction>();
        }
        
        FutureFunctionReturn AsFuture(Factory* factory, BaseTypeIn baseType)
        {
            BaseTypeRef value = Transform(baseType);
            if(IsFuture(value))
            {
                // this will AddRef automatically for us
                // we really need a proper Cast<>()
                return FutureFunctionIn(static_cast<FutureFunction*>(value.GetUnsafePointer()));
            }
            else
            {
                FutureFunctionRef result = Future(factory);
                
                result->GetResolver()->Call<void>(value);
                
                return result;
            }
        }

		class ParameterPacker : public FunctorRaw
		{
		public:

			ParameterPacker(EA::Types::Factory* factory, FunctionIn function):
				FunctorRaw(factory, static_cast<ParameterPacker*>(NULL), &ParameterPacker::Do),
				mFunction(function)
			{
				SetObject(this);
			}

		private:

			BaseTypeReturn Do(EA::Types::ArrayIn parameters)
			{
				return mFunction->Call<BaseTypeReturn>(parameters);
			}

			FunctionRef mFunction;
		};

		FunctionReturn PackParameters(FunctionIn function)
		{
			Factory* factory = function->GetFactory();

			return new (factory) ParameterPacker(factory, function);
		}
    }
}
 