///////////////////////////////////////////////////////////////////////////////////////
// April 2006 - Separate data collection thread to support SFD, WinRS, WinRS - Saab.
// See RQThread.h for details

#include <windows.h>
#include <stdio.h>
#include <io.h>
#include <crtdbg.h>
#include "ThreadInternal.h"
#include "SnapshotInternal.h"
#include "DataCollectionErrorInternal.h"
// NDTSErrorInternal.h must be included after RQTError.h so that DC_ReturnCode has been defined.
#include "NDTSError.h"
#include "DataSamplingStatusInternal.h"
#include "RawScanMemoryBlockNodeProtected.h"
#include "RawScanProtected.h"
#include "RawScanPools.h"
#include "RawScanOffline.h"
#include "NDTSCommonHal.h"
#include "NDTSCommonHalProtected.h"
#include "SDKUtilities.h"

DC_API DC_State *pThreadStates = 0; // One per card - like pBoardStates in the base sdk.  There will probably be one thread for each card
unsigned long activeThreads = 0;


DC_API NDTS_Boolean DC_Initialise(DC_AssociatedSDK *pAssociatedSDK)
{
	DC_State *pThreadState ;
	char tempStr[100] ;
	DWORD dwThreadId ;
	unsigned long cardIndex;

	NDTS_RegisterGetCodeStringFunction(NDTS_ERROR_DC_SDK_INDEX, &DC_GetCodeString) ;
	if (pThreadStates != 0)
	{
		DC_HandleError(DC_Initialise_AlreadyInititialised);
		return NDTS_BFalse;
	}

	pThreadStates = malloc(pAssociatedSDK->numberOfBoards * sizeof(DC_State));
	if (pThreadStates == 0)
	{
		DC_HandleError(DC_OutOfMemory);
		return NDTS_BFalse;
	}

	if (RawScan_Initialise() == NDTS_BFalse) 
		return NDTS_BFalse;


	for (cardIndex=0; cardIndex < pAssociatedSDK->numberOfBoards; cardIndex++)
	{
		pThreadState = &pThreadStates[cardIndex];
		pThreadState->dataSamplingStatus = 0;
		pThreadState->associatedSDK = *pAssociatedSDK;
		// Statistics for CRC failures
		memset(&pThreadState->crcState.statistics, 0, sizeof(DC_CRCStatistics)) ;
		// Statistics for timing A Scan DMA
		memset(&pThreadState->aScanDMATiming, 0, sizeof(DC_AScanDMATiming)) ;
		pThreadState->aScanDMATiming.doTimeAScanDMAs = NDTS_BFalse ;
		// Application will fill these in later if required
		pThreadState->crcState.pCRCBadScanHook = 0;
		pThreadState->crcState.pCRCBadScanHookContext = 0;


		if (pAssociatedSDK->hasCRC)
		{
			pThreadState->crcState.pCRCTable = malloc(256 * sizeof(unsigned long)) ;
			SDKUtilities_GenerateCRCTable(pThreadState->crcState.pCRCTable) ;
		}
		else
		{
			pThreadState->crcState.pCRCTable = 0;
		}

		if (!pThreadState->associatedSDK.fpSetUpdateSampleParametersHook(cardIndex, DC_UpdateParameters)) 
		{
			if (!DC_IsErrorNonFatal()) 
				return NDTS_BFalse;
		}

		// Assume interrupts don't work by setting timeout to fast.  If we get an interrupt, set timeout to slow.
		pThreadState->plxWaitTimeout		= DC_PLX_TIMEOUT_FAST ;
		pThreadState->threadBankSwaps		= 0 ;
		pThreadState->appBankSwaps			= 0 ;
		pThreadState->currentThreadState	= DC_SMSNotStarted ;
		pThreadState->requestedThreadState	= DC_SMSNotStarted ;
		pThreadState->collectState.rawScansInitialised = 0;

		// Fake PLX Semaphore - to be replaced when getting interrupts worki_CRT_SECURE_NO_DEPRECATEng
		// Created with us as initial owner to simulate the interrupt never going off in the thread
		sprintf(tempStr, "DC_PlxSemaphore%d", cardIndex);
		pThreadState->plxSemaphore = CreateSemaphore(NULL, 0, 1, tempStr); // Initial count 0 - block on next wait. Max count of 1
		if (!pThreadState->plxSemaphore) 
		{
			DC_HandleError(DC_Initialise_FailedToCreatePLXInterrupt);
			return NDTS_BFalse;
		}

		// Semaphore that is released each time the thread is run
		sprintf(tempStr, "DC_ThreadHasRunSemaphore%d", cardIndex);
		pThreadState->threadHasRun = CreateSemaphore(NULL, 0, 1, tempStr); // Initial count 0 - block on next wait. Max count of 1
		if (!pThreadState->threadHasRun) 
		{
			DC_HandleError(DC_OutOfMemory);
			return NDTS_BFalse;
		}

		InitializeCriticalSection(&(pThreadState->threadCriticalSection)) ;

		pThreadState->currentThreadState = DC_SMSNotStarted ;

		if (pAssociatedSDK->isRealHardwareFound)
		{
			HANDLE hThread ;
			static int staticCardIndex;
			int i,j;

			staticCardIndex = cardIndex;
			hThread = CreateThread(0, 0, DC_ThreadMain, (void *)&staticCardIndex, 0, &dwThreadId) ;
			if (!hThread)
			{
				DC_HandleError(DC_OutOfMemory);
				return NDTS_BFalse;
			}

			//		i = SetThreadPriority(hThread, THREAD_PRIORITY_HIGHEST) ;
			i = SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL) ;
			j=1;
			j++;
		}
		else
		{
			// Don't create thread if no real hardware

		}
	}
	return NDTS_BTrue;
}

DC_API NDTS_Boolean DC_Uninitialise()
{
	DC_AssociatedSDK *pAssociatedSDK = &(DC_ReturnThreadStatePointer(0)->associatedSDK );

	unsigned long cardIndex;
	for (cardIndex=0; cardIndex < pAssociatedSDK->numberOfBoards; cardIndex++)
	{
		DC_State *pThreadState = DC_ReturnThreadStatePointer(cardIndex) ;
		if (!pThreadState) 
			return NDTS_BFalse; 

		DC_SetThreadState(cardIndex, DC_SMSShutdown) ;

		DeleteCriticalSection(&(pThreadState->threadCriticalSection));

		if (--activeThreads == 0)
		{
			free(pThreadStates);
			pThreadStates = 0;
		}
		DC_FreeRawScanPools(cardIndex) ;

		free(pThreadState->crcState.pCRCTable) ;
		pThreadState->crcState.pCRCTable = 0 ;


	}

	return NDTS_BTrue;
}


