/* 
implementation of memory manager
*/

#if defined(PS3)

#define eCryModule eCryM_Launcher
#include <CryModuleDefs.h>
#include <ILog.h>
#include "SPUMemAreaMan.h"
#include <vector>
#include <algorithm>
#include <cmath>

extern ILog* GetILog();

void NPPU::CSPUMemAreaMan::HandleMemRequest(const uint32 cSPUIndex)
{
	int retRes = 0;
	assert(cSPUIndex < scMaxSPU);
	volatile SPPUMemRequestData& __restrict rRequestData = m_Requests[cSPUIndex];
	while(rRequestData.valid == 0){};//should be 1, dma transfer should be finished by the time we get here
	if(rRequestData.type == eMR_Delete || rRequestData.type == eMR_ReAlloc)
		delete [] (char*)rRequestData.address;
	if(rRequestData.type == eMR_Alloc || rRequestData.type == eMR_ReAlloc)
		rRequestData.address = (uint32)(char*)(new char[rRequestData.size]);//return new address to SPU
	rRequestData.valid = 0;//reset
	//send signal to SPU if required
	if(rRequestData.type != eMR_Delete)
		WriteSPUProbReg(cSPUIndex, scPCSigNotify1, (uint32)rRequestData.address);
}

#if defined(DO_SPU_PROFILING)
#include <sys/paths.h>
#include <cell/cell_fs.h>
#include <math.h>

struct SLookupData
{
	uint32 id;
	float perFromTotal;
	float percMisses;
	uint32 count;

	SLookupData() : id(0xFFFFFFFF), perFromTotal(0.f), count(0), percMisses(0.f)
	{}

	SLookupData(const uint32 cID, const float cPerFromTotal, const float cPercMisses, const uint32 cCount) 
		: id(cID), perFromTotal(cPerFromTotal), count(cCount), percMisses(cPercMisses)
	{}

	//sort in descending order
	const bool operator <(const SLookupData& crOther) const
	{
		if(crOther.count == count)
			return id < crOther.id;
		return crOther.count < count;
	}
};

struct SMissData : public SLookupData
{
	float prefFailedPerc;

	SMissData() : SLookupData(), prefFailedPerc(0.f)
	{}

	SMissData(const uint32 cID, const float cPerFromTotal, const uint32 cCount, const float cPrefFailedPerc) 
		: SLookupData(cID, cPerFromTotal, 0.f, cCount), prefFailedPerc(cPrefFailedPerc)
	{}

};

static const char* GetSpaces(const uint32 cCount)
{
	static char *pSpaceBuf[7] = {"", " ", "  ", "   ", "    ", "     ", "      "};
	const uint32 cExp = (uint32)log10((float)cCount);
	switch(cExp)
	{
	case 0:
		return pSpaceBuf[5];
	case 1:
		return pSpaceBuf[4];
	case 2:
		return pSpaceBuf[3];
	case 3:
		return pSpaceBuf[2];
	case 4:
		return pSpaceBuf[1];
	case 5:
		return pSpaceBuf[0];
	case 6:
	default:
		return pSpaceBuf[0];
	}
	return pSpaceBuf[0];
}

static const char* GetSpacesFromID(const uint32 cID)
{
	static char *pSpaceBuf[4] = {"", " ", "  ", "   "};
	if(cID >= 1000)
		return pSpaceBuf[0];
	if(cID >= 100)
		return pSpaceBuf[1];
	if(cID >= 10)
		return pSpaceBuf[2];
	return pSpaceBuf[3];
}

static const char* GetSpacesFloat(const float cPercVal)
{
	static char *pSpaceBuf[3] = {"", " ", "  "};
	if(cPercVal >= 100.f)
		return pSpaceBuf[0];
	if(cPercVal >= 10.f)
		return pSpaceBuf[1];
	return pSpaceBuf[2];
}

