#include <windows.h>
#include <stdio.h>
#include <crtdbg.h>
#include "SDKUtilities.h"
#include "NDTSCommonHAL.h"
#include "NDTSCommonHALConstants.h"
#include "NDTSCommonHALProtected.h"

const unsigned long ndts2100_min_tcg_address = 0x00000000;
const unsigned long ndts2100_max_tcg_address = 0x00100000;
const unsigned long ndts2100_min_reg_address = 0x00300000;
const unsigned long ndts2100_max_reg_address = 0x00305fff;
const unsigned long ndts2100_min_ram_address = 0x00400000;
const unsigned long ndts2100_max_ram_address = 0x007fffff;

const unsigned long rq_min_reg_address = 0x00500000;  
const unsigned long rq_max_reg_address = 0x006004ff;  // Include the EEPROM above the registers
const unsigned long rq_min_ram_address = 0x00000000;
const unsigned long rq_max_ram_address = 0x004fffff;  // Include the system RAM where the FPGA image is stored

      
// Turn off warning C4100 : unreferenced formal parameter for this function
//add this line
#pragma warning(push)
#pragma warning(disable : 4100) 
 
BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{ 
	switch (ul_reason_for_call)
	{ 
		case DLL_PROCESS_ATTACH :
			NDTS_HAL_DLL_Initialise() ;
			break ;
		case DLL_PROCESS_DETACH :
			NDTS_HAL_DLL_Uninitialise() ;
			break ;
		case DLL_THREAD_ATTACH :
			break ;
		case DLL_THREAD_DETACH :
			break ;
    }

    return TRUE ;
}

#pragma warning(pop)


NDTSHAL_API NDTS_Boolean CALL_CONV NDTS_HAL_Initialise()
{
	if (halGlobalState.isInitialised)
		return NDTS_BTrue ;

	// Initialise global state with zeros
	NDTS_HAL_ClearGlobalState() ;

	halGlobalState.isInitialised = NDTS_BTrue ;

	/*
	// Timing testing code
	{
		const unsigned long numberOfValues = 609712 ;
		unsigned long *pInValues = malloc(numberOfValues * sizeof(unsigned long)),
			          *pOutValues = malloc(numberOfValues * sizeof(unsigned long)) ;
		unsigned long i = 0,
			          micros = 0 ;
		LARGE_INTEGER timerFreq, timeCountA, timeCountB ;

		for (i = 0 ; i < numberOfValues ; i++)
			pInValues[i] = i ;

		QueryPerformanceFrequency(&timerFreq) ;
		QueryPerformanceCounter(&timeCountA) ;

		SDKUtilities_ByteCopy(pInValues, pOutValues, numberOfValues * sizeof(unsigned long)) ;

		QueryPerformanceCounter(&timeCountB) ;

		micros = (unsigned long) (1000000 * (timeCountB.QuadPart - timeCountA.QuadPart) / timerFreq.QuadPart) ;

		{
			FILE *fp = 0 ;
			char pString[256] ;
			
			sprintf(pString, "%dus\r\n", micros) ;

			fp = fopen("c:\\temp\\timings.txt", "a") ;
			fprintf(fp, pString) ;
			fclose(fp) ;
		}
	}
	*/

	return NDTS_BTrue ; 
}

NDTSHAL_API NDTS_Boolean CALL_CONV NDTS_HAL_Uninitialise()
{
	if (!halGlobalState.isInitialised)
		return NDTS_BTrue ;

	// Free up global state memory on heap
	NDTS_HAL_FreeGlobalState() ;

	return NDTS_BTrue ;
}
 