DC_API NDTS_Boolean CALL_CONV DC_SetThreadState(unsigned long cardIndex, DC_StateMachineState newState)
{
	unsigned long retVal;
	DC_State *pThreadState = DC_ReturnThreadStatePointer(cardIndex);
	if (!pThreadState)
		return NDTS_BFalse;

	if (!pThreadState->associatedSDK.isRealHardwareFound)
	{
		// When running with simulated hardware, the thread is not created but we pretend it has moved to the correct state
		pThreadState->currentThreadState = newState;
		return NDTS_BTrue;
	}

	if (newState != DC_SMSSnapshot && newState != DC_SMSCollect && newState != DC_SMSPause && newState != DC_SMSShutdown)
	{
		DC_HandleError(DC_SetThreadState_InvalidState);
		return NDTS_BFalse;
	}
	
	if (pThreadState->currentThreadState == DC_SMSShutdown)
	{
		DC_HandleError(DC_ThreadHasShutdown);
		return NDTS_BFalse;
	}

//	if (pBoardState->lockEntryCount != 0)
//	{
//		DC_HandleError(DC_SetThreadState_MustNotBeCalledInsideRQLock);
//		return NDTS_BFalse;
//	}

	EnterCriticalSection(&(pThreadState->threadCriticalSection)); // Block until the thread is in the waiting section. 
	pThreadState->requestedThreadState = newState;
	LeaveCriticalSection(&(pThreadState->threadCriticalSection)); 

	// Need two WaitForSingleObject calls.  The thread may have just set the semaphore to empty so the
	// first call sets the semaphore to full and the 
	// second one blocks until the thread has done a release.
	retVal = WaitForSingleObject(pThreadState->threadHasRun, DC_TIME_TO_WAIT_FOR_THREAD);
	if (retVal != WAIT_OBJECT_0)
	{
		DC_HandleError(DC_SetThreadState_TimeoutWaitingForThread);
		DC_Unlock(cardIndex);
		return NDTS_BFalse;
	}

	retVal = WaitForSingleObject(pThreadState->threadHasRun, DC_TIME_TO_WAIT_FOR_THREAD);
	if (retVal != WAIT_OBJECT_0)
	{
		DC_HandleError(DC_SetThreadState_TimeoutWaitingForThread);
		DC_Unlock(cardIndex);
		return NDTS_BFalse;
	}

	if (pThreadState->currentThreadState != newState)
	{
		DC_HandleError(DC_SetThreadState_ThreadFailedToAcknowledgeNewState);
		DC_Unlock(cardIndex);
		return NDTS_BFalse;
	}
	return NDTS_BTrue;
}

NDTS_Boolean CALL_CONV DC_BankSwapAndUpdate(unsigned long cardIndex)
{
	unsigned long currentBankSwaps = 0, value = 0;
	NDTS_Boolean isOK;
	int timeoutCounter = 0;
	NDTS_Boolean isDataAvailable = NDTS_BFalse;
	DC_State *pThreadState = DC_ReturnThreadStatePointer(cardIndex) ;
	
	if (!pThreadState) 
		return NDTS_BFalse; 

	if (pThreadState->currentThreadState == DC_SMSShutdown)
	{
		DC_HandleError(DC_ThreadHasShutdown);
		return NDTS_BFalse;
	}

	EnterCriticalSection(&(pThreadState->threadCriticalSection)); 
	currentBankSwaps = pThreadState->threadBankSwaps;

	// Check for one bank already full because the bank may have filled between the thread running and the app calling this function.
	DC_Lock(cardIndex);
	isOK = DC_GetDataSamplingStatus(cardIndex, &value);
	if (!isOK)
	{
		DC_Unlock(cardIndex);
		LeaveCriticalSection(&(pThreadState->threadCriticalSection)); 
		return NDTS_BFalse ;
	}

	// No banks full - request a swap from the firmware
	if ((value & 0x01) == 0) 
	{
		isOK = DC_SetRequestEarlySampleData(cardIndex) ;

		// Check for OneBankAlreadyFull due to a bank filling between the check above and the call to DC_SetRequestEarlySampleData()
		if (!isOK && DC_GetLastError() != DC_SetRequestEarlySampleData_OneBankAlreadyFull) 
		{
			if (DC_GetLastError() ==  DC_SetRequestEarlySampleData_NoEarlySampleData)
			{
				// Return an error here because there is no data to fetch and so we don't do a bank swap
				DC_HandleError(DC_NoDataAvailable);
				DC_Unlock(cardIndex);
				LeaveCriticalSection(&(pThreadState->threadCriticalSection)); 
				return NDTS_BFalse ;
			}
			else
			{
				// Carry on if there is a non fatal error (e.g. a CRC)
				if (!DC_IsErrorNonFatal())
				{
					DC_Unlock(cardIndex);
					LeaveCriticalSection(&(pThreadState->threadCriticalSection)); 
					return NDTS_BFalse ;
				}
			}
		}
	}

	// Wait for the firmware to finish the scan it is on and set data available
	do
	{
		if (timeoutCounter++ > 1000000)
		{
			DC_HandleError(DC_BankSwapAndUpdate_TimeOutWaitingForBankSwap);
			pThreadState->associatedSDK.fpResetFirmware(cardIndex);
			DC_Unlock(cardIndex);
			LeaveCriticalSection(&(pThreadState->threadCriticalSection)); 
			return NDTS_BFalse ;
		}

		isOK = DC_GetIsSampleDataAvailable(cardIndex, &isDataAvailable) ;

		if (!isOK && !DC_IsErrorNonFatal())
		{
			DC_Unlock(cardIndex);
			LeaveCriticalSection(&(pThreadState->threadCriticalSection)); 
			return NDTS_BFalse;
		}
	}
	while (!isDataAvailable);

	// Get the data from the bank
	if (!DC_ServiceRapiDAQ(cardIndex, SRQApp))
	{
		DC_Unlock(cardIndex);
		LeaveCriticalSection(&(pThreadState->threadCriticalSection));
		return NDTS_BFalse;
	}

	DC_Unlock(cardIndex);
	LeaveCriticalSection(&(pThreadState->threadCriticalSection)); 
	return NDTS_BTrue;
}

// Thread entry point
static DWORD WINAPI DC_ThreadMain(LPVOID lpParameter)
{
	// Data capture thread function
	unsigned long *pParam = (unsigned long *)lpParameter;
	unsigned long cardIndex = *pParam;
	unsigned long threadLoops = 0;
	NDTS_Boolean isOK;
	DC_State *pThreadState = DC_ReturnThreadStatePointer(cardIndex);
	if (!pThreadState)
		return 0;
	do
	{
		EnterCriticalSection(&(pThreadState->threadCriticalSection)) ;
		pThreadState->currentThreadState = pThreadState->requestedThreadState;

		if (pThreadState->currentThreadState == DC_SMSSnapshot || pThreadState->currentThreadState == DC_SMSCollect)
		{
			threadLoops++;
//			sprintf(pBoardState->ppDiagnosticStrings[1], "Thread loops %d", threadLoops);

			DC_Lock(cardIndex);
			isOK = DC_ServiceRapiDAQ(cardIndex, SRQThread);
			if (!isOK)
			{
				DC_Unlock(cardIndex);
				LeaveCriticalSection(&(pThreadState->threadCriticalSection));
				goto breakout ;
			}
			DC_Unlock(cardIndex);
		}
		if (!ReleaseSemaphore(pThreadState->threadHasRun, 1, NULL))
		{
		    DWORD dw = GetLastError(); 
			if (dw != ERROR_TOO_MANY_POSTS) // Too many posts if no-one was waiting for us
			{
				DC_HandleError(DC_ThreadMain_ReleaseSemaphoreReturnedError);
				goto breakout;
			}
		}
		LeaveCriticalSection(&(pThreadState->threadCriticalSection));

		// Yeild the thread in a state dependent way
		if(!DC_YieldThread(cardIndex))
			goto breakout ;
	}
	while (pThreadState->currentThreadState != DC_SMSShutdown);

	pThreadState->threadExitCode = DC_GetLastError();

breakout:
	pThreadState->currentThreadState = DC_SMSShutdown;
	if (!ReleaseSemaphore(pThreadState->threadHasRun, 1, NULL))
		DC_HandleError(DC_ThreadMain_ReleaseSemaphoreReturnedError);

	return 0;
}