void NPPU::CSPUMemAreaMan::HandleProfRequest(const uint32 cSPUIndex, const uint32 cProfFileCntr, const uint32 cJobId)
{
	char *fileBuf = new char[1024];
	char *lineBuf = new char[1024];
	cellFsMkdir(SYS_APP_HOME"/ProfilingData", CELL_FS_DEFAULT_CREATE_MODE_1);
	const char *cpJobName = CJobManSPU::Instance()->GetJobName(cJobId);
	const uint32 cStrLen = strlen(cpJobName);
	uint32 i=0;
	for(; i<cStrLen; ++i)
	{
		if(cpJobName[i] != ':')
			lineBuf[i] = cpJobName[i];
		else
		{
			lineBuf[i] = '_';
			++i;//replace only one ':'
		}
	}
	lineBuf[i] = '\0';
	sprintf(fileBuf, SYS_APP_HOME"/ProfilingData/%s_%d.txt",lineBuf, cProfFileCntr);
	const volatile uint32 * const __restrict cpProfBufLookup	 = (volatile uint32*)(m_CacheProfBufs + cSPUIndex * MAX_PROF_ID * (4*3));
	const volatile uint32 * const __restrict cpProfBufMiss		 = (volatile uint32*)(m_CacheProfBufs + (MAX_PROF_ID << 2) + cSPUIndex * MAX_PROF_ID * (4*3));
	const volatile uint32 * const __restrict cpProfBufPrefMiss = (volatile uint32*)(m_CacheProfBufs + (MAX_PROF_ID << 3) + cSPUIndex * MAX_PROF_ID * (4*3));
	const uint32 cWriteBufSize = 256 + MAX_PROF_ID * 128;
	char *pWriteBuf = new char[cWriteBufSize];
	char *pCurWritePtr = pWriteBuf;
	int out;

//	ILog *pLog = GetILog();

	cellFsUnlink(fileBuf);
	int	ret = cellFsOpen(fileBuf, CELL_FS_O_WRONLY|CELL_FS_O_CREAT, &out, NULL, 0);
	if(ret != CELL_FS_SUCCEEDED)
	{
		printf("Failed to create profiling data file: %s\n",fileBuf);
//		if(pLog)
//			pLog->LogError("Failed to create profiling data file: %s\n",fileBuf);
	}
	else
	{	
		//get total number of misses and hits
		uint32 misses = 0, lookups = 0;
		for(uint32 i=0; i<MAX_PROF_ID; ++i)
		{
			lookups += cpProfBufLookup[i];
			misses  += cpProfBufMiss[i];
		}

		sprintf(lineBuf, "total lookups: %d   total misses: %d\n",lookups, misses);
		uint32 lineLen = strlen(lineBuf);
		memcpy(pCurWritePtr, lineBuf, lineLen);
		pCurWritePtr += lineLen;

		sprintf(lineBuf, "[id][lookups] [%% total] [%% misses]\n");
		lineLen = strlen(lineBuf);
		memcpy(pCurWritePtr, lineBuf, lineLen);
		pCurWritePtr += lineLen;

		std::vector<SLookupData> lookupVec;		lookupVec.reserve(MAX_PROF_ID);
		std::vector<SMissData> missVec;				missVec.reserve(MAX_PROF_ID >> 2);

		if(lookups > 0)
		{
			const float cLookupsInv = 1.f / (float)lookups;
			for(uint32 i=0; i<MAX_PROF_ID; ++i)
			{
				if(cpProfBufLookup[i] != 0)
				{
					const float cPercentage = 100.f * (float)cpProfBufLookup[i] * cLookupsInv;
					const float cPercMisses = 100.f * (float)cpProfBufMiss[i] / (float)cpProfBufLookup[i];
					lookupVec.push_back(SLookupData(i, cPercentage, cPercMisses, cpProfBufLookup[i]));
				}
			}

			std::sort(lookupVec.begin(), lookupVec.end());
			const std::vector<SLookupData>::const_iterator cLookupEnd = lookupVec.end();
			for(std::vector<SLookupData>::const_iterator iter = lookupVec.begin(); iter != cLookupEnd; ++iter)
			{
				const SLookupData& crLookup = *iter;
				sprintf(lineBuf, "%s%d: %s%d   %s%-2.02f%%   %s%-2.02f%%\n",GetSpacesFromID(crLookup.id), crLookup.id, GetSpaces(crLookup.count), crLookup.count, GetSpacesFloat(crLookup.perFromTotal), crLookup.perFromTotal, GetSpacesFloat(crLookup.percMisses), crLookup.percMisses);
				lineLen = strlen(lineBuf);
				memcpy(pCurWritePtr, lineBuf, lineLen);
				pCurWritePtr += lineLen;
				assert((uint32)pCurWritePtr < (uint32)(pWriteBuf + cWriteBufSize));
			}
		}
		//now misses
		sprintf(lineBuf, "\n[id] [misses][%% total][%% DMA pref failed]\n");
		lineLen = strlen(lineBuf);
		memcpy(pCurWritePtr, lineBuf, lineLen);
		pCurWritePtr += lineLen;

		if(misses > 0)
		{
			const float cMissesInv = 1.f / (float)misses;
			for(uint32 i=0; i<MAX_PROF_ID; ++i)
			{
				if(cpProfBufMiss[i] != 0)
				{
					const float cPercentage			= 100.f * (float)cpProfBufMiss[i] * cMissesInv;
					const float cPrefFailedPerc = 100.f * (float)cpProfBufPrefMiss[i] / (float)cpProfBufMiss[i];
					missVec.push_back(SMissData(i, cPercentage, cpProfBufMiss[i], cPrefFailedPerc));
				}
			}
			
			std::sort(missVec.begin(), missVec.end());
			const std::vector<SMissData>::const_iterator cMissEnd = missVec.end();
			for(std::vector<SMissData>::const_iterator iter = missVec.begin(); iter != cMissEnd; ++iter)
			{
				const SMissData& crMiss = *iter;
				sprintf(lineBuf, "%s%d: %s%d  %s%02.02f%%     %s%.01f%%\n",GetSpacesFromID(crMiss.id), crMiss.id, GetSpaces(crMiss.count), crMiss.count, GetSpacesFloat(crMiss.perFromTotal), crMiss.perFromTotal, GetSpacesFloat(crMiss.prefFailedPerc), crMiss.prefFailedPerc);
				lineLen = strlen(lineBuf);
				memcpy(pCurWritePtr, lineBuf, lineLen);
				pCurWritePtr += lineLen;
				assert((uint32)pCurWritePtr < (uint32)(pWriteBuf + cWriteBufSize));
			}
		}

		uint64_t writeLen;
		const uint32 cLen = (uint32)pCurWritePtr - (uint32)pWriteBuf;
		ret = cellFsWrite(out, pWriteBuf, cLen, &writeLen);
		if(ret != CELL_FS_SUCCEEDED || cLen != writeLen) 
		{
			printf("Failed to write to profiling data file: %s\n",fileBuf);
//			if(pLog)
//				pLog->LogError("Failed to write to profiling data file: %s\n",fileBuf);
		}
//		if(pLog)
//			pLog->Log("Profiling data written to %s\n",fileBuf);
//		else
			printf("Profiling data written to %s\n",fileBuf);

		delete [] pWriteBuf;

		cellFsClose(out);
	}
	delete [] fileBuf;
	delete [] lineBuf;
}
#endif //DO_SPU_PROFILING

#if defined(SUPP_SPU_FRAME_STATS)
void NPPU::CSPUMemAreaMan::GetSPUFrameStats(NPPU::SSPUFrameStats& rStats) const
{
	static const double scBaseFactor = 
		1.0 / ((double)NPPU::GetSPUTimeBaseFrequency() 
		/ 100.0/* scale up to 100% */ 
		/ (double)GetTimeBaseFrequency());
	rStats.Reset();
	const float cTickFactor = scBaseFactor / (double)(NPPU::GetTimeTB() - m_LastTime);
	for(uint32 i=0; i<NPPU::scMaxSPU; ++i)
		rStats.spuStatsPerc[i] = (float)((double)m_SPUStats.count[i] * cTickFactor);
}
#endif//SUPP_SPU_FRAME_STATS

#endif //PS3 