NDTSHAL_API NDTS_Boolean CALL_CONV NDTS_HAL_InitialiseCardType(unsigned short vendorID, unsigned short deviceID, NDTS_CardSecondaryIDs cardID, unsigned long *pNumberOfCards)
{
	unsigned long numberOfHALsPresent = 0 ;
	NDTS_Boolean isOK = NDTS_BTrue ;
	unsigned long numberOfBoards = 0, activeHALIndex ;
	unsigned long chosenEnumeratedHALIndex = 0 ;
	NDTS_HAL_ID pHALIDs[NDTS_HAL_NUMBER_OF_BUFFERED_HALS] ;
	NDTS_Boolean areHALCardsAvailable = NDTS_BFalse ;

	*pNumberOfCards = 0 ;

	if (!halGlobalState.isInitialised)
	{
		NDTS_HAL_HandleHALError(NDTS_HAL_InitialiseCardType_NotInitialised) ;
		return NDTS_BFalse ;
	}

	// Find each available HAL implementation
	isOK = NDTS_HAL_EnumerateHALs(pHALIDs, NDTS_HAL_NUMBER_OF_BUFFERED_HALS, &numberOfHALsPresent) ;
	if (!isOK)
		return NDTS_BFalse ;

	// Were any HALs found?
	if (numberOfHALsPresent == 0)
	{
		NDTS_HAL_HandleHALError(NDTS_HAL_InitialiseCardType_NoAvailableHALs) ;
		return NDTS_BFalse ;
	}

	chosenEnumeratedHALIndex = numberOfHALsPresent - 1 ;

	// Enumerate available HALs until we get one which finds the board
	do
	{
		// Initialise currently chosen HAL
		isOK = NDTS_HAL_InitialiseHAL(&pHALIDs[chosenEnumeratedHALIndex], cardID, &activeHALIndex) ;
		if (isOK)		// Get available boards supported by the active HAL and save for later
			isOK = NDTS_HAL_GetBoardsIDs(activeHALIndex, vendorID, deviceID, cardID, halGlobalState.ppHALInfos[activeHALIndex]->ppBoardIDs, NDTS_HAL_NUMBER_OF_BUFFERED_BOARD_IDS, &numberOfBoards) ;
		areHALCardsAvailable = isOK && numberOfBoards > 0 ;
		if (!areHALCardsAvailable)
		{
			NDTS_HAL_UninitialiseLastActiveHAL() ;
			--chosenEnumeratedHALIndex ;
		}
	}
	while (!areHALCardsAvailable && chosenEnumeratedHALIndex != (unsigned long) -1) ;

	if (!areHALCardsAvailable)
		return NDTS_BFalse ;

	// Set up some defaults
	halGlobalState.ppHALInfos[activeHALIndex]->numberOfCardsPresent = numberOfBoards ;
	halGlobalState.ppHALInfos[activeHALIndex]->numberOfActiveCards = 0 ;

	isOK = numberOfBoards > 0 ;
	if (!isOK)
		NDTS_HAL_UninitialiseLastActiveHAL() ;

	*pNumberOfCards = numberOfBoards ;

	return isOK ;
} 

NDTSHAL_API NDTS_Boolean CALL_CONV NDTS_HAL_InitialiseCard(NDTS_CardSecondaryIDs cardID, unsigned long cardIndex)
{
	NDTS_HAL_Info *pHALInfo ;
	NDTS_Boolean isHALFound ;

	// Find the HAL for the cardID
	isHALFound = NDTS_HAL_GetHALInfo(cardID, &pHALInfo) ;

	// Register the board with the HAL
	return isHALFound ? NDTS_HAL_RegisterBoard(pHALInfo->ppBoardIDs[cardIndex], pHALInfo) : NDTS_BFalse ;
}
   
NDTSHAL_API NDTS_Boolean CALL_CONV NDTS_HAL_UninitialiseCard(NDTS_CardSecondaryIDs cardID, unsigned long cardIndex)
{ 
	NDTS_HAL_Info *pHALInfo ;
	NDTS_Boolean isHALFound ;

	// Find the HAL and internal card index for the cardID and cardIndex
	isHALFound = NDTS_HAL_GetHALInfo(cardID, &pHALInfo) ;

	// Unregister the board with the HAL
	return isHALFound ? NDTS_HAL_UnregisterBoard(pHALInfo->ppBoardIDs[cardIndex], pHALInfo) : NDTS_BFalse ;
} 

NDTSHAL_API NDTS_Boolean CALL_CONV NDTS_HAL_OpenDMAChannel(NDTS_CardSecondaryIDs cardID, unsigned long cardIndex)
{
	NDTS_HAL_Info *pHALInfo ;
	NDTS_Boolean isHALFound ;
	unsigned long internalCardIndex ;

	// Find the HAL and internal card index for the cardID and cardIndex
	isHALFound = NDTS_HAL_GetHALData(cardID, cardIndex, &pHALInfo, &internalCardIndex) ;

	// Delegate to HAL implementation
	return isHALFound ? (*pHALInfo->pFuncs->fpNDTS_HAL_OpenDMAChannel)(internalCardIndex) : NDTS_BFalse ;
}

NDTSHAL_API NDTS_Boolean CALL_CONV NDTS_HAL_CloseDMAChannel(NDTS_CardSecondaryIDs cardID, unsigned long cardIndex)
{
	NDTS_HAL_Info *pHALInfo ;
	NDTS_Boolean isHALFound ;
	unsigned long internalCardIndex ;

	// Find the HAL and internal card index for the cardID and cardIndex
	isHALFound = NDTS_HAL_GetHALData(cardID, cardIndex, &pHALInfo, &internalCardIndex) ;

	// Delegate to HAL implementation
	return isHALFound ? (*pHALInfo->pFuncs->fpNDTS_HAL_CloseDMAChannel)(internalCardIndex) : NDTS_BFalse ;
}

