/////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
/////////////////////////////////////////////////////////////////////////////


///////////////////////////////////////////////////////////////////////////////
// This file implements the eastl::any which is part of the C++ standard STL
// library specification.  
//
// eastl::any is a type-safe container for single values of any type.  Our
// implementation makes use of the "small local buffer" optimization to avoid
// unnecessary dynamic memory allocation if the specified type is eligible to
// be stored in its local buffer.  The user type must satisfy the size
// requirements and must be no-throw move-constructible to qualify for the local
// buffer optimization.
//
// To consider:  Implement a fixed_any<SIZE> variant to allow users to customize
// the size of the "small local buffer" optimization.
//
// http://en.cppreference.com/w/cpp/utility/any
///////////////////////////////////////////////////////////////////////////////


#pragma once

#include <EASTL/internal/config.h>
#include <EASTL/internal/in_place_t.h>
#if EASTL_RTTI_ENABLED
	#include <typeinfo>
#endif
#if EASTL_EXCEPTIONS_ENABLED
	#include <exception>
#endif


namespace eastl
{
	///////////////////////////////////////////////////////////////////////////////
	// bad_any_cast
	// 
	// The type thrown by any_cast on failure.
	//
	// http://en.cppreference.com/w/cpp/utility/any/bad_any_cast
	//
	#if EASTL_EXCEPTIONS_ENABLED
	struct bad_cast : std::exception
	{
		const char* what() const EA_NOEXCEPT EA_OVERRIDE
			{ return "bad cast"; }
	};

	struct bad_any_cast : public bad_cast 
	{
		const char* what() const EA_NOEXCEPT EA_OVERRIDE
			{ return "bad_any_cast"; }
	};
    #endif

	namespace Internal
	{
		// utility to switch between exceptions and asserts
		inline void DoBadAnyCast()
		{
			#if EASTL_EXCEPTIONS_ENABLED
				throw bad_any_cast();
			#else
				EASTL_ASSERT_MSG(false, "bad_any_cast\n");

				// NOTE(rparolin): CRASH!
				// You crashed here because you requested a type that was not contained in the object.
				// We choose to intentionally crash here instead of returning invalid data to the calling 
				// code which could cause hard to track down bugs. 
				*((volatile int*)0) = 0xDEADC0DE;
			#endif
		}

		template<typename T, typename... Args>
		void* DefaultConstruct(Args&&... args)
		{
			auto* pMem = EASTLAllocatorDefault()->allocate(sizeof(T), alignof(T), 0);

			return ::new(pMem) T(eastl::forward<Args>(args)...);
		}

		template<typename T>
		void DefaultDestroy(T* p)
		{
			p->~T();

			EASTLAllocatorDefault()->deallocate(static_cast<void*>(p), sizeof(T));
		}
	}


	///////////////////////////////////////////////////////////////////////////////
	// 20.7.3, class any
	//
	class any
	{
		//////////////////////////////////////////////////////////////////////////////////////////
		// storage_operation 
		//
		// operations supported by the storage handler 
		//
		enum class storage_operation
		{
			GET,
			DESTROY,
			COPY,
			MOVE,
			TYPE_INFO
		};


		//////////////////////////////////////////////////////////////////////////////////////////
		// storage 
		//
		// the underlying storage type which enables the switching between objects stored in
		// the heap and objects stored within the any type.
		//
		union storage
		{
			typedef aligned_storage_t<4 * sizeof(void*), alignment_of<void*>::value> internal_storage_t;

			void* external_storage = nullptr;
			internal_storage_t internal_storage;
		};


		//////////////////////////////////////////////////////////////////////////////////////////
		// use_internal_storage 
		//
		// determines when the "local buffer optimization" is used 
		//
		template <typename T>
		using use_internal_storage = bool_constant
		<
			is_nothrow_move_constructible<T>::value 
			&& (sizeof(T) <= sizeof(storage)) &&
			(alignment_of<storage>::value % alignment_of<T>::value == 0)  
		>;


