////////////////////////////////////////////////////////////////////////////// 
//
// AVP_SmokeTest.cpp
//
// This class is used to store some data and to handle the input
// when we are doing a smoke test in the AVP_DevelopmentFrontEnd class
// i.e. leave when it have been "playing" for a set amount of seconds
//
//////////////////////////////////////////////////////////////////////////////

#include "Core/Asura.h"

#ifdef AVP_LOG_FILE_LOAD_MEMORY_USE

#include "Core/AVP_SmokeTest.h"

#ifdef ASURA_PLATFORM_IS_BIG_ENDIAN
#include "ChunkSystem/Asura_Chunk_Loaders.h"
#endif
#include "Core/Asura_LocalisedText.h"
#include "Core/Asura_Timers.h"
#include "Core/AVP_Core.h"
#include "Core/AVP_StateMachine_Core.h"
#include "Core/AVP_StateMachine_Playing.h"
#include "Debugging/Asura_Logfile.h"
#include "Debugging/Asura_DebugText.h"
#include "FileAccess/Asura_FileAccess.h"
#include "FrontEnd/AVP_DevelopmentFrontEnd.h"

static const char* szSMOKE_TEST_LOG_FILE_NAME = "logfiles\\SmokeTestLog.txt";
static const char* szSMOKE_TEST_FPS_SPREADSHEET = "logfiles\\LevelFPS.csv";
static const char* szSMOKE_TEST_MEMORY_SPREADSHEET = "logfiles\\LoadingMemoryUse.csv";

// If you change these values you also need to change them in the AVP_SmokeTest::ResetData() function
u_int  AVP_SmokeTest::s_uMemoryUsageOutput[5] = {0, 0, 0, 0, 0};
int    AVP_SmokeTest::s_iMemoryBeginData = 0;
int    AVP_SmokeTest::s_iMemoryEndData = 0;
int	   AVP_SmokeTest::s_iNumberOfFrameRatesToFetchEveryLevel = 600;
int    AVP_SmokeTest::s_iNumberOfFrameRatesFetched = 0;
float  AVP_SmokeTest::s_fHighestFPS = -100.f;
float  AVP_SmokeTest::s_fLowestFPS = 100.f;
float  AVP_SmokeTest::s_fSummarizedFrameRate = 0;
bool   AVP_SmokeTest::s_bDoingSmokeTest = false;
bool   AVP_SmokeTest::s_bFetchedDataFromFirstFrame = false;
bool   AVP_SmokeTest::s_bHasFileLoadMemoryUseData = false;
bool   AVP_SmokeTest::s_bLoggingAMap = false;
bool   AVP_SmokeTest::s_bPlaying = false;
bool   AVP_SmokeTest::s_bNegativeDifference = false;

AVP_SmokeTest::FileLoadMemoryUseData AVP_SmokeTest::s_xCurrentFileLoadMemoryUseData;

void AVP_SmokeTest::ResetData()
{
	s_fHighestFPS = -100.f;
	s_fLowestFPS = 100.f;
	s_iMemoryBeginData = 0;
	s_iMemoryEndData = 0;
	s_iNumberOfFrameRatesFetched = 0;
	s_fSummarizedFrameRate = 0;
	s_bFetchedDataFromFirstFrame = false;
}

void AVP_SmokeTest::ClearLogFiles()
{
	Asura_SimpleLogfileClear(szSMOKE_TEST_LOG_FILE_NAME);
	Asura_SimpleLogfileOutput((szSMOKE_TEST_LOG_FILE_NAME, "\r\n"));
	Asura_SimpleLogfileClear(szSMOKE_TEST_MEMORY_SPREADSHEET);
	Asura_SimpleLogfileOutput((szSMOKE_TEST_MEMORY_SPREADSHEET, "File name,Size,Initial free,Final free,Allocated mem,Difference,Memory first frame, Memory last frame\n"));
	Asura_SimpleLogfileClear(szSMOKE_TEST_FPS_SPREADSHEET);
	Asura_SimpleLogfileOutput((szSMOKE_TEST_FPS_SPREADSHEET, "File name,Average FPS,Highest FPS,Lowest FPS\r\n"));
}