NDTSHAL_API NDTS_Boolean CALL_CONV NDTS_HAL_GetCommonBufferInfo(NDTS_CardSecondaryIDs cardID, unsigned long cardIndex, void **ppPhysicalAddress, unsigned long *pNumberOfBufferBytes)
{
	NDTS_HAL_Info *pHALInfo ;
	NDTS_Boolean isHALFound ;
	unsigned long internalCardIndex ;

	// Find the HAL and internal card index for the cardID and cardIndex
	isHALFound = NDTS_HAL_GetHALData(cardID, cardIndex, &pHALInfo, &internalCardIndex) ;

	// Delegate to HAL implementation
	return isHALFound ? (*pHALInfo->pFuncs->fpNDTS_HAL_GetCommonBufferInfo)(internalCardIndex, ppPhysicalAddress, pNumberOfBufferBytes) : NDTS_BFalse ;
}

NDTSHAL_API NDTS_Boolean CALL_CONV NDTS_HAL_SetWord(NDTS_CardSecondaryIDs cardID, unsigned long cardIndex, unsigned long address, unsigned long value)
{
	NDTS_HAL_Info *pHALInfo ;
	NDTS_Boolean isHALFound ;
	unsigned long internalCardIndex ;

	_ASSERTE(cardID != NDTS_SID2100 ||
			(address >= ndts2100_min_reg_address && address <= ndts2100_max_reg_address) || 
			(address >= ndts2100_min_tcg_address && address <= ndts2100_max_tcg_address) || 
			(address >= ndts2100_min_ram_address && address <= ndts2100_max_ram_address));

	_ASSERTE(cardID != NDTS_SIDRapiDAQ ||
			(address >= rq_min_reg_address && address <= rq_max_reg_address) || 
			(address >= rq_min_ram_address && address <= rq_max_ram_address));

	// Find the HAL and internal card index for the cardID and cardIndex
	isHALFound = NDTS_HAL_GetHALData(cardID, cardIndex, &pHALInfo, &internalCardIndex) ;

	// Delegate to HAL implementation
	return isHALFound ? (*pHALInfo->pFuncs->fpNDTS_HAL_SetWord)(internalCardIndex, address, value) : NDTS_BFalse ;
}

NDTSHAL_API NDTS_Boolean CALL_CONV NDTS_HAL_GetWord(NDTS_CardSecondaryIDs cardID, unsigned long cardIndex, unsigned long address, unsigned long *pValue)
{
	NDTS_HAL_Info *pHALInfo ;
	NDTS_Boolean isHALFound ;
	unsigned long internalCardIndex ;

	_ASSERTE(cardID != NDTS_SID2100 ||
			(address >= ndts2100_min_reg_address && address <= ndts2100_max_reg_address) || 
			(address >= ndts2100_min_tcg_address && address <= ndts2100_max_tcg_address) || 
			(address >= ndts2100_min_ram_address && address <= ndts2100_max_ram_address));

	_ASSERTE(cardID != NDTS_SIDRapiDAQ ||
			(address >= rq_min_reg_address && address <= rq_max_reg_address) || 
			(address >= rq_min_ram_address && address <= rq_max_ram_address));

	// Find the HAL and internal card index for the cardID and cardIndex
	isHALFound = NDTS_HAL_GetHALData(cardID, cardIndex, &pHALInfo, &internalCardIndex) ;

	// Delegate to HAL implementation
	return isHALFound ? (*pHALInfo->pFuncs->fpNDTS_HAL_GetWord)(internalCardIndex, address, pValue) : NDTS_BFalse ;
}

NDTSHAL_API NDTS_Boolean CALL_CONV NDTS_HAL_PerformDMARead(NDTS_CardSecondaryIDs cardID, unsigned long cardIndex, unsigned long localAddress, unsigned long numberOfBytes)
{
	NDTS_HAL_Info *pHALInfo ;
	NDTS_Boolean isHALFound ;
	unsigned long internalCardIndex ;

	_ASSERTE(cardID != NDTS_SID2100 ||
			(localAddress >= ndts2100_min_reg_address && localAddress <= ndts2100_max_reg_address) || 
			(localAddress >= ndts2100_min_tcg_address && localAddress <= ndts2100_max_tcg_address) || 
			(localAddress >= ndts2100_min_ram_address && localAddress <= ndts2100_max_ram_address));

	_ASSERTE(cardID != NDTS_SIDRapiDAQ ||
			(localAddress >= rq_min_reg_address && localAddress <= rq_max_reg_address) || 
			(localAddress >= rq_min_ram_address && localAddress <= rq_max_ram_address));

	// Find the HAL and internal card index for the cardID and cardIndex
	isHALFound = NDTS_HAL_GetHALData(cardID, cardIndex, &pHALInfo, &internalCardIndex) ;

	// Delegate to HAL implementation
	return isHALFound ? (*pHALInfo->pFuncs->fpNDTS_HAL_PerformDMARead)(internalCardIndex, localAddress, numberOfBytes) : NDTS_BFalse ;
}

