#include <windows.h>

#include "NDTSCommonHAL.h"
#include "NDTSCommonHALConstants.h"
#include "NDTSCommonHALProtected.h"
    
      
// Turn off warning C4100 : unreferenced formal parameter for this function
#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 ;

	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, unsigned long cardID, unsigned long *pNumberOfCards)
{
	unsigned long numberOfHALsPresent ;
	NDTS_Boolean isOK ;
	unsigned long numberOfBoards, activeHALIndex ;
	unsigned long chosenEnumeratedHALIndex = 0 ;
	NDTS_HAL_ID pHALIDs[NDTS_HAL_NUMBER_OF_BUFFERED_HALS] ; 

	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 ;
	}

	// At a lack of any better ideas, for the moment, just use the first available HAL.
	// We could use some kind of enumeration callback function passed into NDTS_HAL_InitialiseCards
	// which chooses which one to use based on name/version/et cetera.
	isOK = NDTS_HAL_InitialiseHAL(&pHALIDs[chosenEnumeratedHALIndex], cardID, &activeHALIndex) ;
	if (!isOK)
		return NDTS_BFalse ;

	// 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) ;
	if (!isOK)
		return NDTS_BFalse ;

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

	*pNumberOfCards = numberOfBoards ;

	return NDTS_BTrue ;
} 

NDTSHAL_API NDTS_Boolean CALL_CONV NDTS_HAL_InitialiseCard(unsigned long 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(unsigned long 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(unsigned long 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(unsigned long 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(unsigned long 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(unsigned long cardID, unsigned long cardIndex, unsigned long address, unsigned long value)
{
	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_SetWord)(internalCardIndex, address, value) : NDTS_BFalse ;
}

NDTSHAL_API NDTS_Boolean CALL_CONV NDTS_HAL_GetWord(unsigned long cardID, unsigned long cardIndex, unsigned long address, unsigned long *pValue)
{
	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_GetWord)(internalCardIndex, address, pValue) : NDTS_BFalse ;
}

NDTSHAL_API NDTS_Boolean CALL_CONV NDTS_HAL_PerformDMARead(unsigned long cardID, unsigned long cardIndex, unsigned long localAddress, unsigned long numberOfBytes)
{
	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_PerformDMARead)(internalCardIndex, localAddress, numberOfBytes) : NDTS_BFalse ;
}

NDTSHAL_API NDTS_Boolean CALL_CONV NDTS_HAL_PerformDMAWrite(unsigned long cardID, unsigned long cardIndex, unsigned long localAddress, unsigned long numberOfBytes)
{
	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_PerformDMAWrite)(internalCardIndex, localAddress, numberOfBytes) : NDTS_BFalse ;
}

NDTSHAL_API NDTS_Boolean CALL_CONV NDTS_HAL_PerformDMATransfer(unsigned long cardID, unsigned long cardIndex, void *pData, unsigned long localAddress, unsigned long numberOfBytes, NDTS_Boolean doRead)
{
	typedef NDTS_Boolean (* FPNDTS_HAL_DMAFunction)(unsigned long 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 ;

	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)
			NDTS_HAL_ByteCopy((unsigned char *) pData + byteOffset, pCommonBuffer, numBytesInBlock) ;

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

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

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

	if (!isOK)
		return NDTS_BFalse ;

	return NDTS_BTrue ;
}