NDTS_Boolean DC_YieldThread(unsigned long cardIndex)
{
	unsigned long retVal;
	DC_State *pThreadState = DC_ReturnThreadStatePointer(cardIndex);
	if (!pThreadState)
		return NDTS_BFalse;

	switch(pThreadState->currentThreadState)
	{
	case DC_SMSSnapshot:
	case DC_SMSCollect:
		retVal = WaitForSingleObject(pThreadState->plxSemaphore, pThreadState->plxWaitTimeout);
		if (retVal == WAIT_OBJECT_0)
		{
			// If the PLX hasn't generated any interrupts yet, plxWaitTimeout is set to fast
			// in case the hardware doesn't support interrupts.  
			// Once PLX has generated an interrupt, set the timeout to slow from now on to 
			// reduce load on the processor.
			pThreadState->plxWaitTimeout = DC_PLX_TIMEOUT_SLOW;
		} 
		else if (retVal != WAIT_TIMEOUT)
		{
			DC_HandleError(DC_ThreadMain_ReleaseSemaphoreReturnedError);
			return NDTS_BFalse ;
		}
		break;
	case DC_SMSPause:
		Sleep(DC_PAUSE_TIME);
		break;
	case DC_SMSNotStarted:
		Sleep(1);  // Poll quickly but yeild regularly for a faster startup.
		break;	
	}

	return NDTS_BTrue ;
}

DC_State *DC_ReturnThreadStatePointer(unsigned long cardIndex) 
{
	if (cardIndex >= pThreadStates[0].associatedSDK.numberOfBoards) 
	{
		DC_HandleError(DC_ReturnThreadStatePointer_InvalidCardIndex) ;
		return 0;
	}
	return &pThreadStates[cardIndex] ;
}


void DC_UpdateParameters(unsigned long cardIndex)
{
	DC_State *pThreadState = DC_ReturnThreadStatePointer(cardIndex) ;
	if (!pThreadState) return ; 

	switch(pThreadState->currentThreadState)
	{
	case DC_SMSSnapshot:
		DC_UpdateSnapshotSampleStorage(cardIndex);
		break;
	case DC_SMSCollect:
		DC_UpdateParametersCollectHook(cardIndex);
		break;
	}
}


NDTS_Boolean DC_UpdateDataSamplingStats(unsigned long cardIndex)
{
	NDTS_Boolean	isDataAvailable, 
		isSomeDataSampled, 
		hasNewTriggerOccured, 
		hasTriggerOccurredBeforeProcessComplete, 
		isDataFullError;


	static unsigned long someDataSampled = 0;
	static unsigned long newTriggerOccurred = 0;
	static unsigned long triggerOccurredBeforeDownloadComplete = 0;
	static unsigned long dataFullError = 0;
	static unsigned long dataAvailable = 0;
	DC_State *pThreadState = DC_ReturnThreadStatePointer(cardIndex) ;
	
	if (!pThreadState)
		return	NDTS_BFalse; 

	if (!DC_GetSamplingStatus(cardIndex, 
		&isDataAvailable, 
		&isSomeDataSampled, 
		&hasNewTriggerOccured, 
		&hasTriggerOccurredBeforeProcessComplete, 
		&isDataFullError)) 
		return NDTS_BFalse;

	// zero bank swaps
	// one thread loops
	if(isDataAvailable)
	{
//		OutputDebugString("dataAvailable\n");
//		sprintf(pBoardState->ppDiagnosticStrings[4], "data avail %d", ++dataAvailable);
		// This gives same reading as the same as the bank swaps counter
	}

	if(isSomeDataSampled)	
	{
//		OutputDebugString("someDataSampled\n");
//		sprintf(pBoardState->ppDiagnosticStrings[2], "isSomeDataSampled %d", ++someDataSampled);
	}

	if(hasNewTriggerOccured)
	{
//		OutputDebugString("newTriggerOccured\n");
	}

	if(hasTriggerOccurredBeforeProcessComplete)
	{
//		OutputDebugString("triggerOccurredBeforeDownloadComplete\n");
// using 3 for noise filter samples		sprintf(pBoardState->ppDiagnosticStrings[3], "triggerOccurredBeforeDownloadComplete %d", ++triggerOccurredBeforeDownloadComplete);
	}

	if(isDataFullError)	
	{
//		sprintf(pBoardState->ppDiagnosticStrings[4], "dataFullError %d", ++dataFullError);
		//filelogt("Data Full Error %d\n", dataFullError);
		pThreadState->associatedSDK.fpDataFullRecovery(cardIndex);
	}
	return NDTS_BTrue;
}
// Check for data available and service the RapiDAQ card if it is.
// threadCriticalSection and DC_Lock() must both be taken (in that order) before this is called.
NDTS_Boolean DC_ServiceRapiDAQ(unsigned long cardIndex, enum SRQCaller caller)
{
	NDTS_Boolean isDataAvailable = NDTS_BFalse;
	NDTS_Boolean isOK = NDTS_BFalse;
	DC_State *pThreadState = DC_ReturnThreadStatePointer(cardIndex);
	if (!pThreadState)
		return NDTS_BFalse;

	isOK = DC_UpdateDataSamplingStats(cardIndex);
	if (!isOK && !DC_IsErrorNonFatal()) 
		return NDTS_BFalse;

	isOK = DC_GetIsSampleDataAvailable(cardIndex, &isDataAvailable) ;
	if (!isOK && !DC_IsErrorNonFatal())
		return NDTS_BFalse;

	if (isDataAvailable) // Bank swap detected
	{
		switch(pThreadState->currentThreadState)
		{
		case DC_SMSSnapshot:
			isOK = DC_UpdateSnapshot(cardIndex);
			if (!isOK) 	
				return NDTS_BFalse;
			break;
		case DC_SMSCollect:	
			isOK = DC_UpdateCollect(cardIndex);
			if (!isOK) 	
				return NDTS_BFalse;
			break;
		}
		switch(caller)
		{
		case SRQThread:
			pThreadState->threadBankSwaps++;
			break;
		case SRQApp:
			pThreadState->appBankSwaps++;
			break;
		default:
			_ASSERTE(0);
		}
//		sprintf(pBoardState->ppDiagnosticStrings[0], "Swaps: thread %d, app %d", pThreadState->threadBankSwaps, pThreadState->appBankSwaps);
	}
	return NDTS_BTrue;
}

DC_API void DC_GetLogLevel(NDTS_LogLevel *pFile, NDTS_LogLevel *pApp, NDTS_LogLevel *pDebugger)
{
	NDTS_GetLogLevel(pFile, pApp, pDebugger);
}

DC_API void DC_SetLogLevel(NDTS_LogLevel file, NDTS_LogLevel app, NDTS_LogLevel debugger)
{
	NDTS_SetLogLevel(file, app, debugger);
}