NDTSHAL_API NDTS_Boolean CALL_CONV NDTS_HAL_PerformDMAWrite(NDTS_CardSecondaryIDs cardID, unsigned long cardIndex, unsigned long localAddress, unsigned long numberOfBytes)
{
	NDTS_HAL_Info *pHALInfo ;
	NDTS_Boolean isHALFound ;
	unsigned long internalCardIndex ;

	_ASSERTE(cardID != NDTS_SID2100 ||
			(localAddress >= ndts2100_min_reg_address && localAddress <= ndts2100_max_reg_address) || 
			(localAddress >= ndts2100_min_tcg_address && localAddress <= ndts2100_max_tcg_address) || 
			(localAddress >= ndts2100_min_ram_address && localAddress <= ndts2100_max_ram_address));

	_ASSERTE(cardID != NDTS_SIDRapiDAQ ||
			(localAddress >= rq_min_reg_address && localAddress <= rq_max_reg_address) || 
			(localAddress >= rq_min_ram_address && localAddress <= rq_max_ram_address));

	// Find the HAL and internal card index for the cardID and cardIndex
	isHALFound = NDTS_HAL_GetHALData(cardID, cardIndex, &pHALInfo, &internalCardIndex) ;

	// Delegate to HAL implementation
	return isHALFound ? (*pHALInfo->pFuncs->fpNDTS_HAL_PerformDMAWrite)(internalCardIndex, localAddress, numberOfBytes) : NDTS_BFalse ;
}

NDTSHAL_API NDTS_Boolean CALL_CONV NDTS_HAL_PerformDMATransfer(NDTS_CardSecondaryIDs cardID, unsigned long cardIndex, void *pData, unsigned long localAddress, unsigned long numberOfBytes, NDTS_Boolean doRead)
{
	typedef NDTS_Boolean (* FPNDTS_HAL_DMAFunction)(NDTS_CardSecondaryIDs cardID, unsigned long cardIndex, unsigned long localAddress, unsigned long numberOfBytes) ;
	
	unsigned long numberOfBufferBytes, numBytesToTransfer, byteOffset, numBytesInBlock ;
	void *pCommonBuffer ;
	FPNDTS_HAL_DMAFunction pDMAFunction ;
	NDTS_Boolean isOK ;

	_ASSERTE(cardID != NDTS_SID2100 ||
			(localAddress >= ndts2100_min_reg_address && localAddress <= ndts2100_max_reg_address) || 
			(localAddress >= ndts2100_min_tcg_address && localAddress <= ndts2100_max_tcg_address) || 
			(localAddress >= ndts2100_min_ram_address && localAddress <= ndts2100_max_ram_address));

	_ASSERTE(cardID != NDTS_SIDRapiDAQ ||
			(localAddress >= rq_min_reg_address && localAddress <= rq_max_reg_address) || 
			(localAddress >= rq_min_ram_address && localAddress <= rq_max_ram_address));

	isOK = NDTS_HAL_GetCommonBufferInfo(cardID, cardIndex, &pCommonBuffer, &numberOfBufferBytes) ;
	if (!isOK)
		return NDTS_BFalse ;

	pDMAFunction = doRead ? &NDTS_HAL_PerformDMARead : NDTS_HAL_PerformDMAWrite ;

	numBytesToTransfer = numberOfBytes ;
	byteOffset = 0 ;

	do
	{
		numBytesInBlock = numBytesToTransfer > numberOfBufferBytes ? numberOfBufferBytes : numBytesToTransfer ;

		if (!doRead)
			SDKUtilities_ByteCopy((unsigned char *) pData + byteOffset, pCommonBuffer, numBytesInBlock) ;

		isOK = (*pDMAFunction)(cardID, cardIndex, localAddress + byteOffset, numBytesInBlock) ;

		if (doRead)
			SDKUtilities_ByteCopy(pCommonBuffer, (unsigned char *) pData + byteOffset, numBytesInBlock) ;

		numBytesToTransfer -= numBytesInBlock ;
		byteOffset += numBytesInBlock ;
	}
	while (numBytesToTransfer > 0 && isOK) ;

	return isOK ;
}
