/** @file assetstreamfilehandler.cpp
 *  Implementation for the AssetStreamFileHandler class
 * 	@headerfile assetstreamfilehandler.h "ux_base/assetstreamfilehandler.h"
 *	@author Chris Burns (cburns@ea.com)
 *
 *	UX
 *	(c) 2011 Electronic Arts Inc. 
 */

#include "EABaseTypesAssetStream/assetstreamfilehandler.h"

#include "EABaseTypesPlus/vrefcountedica.h"
#include "EABaseTypesPlus/path.h"

#include "EABaseTypes/string.h"
#include "EABaseTypes/profile.h"
#include "EABaseTypes/factory.h"

#include "assetstream/rawasset.h"
#include "assetstream/assetstream.h"

#include "rw/core/filesys/async.h"

#include "coreallocator/icoreallocatormacros.h"

#include "EAAssert/eaassert.h"

namespace EA
{
	namespace Types
	{
		/*! @class	AssetStreamFileResource
		*	@brief	Implementation of IFileResource for AssetStream
		*	Trivial, backed by a RawAsset
		*/
		class AssetStreamFileResource : public EA::Types::VRefCountedICA<EA::Types::IFileResource>
		{
		public:

			AssetStreamFileResource(EA::Allocator::ICoreAllocator* allocator, AssetStream::RawAsset* asset):
				EA::Types::VRefCountedICA<EA::Types::IFileResource>(allocator),
				mAsset(asset)
			{
//FIFASTART - dstreat - May 9, 2014 - Prevent deletion while this object refs mAsset
				mAsset->AddRef();
//FIFAEND
			}

			virtual ~AssetStreamFileResource()
			{
				mAsset->Release();
				mAsset = NULL;
			}

			virtual void* GetData()
			{
				return mAsset->GetPointer();
			}

			virtual unsigned int GetSize() const
			{
				return static_cast<unsigned int>(mAsset->GetSize());
			}

		private:

			AssetStream::RawAsset*			mAsset;
		};

		AssetStreamFileHandler::AssetStreamFileHandler(EA::Allocator::ICoreAllocator *allocator, const char* root)
			: VRefCountedICA<IFileHandler>(allocator)
			, mAssets(allocator)
			, mRoot(allocator, root)
		{

		}

		/* virtual */ bool AssetStreamFileHandler::Exists(EA::Allocator::ICoreAllocator* allocator, const char* fileName)
		{
			AssetStream::RawAsset::TranslatorData td;
			td.SetPriority(AssetStream::Asset::TranslatorData::kHighestLoadPriority);
			td.SetAllocator(allocator, EA::Allocator::MEM_PERM);

			EA::RawString path(allocator);
			EA::Types::Path::AppendRaw(path, mRoot, fileName);

			AssetStream::RawAsset* asset = AssetStream::Get<AssetStream::RawAsset>(path.c_str(), td);
			AssetStream::WaitOn(asset);

			bool result = asset->GetPointer() != NULL;

			asset->Release();

			return result;
		}

		//PcLint can't deal with AssetStream templates
		//lint -efunc(64, UX::AssetStreamFileHandler::Load)
		//lint -efunc(916, UX::AssetStreamFileHandler::Load)
		//lint -efunc(1025, UX::AssetStreamFileHandler::Load)
		//lint -efunc(904, UX::AssetStreamFileHandler::Load)
		/* virtual */ EA::Types::IFileResourceReturn AssetStreamFileHandler::Load(EA::Allocator::ICoreAllocator* allocator, const char* fileName)
		{
			AssetStream::RawAsset::TranslatorData td;
			td.SetPriority(AssetStream::Asset::TranslatorData::kHighestLoadPriority);
			td.SetAllocator(allocator, EA::Allocator::MEM_PERM);

			EA::RawString path(allocator);
			EA::Types::Path::AppendRaw(path, mRoot, fileName);

			AssetStream::RawAsset* asset = AssetStream::Get<AssetStream::RawAsset>(path.c_str(), td);
			AssetStream::WaitOn(asset);

			if(asset->GetPointer() != NULL)
			{
				// if the pointer is not NULL, then the load succeeded
				return CORE_NEW(allocator, "AssetStreamFileResource", EA::Allocator::MEM_TEMP) AssetStreamFileResource(allocator, asset);
			}
			else
			{
				// otherwise, it failed, so release the RawAsset and return NULL
				asset->Release();

				EA_FAIL_FORMATTED(("AssetStreamFileHandler: Failed loading: %s", fileName));

				return NULL;
			}
		}