NDTS_Boolean CALL_CONV DC_ReadRawScanBlocks(unsigned long cardIndex, 
											unsigned long sourceByteOffset, 
											RawScan_CardBlockDescription* pSourceBlockDescriptionBegin, 
											RawScan_CardBlockDescription* pSourceBlockDescriptionEnd, 
											RawScan_MemoryHandle *pDestinationBlocks, 
											const unsigned long *pCRCTable, 
											unsigned long *pSourceBlocksRead)
{
	unsigned long sourceByteEnd ;
	char *pTransferByteIt, *pTransferByteEnd ;
	RawScan_CardBlockDescription* pSourceBlockDescriptionIt ;
	RawScan_MemoryHandle *pDestinationBlockIt ;
	NDTS_Boolean isOK ;
	char *pCommonBuffer ;
	unsigned long numberOfCommonBufferBytes ;
	DC_State *pThreadState = DC_ReturnThreadStatePointer(cardIndex);
	if (!pThreadState)
		return NDTS_BFalse;

	*pSourceBlocksRead = 0 ;

	isOK = NDTS_HAL_GetCommonBufferInfo(pThreadState->associatedSDK.SID, cardIndex, &pCommonBuffer, &numberOfCommonBufferBytes) ;
	if (!isOK)
		return NDTS_BFalse ;

	// Set card buffer byte iterators.
	sourceByteEnd = sourceByteOffset ;

	for(pSourceBlockDescriptionIt = pSourceBlockDescriptionBegin; pSourceBlockDescriptionIt != pSourceBlockDescriptionEnd; ++pSourceBlockDescriptionIt)
		sourceByteEnd += pSourceBlockDescriptionIt->size + (pSourceBlockDescriptionIt->flags & RawScan_CBDIsCRCCheckPresent ? 4 : 0);

	// Iterate over the card buffer blocks.
	for(pSourceBlockDescriptionIt = pSourceBlockDescriptionBegin, 
		pTransferByteIt = pCommonBuffer, 
		pTransferByteEnd = pTransferByteIt,
		pDestinationBlockIt = pDestinationBlocks; 

		pSourceBlockDescriptionIt != pSourceBlockDescriptionEnd; 

		++pSourceBlockDescriptionIt, 
		++pDestinationBlockIt)
	{
		unsigned char *pDestinationByteIt, *pDestinationByteEnd ;
		unsigned long crcAccum ;
		unsigned char currentByte ;
		int isCRCCheckPresent ;

		crcAccum = ~0ul ; // Only used if the block has CRC check.
		isCRCCheckPresent = pSourceBlockDescriptionIt->flags & RawScan_CBDIsCRCCheckPresent ;

		// Set destination byte iterators.
		pDestinationByteIt = pDestinationBlockIt->p ;
		pDestinationByteEnd = pDestinationByteIt + pSourceBlockDescriptionIt->size ; // This assumes the destination block is big enough.

		// Iterate over the bytes in the block buffer, filling them with the bytes in the transfer buffer.
		do
		{
			if(pTransferByteIt == pTransferByteEnd)
			{
				unsigned long transferCount = 0 ;

				// The transfer buffer is empty, so fill it.
				isOK = DC_FetchRawScanBytes(cardIndex, sourceByteOffset, sourceByteEnd, &transferCount) ;
				if(!isOK) 
					return NDTS_BFalse ; 

				// Set source / transfer buffer iterators.
				sourceByteOffset += transferCount ;

				pTransferByteIt = pCommonBuffer ;
				pTransferByteEnd = pTransferByteIt + transferCount ;
			}

			currentByte = *pDestinationByteIt++ = *pTransferByteIt++ ;

			// CRC.
			if(isCRCCheckPresent)
				crcAccum = (crcAccum << 8) ^ pCRCTable[((unsigned long) (crcAccum >> 24) ^ currentByte) & 0xff] ;
		}
		while(pDestinationByteIt != pDestinationByteEnd) ;

		if(isCRCCheckPresent)
		{
			union 
			{
				unsigned char bytes[4] ;
				unsigned long unsignedLong ;
			} crc ;

			// Set destination byte iterators.
			pDestinationByteIt = crc.bytes ;
			pDestinationByteEnd = pDestinationByteIt + 4 ;

			// Iterate over the bytes in the block buffer, filling them with the bytes in the transfer buffer.
			do
			{
				if(pTransferByteIt == pTransferByteEnd)
				{
					unsigned long transferCount = 0 ;

					// The transfer buffer is empty, so fill it.
					isOK = DC_FetchRawScanBytes(cardIndex, sourceByteOffset, sourceByteEnd, &transferCount) ;
					if(!isOK) 
						return NDTS_BFalse ; 

					// Set source / transfer buffer iterators.
					sourceByteOffset += transferCount ;

					pTransferByteIt = (char *) pCommonBuffer ;
					pTransferByteEnd = pTransferByteIt + transferCount ;
				}

				*pDestinationByteIt++ = *pTransferByteIt++ ;
			}
			while(pDestinationByteIt != pDestinationByteEnd) ;

			if(crcAccum != ~crc.unsignedLong && !pThreadState->associatedSDK.ignoreHeaderAndCRC)
			{
#ifdef _DEBUG
				//unsigned long crc = DC_CalculateCRC32(~0ul, pCRCTable, (char*)(pDestinationBlockIt->p), pSourceBlockDescriptionIt->size) ;
				//assert((crcAccum != crc.unsignedLong)) ;
#endif

				DC_HandleError(DC_ReadRawScanBlocks_BadBlockCRC) ;
				return NDTS_BFalse ;
			}
		}

		// Another block successfully read.
		(*pSourceBlocksRead)++ ;
	}

	return NDTS_BTrue ;
}
// The compiler doesn't realise that the QueryPerformanceCounter(&countLargeA) line is always executed when countLargeA is used in the calculation
#pragma warning(push)
#pragma warning(disable : 4701) 

NDTS_Boolean CALL_CONV DC_FetchRawScanBytes(unsigned long cardIndex, 
											unsigned long begin, 
											unsigned long end, 
											unsigned long *pTransferCount)
{
	//No more bytes left in the transfer buffer! Get some more ...
	LARGE_INTEGER timerFreq, timeCountA, timeCountB ;
	unsigned char *pCommonBuffer ;
	unsigned long numberOfCommonBufferBytes ;
	NDTS_Boolean isOK ;
	DC_State *pThreadState = DC_ReturnThreadStatePointer(cardIndex);
	if (!pThreadState)
		return NDTS_BFalse;

	isOK = NDTS_HAL_GetCommonBufferInfo(pThreadState->associatedSDK.SID, cardIndex, &pCommonBuffer, &numberOfCommonBufferBytes) ;
	if (!isOK)
		return NDTS_BFalse ;

	// We can't transfer everything at once so cap it.
	*pTransferCount = end - begin ;

	_ASSERTE(*pTransferCount > 0) ;
	if(*pTransferCount > numberOfCommonBufferBytes)
		*pTransferCount = numberOfCommonBufferBytes ;

	_ASSERTE(end < 0x00400000 || pThreadState->associatedSDK.SID != NDTS_SIDRapiDAQ); 
	_ASSERTE(end < 0x007fffff || pThreadState->associatedSDK.SID != NDTS_SID2100); 

	if (pThreadState->aScanDMATiming.doTimeAScanDMAs)
	{
		QueryPerformanceFrequency(&timerFreq) ;
		QueryPerformanceCounter(&timeCountA) ;
	}

	isOK = NDTS_HAL_PerformDMARead(pThreadState->associatedSDK.SID, cardIndex, begin, *pTransferCount) ;
	if (!isOK)
		return NDTS_BFalse ;

	if (pThreadState->aScanDMATiming.doTimeAScanDMAs)
	{
		static unsigned long numberOfBytesToAverage = 50 * 1024 * 1024 ;

		QueryPerformanceCounter(&timeCountB) ;

		pThreadState->aScanDMATiming.aScanTimingAccumulatedBytes += *pTransferCount ;
		pThreadState->aScanDMATiming.aScanTimingAccumulatedMicros += (unsigned long) (1000000 * (timeCountB.QuadPart - timeCountA.QuadPart) / timerFreq.QuadPart) ;

		if (pThreadState->aScanDMATiming.aScanTimingAccumulatedBytes > numberOfBytesToAverage)
		{
			__int64 aScanTimingAccumulatedBytes = (__int64) pThreadState->aScanDMATiming.aScanTimingAccumulatedBytes ;
			__int64 aScanTimingAccumulatedMicros = (__int64) pThreadState->aScanDMATiming.aScanTimingAccumulatedMicros ;

			pThreadState->aScanDMATiming.lastAScanDMAKBPerSec = (unsigned long) (1000000 * aScanTimingAccumulatedBytes / aScanTimingAccumulatedMicros / 1024) ;

			pThreadState->aScanDMATiming.aScanTimingAccumulatedBytes = 0 ;
			pThreadState->aScanDMATiming.aScanTimingAccumulatedMicros = 0 ;
		}
	}
	//NDTS_LogLine(NDTS_LLErr, "begin %d end %d transfer %d \n", begin, end, *pTransferCount);

	return NDTS_BTrue ;
}
#pragma warning(pop)

