#include <iostream>
#include <iomanip>
#include "Include/StressTest.h"

#ifdef __WIN_OS__
#include <windows.h>
#include <ctime>
#endif
#ifdef __LIN_OS__
#include <sys/time.h>
#include <sys/sysinfo.h>
#include <sys/mman.h>
#include <cstdlib>
#endif

bool IMCStressTest::CheckTimeout(long endClock)
{
	if (endClock == -1)
	{
		return false;
	}
#ifdef __WIN_OS__
	if (clock() >= endClock)
	{
		return true;
	}
	return false;
#else	
	struct timespec currentClock;
	clock_gettime(CLOCK_REALTIME, &currentClock);
	if (currentClock.tv_sec >= endClock)
	{
		return true;
	}
	return false;
#endif
}

unsigned long long IMCStressTest::GetFreeMemorySize()
{
#ifdef __WIN_OS__
	MEMORYSTATUSEX statex;

	statex.dwLength = sizeof (statex);

	GlobalMemoryStatusEx (&statex);

	return statex.ullAvailPhys;
#else
	struct sysinfo memInfo;
	sysinfo (&memInfo);
	long long totalPhysMem = memInfo.freeram;
	//Multiply in next statement to avoid int overflow on right hand side...
	totalPhysMem *= memInfo.mem_unit;
	return totalPhysMem;
#endif
}

bool IMCStressTest::SetProcessSize(int size)
{
#ifdef __WIN_OS__
	if (!SetProcessWorkingSetSize(GetCurrentProcess(), size, size))
	{
		return false;
	}
	return true;
#else
	return true;
#endif
}

//int IMCStressTest::Allocate(int chunkSize, int chunkTotal, bool NoCache, long startClock, long endClock)
int IMCStressTest::Allocate(int chunkSize, int chunkTotal, bool NoCache)
{
	lockSize = chunkSize;
	chunkAmount = chunkTotal;
	//_startClock = startClock;
	//_endClock = endClock;  // ss edit
#ifdef __WIN_OS__
	BOOL bLocked;                 // address of the guarded memory

	p_lpvAddr = new void*[chunkAmount];
	lpvAddr = p_lpvAddr;
	delete[] p_lpvAddr;
	///std::cout << std::endl << "p_lpvAddr = " << p_lpvAddr << std::endl;
	///std::cout << std::endl << "lpvAddr = " << lpvAddr << std::endl;
	for (int i =0; i < chunkAmount; i++)
	{
		// Try to allocate the memory.
		if (NoCache)
		{
			lpvAddr[i] = VirtualAlloc(NULL, lockSize,
				MEM_RESERVE | MEM_COMMIT,
				PAGE_READWRITE | PAGE_NOCACHE);
		}
		else
		{
			lpvAddr[i] = VirtualAlloc(NULL, lockSize,
				MEM_RESERVE | MEM_COMMIT,
				PAGE_READWRITE);
		}
		if (lpvAddr[i] == NULL) {
			for (int j =0; j < i; j++)
			{
				VirtualFree(
					lpvAddr[j],       // Base address of block
					0,             // Bytes of committed pages
					MEM_RELEASE);  // Decommit the pages
			}
			return 1;
		}	

		bLocked = VirtualLock(lpvAddr[i], lockSize);
		if (!bLocked) {
			for (int j = 0; j <= i; j++)
			{
				VirtualFree(
					lpvAddr[j],       // Base address of block
					0,             // Bytes of committed pages
					MEM_RELEASE);  // Decommit the pages
			}
			#if defined __WIN_32__ 
			#endif
			return 2;
		}

//#ifdef __WIN_OS__
//		int currentTime = (int)((clock() - _startClock) / CLOCKS_PER_SEC);
//#else
//		struct timespec tempClock;
//		clock_gettime(CLOCK_REALTIME, &tempClock);
//		int currentTime = (int)(tempClock.tv_sec - _startClock);
//#endif
//		if (CheckTimeout(_endClock) == true)
//		{
//			return 0;
//		}  // ss edit
	}
	return 0;

#else
	lpvAddr = (void**)malloc(chunkAmount * lockSize);
	for (int i =0; i < chunkAmount; i++)
	{
		lpvAddr[i] = (void*)malloc(lockSize);
		if (lpvAddr[i] == NULL)
		{
			for (int j =0; j< i; j++)
			{
				if (lpvAddr[j] != NULL)
				{
					munlock(lpvAddr[j], lockSize);
					free(lpvAddr[j]);
					lpvAddr[j] = NULL;
					free(lpvAddr);
				}
			}
			return 1;
		}
		if (mlock (lpvAddr[i], lockSize) == -1)
		{
			for (int j =0; j<chunkAmount; j++)
			{
				if (lpvAddr[j] != NULL)
				{
					munlock(lpvAddr[j], lockSize);
					free(lpvAddr[j]);
					lpvAddr[j] = NULL;
				}
			}
			free(lpvAddr[i]);
			lpvAddr[i] = NULL;
			free(lpvAddr);
			return 2;
		}
//#ifdef __WIN_OS__
//		int currentTime = (int)((clock() - _startClock) / CLOCKS_PER_SEC);
//#else
//		struct timespec tempClock;
//		clock_gettime(CLOCK_REALTIME, &tempClock);
//		int currentTime = (int)(tempClock.tv_sec - _startClock);
//#endif
//		if (CheckTimeout(_endClock) == true)
//		{
//			return 0;
//		}  // ss edit
	}
	return 0;
#endif
}

