#include <crtdbg.h>
#include "SnapshotInternal.h"
#include "ThreadInternal.h"
#include "DataCollectionErrorInternal.h"
#include "DataSamplingStatusInternal.h"
#include "RawScanProtected.h"
#include "RawScanOffline.h"
#include "Collect.h"
#include "NDTSCommonHal.h"
#include "NDTSCommonHalProtected.h"
#include "SDKUtilities.h"
#include "NDTSError.h"

// Called on data available.  DC_Lock and threadCriticalSection lock are already held.
NDTS_Boolean DC_UpdateCollect(unsigned long cardIndex)
{
	DC_State *pThreadState = DC_ReturnThreadStatePointer(cardIndex) ;
	NDTS_RawScan *pRawScan ;
	// unsigned long writeBank;
	NDTS_Boolean isOk = NDTS_BTrue ;
	static int consecutiveRawScanFailures = 0 ;

	if (!pThreadState) 
		return NDTS_BFalse; 

	if(pThreadState->collectState.rawScansInitialised == 0)
	{
		RawScan_InitialiseRawScan(&(pThreadState->collectState.rawScans[0]));
		pThreadState->collectState.rawScansInitialised = 1 ;
	}

	// writeBank = (pThreadState-> collectState.readBank) ? 0 : 1 ;
	pRawScan = &(pThreadState->collectState.rawScans[0/*writeBank*/]) ;
	if(pRawScan->numberOfBScans == 0)
	{
		// The raw scan in the write bank is empty. So fetch the raw scan straight into it.
		isOk = DC_GetRawScanNoBankSwap(cardIndex, pRawScan) ;
	}
	else
	{
		// The write bank contains a valid raw scan. Read the raw scan from the card and add
		// it's data to the raw scan in the write bank.
		NDTS_RawScan collectedRawScan ;

		RawScan_InitialiseRawScan(&collectedRawScan) ;

		isOk = DC_GetRawScanNoBankSwap(cardIndex, &collectedRawScan) ;
		if(isOk)
		{
			if(((RawScan_RawScanProtected*)(collectedRawScan.pInternal))->pDataB)
			{
				_ASSERTE(((RawScan_RawScanProtected*)(collectedRawScan.pInternal))->pDataB);
				RawScan_MoveRawScanDataToRawScan(pRawScan, &collectedRawScan) ;
			}
		}

		// Don't need the temporary raw scan anymore.
		RawScan_UninitialiseRawScan(&collectedRawScan) ;
	}

	if(isOk)
	{
		consecutiveRawScanFailures = 0 ;

//		isOk = DC_RequestBankSwap(cardIndex) ;
		isOk = DC_IndicateSampleDataDownloaded(cardIndex) ;
		if(!isOk) 
		{
			if (!DC_IsErrorNonFatal()) 
				return NDTS_BFalse;
		}
	}
	else
	{
		DC_ReturnCode rc = DC_GetLastError();
 		if (rc == DC_InternalDataFormatError || 
			rc == DC_AScanDesynchronisation ||
			rc == DC_CScanDesynchronisation ||
//			rc == DC_GetRawScan_CRCSubSystemInternalError ||
			rc == DC_ReadRawScanBlocks_BadBlockCRC)
//			rc == DC_GetRawScan_RawScanNotLargeEnoughForData)
		{
			// The block is corrupted - maybe by scanning too fast.  Drop this block but don't abort the scan
			// All the above cases are caused by firmware issues that could be resolved sometime in the future
			if (++consecutiveRawScanFailures > 3 ||	rc == DC_WaitForBankSwap_MissingTrigger)
			{
				pThreadState->associatedSDK.fpResetFirmware(cardIndex) ;
				consecutiveRawScanFailures = 0 ;

				return NDTS_BTrue ;
			}	
		}
		else if (rc == DC_WaitForBankSwap_MissingTrigger || rc == DC_WaitForBankSwap_SampleIncomplete)
			return NDTS_BFalse ;
		else
			return NDTS_BFalse ;

		if (!DC_IsErrorNonFatal()) 
			return NDTS_BFalse ;
	}

	return NDTS_BTrue;
}