		//////////////////////////////////////////////////////////////////////////////////////////
		// non-member friend functions	
		//
    	template <class ValueType> friend const ValueType* any_cast(const any* pAny) EA_NOEXCEPT;
		template <class ValueType> friend ValueType* any_cast(any* pAny) EA_NOEXCEPT; 
		template <class ValueType> friend ValueType any_cast(const any& operand);
		template <class ValueType> friend ValueType any_cast(any& operand);
		template <class ValueType> friend ValueType any_cast(any&& operand);

		//Adding Unsafe any cast operations
		template <class ValueType> friend const ValueType* unsafe_any_cast(const any* pAny) EA_NOEXCEPT;
		template <class ValueType> friend ValueType* unsafe_any_cast(any* pAny) EA_NOEXCEPT;


		//////////////////////////////////////////////////////////////////////////////////////////
		// internal storage handler
		//
		template <typename T>
		struct storage_handler_internal
		{
			template <typename V>
			static void construct(storage& s, V&& v)
			{
				::new(&s.internal_storage) T(eastl::forward<V>(v));
			}

			template <typename... Args>
			static void construct_inplace(storage& s, Args... args)
			{
				::new(&s.internal_storage) T(eastl::forward<Args>(args)...);
			}

			template <class NT, class U, class... Args>
			static void construct_inplace(storage& s, std::initializer_list<U> il, Args&&... args)
			{
				::new(&s.internal_storage) NT(il, eastl::forward<Args>(args)...);
			}

			static inline void destroy(any& refAny)
			{
				T& t = *static_cast<T*>(static_cast<void*>(&refAny.m_storage.internal_storage));
				EA_UNUSED(t);
				t.~T();

				refAny.m_handler = nullptr;
			}

			static void* handler_func(storage_operation op, const any* pThis, any* pOther)
			{
				switch (op)
				{
					case storage_operation::GET:
					{
						EASTL_ASSERT(pThis);
						return (void*)(&pThis->m_storage.internal_storage);
					}
					break;

					case storage_operation::DESTROY:
					{
						EASTL_ASSERT(pThis);
						destroy(const_cast<any&>(*pThis));
					}
					break;

					case storage_operation::COPY:
					{
						EASTL_ASSERT(pThis);
						EASTL_ASSERT(pOther);
						construct(pOther->m_storage, *(T*)(&pThis->m_storage.internal_storage));
					}
					break;

					case storage_operation::MOVE:
					{
						EASTL_ASSERT(pThis);
						EASTL_ASSERT(pOther);
						construct(pOther->m_storage, eastl::move(*(T*)(&pThis->m_storage.internal_storage)));
						destroy(const_cast<any&>(*pThis));
					}
					break;

					case storage_operation::TYPE_INFO:
					{
					#if EASTL_RTTI_ENABLED
						return (void*)&typeid(T);
					#endif
					}
					break;

					default:
					{
						EASTL_ASSERT_MSG(false, "unknown storage operation\n");
					}
					break;
				};

				return nullptr;
			}
		};



		//////////////////////////////////////////////////////////////////////////////////////////
		// external storage handler
		//
		template <typename T>
		struct storage_handler_external
		{
			template <typename V>
			static inline void construct(storage& s, V&& v) 
			{
				s.external_storage = Internal::DefaultConstruct<T>(eastl::forward<V>(v));
			}

			template <typename... Args>
			static inline void construct_inplace(storage& s, Args... args)
			{
				s.external_storage = Internal::DefaultConstruct<T>(eastl::forward<Args>(args)...);
			}

			template <class NT, class U, class... Args>
			static inline void construct_inplace(storage& s, std::initializer_list<U> il, Args&&... args)
			{
				s.external_storage = Internal::DefaultConstruct<NT>(il, eastl::forward<Args>(args)...);
			}

			static inline void destroy(any& refAny)
			{
				Internal::DefaultDestroy(static_cast<T*>(refAny.m_storage.external_storage));

				refAny.m_handler = nullptr;
			}