bool IMCStressTest::Test1Write()
{
	for (int i =0; i< chunkAmount; i++)
	{
		char *add =(char*)(lpvAddr[i]);
		int checkSize = lockSize/sizeof(char);
		unsigned char targetValue = 0xFF;
		for (int j=0; j< checkSize; j++)
		{
			if (targetValue < 0 || targetValue > 255)
			{
				std::cout << std::endl << "Arithmetic Overflow of variable targetValue." << std::endl;
				return false;
			}
			if (targetValue == 0)
			{
				targetValue = 0xFF;
			}
			else
			{
				targetValue = 0;
			}
			if (add[j] <= 0xFF)
			{
				add[j] = targetValue;
			}
		}

#ifdef __WIN_OS__
		int currentTime = (int)((clock() - _startClock) / CLOCKS_PER_SEC);
#else
		struct timespec tempClock;
		clock_gettime(CLOCK_REALTIME, &tempClock);
		int currentTime = (int)(tempClock.tv_sec - _startClock);
#endif
		if (CheckTimeout(_endClock) == true)
		{
			return true;
		}
	}
	return true;
}

bool IMCStressTest::Test1Verify()
{
	for (int i =0; i< chunkAmount; i++)
	{
		char *add =(char*)(lpvAddr[i]);
		int checkSize = lockSize/sizeof(char);
		long targetValue = 0xFF;
		for (int j=0; j< checkSize; j++)
		{
			if (targetValue == 0)
			{
				targetValue = 0xFF;
			}
			else
			{
				targetValue = 0;
			}
			if ((add[j] & 0xff) != targetValue)
			{
				return false;
			}			
		}
#ifdef __WIN_OS__
		int currentTime = (int)((clock() - _startClock) / CLOCKS_PER_SEC);
#else
		struct timespec tempClock;
		clock_gettime(CLOCK_REALTIME, &tempClock);
		int currentTime = (int)(tempClock.tv_sec - _startClock);
#endif
		if (CheckTimeout(_endClock) == true)
		{
			return true;
		}
	}
	return true;
}

