#include <crtdbg.h>

#include "NDTSCommonHALProtected.h"
#include "NDTSCommonHALEntryPoints.h"
#include "NDTSCommonHALErrors.h"

NDTS_HAL_GlobalState halGlobalState ;


NDTSHAL_API NDTS_Boolean NDTS_HAL_EnumerateHALs(NDTS_HAL_ID *pHALIDs, unsigned long numberOfBufferedIDs, unsigned long *pNumberOfHALsPresent)
{
	NDTS_Boolean isOK ;
	unsigned long i = 0, numberOfHALsPresent = 0 ;

	_ASSERTE(pHALIDs != 0) ;
	_ASSERTE(pNumberOfHALsPresent != 0) ;

	// Get HAL IDs for each registered implementation
	while (i < numberOfBufferedIDs && i < NDTS_HAL_NUMBER_OF_IMPLEMENTATIONS)
	{
		isOK = (*pfpNDTS_HAL_GetHALIDs[i])(&pHALIDs[numberOfHALsPresent]) ;
		if (isOK)
		{
			// Check HAL name is not too long
			_ASSERT(strlen(pHALIDs[numberOfHALsPresent].pName) < NDTS_HAL_NAME_STRING_MAXIMUM_LENGTH) ;

			if (pHALIDs[numberOfHALsPresent].isAvailable)
				numberOfHALsPresent++ ;
		}

		i++ ;
	}

	*pNumberOfHALsPresent = numberOfHALsPresent ;

	// Was the buffer passed in two small?
	if (numberOfBufferedIDs < NDTS_HAL_NUMBER_OF_IMPLEMENTATIONS)
	{
		NDTS_HAL_HandleHALError(NDTS_HAL_EnumerateHALs_InsufficientHALInputBufferSize) ;
		return NDTS_BFalse ;
	}

	return NDTS_BTrue ;
}

NDTSHAL_API NDTS_Boolean NDTS_HAL_InitialiseHAL(NDTS_HAL_ID *pHALID, NDTS_CardSecondaryIDs cardID, unsigned long *pHALIndex)
{
	unsigned long halIndex = halGlobalState.numberOfActiveHALs ;
	NDTS_Boolean isOK = NDTS_BTrue ;

	_ASSERTE(pHALID != 0) ;
	_ASSERTE(pHALIndex != 0) ;
 
	// Grab some memory for the HAL_Info
	halGlobalState.ppHALInfos[halIndex] = (NDTS_HAL_Info *) malloc(sizeof(NDTS_HAL_Info)) ;
	// Set up some defaults
	halGlobalState.ppHALInfos[halIndex]->numberOfActiveCards = 0 ;
	halGlobalState.ppHALInfos[halIndex]->numberOfCardsPresent = 0 ;
	halGlobalState.ppHALInfos[halIndex]->secondaryDeviceID = cardID ;
	// Save the HAL ID
	halGlobalState.ppHALInfos[halIndex]->id = *pHALID ;

	// Grab some memory for the HAL_Info function pointers
	halGlobalState.ppHALInfos[halIndex]->pFuncs = (NDTS_HAL_Funcs *) malloc(sizeof(NDTS_HAL_Funcs)) ;

	// Clear down function pointers
	NDTS_HAL_ClearHALFunctionPointers(halGlobalState.ppHALInfos[halIndex]->pFuncs) ;
	// Fill in function pointers
	isOK = (*pHALID->fpNDTS_HAL_InitialiseHAL)(halGlobalState.ppHALInfos[halIndex]->pFuncs) ;
	if (!isOK)
	{
		// Clean up memory on heap
		free(halGlobalState.ppHALInfos[halIndex]->pFuncs) ;
		free(halGlobalState.ppHALInfos[halIndex]) ;
		halGlobalState.ppHALInfos[halIndex] = 0 ;
		return NDTS_BFalse ;
	}
	// Check function pointers
	isOK = NDTS_HAL_CheckHALFunctionPointers(halGlobalState.ppHALInfos[halIndex]->pFuncs) ;
	if (!isOK)
	{
		// Clean up memory on heap
		free(halGlobalState.ppHALInfos[halIndex]->pFuncs) ;
		free(halGlobalState.ppHALInfos[halIndex]) ;
		halGlobalState.ppHALInfos[halIndex] = 0 ;
		NDTS_HAL_HandleHALError(NDTS_HAL_InitialiseHAL_MissingFunctionPointers) ;
		return NDTS_BFalse ;
	}

	// Output new HAL index and increment active HAL count
	*pHALIndex = halIndex ;
	halGlobalState.numberOfActiveHALs++ ;

	return NDTS_BTrue ;
}

NDTSHAL_API void NDTS_HAL_UninitialiseLastActiveHAL()
{
	--halGlobalState.numberOfActiveHALs ;
	free(halGlobalState.ppHALInfos[halGlobalState.numberOfActiveHALs]->pFuncs) ;
	free(halGlobalState.ppHALInfos[halGlobalState.numberOfActiveHALs]) ;
	halGlobalState.ppHALInfos[halGlobalState.numberOfActiveHALs] = 0 ;
}