			static void* handler_func(storage_operation op, const any* pThis, any* pOther)
			{
				switch (op)
				{
					case storage_operation::GET:
					{
						EASTL_ASSERT(pThis);
						EASTL_ASSERT(pThis->m_storage.external_storage);
						return static_cast<void*>(pThis->m_storage.external_storage);
					}
					break;

					case storage_operation::DESTROY:
					{
						EASTL_ASSERT(pThis);
						destroy(*const_cast<any*>(pThis));
					}
					break;

					case storage_operation::COPY:
					{
						EASTL_ASSERT(pThis);
						EASTL_ASSERT(pOther);
						construct(pOther->m_storage, *static_cast<T*>(pThis->m_storage.external_storage));
					}
					break;

					case storage_operation::MOVE:
					{
						EASTL_ASSERT(pThis);
						EASTL_ASSERT(pOther);
						construct(pOther->m_storage, eastl::move(*(T*)(pThis->m_storage.external_storage)));
						destroy(const_cast<any&>(*pThis));
					}
					break;

					case storage_operation::TYPE_INFO:
					{
					#if EASTL_RTTI_ENABLED
						return (void*)&typeid(T);
					#endif
					}
					break;

					default:
					{
						EASTL_ASSERT_MSG(false, "unknown storage operation\n");
					}
					break;
				};

				return nullptr;
			}
		};


		//////////////////////////////////////////////////////////////////////////////////////////
		// storage_handler_ptr 
		//
		// defines the function signature of the storage handler that both the internal and
		// external storage handlers must implement to retrieve the underlying type of the any
		// object.
		//
		using storage_handler_ptr = void* (*)(storage_operation, const any*, any*);


		//////////////////////////////////////////////////////////////////////////////////////////
		// storage_handler 
		//
		// based on the specified type T we select the appropriate underlying storage handler
		// based on the 'use_internal_storage' trait.
		//
		template <typename T>
		using storage_handler = typename conditional<use_internal_storage<T>::value,
		                                             storage_handler_internal<T>,
		                                             storage_handler_external<T>>::type;


		//////////////////////////////////////////////////////////////////////////////////////////
		// data layout
		//
		storage m_storage;
		storage_handler_ptr m_handler;

	public:
			#ifndef EA_COMPILER_GNUC
				// TODO(rparolin):  renable constexpr for GCC
				EA_CONSTEXPR 
			#endif
			any() EA_NOEXCEPT 
			: m_storage(), m_handler(nullptr) {}

		any(const any& other) : m_handler(nullptr)
		{
			if (other.m_handler)
			{
				// NOTE(rparolin): You can not simply copy the underlying
				// storage because it could hold a pointer to an object on the
				// heap which breaks the copy semantics of the language. 
				other.m_handler(storage_operation::COPY, &other, this);
				m_handler = other.m_handler;
			}
		}

		any(any&& other) EA_NOEXCEPT : m_handler(nullptr)
		{ 
			if(other.m_handler)
			{
				// NOTE(rparolin): You can not simply move the underlying
				// storage because because the storage class has effectively
				// type erased user type so we have to defer to the handler
				// function to get the type back and pass on the move request.
				m_handler = eastl::move(other.m_handler);
				other.m_handler(storage_operation::MOVE, &other, this);
			}
		}

		~any() { reset(); }

		template <class ValueType>
		any(ValueType&& value,
		    typename eastl::enable_if<!eastl::is_same<typename eastl::decay<ValueType>::type, any>::value>::type* = 0)
		{
			typedef decay_t<ValueType> DecayedValueType;
			static_assert(is_copy_constructible<DecayedValueType>::value, "ValueType must be copy-constructible");
			storage_handler<DecayedValueType>::construct(m_storage, eastl::forward<ValueType>(value));
			m_handler = &storage_handler<DecayedValueType>::handler_func;
		}

		template <class T, class... Args>
		explicit any(in_place_type_t<T>, Args&&... args) 
		{
			typedef storage_handler<decay_t<T>> StorageHandlerT;
			static_assert(eastl::is_constructible<T, Args...>::value, "T must be constructible with Args...");

			StorageHandlerT::construct_inplace(m_storage, eastl::forward<Args>(args)...);
			m_handler = &StorageHandlerT::handler_func;
		}