bool IMCStressTest::Test2Write()
{
	for (int i =0; i< chunkAmount; i++)
	{
		long *add =(long*)lpvAddr[i]; 

		int checkSize = lockSize/sizeof(long);

		for (int j=0; j< checkSize; j++)
		{
			int shiftValue = j % (sizeof(long) * 8);
			add[j]= 1<<shiftValue;			
		}
#ifdef __WIN_OS__
		int currentTime = (int)((clock() - _startClock) / CLOCKS_PER_SEC);
#else
		struct timespec tempClock;
		clock_gettime(CLOCK_REALTIME, &tempClock);
		int currentTime = (int)(tempClock.tv_sec - _startClock);
#endif
		if (CheckTimeout(_endClock) == true)
		{
			return true;
		}
	}
	return true;
}

bool IMCStressTest::Test2Verify()
{
	for (int i =0; i< chunkAmount; i++)
	{
		long *add =(long*)lpvAddr[i]; 

		int checkSize = lockSize/sizeof(long);

		for (int j=0; j< checkSize; j++)
		{
			int shiftValue = j % (sizeof(long) * 8);
			int targetValue = 1<<shiftValue;
			if (add[j] != targetValue)
			{
				return false;
			}			
		}
#ifdef __WIN_OS__
		int currentTime = (int)((clock() - _startClock) / CLOCKS_PER_SEC);
#else
		struct timespec tempClock;
		clock_gettime(CLOCK_REALTIME, &tempClock);
		int currentTime = (int)(tempClock.tv_sec - _startClock);
#endif
		if (CheckTimeout(_endClock) == true)
		{
			return true;
		}
	}
	return true;
}

bool IMCStressTest::Test3Write()
{
	for (int i =0; i< chunkAmount; i++)
	{
		long *add =(long*)lpvAddr[i]; 

		int checkSize = lockSize/sizeof(long);

		for (int j=0; j < checkSize; j++)
		{
			int shiftValue = j % (sizeof(long) * 8);
			add[j] = (4294967295) & (0<<shiftValue);			
		}
#ifdef __WIN_OS__
		int currentTime = (int)((clock() - _startClock) / CLOCKS_PER_SEC);
#else
		struct timespec tempClock;
		clock_gettime(CLOCK_REALTIME, &tempClock);
		int currentTime = (int)(tempClock.tv_sec - _startClock);
#endif
		if (CheckTimeout(_endClock) == true)
		{
			return true;
		}
	}
	return true;
}

bool IMCStressTest::Test3Verify()
{
	for (int i =0; i< chunkAmount; i++)
	{
		long *add =(long*)lpvAddr[i]; 

		int checkSize = lockSize/sizeof(long);

		for (int j=0; j< checkSize; j++)
		{
			int shiftValue = j % (sizeof(long) * 8);
			int targetValue = (4294967295) & (0<<shiftValue);
			if (add[j] != targetValue)
			{
				return false;
			}			
		}
#ifdef __WIN_OS__
		int currentTime = (int)((clock() - _startClock) / CLOCKS_PER_SEC);
#else
		struct timespec tempClock;
		clock_gettime(CLOCK_REALTIME, &tempClock);
		int currentTime = (int)(tempClock.tv_sec - _startClock);
#endif
		if (CheckTimeout(_endClock) == true)
		{
			return true;
		}
	}
	return true;
}

bool IMCStressTest::Release()
{
#ifdef __WIN_OS__
	for (int i =0; i< chunkAmount; i++)
	{
		if (lpvAddr[i] != NULL)
		{
			VirtualFree(
				lpvAddr[i],       // Base address of block
				0,             // Bytes of committed pages
				MEM_RELEASE);  // Decommit the pages
		}
	}
	return true;
#else
	for (int i =0; i< chunkAmount; i++)
	{
		if (lpvAddr[i] != NULL)
		{
			munlock(lpvAddr[i], lockSize);
			free(lpvAddr[i]);
			lpvAddr[i] = NULL;
		}
	}
	free(lpvAddr);
	return true;
#endif
}