void AVP_SmokeTest::Update()
{
	if( s_bPlaying  )
	{
		if( !s_bFetchedDataFromFirstFrame )
		{
			s_iMemoryBeginData = AVP_Core::GetAvailableMemory();
			s_bFetchedDataFromFirstFrame = true;
		}
		
		float fFrameRate = Asura_Timers::GetFrameRate();

		if( s_fLowestFPS > fFrameRate )
		{
			s_fLowestFPS = fFrameRate;
		}
		if( s_fHighestFPS < fFrameRate )
		{
			s_fHighestFPS = fFrameRate;
		}

		s_fSummarizedFrameRate += fFrameRate;
		s_iNumberOfFrameRatesFetched++;

		if( s_iNumberOfFrameRatesFetched >= s_iNumberOfFrameRatesToFetchEveryLevel )
		{
			s_bPlaying = false;
			if( s_bDoingSmokeTest )
			{
				s_iMemoryEndData = AVP_Core::GetAvailableMemory();
				AVP_Core::RequestGameExit();
			}
		}
	}
}

void AVP_SmokeTest::PrintOutputToLog()
{
	float fAverageFPS = s_fSummarizedFrameRate / static_cast<float>(s_iNumberOfFrameRatesFetched);
	
	// Send the log data to the logfile
	Asura_SimpleLogfileOutput(( szSMOKE_TEST_LOG_FILE_NAME, 
		"Memory first frame: %d bytes (%.1f MB)\r\nMemory last frame: %d bytes (%.1f MB)\r\nAverage fps: %.1f\r\nHighestFPS: %.1f\r\nLowestFPS: %.1f\r\n\r\n", 
		s_iMemoryBeginData,
		static_cast<float>( s_iMemoryBeginData ) / 1048576.0f,
		s_iMemoryEndData,
		static_cast<float>( s_iMemoryEndData ) / 1048576.0f,
		fAverageFPS,
		s_fHighestFPS,
		s_fLowestFPS));

	// Send the fps data and the memory data to there respective csv files
	Asura_SimpleLogfileOutput((szSMOKE_TEST_FPS_SPREADSHEET, "%s,%.1f,%.1f,%.1f\n", AVP_Settings::GetLevelName(), fAverageFPS, s_fHighestFPS, s_fLowestFPS ));
	if( s_bNegativeDifference )
	{
		Asura_SimpleLogfileOutput((szSMOKE_TEST_MEMORY_SPREADSHEET, "%s,%d,%d,%d,%d,-%d,%d,%d\n\n",
			AVP_Settings::GetLevelName(),
			s_uMemoryUsageOutput[0], s_uMemoryUsageOutput[1], s_uMemoryUsageOutput[2], s_uMemoryUsageOutput[3],
			s_uMemoryUsageOutput[4], s_iMemoryBeginData, s_iMemoryEndData ));
	}
	else
	{
		Asura_SimpleLogfileOutput((szSMOKE_TEST_MEMORY_SPREADSHEET, "%s,%d,%d,%d,%d,%d,%d,%d\n\n",
			AVP_Settings::GetLevelName(),
			s_uMemoryUsageOutput[0], s_uMemoryUsageOutput[1], s_uMemoryUsageOutput[2], s_uMemoryUsageOutput[3],
			s_uMemoryUsageOutput[4], s_iMemoryBeginData, s_iMemoryEndData ));
	}

}

void AVP_SmokeTest::SetNumberOfFrameratesToGet( int iNumber )
{
	s_iNumberOfFrameRatesToFetchEveryLevel = iNumber;
}

void AVP_SmokeTest::SetPlaying( bool bPlaying )
{
	AVP_SmokeTest::s_bPlaying = bPlaying;
	
	if( bPlaying == false && !s_bDoingSmokeTest )
	{
		PrintOutputToLog();
		ResetData();
	}
}

void AVP_SmokeTest::SetRunningSmokeTest( bool bRunSmokeTest )
{
	s_bDoingSmokeTest = bRunSmokeTest;
	AsuraAssert_SetIgnoreAll( bRunSmokeTest );
}