		template <class T, class U, class... Args>
		explicit any(in_place_type_t<T>,
		             std::initializer_list<U> il,
		             Args&&... args,
		             typename eastl::enable_if<eastl::is_constructible<T, std::initializer_list<U>&, Args...>::value,
		                                       void>::type* = 0)
		{
			typedef storage_handler<decay_t<T>> StorageHandlerT;

			StorageHandlerT::construct_inplace(m_storage, il, eastl::forward<Args>(args)...);
			m_handler = &StorageHandlerT::handler_func;
		}

		// 20.7.3.2, assignments
		template <class ValueType>
		any& operator=(ValueType&& value)
		{
			static_assert(is_copy_constructible<decay_t<ValueType>>::value, "ValueType must be copy-constructible");
			any(eastl::forward<ValueType>(value)).swap(*this);
			return *this;
		}

		any& operator=(const any& other) 
		{ 
			any(other).swap(*this);
			return *this; 
		}

		any& operator=(any&& other) EA_NOEXCEPT 
		{ 
			any(eastl::move(other)).swap(*this);
			return *this; 
		}

        // 20.7.3.3, modifiers
		#if EASTL_VARIADIC_TEMPLATES_ENABLED
			template <class T, class... Args>
			void emplace(Args&&... args)
			{
			    typedef storage_handler<decay_t<T>> StorageHandlerT;
				static_assert(eastl::is_constructible<T, Args...>::value, "T must be constructible with Args...");

			    reset();
				StorageHandlerT::construct_inplace(m_storage, eastl::forward<Args>(args)...);
				m_handler = &StorageHandlerT::handler_func;
			}

			template <class NT, class U, class... Args>
		    typename eastl::enable_if<eastl::is_constructible<NT, std::initializer_list<U>&, Args...>::value, void>::type
			emplace(std::initializer_list<U> il, Args&&... args)
			{
			    typedef storage_handler<decay_t<NT>> StorageHandlerT;

				reset();
				StorageHandlerT::construct_inplace(m_storage, il, eastl::forward<Args>(args)...);
				m_handler = &StorageHandlerT::handler_func;
			}
        #endif

		void reset() EA_NOEXCEPT 
		{
			if(m_handler)
				m_handler(storage_operation::DESTROY, this, nullptr);
		}

		void swap(any& other) EA_NOEXCEPT 
		{
			if(this == &other)
				return;

			if(m_handler && other.m_handler)
			{
				any tmp;
				tmp.m_handler = other.m_handler;
				other.m_handler(storage_operation::MOVE, &other, &tmp);

				other.m_handler = m_handler;
				m_handler(storage_operation::MOVE, this, &other);

				m_handler = tmp.m_handler;
				tmp.m_handler(storage_operation::MOVE, &tmp, this);
			}
			else if (m_handler == nullptr && other.m_handler)
			{
				eastl::swap(m_handler, other.m_handler);
				m_handler(storage_operation::MOVE, &other, this);
			}
			else if(m_handler && other.m_handler == nullptr)
			{
				eastl::swap(m_handler, other.m_handler);
				other.m_handler(storage_operation::MOVE, this, &other);
			}
			//else if (m_handler == nullptr && other.m_handler == nullptr)
			//{
			//     // nothing to swap 
			//}
		}

	    // 20.7.3.4, observers
		bool has_value() const EA_NOEXCEPT { return m_handler != nullptr; }

        #if EASTL_RTTI_ENABLED
			inline const std::type_info& type() const EA_NOEXCEPT 
			{
				if(m_handler)
				{
					auto* pTypeInfo = m_handler(storage_operation::TYPE_INFO, this, nullptr);
					return *static_cast<const std::type_info*>(pTypeInfo);
				}
				else
				{
					return typeid(void);
				}
			}
		#endif
	};



