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

///////////////////////////////////////////////////////////////////////////////
// This class implements a linked_array template, which is an array version
// of linked_ptr. See linked_ptr for detailed documentation.
///////////////////////////////////////////////////////////////////////////////


#pragma once

#include <EASTL/internal/config.h>
#include <EASTL/internal/smart_ptr.h>   // Defines smart_array_deleter
#include <EASTL/linked_ptr.h>           // Defines linked_ptr_base 
#include <stddef.h>                     // Definition of ptrdiff_t


namespace eastl
{

	/// class linked_array
	///
	/// This class implements a linked_array template, which is an array version
	/// of linked_ptr. See linked_ptr for detailed documentation.
	///
	template <typename T, typename Deleter = smart_array_deleter<T> >
	class linked_array
	{
	
	protected:
	
		/// this_type
		/// This is an alias for linked_array<T>, this class.
		typedef linked_array<T> this_type;

		/// deleter_type
		typedef Deleter deleter_type;

		T* mpArray;
		mutable const this_type* mpPrev;
		mutable const this_type* mpNext;

		void link(const linked_array& linkedArray)
		{   // This code can only be called when we are in a reset state.
			// assert(!mpArray && (mpNext == mpPrev));
			mpNext             = linkedArray.mpNext;
			mpNext->mpPrev     = this;
			mpPrev             = &linkedArray;
			linkedArray.mpNext = this;
		}

	public:
		/// element_type
		/// Synonym for type T, useful for external code to reference the 
		/// type in a generic way.
		typedef T element_type;


		/// linked_array
		/// Takes ownership of the pointer. It is OK if the input pointer is null.
		explicit linked_array(T* pArray = NULL) 
			: mpArray(pArray)
		{
			mpPrev = mpNext = this;
		}


		/// linked_array
		/// Shares ownership of a pointer with another instance of linked_array.
		linked_array(const linked_array& linkedArray)
			: mpArray(linkedArray.mpArray)
		{
			if(mpArray)
				link(linkedArray);
			else
				mpPrev = mpNext = this;
		}


		/// ~linked_array
		/// Removes this object from the of objects using the shared pointer.
		/// If this object is the last owner of the shared pointer, the shared 
		/// pointer is deleted.
		~linked_array() 
		{
			reset();
		}


		/// operator=
		/// Copies another linked_array to this object. Note that this object
		/// may already own a shared pointer with another different pointer
		/// (but still of the same type) before this call. In that case,
		/// this function removes ownership of the old pointer and takes shared 
		/// ownership of the new pointer and increments its reference count.
		linked_array& operator=(const linked_array& linkedArray)
		{
			if(linkedArray.mpArray != mpArray)
			{
				reset(linkedArray.mpArray);
				if(linkedArray.mpArray)
					link(linkedArray);
			}
			return *this;
		}


		/// operator=
		/// Assigns a new pointer. If the new pointer is equivalent
		/// to the current pointer, nothing is done. Otherwise the 
		/// current pointer is unlinked and possibly destroyed.
		/// The new pointer can be NULL.
		linked_array& operator=(T* pArray)
		{
			reset(pArray);
			return *this;
		}


		/// reset
		/// Releases the owned pointer and takes ownership of the 
		/// passed in pointer. If the passed in pointer is the same
		/// as the owned pointer, nothing is done. The passed in pointer
		/// can be null, in which case the use count is set to 1.
		void reset(T* pArray = NULL)
		{
			if(pArray != mpArray)
			{
				if(unique())
				{
					deleter_type del;
					del(mpArray);
				}
				else
				{
					mpPrev->mpNext  = mpNext;
					mpNext->mpPrev  = mpPrev;
					mpPrev = mpNext = this;
				}
				mpArray = pArray;
			}
		}


		/// swap
		/// Exchanges the owned pointer beween two linkedArray objects.
		///
		/// This function is disabled as it is currently deemed unsafe.
		/// The problem is that the only way to implement this function
		/// is to transfer pointers between the objects; you cannot 
		/// transfer the linked list membership between the objects. 
		/// Thus unless both linked_array objects were 'unique()', the 
		/// shared pointers would be duplicated amongst containers, 
		/// resulting in a crash.
		//void swap(linked_array& linkedArray)
		//{
		//    if(linkedArray.mpArray != mpArray)
		//    {   // This is only safe if both linked_arrays are unique().
		//        linkedArray::element_type* const pArrayTemp = linkedArray.mpArray;
		//        linkedArray.reset(mpArray);
		//        reset(pArrayTemp);
		//    }
		//}