void DC_Lock(unsigned long cardIndex)
{
	DC_State *pThreadState = DC_ReturnThreadStatePointer(cardIndex) ;
	DC_AssociatedSDK *pSdk = &(pThreadState->associatedSDK) ;
	pSdk->fpLock(cardIndex);
}

void DC_Unlock(unsigned long cardIndex)
{
	DC_State *pThreadState = DC_ReturnThreadStatePointer(cardIndex) ;
	DC_AssociatedSDK *pSdk = &(pThreadState->associatedSDK) ;
	pSdk->fpUnlock(cardIndex);
}




// Doesn't DC_Lock(),  wait for the bank swap or indicateSampleDownloaded
/* OverSampling (interleaving) produces data as below with the timing as below.  The B Scan Rep Rate only applies if free running.
A Over0 Seq0  A Over1 Seq0  A Over2 Seq0  A Over3 Seq0  A Over0 Seq1  A Over1 Seq1  A Over2 Seq1  A Over3 Seq1      A Over0 Seq0  A Over1 Seq0  A Over2 Seq0 A Over3 Seq0  A Over0 Seq1   A Over1 Seq1  A Over2 Seq1  A Over3 Seq1       A Over0 Seq0  
|A Scn rep rt |A Scn rep rt |A Scn rep rt |A Scn rep rt |A Scn rep rt |A Scn rep rt |A Scn rep rt |                 |A Scn rep rt |A Scn rep rt |A Scn rep rt |A Scn rep rt |A Scn rep rt |A Scn rep rt |A Scn rep rt |                  |
|--------------------------------------- B Scan Rep Rate -----------------------------------------------------------||--------------------------------------- B Scan Rep Rate -----------------------------------------------------------|

Cscan data is interleaved / oversampled in a similar way, i.e. the gate information is built from each individual A-scan not the composite high resolution scan.
*/

NDTS_Boolean CALL_CONV DC_GetAScanNoBankSwap(unsigned long cardIndex, short *pData, unsigned long numberOfBufferValues, unsigned long aScanIndex, unsigned long bScanIndex)
{
	NDTS_Boolean isOK ;
	unsigned long numberOfBytesPerSample, bytesToTransfer, localAddress ;
	NDTS_SampleSize sampleSize ;
	DC_State *pThreadState = DC_ReturnThreadStatePointer(cardIndex);
	unsigned short sampleCount, samplesPerOversample, numberOfOversamples;

	if (!pThreadState)
		return NDTS_BFalse;

	if (!pThreadState->associatedSDK.fpGetSampleStorageMode(cardIndex, &sampleSize)) return NDTS_BFalse ;

	numberOfBytesPerSample = sampleSize == NDTS_SS12Bit ? 2 : 1 ;
	sampleCount = (short)pThreadState->associatedSDK.fpReturnAScanSampleValuesThisStep(cardIndex, aScanIndex);  
	numberOfOversamples =  (unsigned short)(pThreadState->associatedSDK.fpReturnOversampleCountThisStep(cardIndex, aScanIndex));
	samplesPerOversample = sampleCount / numberOfOversamples;

	localAddress = pThreadState->associatedSDK.fpReturnAScanOffsetThisStep(cardIndex, aScanIndex) + 
		pThreadState->associatedSDK.fpReturnAScanBytesPerBScan(cardIndex) * bScanIndex ;

	if (pThreadState->associatedSDK.fpReturnOversampleCountThisStep(cardIndex, aScanIndex) == 1)
	{
		bytesToTransfer = sampleCount * numberOfBytesPerSample;
		if (numberOfBufferValues < sampleCount)
		{
			bytesToTransfer = numberOfBufferValues << 1 ;
		}
		// copy straight to destination
		isOK = NDTS_HAL_PerformDMATransfer(pThreadState->associatedSDK.SID, cardIndex, pData, localAddress, bytesToTransfer, NDTS_BTrue) ;

		// Bug 786 - DSP_124 has the A Scan data as sign and magnitude.  If this function is used in
		// non sequenced applications, the A Scans will need to be converted back.
		if (pThreadState->associatedSDK.fpReturnIsSignAndMag(cardIndex) )
		{
			SDKUtilities_SignAndMagToTwosComp((unsigned char*)pData, bytesToTransfer / 2);
		}
	}
	else
	{
		int sample, oversample;
		short *pDest = pData;
		int offsetPerOversample = samplesPerOversample + 2;
		short aScanBuffer[NDTS_MAXIMUM_NUMBER_OF_SAMPLES + 20 * 2]; // max samples + 20 headers in the worst case
		int maxIters;

		bytesToTransfer = sampleCount * numberOfBytesPerSample + pThreadState->associatedSDK.fpReturnOversampleCountThisStep(cardIndex, aScanIndex) * 4 - 4;
		
		// copy to holding buffer than unravel the oversamples

		isOK = NDTS_HAL_PerformDMATransfer(pThreadState->associatedSDK.SID, cardIndex, aScanBuffer, localAddress, bytesToTransfer, NDTS_BTrue) ;

		maxIters = samplesPerOversample < numberOfBufferValues / numberOfOversamples ? samplesPerOversample : numberOfBufferValues / numberOfOversamples ;

		for (sample = 0; sample < maxIters ; sample++)
		{
			// Somewhat counterintuitively the oversample with the longest delay is the
			// one that we put first.  Draw some pictures and satisfy yourself that
			// this is the right thing to do

			// Somewhat anticounterintuitively this is wrong although it was working before.
			// Perhaps something else has been changed that I don't know about...
			// maybe not
			for (oversample = numberOfOversamples - 1; oversample >= 0; oversample--)
			//for (oversample = 0; oversample < numberOfOversamples; oversample++)
			{
				*pDest++ = aScanBuffer[oversample * offsetPerOversample + sample];
			}
		}

		for ( ; sample < (int) numberOfBufferValues ; sample++)
			*pDest++ = 0 ;
	}

	if (!isOK)
	{
		DC_HandleError(DC_GetSamples_PerformDMATransferFail) ;
		DC_IndicateSampleDataDownloaded(cardIndex) ;
		return NDTS_BFalse ;
	}

	return NDTS_BTrue;
}
/* This is the old RQ_GetRawScan that gets a RawScan without using threading - probably redundant now.
DC_API NDTS_Boolean CALL_CONV DC_GetRawScan(unsigned long cardIndex, NDTS_RawScan *pRawScan)
{
	NDTS_Boolean isOK;
	DC_Lock(cardIndex);

	isOK = DC_WaitForBankSwap(cardIndex);
	if (!isOK) goto breakout;

	isOK = DC_GetRawScanNoBankSwap(cardIndex, pRawScan);
	if (!isOK) goto breakout;

breakout:
	DC_IndicateSampleDataDownloaded(cardIndex) ;
	DC_Unlock(cardIndex) ;
	return isOK;
}
*/