void AVP_SmokeTest::LogBeginFileLoad(const char* szFilename)
{
	Asura_Assert(!s_bHasFileLoadMemoryUseData, "File load memory use logging doesn't support recursive loading.");
	if (s_bHasFileLoadMemoryUseData) { return; }

	s_bLoggingAMap = false;

	u_int uAvailableMemory = AVP_Core::GetAvailableMemory();
	if (uAvailableMemory != 0) // Use 0 as meaning this platform doesn't bother doing the tests (probably just PC).
	{
		s_xCurrentFileLoadMemoryUseData.m_uMemoryAvailableAtStart = uAvailableMemory;
		s_xCurrentFileLoadMemoryUseData.m_uFileSize = CalculateTotalSizeOfFiles(szFilename);

		float fFileSizeInMegabytes = static_cast<float>(s_xCurrentFileLoadMemoryUseData.m_uFileSize);
		fFileSizeInMegabytes /= 1048576.0f;
		Asura_SimpleLogfileOutput((szSMOKE_TEST_LOG_FILE_NAME, "Begun loading %s\r\n", szFilename));
		Asura_SimpleLogfileOutput((szSMOKE_TEST_LOG_FILE_NAME, "File size (including subfiles): %d bytes (%.1f MB)\r\n", s_xCurrentFileLoadMemoryUseData.m_uFileSize, fFileSizeInMegabytes));
		Asura_SimpleLogfileOutput((szSMOKE_TEST_LOG_FILE_NAME, "Memory available at start: %d bytes (%.1f MB)\r\n", uAvailableMemory, static_cast<float>(uAvailableMemory)/1048576.0f ));
		
		//If we are not logging a map print the data now, otherwise save the data to print at a later state with more information
		if( !s_bLoggingAMap )
		{
			Asura_SimpleLogfileOutput((szSMOKE_TEST_MEMORY_SPREADSHEET, "%s,%d,%d,", szFilename, s_xCurrentFileLoadMemoryUseData.m_uFileSize, s_xCurrentFileLoadMemoryUseData.m_uMemoryAvailableAtStart));
		}
		else
		{
			s_uMemoryUsageOutput[0] = s_xCurrentFileLoadMemoryUseData.m_uFileSize;
			s_uMemoryUsageOutput[1] = s_xCurrentFileLoadMemoryUseData.m_uMemoryAvailableAtStart;
		}
	}

	s_bHasFileLoadMemoryUseData = true;
}


void AVP_SmokeTest::LogEndFileLoad()
{
	Asura_Assert(s_bHasFileLoadMemoryUseData, "Can't end file load memory use logging when it hasn't begun.");
	if (!s_bHasFileLoadMemoryUseData) { return; }

	u_int uAvailableMemory = AVP_Core::GetAvailableMemory();
	if (uAvailableMemory != 0) // Use 0 as meaning this platform doesn't bother doing the tests (probably just PC).
	{
		u_int uMemoryUsed = s_xCurrentFileLoadMemoryUseData.m_uMemoryAvailableAtStart - uAvailableMemory;

		float fMemoryUsedInMegabytes = static_cast<float>(uMemoryUsed);
		fMemoryUsedInMegabytes /= 1048576.0f;

		Asura_SimpleLogfileOutput((szSMOKE_TEST_LOG_FILE_NAME, "Memory allocated: %d bytes (%.1f MB)\r\n", uMemoryUsed, fMemoryUsedInMegabytes));

		if (uMemoryUsed != s_xCurrentFileLoadMemoryUseData.m_uFileSize)
		{
			if (uMemoryUsed > s_xCurrentFileLoadMemoryUseData.m_uFileSize)
			{
				u_int uDifference = uMemoryUsed - s_xCurrentFileLoadMemoryUseData.m_uFileSize;
				float fDiffInMegabytes = static_cast<float>(uDifference);
				fDiffInMegabytes /= 1048576.0f;

				Asura_SimpleLogfileOutput((szSMOKE_TEST_LOG_FILE_NAME, "Allocated %d bytes (%.1f MB) more than size of file.\r\n", uDifference, fDiffInMegabytes));
				if( !s_bLoggingAMap )
				{
					Asura_SimpleLogfileOutput((szSMOKE_TEST_MEMORY_SPREADSHEET, "%d,%d,%d\n", uAvailableMemory, uMemoryUsed, uDifference));
				}
				else
				{
					s_bNegativeDifference = false;
					s_uMemoryUsageOutput[2] = uAvailableMemory;
					s_uMemoryUsageOutput[3] = uMemoryUsed;
					s_uMemoryUsageOutput[4] = uDifference;
				}
			}
			else
			{
				u_int uDifference = s_xCurrentFileLoadMemoryUseData.m_uFileSize - uMemoryUsed;
				float fDiffInMegabytes = static_cast<float>(uDifference);
				fDiffInMegabytes /= 1048576.0f;

				Asura_SimpleLogfileOutput((szSMOKE_TEST_LOG_FILE_NAME, "File size %d bytes (%.1f MB) greater than memory allocated.\r\n", uDifference, fDiffInMegabytes));
				if( !s_bLoggingAMap )
				{
					Asura_SimpleLogfileOutput((szSMOKE_TEST_MEMORY_SPREADSHEET, "%d,%d,-%d\n", uAvailableMemory, uMemoryUsed, uDifference));
				}
				else
				{
					s_bNegativeDifference = true;
					s_uMemoryUsageOutput[2] = uAvailableMemory;
					s_uMemoryUsageOutput[3] = uMemoryUsed;
					s_uMemoryUsageOutput[4] = uDifference;
				}
			}
		}
		else
		{
			Asura_SimpleLogfileOutput((szSMOKE_TEST_LOG_FILE_NAME, "Memory allocated matches file size.\r\n"));
			if( !s_bLoggingAMap )
			{
				Asura_SimpleLogfileOutput((szSMOKE_TEST_MEMORY_SPREADSHEET, "%d,%d,0\n", uAvailableMemory, uMemoryUsed));
			}
			else
			{
				s_uMemoryUsageOutput[2] = uAvailableMemory;
				s_uMemoryUsageOutput[3] = uMemoryUsed;
				s_uMemoryUsageOutput[4] = 0;
			}

		}
		
		Asura_SimpleLogfileOutput((szSMOKE_TEST_LOG_FILE_NAME, "Memory available after loading: %d bytes (%.1f MB)\r\n",uAvailableMemory, static_cast<float>(uAvailableMemory)/1048576.0f));
		
		Asura_SimpleLogfileOutput((szSMOKE_TEST_LOG_FILE_NAME, "Load complete.\r\n\r\n"));
	}

	s_bHasFileLoadMemoryUseData = false;
}