NDTSHAL_API NDTS_Boolean NDTS_HAL_GetBoardsIDs(unsigned long halIndex, unsigned short vendorID, unsigned short deviceID, NDTS_CardSecondaryIDs cardID, NDTS_HAL_BoardID **ppBoardIDs, unsigned long numberOfBufferedBoardIDs, unsigned long *pNumberOfBoards)
{
	_ASSERTE(ppBoardIDs != 0) ;
	_ASSERTE(pNumberOfBoards != 0) ;

	*pNumberOfBoards = 0 ;
	
	// Delegate this to HAL implementation
	return (*halGlobalState.ppHALInfos[halIndex]->pFuncs->fpNDTS_HAL_GetBoardsIDs)(vendorID, deviceID, cardID, ppBoardIDs, numberOfBufferedBoardIDs, pNumberOfBoards) ;
}

NDTSHAL_API NDTS_Boolean NDTS_HAL_RegisterBoard(NDTS_HAL_BoardID *pBoardID, NDTS_HAL_Info *pHALInfo)
{
	unsigned long i = 0 ;
	NDTS_Boolean isSecIDFound = NDTS_BFalse, 
		         isOK = NDTS_BTrue ;

	_ASSERTE(pBoardID != 0) ;
	_ASSERTE(pHALInfo != 0) ;

	// Is the HAL already associated with a different secondary device ID?
	if (pHALInfo->secondaryDeviceID != pBoardID->secondaryDeviceID)
	{
		NDTS_HAL_HandleHALError(NDTS_HAL_RegisterBoard_DifferentCardTypeAlreadyAssignedToHAL) ;
		return NDTS_BFalse ;
	}

	// Is a different HAL already associated with the secondary device ID?
	i = 0 ;
	do
	{
		isSecIDFound = halGlobalState.ppHALInfos[i]->secondaryDeviceID == pBoardID->secondaryDeviceID &&
			           pHALInfo != halGlobalState.ppHALInfos[i] ? NDTS_BTrue : NDTS_BFalse ;
		i++ ;
	}
	while (!isSecIDFound && i < halGlobalState.numberOfActiveHALs) ;
	if (isSecIDFound)
	{
		NDTS_HAL_HandleHALError(NDTS_HAL_RegisterBoard_CardTypeAlreadyAssignedToDifferentHAL) ;
		return NDTS_BFalse ;
	}

	// Set board as active
	pBoardID->isActive = NDTS_BTrue ;

	// Tell HAL implementation to add the board
	isOK = (*pHALInfo->pFuncs->fpNDTS_HAL_AddBoard)(pBoardID) ;
	if (isOK)
		++pHALInfo->numberOfActiveCards ;

	return isOK ;
}

NDTSHAL_API NDTS_Boolean NDTS_HAL_UnregisterBoard(NDTS_HAL_BoardID *pBoardID, NDTS_HAL_Info *pHALInfo)
{
	// Set card as inactive
	pBoardID->isActive = NDTS_BFalse ;

	// Tell HAL implementation to remove the board
	return (*pHALInfo->pFuncs->fpNDTS_HAL_RemoveBoard)(pBoardID) ;
}

NDTSHAL_API NDTS_Boolean CALL_CONV NDTS_HAL_GetHALData(NDTS_CardSecondaryIDs cardID, unsigned long cardIndex, NDTS_HAL_Info **ppHALInfo, unsigned long *pInternalCardIndex)
{
	NDTS_HAL_Info *pHALInfo ;
	NDTS_Boolean isOK ;

	// Get HAL info
	isOK = NDTS_HAL_GetHALInfo(cardID, &pHALInfo) ;
	if (!isOK)
		return NDTS_BFalse ;

	// Get card internal index
	isOK = NDTS_HAL_GetCardInternalIndex(pHALInfo, cardID, cardIndex, pInternalCardIndex) ;
	if (!isOK)
		return NDTS_BFalse ;

	*ppHALInfo = pHALInfo ;

	return NDTS_BTrue ;
}


NDTS_Boolean CALL_CONV NDTS_HAL_BoardByteCopy(NDTS_CardSecondaryIDs cardID, unsigned long cardIndex, unsigned long localAddress, void *pOut, unsigned long numberOfBytes)
{
	NDTS_HAL_Info *pHALInfo ;
	NDTS_Boolean isHALFound ;

	isHALFound = NDTS_HAL_GetHALInfo(cardID, &pHALInfo) ;

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

NDTS_Boolean CALL_CONV NDTS_HAL_WriteSecondaryDeviceID(unsigned long busIndex, unsigned long slotIndex, NDTS_CardSecondaryIDs cardID)
{
	NDTS_HAL_Info *pHALInfo ;
	NDTS_Boolean isHALFound ;

	isHALFound = NDTS_HAL_GetHALInfo(NDTS_HAL_WILDCARD_SECONDARY_DEVICE_ID, &pHALInfo) ;

	// Delegate to HAL implementation
	return isHALFound ? (*pHALInfo->pFuncs->fpNDTS_HAL_WriteSecondaryDeviceID)(busIndex, slotIndex, cardID) : NDTS_BFalse ;
}