// This is currently in the DataCollection.dll  rather than the RawScan.dll because it needs to take locks.
// If it were to be moved to the RawScan.dll, it would be best to pass in a lock function pointer from the 
// SDK in the same way that there is a lock pointer passed in to the DataCollection.dll
// Creating a new lock for the memory pool would increase the chance of deadlocks.

DC_API NDTS_Boolean CALL_CONV DC_FreeRawScanData(NDTS_RawScan *pRawScan, DC_FreeRawScanMode mode)
{
	RawScan_RawScanProtected *pRawInternal ;
	
	// Preconditions.
	_ASSERTE(pRawScan) ;

	pRawInternal = RawScan_ReturnRawScanInternal(pRawScan) ;
	if(pRawInternal == 0)
		// Raw scan contains no data. This is not an error.
		return NDTS_BTrue ;

	DC_Lock(pRawScan->cardIndex) ;

	if (mode != DC_FRSOAll && mode != DC_FRSOAScans && mode != DC_FRSOGates)
	{
		DC_HandleError(DC_FreeRawScanData_InvalidMode) ;
		DC_Unlock(pRawScan->cardIndex) ;
		return NDTS_BFalse ;
	}

	if (mode == DC_FRSOAll || mode == DC_FRSOAScans)
	{
		if(pRawInternal->pDataA) 
		{
			if (!RawScan_FreeRawScanDataAMemoryBlockNode(pRawScan))
			{
				DC_Unlock(pRawScan->cardIndex) ;
				return NDTS_BFalse;
			}
		}
		else
		{
			if (mode == DC_FRSOAScans)
			{
				DC_HandleError(DC_FreeRawScanData_MissingData) ;
				DC_Unlock(pRawScan->cardIndex) ;
				return NDTS_BFalse ;
			}
		}
	}

	if (mode == DC_FRSOAll || mode == DC_FRSOGates)
	{
		if(pRawInternal->pDataB)
		{
			if (!RawScan_FreeRawScanDataBMemoryBlockNode(pRawScan))
			{
				DC_Unlock(pRawScan->cardIndex) ;
				return NDTS_BFalse;
			}
		}
		else
		{
			if (mode == DC_FRSOGates)
			{
				DC_HandleError(DC_FreeRawScanData_MissingData) ;
				DC_Unlock(pRawScan->cardIndex) ;
				return NDTS_BFalse ;
			}
		}
	}

	if(pRawInternal->pDataA == 0 && pRawInternal->pDataB == NULL)
		// There is b-scans in the raw scan.
		pRawScan->numberOfBScans = 0 ;

	DC_Unlock(pRawScan->cardIndex) ;

	return NDTS_BTrue ;
}

DC_API NDTS_Boolean CALL_CONV DC_SetDoTimeAScanDMAs(unsigned long cardIndex, NDTS_Boolean doTimeAScanDMAs)
{
	DC_State *pThreadState = DC_ReturnThreadStatePointer(cardIndex);
	if (!pThreadState)
		return NDTS_BFalse;

	pThreadState->aScanDMATiming.doTimeAScanDMAs = doTimeAScanDMAs ;

	if (doTimeAScanDMAs)
	{
		pThreadState->aScanDMATiming.aScanTimingAccumulatedMicros = 0 ;
		pThreadState->aScanDMATiming.aScanTimingAccumulatedBytes = 0 ;
		pThreadState->aScanDMATiming.lastAScanDMAKBPerSec = 0 ;
	}

	return NDTS_BTrue ;
}

DC_API NDTS_Boolean CALL_CONV DC_GetAScanDMARate(unsigned long cardIndex, unsigned long *pKBsPerSec)
{
	DC_State *pThreadState = DC_ReturnThreadStatePointer(cardIndex);
	if (!pThreadState)
		return NDTS_BFalse;

	*pKBsPerSec = pThreadState->aScanDMATiming.lastAScanDMAKBPerSec;

	return NDTS_BTrue ;
}


DC_API NDTS_Boolean CALL_CONV DC_WaitForBankSwap(unsigned long cardIndex)
{
	NDTS_Boolean isOK, hasTimedOut, areSamplesAvailable, hasSomeDataBeenSampled, 
		hasNewTriggerOccurred, hasTriggerOccurredBeforeProcessComplete, isDataFullError ;
	LARGE_INTEGER freqLarge, countLargeA, countLargeB ;
	float numSeconds ;
	BOOL isWIN32OK ;

	isWIN32OK = QueryPerformanceFrequency(&freqLarge) ;
	if (!isWIN32OK)
	{
		DC_HandleError(DC_WaitForBankSwap_QueryTimerFail) ;
		return NDTS_BFalse ;
	}

	QueryPerformanceCounter(&countLargeA) ;

	hasNewTriggerOccurred = NDTS_BFalse ;
	hasTriggerOccurredBeforeProcessComplete = NDTS_BFalse ;

	do
	{
		QueryPerformanceCounter(&countLargeB) ;

		isOK = DC_GetSamplingStatus(cardIndex, &areSamplesAvailable, &hasSomeDataBeenSampled, !hasNewTriggerOccurred ? &hasNewTriggerOccurred : 0,
									!hasTriggerOccurredBeforeProcessComplete ? &hasTriggerOccurredBeforeProcessComplete : 0, &isDataFullError) ;
		if (!isOK)
			return NDTS_BFalse ;

		numSeconds = (float) (countLargeB.QuadPart - countLargeA.QuadPart) / (float) freqLarge.QuadPart ;
		hasTimedOut = numSeconds > DC_SAMPLE_DATA_AVAILABLE_MAX_WAIT ? NDTS_BTrue : NDTS_BFalse ; 
	}
	while (areSamplesAvailable == NDTS_BFalse && !hasTimedOut) ;

	if (areSamplesAvailable == NDTS_BFalse)
	{
		if (!hasNewTriggerOccurred)
			DC_HandleError(DC_WaitForBankSwap_MissingTrigger) ;
		else if (isDataFullError)
			DC_HandleError(DC_WaitForBankSwap_DataFull) ;
		else if (hasTriggerOccurredBeforeProcessComplete)
			DC_HandleError(DC_WaitForBankSwap_TriggerOccurredBeforeProcessComplete) ;
		else if (hasSomeDataBeenSampled)
			DC_HandleError(DC_WaitForBankSwap_SampleIncomplete) ;
		else
			DC_HandleError(DC_WaitForBankSwap_TimeOut) ;

		return NDTS_BFalse ;
	}

	return NDTS_BTrue ;
}

DC_API NDTS_Boolean CALL_CONV DC_GetBScansPerSecond(unsigned long cardIndex, int *bScansPerSecond)
{
	DC_State *pThreadState = DC_ReturnThreadStatePointer(cardIndex);
	if (!pThreadState)
		return NDTS_BFalse;

	DC_Lock(cardIndex) ;
	*bScansPerSecond = pThreadState->aScanDMATiming.bScansPerSecond;
	DC_Unlock(cardIndex) ;

	return NDTS_BTrue ;
}