u_int AVP_SmokeTest::CalculateTotalSizeOfFiles(const char* szFilename)
{
	u_int uSize = Asura_FileAccess::GetFileSize(szFilename);

	// If this is a mission file, we need to additionally find both the file containing the main environment (if it isn't in the first file) and the localised sounds file.
	Asura_String xBaseFilename(szFilename);
	xBaseFilename.MakeUppercase();
	if (xBaseFilename.Contains("ENVS\\"))
	{
		s_bLoggingAMap = true;
		Asura_String xPath, xName, xExtension;
		xBaseFilename.SplitFilename(&xPath, &xName, &xExtension);

		// Every level may have a localised text file.
		Asura_String xNewFile = xPath;
		xNewFile += "\\";
		xNewFile += xName;
		xNewFile += ".";
		xNewFile += Asura_LocalisedText::GetTwoCharCodeForCurLang();
#ifdef ASURA_PLATFORM_IS_BIG_ENDIAN
		xNewFile += ASURA_STANDARD_FILE_EXTENSION_BE_SUFFIX;
#endif
		uSize += Asura_FileAccess::GetFileSize(xNewFile.GetString());

		bool bHasSeparateEnvironment = true;
		// HAHAHAHAHA!
		if (xName.Contains("COLONY"))
		{
			xName = "COLONY";
		}
		else if (xName.Contains("JUNGLE"))
		{
			xName = "JUNGLE";
		}
		else if (xName.Contains("LAB"))
		{
			xName = "LAB";
		}
		else if (xName.Contains("PYRAMID"))
		{
			xName = "PYRAMID";
		}
		else if (xName.Contains("REFINERY"))
		{
			xName = "REFINERY";
		}
		else if (xName.Contains("RUINS"))
		{
			xName = "RUINS";
		}
		else
		{
			bHasSeparateEnvironment = false;
		}

		if (bHasSeparateEnvironment)
		{
			xNewFile = xPath;
			xNewFile += "\\";
			xNewFile += xName;
			xNewFile += ".";
			xNewFile += xExtension;

			uSize += Asura_FileAccess::GetFileSize(xNewFile.GetString());
		}
	}

	return uSize;
}

#endif // #ifdef AVP_LOG_FILE_LOAD_MEMORY_USE