		/// operator[]
		/// Returns a reference to the specified item in the owned pointer array. 
		T& operator[](ptrdiff_t i) const
		{
			// assert(mpArray && (i >= 0));
			return mpArray[i];
		}


		/// operator*
		/// Returns the owner pointer dereferenced.
		T& operator*() const
		{
			return *mpArray;
		}


		/// operator->
		/// Allows access to the owned pointer via operator->()
		T* operator->() const
		{
			return mpArray;
		}


		/// get
		/// Returns the owned pointer. Note that this class does 
		/// not provide an operator T() function. This is because such
		/// a thing (automatic conversion) is deemed unsafe.
		T* get() const
		{
			return mpArray;
		}


		/// use_count
		/// Returns the use count of the shared pointer.
		/// The return value is one if the owned pointer is null.
		/// This function is provided for compatibility with the 
		/// proposed C++ standard and for debugging purposes. It is not
		/// intended for runtime use given that its execution time is
		/// not constant.
		int use_count() const
		{
			int useCount(1);
			
			for(const linked_ptr_base* pCurrent = this; pCurrent->mpNext != this; pCurrent = pCurrent->mpNext)
				++useCount;

			return useCount;
		}


		/// unique
		/// Returns true if the use count of the owned pointer is one.
		/// The return value is true if the owned pointer is null.
		bool unique() const
		{
			return (mpNext == this);
		}


		/// Implicit operator bool
		/// Allows for using a linked_array as a boolean. 
		/// Note that below we do not use operator bool(). The reason for this
		/// is that booleans automatically convert up to short, int, float, etc.
		/// The result is that this: if(linkedArray == 1) would yield true (bad).
		typedef T* (this_type::*bool_)() const;
		operator bool_() const
		{
			if(mpArray)
				return &this_type::get;
			return NULL;
		}


		/// operator!
		/// This returns the opposite of operator bool; it returns true if 
		/// the owned pointer is null. Some compilers require this and some don't.
		bool operator!()
		{
			return (mpArray == NULL);
		}


		/// force_delete
		/// Forces deletion of the shared pointer. Fixes all references to the 
		/// pointer by any other owners to be NULL.
		void force_delete()
		{
			T* const pArray = mpArray;

			this_type* p = this;
			do
			{
				this_type* const pNext = const_cast<this_type*>(p->mpNext);
				p->mpArray = NULL;
				p->mpNext  = p->mpPrev = p;
				p = pNext;
			}
			while(p != this);

			deleter_type del;
			del(pArray);
		}

	}; // class linked_array



	/// get_pointer
	/// Returns linked_array::get() via the input linked_array. Provided for compatibility
	/// with certain well-known libraries that use this functionality.
	template <typename T>
	inline T* get_pointer(const linked_array<T>& linkedArray)
	{
		return linkedArray.get();
	}


	/// operator==
	/// Compares two linked_array objects for equality. Equality is defined as 
	/// being true when the pointer shared between two linked_array objects is equal.
	template <typename T, typename TD, typename U, typename UD>
	inline bool operator==(const linked_array<T, TD>& linkedArray1, const linked_array<U, UD>& linkedArray2)
	{
		return (linkedArray1.get() == linkedArray2.get());
	}


	/// operator!=
	/// Compares two linked_array objects for inequality. Equality is defined as 
	/// being true when the pointer shared between two linked_array objects is equal.
	template <typename T, typename TD, typename U, typename UD>
	inline bool operator!=(const linked_array<T, TD>& linkedArray1, const linked_array<U, UD>& linkedArray2)
	{
		return (linkedArray1.get() != linkedArray2.get());
	}


	/// operator<
	/// Returns which linked_array is 'less' than the other. Useful when storing
	/// sorted containers of linked_array objects.
	template <typename T, typename TD, typename U, typename UD>
	inline bool operator<(const linked_array<T, TD>& linkedArray1, const linked_array<U, UD>& linkedArray2)
	{
		return (linkedArray1.get() < linkedArray2.get());
	}


} // namespace eastl