// In DataCollection rather than RawScan because we need information on the size of each A Scan for each card.
NDTS_Boolean CALL_CONV DC_AllocCardMemoryPools(unsigned long cardIndex, NDTS_Boolean isSequence)
{
	unsigned long bScanTrailerSize, gateValuesBodySize, gateValuesTrailerSize, numberOfBytesPerSample ;
	NDTS_Boolean isOK ;
	NDTS_ScanStorage storageMode;
	
	DC_State *pThreadState = DC_ReturnThreadStatePointer(cardIndex);
	if (!pThreadState)
		return NDTS_BFalse;

	DC_Lock(cardIndex) ;

	pThreadState->associatedSDK.fpGetSampleStorageMode(cardIndex, &storageMode);
	numberOfBytesPerSample = (storageMode == NDTS_SS12Bit) ? 2 : 1;

	// Allocate memory pool to hold raw scan a-scan bodies.
	if(isSequence)
	{
		unsigned long aScanIndex ;
		// When called from DC_UpdateSnapshotSampleStorage in SnapshotInternal, the bank 0 raw scan has just been set from the board parameters
		for(aScanIndex = 0 ; aScanIndex < pThreadState->snapshotState.pRawScans[0].numberOfAScansInBScan; ++aScanIndex)
		{
			// When the number of steps in an SFD is increased, the aScanRange of the new steps defaults to zero
			unsigned long bodySize = pThreadState->associatedSDK.fpReturnAScanSampleValuesThisStep(cardIndex, aScanIndex) * numberOfBytesPerSample;
			if (bodySize) 
			{
				isOK = RawScan_AllocRawScanAScanBodyMemoryPool(cardIndex, aScanIndex, bodySize) ;
				if (!isOK)
				{
					DC_Unlock(cardIndex) ;
					return NDTS_BFalse ;
				}

			}
		}
	}  
	else
	{
		isOK = RawScan_AllocRawScanAllAScanBodyMemoryPool(cardIndex, pThreadState->associatedSDK.fpReturnAScanSampleValuesThisStep(cardIndex,0) * numberOfBytesPerSample) ;
		if (!isOK)
		{
			DC_Unlock(cardIndex) ;
			return NDTS_BFalse ;
		}

		//NDTS_LogLine(NDTS_LLNotice, "Number of samples: %d. Bytes per sample: %d.", pBoardState->aScanSamplesThisStep[0], pBoardState->numberOfBytesPerSample) ;
	}

	// Allocate memory pool to hold raw scan b-scan trailers.
	bScanTrailerSize = (pThreadState->crcState.pCRCTable ? 4 : 0) + pThreadState->associatedSDK.numberOfEncodersInHardware * 4 ;
	isOK = RawScan_AllocRawScanBScanTrailerMemoryPool(cardIndex, bScanTrailerSize) ;
	if (!isOK)
	{
		DC_Unlock(cardIndex) ;
		return NDTS_BFalse ;
	}

	// Allocate memory pool to hold raw scan gate value bodies.
	gateValuesBodySize = pThreadState->associatedSDK.fpReturnGateBytesPerBScan(cardIndex) - pThreadState->associatedSDK.numberOfEncodersInHardware * 4 ;
	isOK = RawScan_AllocRawScanGateValuesBodyMemoryPool(cardIndex, gateValuesBodySize) ;
	if (!isOK)
	{
		DC_Unlock(cardIndex) ;
		return NDTS_BFalse ;
	}

	// Allocate memory pool to hold raw scan gate value trailers.
	gateValuesTrailerSize = pThreadState->associatedSDK.numberOfEncodersInHardware * 4 ;
	isOK = RawScan_AllocRawScanGateValuesTrailerMemoryPool(cardIndex, gateValuesTrailerSize) ;
	if (!isOK)
	{
		DC_Unlock(cardIndex) ;
		return NDTS_BFalse ;
	}

	DC_Unlock(cardIndex) ;

	return NDTS_BTrue;
}

// The functions below are in DataCollection rather than RawScan because of the locks.
DC_API NDTS_Boolean CALL_CONV DC_FreeAScan(unsigned long cardIndex, void *pData)
{
	// Does this function need locks?
	DC_Lock(cardIndex) ;

	MemoryFactory_Free(pData) ;

	DC_Unlock(cardIndex) ;

	return NDTS_BTrue ;
}

DC_API NDTS_Boolean CALL_CONV DC_FreeRawScanPools(unsigned long cardIndex)
{
	NDTS_Boolean isOK ;

	// This is not right. ALL pools will be freed and not just the pools associated with
	// the specified card.
	DC_Lock(cardIndex) ;

	MemoryFactory_FreePools() ;
	isOK = RawScan_InitRawScanMemoryPools() ;
	if (!isOK)
	{
		DC_Unlock(cardIndex) ;
		return NDTS_BFalse ;
	}

	DC_Unlock(cardIndex) ;

	return NDTS_BTrue ;
}