	//////////////////////////////////////////////////////////////////////////////////////////
	// 20.7.4, non-member functions
	//
	inline void swap(any& rhs, any& lhs) EA_NOEXCEPT { rhs.swap(lhs); }


	//////////////////////////////////////////////////////////////////////////////////////////
	// 20.7.4, The non-member any_cast functions provide type-safe access to the contained object.
	//
	template <class ValueType>
	inline ValueType any_cast(const any& operand)
	{
		static_assert(eastl::is_reference<ValueType>::value || eastl::is_copy_constructible<ValueType>::value,
		              "ValueType must be a reference or copy constructible");

		auto* p = any_cast<typename add_const<typename remove_reference<ValueType>::type>::type>(&operand);

		if(p == nullptr)
			Internal::DoBadAnyCast();

		return *p;
	}

	template <class ValueType>
    inline ValueType any_cast(any& operand)
    {
		static_assert(eastl::is_reference<ValueType>::value || eastl::is_copy_constructible<ValueType>::value,
		              "ValueType must be a reference or copy constructible");

		auto* p = any_cast<typename remove_reference<ValueType>::type>(&operand);

		if(p == nullptr)
			Internal::DoBadAnyCast();

		return *p;
    }

	template <class ValueType>
	inline ValueType any_cast(any&& operand)
	{
		static_assert(eastl::is_reference<ValueType>::value || eastl::is_copy_constructible<ValueType>::value,
		              "ValueType must be a reference or copy constructible");

		auto* p = any_cast<typename remove_reference<ValueType>::type>(&operand);

		if (p == nullptr)
			Internal::DoBadAnyCast();

		return *p;
	}

	// NOTE(rparolin): The runtime type check was commented out because in DLL builds the templated function pointer
	// value will be different -- completely breaking the validation mechanism.  Due to the fact that eastl::any uses
	// type erasure we can't refresh (on copy/move) the cached function pointer to the internal handler function because
	// we don't statically know the type.
	template <class ValueType>
	inline const ValueType* any_cast(const any* pAny) EA_NOEXCEPT
	{
		return (pAny && pAny->m_handler EASTL_IF_NOT_DLL(== &any::storage_handler<decay_t<ValueType>>::handler_func)
				#if EASTL_RTTI_ENABLED
					&& pAny->type() == typeid(typename remove_reference<ValueType>::type)
				#endif
				) ?
		           static_cast<const ValueType*>(pAny->m_handler(any::storage_operation::GET, pAny, nullptr)) :
		           nullptr;
	}

	template <class ValueType>
	inline ValueType* any_cast(any* pAny) EA_NOEXCEPT
	{
		return (pAny && pAny->m_handler EASTL_IF_NOT_DLL(== &any::storage_handler<decay_t<ValueType>>::handler_func)
				#if EASTL_RTTI_ENABLED
					&& pAny->type() == typeid(typename remove_reference<ValueType>::type)
				#endif
				) ?
		           static_cast<ValueType*>(pAny->m_handler(any::storage_operation::GET, pAny, nullptr)) :
		           nullptr;
	}

	//Unsafe operations - use with caution
	template <class ValueType>
	inline const ValueType* unsafe_any_cast(const any* pAny) EA_NOEXCEPT
	{
		return unsafe_any_cast<ValueType>(const_cast<any*>(pAny));
	}

	template <class ValueType>
	inline ValueType* unsafe_any_cast(any* pAny) EA_NOEXCEPT
	{
		return static_cast<ValueType*>(pAny->m_handler(any::storage_operation::GET, pAny, nullptr));
	}

	//////////////////////////////////////////////////////////////////////////////////////////
	// make_any
	//
	#if EASTL_VARIADIC_TEMPLATES_ENABLED
		template <class T, class... Args>
		inline any make_any(Args&&... args)
		{
			return any(eastl::in_place<T>, eastl::forward<Args>(args)...);
		}

		template <class T, class U, class... Args>
		inline any make_any(std::initializer_list<U> il, Args&&... args)
		{
			return any(eastl::in_place<T>, il, eastl::forward<Args>(args)...);
		}
    #endif

} // namespace eastl