// Called from UpdateSampleParameters in rq.dll.  DC_Lock() will be on before this is called.
void DC_UpdateParametersCollectHook(unsigned long cardIndex)
{
	DC_State *pThreadState = DC_ReturnThreadStatePointer(cardIndex);
	if (!pThreadState) return ;

	// This function is (indirectly?) called by a function in RQ.dll and is, therefore, enclosed in locks.

	// Code needs to be added here if the shape of raw scan in this DLLis different from
	// the settings in the application / RQ.dll eg the range has changed between the application
	// going into RQT.dll to collect a raw scan and it getting one.


	DC_AllocCardMemoryPools(cardIndex, NDTS_BFalse) ;

	if(pThreadState->collectState.rawScansInitialised)
		_ASSERTE(!DC_HaveCollectedBScansInCurrentBank(cardIndex)) ;
}

// Return true if we have downloaded any B Scans from the card and are storing them ready for collection by the app
NDTS_Boolean DC_HaveCollectedBScansInCurrentBank(unsigned long cardIndex)
{
	DC_State *pThreadState = DC_ReturnThreadStatePointer(cardIndex);
	if (!pThreadState) return NDTS_BFalse;
	return (pThreadState->collectState.rawScans[0].numberOfBScans == 0) ? NDTS_BFalse : NDTS_BTrue;
}