DC_API NDTS_Boolean CALL_CONV DC_PurgeRawScanPools(unsigned long cardIndex)
{
	DC_Lock(cardIndex) ;

	MemoryFactory_PurgeMemoryPool(RawScan_ReturnRawScanAScanBodyMemoryPool(cardIndex, 0)) ;

	DC_Unlock(cardIndex) ;

	return NDTS_BTrue ;
}
NDTS_Boolean DC_ReadRawScanGateValues(unsigned long cardIndex,
									   NDTS_RawScan *pRawScan, 
									   RawScan_CardBlockDescription *pDataBCardBlockDescriptions, 
									   unsigned long numberOfDescriptions)
{
	unsigned long cScanBaseSRAMAddress, numberOfBlocksRead, numberOfCScanDesynchronisations, numberOfCorruptCScanHeaders,
		aScanIndex, bScanIndex, numberOfOversamples/*, numberOfBScansInBank*/, oversampleIndex,
		cardCScanBaseAddressOffset, cardCScanHeader, cardCScanRecordAddressOffset ;
	unsigned char *pOurCScanBaseAddress, /* *pCardCScanBaseAddress,*/ *pOurCScanRecordAddress/*,  *pCardCScanRecordAddress*/ ;
	NDTS_Boolean isOK, isCScanHeaderOK, isCScanHeaderIDPresent, isCScanHeaderRecoverAttempted ;
	//	unsigned long long previousNumberOfUnrecoveredCScanSamples  ;
	RawScan_MemoryHandle *pDestinationBlockIt ;
	DC_State *pThreadState = DC_ReturnThreadStatePointer(cardIndex);
	if (!pThreadState)
		return NDTS_BFalse;

	isOK = pThreadState->associatedSDK.fpGetCScanBaseSRAMAddress(cardIndex, &cScanBaseSRAMAddress) ;

	if (!isOK)
		return NDTS_BFalse ;

	/*
	isOK = DC_GetNumberOfBScansInSRAM(cardIndex, &numberOfBScansInBank) ;
	if (!isOK)
	return NDTS_BFalse ;

	if (numberOfBScansInBank != pRawScan->numberOfBScans)
	{
	// I think this is happening in non thread WinRS when it takes so long to malloc the data
	// structure that the card has filled the other bank and then filled our bank since the bank swap
	// at the start of getRawScan.
	DC_HandleRQError(DC_GetRawScan_RawScanNotLargeEnoughForData) ; - Daniel summer 08 rearrange - this was commented out so removed the error rather than putting it into DC - it was a non fatal
	return NDTS_BFalse ;
	}
	*/
	isOK = RawScan_GetMemoryBlockList(RawScan_ReturnRawScanInternal(pRawScan)->pDataB, &pDestinationBlockIt) ;
	if(!isOK) 
		return NDTS_BFalse ; 

	//  Get gate results data from card into scattered memory 
	isOK = DC_ReadRawScanBlocks(cardIndex, 
		cScanBaseSRAMAddress, 
		pDataBCardBlockDescriptions, 
		pDataBCardBlockDescriptions + numberOfDescriptions, 
		pDestinationBlockIt + 1,  // Skip over header pointer, one void*
		pThreadState->crcState.pCRCTable, 
		&numberOfBlocksRead) ;
	if (!isOK)
		return NDTS_BFalse ;

	if (numberOfBlocksRead != numberOfDescriptions)
	{
		// I don't think this should happen ie DC_ReadRawScanBlocks() should have returned false.
		_ASSERTE(0) ;

		DC_HandleError(DC_GetRawScan_NotAllBlocksDMAOK) ;
		return NDTS_BFalse ;
	}

	// Check the gated data header
	cardCScanBaseAddressOffset = cScanBaseSRAMAddress ;

	numberOfCScanDesynchronisations = 0 ;
	numberOfCorruptCScanHeaders = 0 ;

	/* OverSampling (interleaving) produces data as below with the timing as below.  The B Scan Rep Rate only applies if free running.
	A Over0 Seq0  A Over1 Seq0  A Over2 Seq0  A Over3 Seq0  A Over0 Seq1  A Over1 Seq1  A Over2 Seq1  A Over3 Seq1      A Over0 Seq0  A Over1 Seq0  A Over2 Seq0 A Over3 Seq0  A Over0 Seq1   A Over1 Seq1  A Over2 Seq1  A Over3 Seq1       A Over0 Seq0  
	|A Scn rep rt |A Scn rep rt |A Scn rep rt |A Scn rep rt |A Scn rep rt |A Scn rep rt |A Scn rep rt |                 |A Scn rep rt |A Scn rep rt |A Scn rep rt |A Scn rep rt |A Scn rep rt |A Scn rep rt |A Scn rep rt |                  |
	|--------------------------------------- B Scan Rep Rate -----------------------------------------------------------||--------------------------------------- B Scan Rep Rate -----------------------------------------------------------|

	Cscan data is interleaved / oversampled in a similar way, i.e. the gate information is built from each individual A-scan not the composite high resolution scan.
	*/


	for (bScanIndex = 0 ; bScanIndex < pRawScan->numberOfBScans ; bScanIndex++)
	{
		RawScan_MemoryHandle *pGateValuesBody ;
		int sampleIndex = 0;

		isOK = RawScan_GetRawScanGateValuesBody(pRawScan, bScanIndex, &pGateValuesBody) ;
		if(!isOK) 
			return NDTS_BFalse ; 

		pOurCScanBaseAddress = pGateValuesBody->p ;
		for (aScanIndex = 0 ; aScanIndex < pRawScan->numberOfAScansInBScan; aScanIndex++)
		{
			RawScanGateSettings *pGateSettings = pRawScan->ppGateSettings[aScanIndex];

			numberOfOversamples = pThreadState->associatedSDK.fpReturnOversampleCountThisStep(cardIndex, aScanIndex);

			for (oversampleIndex = 0; oversampleIndex < numberOfOversamples; oversampleIndex++)
			{

				pOurCScanRecordAddress = pOurCScanBaseAddress + 
					oversampleIndex * (4 + 4 * pRawScan->numberOfGates[aScanIndex]) + 
					pThreadState->associatedSDK.fpReturnGateOffsetThisStep(cardIndex, aScanIndex) - 4 ;
				cardCScanRecordAddressOffset = cardCScanBaseAddressOffset +
					oversampleIndex * (4 + 4 * pRawScan->numberOfGates[aScanIndex]) + 
					pThreadState->associatedSDK.fpReturnGateOffsetThisStep(cardIndex, aScanIndex) - 4 ;

				// Used when testing the NDTS2100 with faulty RAM 
				if (pThreadState->associatedSDK.ignoreHeaderAndCRC)
				{
					cardCScanHeader =  (0x43534e00 | aScanIndex) ;
					// Unlike the A Scan header, we don't repair the C Scan header because it is hard to get at and not checked during the extract functions.
				}
				else
				{
					// Only read the header from the card if not ignoring the checks
					// This looks like it may be reading every header directly over PCI - if so maybe it could be changed to check 
					// the header on the RawScan and only go to the card if there is a problem.

					isOK = NDTS_HAL_GetWord(pThreadState->associatedSDK.SID, cardIndex, cardCScanRecordAddressOffset, &cardCScanHeader) ;
					if (!isOK)
						return NDTS_BFalse ;

				}
				isCScanHeaderOK = RawScan_CheckCScanHeader(cardCScanHeader, sampleIndex, &isCScanHeaderIDPresent) ;
				if (!isCScanHeaderOK)
				{
					*(unsigned long *) pOurCScanRecordAddress = cardCScanHeader ;

					isCScanHeaderOK = RawScan_CheckCScanHeader(*(unsigned long *) pOurCScanRecordAddress, sampleIndex, &isCScanHeaderIDPresent) ;

					isCScanHeaderRecoverAttempted = NDTS_BTrue ;
				}
				else
					isCScanHeaderRecoverAttempted = NDTS_BFalse ;

				if (!isCScanHeaderOK)
					if (isCScanHeaderIDPresent)
					{
						numberOfCScanDesynchronisations++ ;
						pThreadState->crcState.statistics.cScanHeaderCounter.numberOfDesynchronisations++ ;
					}
					else
					{
						numberOfCorruptCScanHeaders++ ;
						pThreadState->crcState.statistics.cScanHeaderCounter.numberOfUnrecovered++ ;
					}
				else if (!isCScanHeaderRecoverAttempted)
					pThreadState->crcState.statistics.cScanHeaderCounter.numberOfGood++ ;
				else
					pThreadState->crcState.statistics.cScanHeaderCounter.numberOfRecovered++ ;

				DC_AdjustGateDepths(pGateSettings, pOurCScanRecordAddress);
				sampleIndex++;
			}
		}

		cardCScanBaseAddressOffset += pThreadState->associatedSDK.fpReturnGateBytesPerBScan(cardIndex);
	}

	if(numberOfCorruptCScanHeaders > 0)
	{
		DC_HandleError(DC_InternalDataFormatError) ;
		return NDTS_BFalse ;
	}
	else if(numberOfCScanDesynchronisations > 0)
	{
		DC_HandleError(DC_CScanDesynchronisation) ;
		return NDTS_BFalse ;
	}
/*
	previousNumberOfUnrecoveredCScanSamples = pBoardState->crcState.statistics.cScanSampleCounter.numberOfUnrecovered ;
	if (previousNumberOfUnrecoveredCScanSamples !=  pBoardState->crcState.statistics.cScanSampleCounter.numberOfUnrecovered) 
	{
		DC_HandleRQError(DC_GetRawScan_CRCSubSystemInternalError) ; - Error not currently in use - would need to add to Data Collection list if it were as its non fatal 
		return NDTS_BFalse ;
	}
*/
	return NDTS_BTrue ;
}