		/* virtual */ void AssetStreamFileHandler::Load(EA::Types::IFileHandlerCallbackIn callback, EA::Allocator::ICoreAllocator *allocator, const char* fileName)
		{
			AssetStream::RawAsset::TranslatorData td;
			td.SetPriority(AssetStream::Asset::TranslatorData::kHighestLoadPriority);
			td.SetAllocator(allocator, EA::Allocator::MEM_PERM);

			EA::RawString path(allocator);
			EA::Types::Path::AppendRaw(path, mRoot, fileName);

			AssetStream::RawAsset* asset = AssetStream::Get<AssetStream::RawAsset>(path.c_str(), td);

			AssetData& assetData = mAssets.push_back();

			assetData.mCallback = callback;
			assetData.mAllocator = allocator;
			assetData.mAsset = asset;
		}

		/* virtual */ void AssetStreamFileHandler::Update()
		{
			EABASETYPES_PROFILE_AUTO(a, 0, "AssetStreamFileHandler_Update");

			AssetVector temp = mAssets;

			mAssets.clear();

			AssetVector::const_iterator it = temp.begin();
			AssetVector::const_iterator end = temp.end();

			for(; it != end; it++)
			{
				const AssetData& assetData = (*it);

				AssetStream::Asset::eState state = assetData.mAsset->GetState();

				if(state == AssetStream::Asset::eStreamingSuccess)
				{
					assetData.mCallback->LoadComplete(EA::Types::IFileResourceRef(CORE_NEW(assetData.mAllocator, "AssetStreamFileResource", EA::Allocator::MEM_TEMP) AssetStreamFileResource(assetData.mAllocator, assetData.mAsset)));
				}
				else if(state == AssetStream::Asset::eStreamingFailure)
				{
					// FIFA_BEGIN | DIVERGENCE | cburns | 25-02-2015 | We can't assert because we will intentionally load some files that don't exist (since it is the most efficient way to find out if they exist)
					//EA_FAIL_FORMATTED(("AssetStreamFileHandler: Failed loading: %s", assetData.mAsset->GetFileName()));
					// FIFA_END

//FIFASTART - dstreat - May 9, 2014 - mAsset will be Released on destruction
					//// it failed, so release the RawAsset and return NULL
					//assetData.mAsset->Release();
//FIFAEND

					assetData.mCallback->LoadComplete(EA::Types::IFileResourceRef(NULL));
				}
				else
				{
					// copy it over for the next frame
					mAssets.push_back(assetData);
				}
			}
		}

//FIFASTART - dstreat - May 9, 2014 - Prevent deletion while this object refs mAsset
		AssetStreamFileHandler::AssetData::AssetData()
			: mAllocator(NULL)
			, mAsset(NULL)
		{
			
		}

		AssetStreamFileHandler::AssetData::AssetData(const AssetStreamFileHandler::AssetData& other)
		{
			*this = other;
		}

		AssetStreamFileHandler::AssetData& AssetStreamFileHandler::AssetData::operator= (const AssetStreamFileHandler::AssetData& other)
		{
			mCallback = other.mCallback;
			mAllocator = other.mAllocator;
			mAsset = other.mAsset;

			if (mAsset != NULL)
			{
				mAsset->AddRef();
			}

			return *this;
		}

		AssetStreamFileHandler::AssetData::~AssetData()
		{
			if (mAsset != NULL)
			{
				mAsset->Release();
			}
		}
//FIFAEND
	}
}