NDTS_Boolean CALL_CONV DC_GetRawScanNoBankSwap(unsigned long cardIndex, NDTS_RawScan *pRawScan)
{
	/*
		NDTS_RawScan::pDataA represents the scan data as an array of pointers.  The first pointer
		is to the header of type DC_DataAHeader. The address pDataA + 4 bytes represents the 
		first header of the scan data, which is of the same form 
		as was stored on the card.  pDataA + 8 bytes is the first block of A scan data, including the CRC if present.
		pDataA + 12 bytes is the second A scan header.

		NDTS_RawScan::pDataB represents the gate data (should it exist) with a four byte header of type DC_DataBHeader. 
		The address pDataB + 4 bytes points to the first B-scans worth of gate result data, including headers and the CRC
		if present.  pDataB + 8 bytes points to the block of encoder data if present, otherwise to the next B scan's worth
		of gate result data.
	*/
//	static unsigned long totalB = 0;
	unsigned long numberOfBScansInBank,	numberOfDescriptions ;
	NDTS_Boolean isOK;
	NDTS_ScanStorage scanStorage ;
	RawScan_CardBlockDescription *pDataACardBlockDescriptions = 0, *pDataBCardBlockDescriptions  = 0 ; 
	NDTS_Boolean result = NDTS_BFalse ;
	LARGE_INTEGER freqLarge, countLargeA, countLargeB ;
	DC_State *pThreadState = DC_ReturnThreadStatePointer(cardIndex);
	if (!pThreadState)
		return NDTS_BFalse;

	// Preconditions.
	_ASSERTE(pRawScan) ; // pRawScan MUST be initialised.

	DC_Lock(cardIndex) ;

	QueryPerformanceCounter(&countLargeA) ;
	
	isOK = pThreadState->associatedSDK.fpGetScanStorageOptions(cardIndex, &scanStorage) ;
	if (!isOK)
	{
		DC_Unlock(cardIndex) ;
		return NDTS_BFalse ;
	}

	
	isOK = pThreadState->associatedSDK.fpGetNumberOfBScansInSRAM(cardIndex, &numberOfBScansInBank) ;
	if (!isOK)
	{
		DC_Unlock(cardIndex) ;
		return NDTS_BFalse ;
	}

	{
		if (numberOfBScansInBank == 0)
		{
			DC_HandleError(DC_GetRawScan_InvalidNumberOfBScansInBank) ;
			DC_Unlock(cardIndex) ;
			return NDTS_BFalse ;
		}
	}
//	totalB += numberOfBScansInBank;
//	filelogt("RQ.C bscans %d\n", totalB);

	isOK = pThreadState->associatedSDK.fpCopyStateToRawScan(cardIndex, pRawScan, NDTS_BTrue) ;
	if (!isOK)
	{
		DC_Unlock(cardIndex) ;
		return NDTS_BFalse ;
	}

	// Allocate raw scan memory pools.
	/*
	isOK = RawScan_AllocRawScanMemoryPools(pRawScan) ;
	if (!isOK) 
	{
		DC_Unlock(cardIndex) ;
		return NDTS_BFalse ;
	}
	*/

	// Create a-scan data card buffer descriptions.
	if(RawScan_CreateRawScanBScanDescriptions(pRawScan, &pDataACardBlockDescriptions, &numberOfDescriptions) == NDTS_BTrue)
	{
		// Allocate raw scan a-scan data memory.
		if(RawScan_AllocRawScanDataAMemoryBlockNode(pRawScan) == NDTS_BTrue)
		{
			// Get a-scan data.
			if(DC_ReadRawScanBScans(cardIndex, pRawScan, pDataACardBlockDescriptions, numberOfDescriptions, pThreadState->crcState.pCRCTable) == NDTS_BTrue)
			{
				if (pRawScan->scanStorage  != NDTS_SSMultipleAScanNoCScan)
				{
					// Create descriptions of raw gate result data. 
					if(RawScan_CreateRawGateValueDescriptions(pRawScan, &pDataBCardBlockDescriptions, &numberOfDescriptions) == NDTS_BTrue)
					{
						// Allocate the scattered memory for the raw scan.
						if(RawScan_AllocRawScanDataBMemoryBlockNode(pRawScan) == NDTS_BTrue)
						{
							if(DC_ReadRawScanGateValues(cardIndex, pRawScan, pDataBCardBlockDescriptions, numberOfDescriptions) == NDTS_BTrue)
							{
#ifdef _DEBUG
								RawScan_ValidateRawScan(pRawScan);
#endif
								result = NDTS_BTrue ;
							}
						}
						RawScan_FreeRawDescriptions(pDataBCardBlockDescriptions) ;
					}
				}
			}
		}
		RawScan_FreeRawDescriptions(pDataACardBlockDescriptions) ;
	}

	if(result == NDTS_BFalse)
	{
		// The raw scan was not filled with data successfully. Dump what data is in there.
		DC_FreeRawScanData(pRawScan, DC_FRSOAll) ;
	}

	{
		float secs ; 
		QueryPerformanceFrequency(&freqLarge) ;
		QueryPerformanceCounter(&countLargeB) ;
		secs  = (float) (countLargeB.QuadPart - countLargeA.QuadPart) / (float) freqLarge.QuadPart ;
		pThreadState->aScanDMATiming.bScansPerSecond = (int)((float)numberOfBScansInBank / secs);
	}

	DC_Unlock(cardIndex) ;

	return result ;
}
NDTS_Boolean CALL_CONV DC_ReadRawScanBScans(unsigned long cardIndex,
											NDTS_RawScan *pRawScan,
											RawScan_CardBlockDescription* pSourceBlockDescriptionBegin, 
											unsigned long numberOfDescriptions, 
											const unsigned long *pCRCTable)
{
	NDTS_Boolean isOK ;
	DC_CRCState *pCRCState ;
	unsigned long sourceBlockOffset, aScanBaseSRAMAddress ;
	RawScan_CardBlockDescription* pSourceBlockDescriptionIt ;
	RawScan_MemoryHandle *pDestinationBlockIt ;
	unsigned long totalNumberOfBlocksRead = 0 ;
	unsigned long sourceAScanBaseAddressOffset, aScanBaseAddress, boardRawCRCWord ;
	NDTS_LogLevel badBlockLogLevel = NDTS_LLNever;
	DC_State *pThreadState = DC_ReturnThreadStatePointer(cardIndex);
	if (!pThreadState)
		return NDTS_BFalse;

	// Preconditions.
	_ASSERTE(pRawScan) ;
	_ASSERTE(pSourceBlockDescriptionBegin) ;
	_ASSERTE(numberOfDescriptions > 0) ;
//	_ASSERTE(pCRCTable) ; Don't have a CRC table with HLPRX full A capture

	pThreadState->associatedSDK.fpGetAScanBaseSRAMAddress(cardIndex, &aScanBaseAddress);
	// Use the reserved field to record the offset of each block in the card raw scan.
	for(sourceBlockOffset = 0, pSourceBlockDescriptionIt = pSourceBlockDescriptionBegin ; 
		pSourceBlockDescriptionIt != pSourceBlockDescriptionBegin + numberOfDescriptions ;
		++pSourceBlockDescriptionIt)
	{
		pSourceBlockDescriptionIt->reserved = sourceBlockOffset ;

		sourceBlockOffset += pSourceBlockDescriptionIt->size 
			+ (pSourceBlockDescriptionIt->flags & RawScan_CBDIsCRCCheckPresent ? 4 : 0) ;
	}

	
	pCRCState = &pThreadState->crcState ;

	pSourceBlockDescriptionIt = pSourceBlockDescriptionBegin ;

	isOK = RawScan_GetMemoryBlockList(RawScan_ReturnRawScanInternal(pRawScan)->pDataA, &pDestinationBlockIt) ;
	if(!isOK) 
		return NDTS_BFalse ; 
	isOK = pThreadState->associatedSDK.fpGetAScanBaseSRAMAddress(cardIndex, &aScanBaseSRAMAddress) ;
	if(!isOK) 
		return NDTS_BFalse ; 


	// Skip the raw scan header.
	pDestinationBlockIt++ ;

	do
	{
		unsigned long numberOfBlocksRead = 0 ;
		isOK = DC_ReadRawScanBlocks(cardIndex, 
			pSourceBlockDescriptionIt->reserved + aScanBaseSRAMAddress,
			pSourceBlockDescriptionIt,
			pSourceBlockDescriptionBegin + numberOfDescriptions,
			pDestinationBlockIt,
			pCRCTable,
			&numberOfBlocksRead) ;

		totalNumberOfBlocksRead += numberOfBlocksRead ;

		if(!isOK)
		{
			unsigned long aScanIndex, bScanIndex ;
			unsigned long sourceAScanSize ;
			unsigned long boardCRC, ourCRC ;
			DC_MemoryCheckCounterUpdateEvent event ;
			unsigned long rawScanBlocksPerAScan, rawScanBlocksPerBScan ;
			int retries = 0;
			unsigned long lastCRC = 0;

			if(DC_GetLastError(cardIndex) != DC_ReadRawScanBlocks_BadBlockCRC) 
			{
				_ASSERTE(0) ;

				// Not a CRC error.
				return NDTS_BFalse ;
			}

			// Calculate the number of blocks per a-scan and b-scan.
			rawScanBlocksPerAScan = RawScan_ReturnRawScanAScanMemoryBlockListSize(pRawScan) ;
			rawScanBlocksPerBScan = RawScan_ReturnRawScanBScanMemoryBlockListSize(pRawScan) ;
			
			// Sync the iterators to point to the faulty block.
			pSourceBlockDescriptionIt += numberOfBlocksRead ;
			pDestinationBlockIt += numberOfBlocksRead ;
			sourceBlockOffset = pSourceBlockDescriptionIt->reserved ;

			// A block CRC error occured. Was it an a-scan (body)? (We don't care about b-scan CRC's) ...

			// Check the block is part of an a-scan and not the b-scan trailer.
			if(totalNumberOfBlocksRead % rawScanBlocksPerBScan == pRawScan->numberOfAScansInBScan * rawScanBlocksPerAScan)
			{
				// This is strange as b-scan trailers don't have CRC's (that we care about anyway).
				_ASSERTE(0) ;

				// Move on to the block following the faulty one.
				++totalNumberOfBlocksRead ;
				++pSourceBlockDescriptionIt ;
				++pDestinationBlockIt ;

				continue ;
			}

			// Check the block is an a-scan body and not an a-scan header.
			if((totalNumberOfBlocksRead % rawScanBlocksPerBScan) % rawScanBlocksPerAScan != 1) 
			{
				// This is strange as a-scan headers don't have CRC's.
				_ASSERTE(0) ;

				// Move on to the block following the faulty one.
				++totalNumberOfBlocksRead ;
				++pSourceBlockDescriptionIt ;
				++pDestinationBlockIt ;

				continue ;
			}

			// It was an a-scan ... 

			// Calculate which a-scan is faulty.
			aScanIndex = (totalNumberOfBlocksRead % rawScanBlocksPerBScan) / rawScanBlocksPerAScan ;
			bScanIndex = totalNumberOfBlocksRead / rawScanBlocksPerBScan ;

			sourceAScanBaseAddressOffset = sourceBlockOffset + aScanBaseAddress;
			sourceAScanSize = pRawScan->aScanSampleValuesThisStep[aScanIndex] * RawScan_ReturnBytesPerSample(pRawScan->sampleSize) ;

			// Re read the CRC using a memory copy 
			isOK = NDTS_HAL_GetWord(pThreadState->associatedSDK.SID, cardIndex, sourceAScanBaseAddressOffset + sourceAScanSize, &boardRawCRCWord) ;
			if (!isOK)
				return NDTS_BFalse ;

			boardCRC = ~boardRawCRCWord ;

			ourCRC = SDKUtilities_CalculateCRC32(~0ul, pCRCState->pCRCTable, (char *)(pDestinationBlockIt->p), sourceAScanSize) ;

			// If the CRC is now OK, the block must have been OK before but the CRC was corrupted
			// Otherwise, re read the data using a memory copy.
			if (boardCRC != ourCRC)
			{
				unsigned long ourNewCRC ;

				isOK = NDTS_HAL_BoardByteCopy(pThreadState->associatedSDK.SID, cardIndex, sourceAScanBaseAddressOffset, (char *)pDestinationBlockIt->p, sourceAScanSize) ;
				if (!isOK)
					return NDTS_BFalse ;

				ourNewCRC = SDKUtilities_CalculateCRC32(~0ul, pCRCState->pCRCTable, (char *)pDestinationBlockIt->p, sourceAScanSize) ;

				if (boardCRC == ourNewCRC)
				{
					// Recovered, reading using a memory copy gives a correct CRC.
					pCRCState->statistics.aScanSampleCounter.numberOfRecovered++ ;
					event = DC_MCCUERecovered ;
				}
				else if (ourCRC == ourNewCRC)
				{
					// Pseudo - recovered, we have got the same data with both DMA and memory copy.
					// However, these CRC's don't match the one calculated by firmware.
					// This points to a problem with either the memory writing or CRC calculation 
					// on the RQ, rather than a PCI data transfer corruption.
					NDTS_LogLine(badBlockLogLevel, "CRC PSEUDO RECOVER firmware %x software %x ",
						boardCRC, ourCRC);

					pCRCState->statistics.aScanSampleCounter.numberOfPseudoRecovered++ ;
					event = DC_MCCUEPseudoRecovered ;
				}
				else
				{
					// Unrecovered, when we re read the data, we get a different CRC from any we have had so far.
					NDTS_LogLine(badBlockLogLevel, "CRC RETRIES firmware %x software dma %x software memcpy %x",
						boardCRC, ourCRC, ourNewCRC);
					do
					{
						isOK = NDTS_HAL_GetWord(pThreadState->associatedSDK.SID, cardIndex, sourceAScanBaseAddressOffset, &boardRawCRCWord) ;
						if (!isOK)
							return NDTS_BFalse ;

						boardCRC = ~boardRawCRCWord ;
						isOK = NDTS_HAL_BoardByteCopy(pThreadState->associatedSDK.SID, cardIndex, sourceAScanBaseAddressOffset, (char *)pDestinationBlockIt->p, sourceAScanSize) ;
						if (!isOK)
							return NDTS_BFalse ;

						lastCRC = ourNewCRC;
						ourNewCRC = SDKUtilities_CalculateCRC32(~0ul, pCRCState->pCRCTable, (char *)pDestinationBlockIt->p, sourceAScanSize) ;
						retries++;
						NDTS_LogLine(badBlockLogLevel, "CRC retry %d firmware %x software %x",
							retries, boardCRC, ourNewCRC);
					}
					while (ourNewCRC != boardCRC && ourNewCRC != lastCRC && retries < 10);
					if (ourNewCRC == lastCRC)
					{
						// Daniel - Sept 07 - With A Scan on RF and DC offset set correctly, there
						// were many errors where the DMA and memcpy differed but then 
						// repeated memcpy gave the same result. The firmware (board) CRC was the 
						// same throughout and "looked wrong", e.g. 0xfff00ff or similar rather than 
						// a completely random number.  These are now logged as pseudo recovered.
						pCRCState->statistics.aScanSampleCounter.numberOfPseudoRecovered++ ;
						event = DC_MCCUEPseudoRecovered ;
					}
					else
					{
						pCRCState->statistics.aScanSampleCounter.numberOfUnrecovered++ ;
						event = DC_MCCUEUnrecovered ;
					}
				}
				if (pCRCState->pCRCBadScanHook != 0)
				{
					DC_BadAScanStatistics statistics ;

					statistics.indices.aScanIndex = aScanIndex ;
					statistics.indices.bScanIndex = bScanIndex ;

					// NB: B-scan data starts at memory address 0.
					statistics.memoryRange.startAddress = sourceBlockOffset ;
					statistics.memoryRange.endAddress = statistics.memoryRange.startAddress + pSourceBlockDescriptionIt->size ;

					statistics.event = event ;

					(*pCRCState->pCRCBadScanHook)(&statistics, 0, pCRCState->pCRCBadScanHookContext) ;
				}
			}

			// Move on to the block following the faulty one.
			++totalNumberOfBlocksRead ;
			++pSourceBlockDescriptionIt ;
			++pDestinationBlockIt ;
		}
	}
	while(totalNumberOfBlocksRead != numberOfDescriptions) ;

	pCRCState->statistics.aScanSampleCounter.numberOfGood += pRawScan->numberOfFullBScans * pRawScan->numberOfAScansInBScan ;

	// Verify a-scan headers.
	isOK = DC_VerifyRawScanAScanHeaders(cardIndex, pRawScan) ;
	if (!isOK)
		return NDTS_BFalse ;

	// Bug 786 - firmware now stores negative A Scan values in sign / mag style format in non sequenced
	if (pThreadState->associatedSDK.fpReturnIsSignAndMag(cardIndex))
		RawScan_ConvertRawScanFromSignMagToTwosComp(pRawScan);

	return NDTS_BTrue ;
}
NDTS_Boolean DC_VerifyRawScanAScanHeaders(unsigned long cardIndex, NDTS_RawScan *pRawScan)
{
	unsigned long numberOfAScanDesynchronisations, numberOfCorruptAScanHeaders, bScanIndex,
		          cardAScanBaseAddressOffset, cardBScanBaseAddressOffset, cardAScanHeader ;
	NDTS_Boolean isOK ;
	DC_State *pThreadState = DC_ReturnThreadStatePointer(cardIndex);
	if (!pThreadState)
		return NDTS_BFalse;

	pThreadState->associatedSDK.fpGetAScanBaseSRAMAddress(cardIndex, &cardBScanBaseAddressOffset);

	numberOfCorruptAScanHeaders = numberOfAScanDesynchronisations = 0 ;

	for (bScanIndex = 0 ; bScanIndex < pRawScan->numberOfFullBScans; bScanIndex++)
	{
		RawScan_MemoryHandle *pAScanHeaders ;
		unsigned long *pOurAScanHeaderAddress ;
		unsigned long aScanIndex ;

		isOK = RawScan_GetRawScanAScanHeader(pRawScan, 0, bScanIndex,  &pAScanHeaders) ;
		if(!isOK) 
			return NDTS_BFalse ; 

		pOurAScanHeaderAddress = (unsigned long*)(pAScanHeaders->p) ;

		for (aScanIndex = 0 ; aScanIndex < pRawScan->numberOfAScansInBScan; aScanIndex++)
		{
			NDTS_Boolean isAScanHeaderOK, isAScanHeaderIDPresent, isAScanHeaderRecoverAttempted ;

			// Used when testing the NDTS2100 with faulty RAM - repair the header here so it passes later checks
			if (pThreadState->associatedSDK.ignoreHeaderAndCRC)
			{
				RawScan_MemoryHandle *pAScanHeader;

				cardAScanHeader =  (0x41534e00 | aScanIndex) ;
				_ASSERTE(pRawScan->isSequence);  // Need RPPS on the aScanIndex if array with ping pong table
				isOK = RawScan_GetRawScanAScanHeader(pRawScan, aScanIndex, bScanIndex, &pAScanHeader) ;
				_ASSERTE(isOK);	
				*(unsigned long *)(pAScanHeader->p)= cardAScanHeader;
			}
			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.
				cardAScanBaseAddressOffset = cardBScanBaseAddressOffset + pThreadState->associatedSDK.fpReturnAScanOffsetThisStep(cardIndex, aScanIndex) - 4; // Point to header rather than scan data

				isOK = NDTS_HAL_GetWord(pThreadState->associatedSDK.SID, cardIndex, cardAScanBaseAddressOffset, &cardAScanHeader) ;
				if (!isOK)
					return NDTS_BFalse ;
			}

			// Check the a-scan header we read from the card.
			isAScanHeaderOK = RawScan_CheckAScanHeader(cardAScanHeader, aScanIndex, &isAScanHeaderIDPresent) ;
			if (!isAScanHeaderOK)
			{
				*pOurAScanHeaderAddress = cardAScanHeader ;

				// It's wrong, so check the a-scan header on the card.
				isAScanHeaderOK = RawScan_CheckAScanHeader(*pOurAScanHeaderAddress, aScanIndex, &isAScanHeaderIDPresent) ;

				isAScanHeaderRecoverAttempted = NDTS_BTrue ;
			}
			else
				isAScanHeaderRecoverAttempted = NDTS_BFalse ;

			if (!isAScanHeaderOK)
				if (isAScanHeaderIDPresent)
				{
					numberOfAScanDesynchronisations++ ;
					pThreadState->crcState.statistics.aScanHeaderCounter.numberOfDesynchronisations++ ;
				}
				else
				{
					numberOfCorruptAScanHeaders++ ;
					pThreadState->crcState.statistics.aScanHeaderCounter.numberOfUnrecovered++ ;
				}
			else if (!isAScanHeaderRecoverAttempted)
				pThreadState->crcState.statistics.aScanHeaderCounter.numberOfGood++ ;
			else
				pThreadState->crcState.statistics.aScanHeaderCounter.numberOfRecovered++ ;

			// We know that the next a-scan header is actually immediately after the current a-scan header.
			pOurAScanHeaderAddress++ ;

		}

		cardBScanBaseAddressOffset += pThreadState->associatedSDK.fpReturnAScanBytesPerBScan(cardIndex);
	}

	if(numberOfCorruptAScanHeaders > 0)
	{
		DC_HandleError(DC_InternalDataFormatError) ;
		return NDTS_BFalse ;
	}
	else if(numberOfAScanDesynchronisations > 0)
	{
		DC_HandleError(DC_AScanDesynchronisation) ;
		return NDTS_BFalse ;
	}

	return NDTS_BTrue;
}

