#include <windows.h>
#include <stdio.h>
#include <io.h>
#include <limits.h>
#include <crtdbg.h>

#include "RQOfflineInternal.h"
#include "RQInternal.h"
#include "RQStrings.h"
#include "RQRawScan.h"

#include "NDTSCommonHALProtected.h"

#ifndef _DEBUG
#include "RQSubversionRevision.h"
#else
#define RQ_SUBVERSION_INFO_STRING "DEBUG"
#endif

// RQOffline has its own error codes as the last error is not card dependent


RQ_API NDTS_Boolean CALL_CONV RQ_ExtractAScan(unsigned long bScanIndex, unsigned long aScanIndex, RQ_RawScan *pRawScan, short *pAScan, unsigned long numberOfBufferValues)
{
	NDTS_Boolean isAScanHeaderOK, isAScanHeaderIDPresent;
	RQ_MemoryHandle *pAScanHeader, *pAScanBody ;
	NDTS_Boolean isOK ;
	RQ_RawScanProtected *pRawInternal ;
	unsigned long numberOfAScanBytes, numberOfBytesToCopy, numberOfBufferBytes ;

	// Preconditions
	_ASSERTE(pRawScan) ;
	_ASSERTE(pAScan) ;

	pRawInternal = RQ_ReturnRawScanInternal(pRawScan) ;
	_ASSERTE(pRawInternal) ;

	if (pRawInternal->pDataA == 0)
	{
		RQ_HandleRQError(RQOffline_ExtractAScan_RawScanDoesNotContainAScanData) ;
		return NDTS_BFalse;
	}

	if (bScanIndex >= pRawScan->numberOfBScans)
	{
		RQ_HandleRQError(RQOffline_ExtractAScan_InvalidBScanIndex) ;
		return NDTS_BFalse ;
	}

	if (aScanIndex >= pRawScan->numberOfAScansInBScan)
	{
		RQ_HandleRQError(RQOffline_ExtractAScan_InvalidAScanIndex) ;
		return NDTS_BFalse ;
	}

	// Check the a-scan header.
	isOK = RQ_GetRawScanAScanHeader(pRawScan, pRawInternal->RPPS[aScanIndex], bScanIndex, &pAScanHeader) ;
	if(!isOK) 
		return NDTS_BFalse ; 

	isAScanHeaderOK = RQ_CheckAScanHeader(*(unsigned long *)(pAScanHeader->p), pRawInternal->RPPS[aScanIndex], &isAScanHeaderIDPresent) ;
	if (!isAScanHeaderOK)
	{
		if (isAScanHeaderIDPresent)
			RQ_HandleRQError(RQOffline_ExtractAScan_AScanDesynchronisation);
		else
			RQ_HandleRQError(RQOffline_ExtractAScan_InternalDataFormatError);

		return NDTS_BFalse;
	}

	// Copy the a-scan body.
	isOK = RQ_GetRawScanAScanBody(pRawScan, pRawInternal->RPPS[aScanIndex], bScanIndex, &pAScanBody) ;
	if(!isOK) 
		return NDTS_BFalse ; 

	numberOfBufferBytes = numberOfBufferValues << 1 ;

	numberOfAScanBytes = pRawScan->aScanSamplesThisStep[pRawInternal->RPPS[aScanIndex]] * RQ_ReturnBytesPerSample(pRawScan->sampleSize) ;
	numberOfBytesToCopy = numberOfAScanBytes < numberOfBufferValues << 1 ? numberOfAScanBytes : numberOfBufferValues << 1 ;

	NDTS_HAL_ByteCopy(pAScanBody->p, pAScan, numberOfBytesToCopy) ;

	// If buffer is larger than A-scan size then fill in rest with zeros.
	if (numberOfBytesToCopy < numberOfBufferBytes)
	{
		unsigned long i ;
		char *pBuffer = (char *) pAScan + numberOfBytesToCopy ;

		for (i = numberOfBytesToCopy ; i < numberOfBufferBytes ; i++)
			*pBuffer++ = 0 ;
	}

	return NDTS_BTrue ;
}

RQ_API NDTS_Boolean CALL_CONV RQ_ExtractBScan(unsigned long bScanIndex, RQ_RawScan *pRawScan, short **ppBScan, unsigned long numberOfAScanPointers, unsigned long numberOfAScanBufferValues)
{
	unsigned long aScanIndex, numberOfAScansToCopy ;
	NDTS_Boolean isAScanHeaderOK, isAScanHeaderIDPresent ;
	RQ_RawScanProtected *pRawInternal ;
	NDTS_Boolean isOK ;
	unsigned long numberOfAScanBytes, numberOfBytesToCopy, numberOfAScanBufferBytes ;

	// Preconditions
	_ASSERTE(pRawScan) ;
	_ASSERTE(ppBScan) ;

	pRawInternal = RQ_ReturnRawScanInternal(pRawScan) ;
	_ASSERTE(pRawInternal) ;

	if (pRawInternal->pDataA == 0)
	{
		RQ_HandleRQError(RQOffline_ExtractBScan_RawScanDoesNotContainBScanData) ;
		return NDTS_BFalse ;
	}

	if (bScanIndex >= pRawScan->numberOfBScans)
	{
		RQ_HandleRQError(RQOffline_ExtractBScan_InvalidBScanIndex) ;
		return NDTS_BFalse ;
	}

	numberOfAScansToCopy = numberOfAScanPointers < pRawScan->numberOfAScansInBScan ? numberOfAScanPointers : pRawScan->numberOfAScansInBScan ;

	for (aScanIndex = 0 ; aScanIndex < numberOfAScansToCopy ; aScanIndex++)
	{
		RQ_MemoryHandle *pAScanHeader, *pAScanBody ;

		// Check the a-scan header.
		isOK = RQ_GetRawScanAScanHeader(pRawScan, pRawInternal->RPPS[aScanIndex], bScanIndex, &pAScanHeader) ;
		if(!isOK) 
			return NDTS_BFalse ; 

		isAScanHeaderOK = RQ_CheckAScanHeader(*(unsigned long *)(pAScanHeader->p), pRawInternal->RPPS[aScanIndex], &isAScanHeaderIDPresent) ;
		if (!isAScanHeaderOK)
		{
			if (isAScanHeaderIDPresent)
				RQ_HandleRQError(RQOffline_ExtractAScan_AScanDesynchronisation);
			else
				RQ_HandleRQError(RQOffline_ExtractAScan_InternalDataFormatError);

			return NDTS_BFalse;
		}

		// Copy the a-scan body.
		isOK = RQ_GetRawScanAScanBody(pRawScan, pRawInternal->RPPS[aScanIndex], bScanIndex, &pAScanBody) ;
		if(!isOK) 
			return NDTS_BFalse ; 

		numberOfAScanBytes = pRawScan->aScanSamplesThisStep[pRawInternal->RPPS[aScanIndex]] * RQ_ReturnBytesPerSample(pRawScan->sampleSize) ;
		numberOfBytesToCopy = numberOfAScanBytes < numberOfAScanBufferValues << 1 ? numberOfAScanBytes : numberOfAScanBufferValues << 1 ;

		numberOfAScanBufferBytes = numberOfAScanBufferValues << 1 ;

		NDTS_HAL_ByteCopy(pAScanBody->p, ppBScan[aScanIndex], numberOfBytesToCopy) ;

		// If buffer is larger than A-scan size then fill in rest with zeros.
		if (numberOfBytesToCopy < numberOfAScanBufferBytes)
		{
			unsigned long i ;
			char *pBuffer = (char *) ppBScan[aScanIndex] + numberOfBytesToCopy ;

			for (i = numberOfBytesToCopy ; i < numberOfAScanBufferBytes ; i++)
				*pBuffer++ = 0 ;
		}
	}

	for (aScanIndex = numberOfAScansToCopy ; aScanIndex < numberOfAScanPointers ; aScanIndex++)
	{
		unsigned long i ;
		char *pBuffer = (char *) ppBScan[aScanIndex] ;

		for (i = 0 ; i < numberOfAScanBufferValues ; i++)
			*pBuffer++ = 0 ;
	}

	return NDTS_BTrue ;
}


/*
	pRPNSequence ends with a -1.

	Need to start enumerated types from sufficiently high indices that the 
	pRPNSequence symbols are distinct.

	Require distinct symbols for gate0..9 amplitude and distance, and operators.

	Max pRPNSequence length = 256
*/

/*
	If numberOfBScans == 0 then extract all B-scans from bScanIndex upwards in pRawScan.
	Therefore, to extract all B-scans call with bScanIndex == 0 and numberOfBScans == 0.
	Not compatible with PRX sequence scans.
*/

RQ_API NDTS_Boolean CALL_CONV RQ_ExtractCScan(int *pRPNSequence, 
											  unsigned long bScanIndex, 
											  unsigned long numberOfBScans, 
											  RQ_RawScan *pRawScan, 
											  RQ_CScan *pCScan,
											  unsigned long numberOfBufferRows, 
											  unsigned long numberOfBufferColumns)
{
	unsigned long internalGateIndex, packedGate, unpackedGate ;
	NDTS_Boolean isInvalidSequence, isComplete ;
	long *pStack ;
	unsigned long stackIndex, seqIndex, i, j ;
	long oprndA, oprndB, unity, unityShift, metric ;
	RQ_BinaryOperator op ;
	RQ_GateOutput gateOutput ;
	unsigned long rgGateIndices[RQ_MAXIMUM_NUMBER_OF_GATES_COMPILE_TIME] ;
	NDTS_Boolean isScaled ;
	unsigned char *pBScanGatesBaseAddress ;
	RQ_RawScanProtected *pRawInternal ;
	RQ_MemoryHandle *pGateValuesBody ;
	NDTS_Boolean isOK ;
	long stack[2 * RQ_MAXIMUM_RPN_SEQUENCE_LENGTH] ;
	unsigned long numberOfRowsToCopy, numberOfColumnsToCopy ;

	// Preconditions
	_ASSERTE(pRawScan) ;
	_ASSERTE(pCScan) ;

	pRawInternal = RQ_ReturnRawScanInternal(pRawScan) ;
	_ASSERTE(pRawInternal) ;

	if (pRawInternal->pDataB == 0)
	{
		RQ_HandleRQError(RQOffline_ExtractCScan_RawScanDoesNotContainGateData) ;
		return NDTS_BFalse ;
	}

	if (bScanIndex + numberOfBScans > pRawScan->numberOfBScans)
	{
		RQ_HandleRQError(RQOffline_ExtractCScan_InvalidBScanBounds) ;
		return NDTS_BFalse ;
	}

	if (numberOfBScans == 0)
		numberOfBScans = pRawScan->numberOfBScans - bScanIndex ;

	for (i = 0 ; i < RQ_ReturnMaximumNumberOfGates(pRawScan->isSequence); i++)
		rgGateIndices[i] = 0xffffffff ;
	
	if (pRawScan->isSequence)
	{
		RQ_HandleRQError(RQOffline_ExtractCScan_NotCompatibleWithSequencedFPGA) ;
		return NDTS_BFalse ;
	}

	packedGate = 0;
	for (unpackedGate = 0; unpackedGate < RQ_ReturnMaximumNumberOfGates(pRawScan->isSequence); unpackedGate++)
	{
		// ppGates now stores the data for all the gates not just the active ones
		if (pRawScan->ppGates[0][unpackedGate].isEnabled)
		{
			rgGateIndices[unpackedGate] = packedGate++;
		}
		if (pRawScan->ppGates[0][unpackedGate].storeGateValue == RQ_SGVAlways)
		{
			// To make this work with gate outputs value all the time - use something like RQ_ExtractIsGateValueValid
			// search for wasAmplitudeMeasured below
			RQ_HandleRQError(RQOffline_ExtractCScan_InvalidGate) ;
			return NDTS_BFalse ;
		}
	}

	if (pRawScan->scanStorage == RQ_SSMultipleAScanNoCScan)
	{
		RQ_HandleRQError(RQOffline_ExtractCScan_MissingCScan) ;
		return NDTS_BFalse ;
	}

	i = 0 ;
	isScaled = NDTS_BFalse ;

	while (pRPNSequence[i] != -1 && !isScaled)
	{
		isScaled = pRPNSequence[i] == RQ_BODivide ;
		i++ ;
	}

	unity = !isScaled ? 1 : 32768 ;
	unityShift = !isScaled ? 0 : 15 ;

	pStack = stack ;

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

	pBScanGatesBaseAddress = pGateValuesBody->p ;

	numberOfRowsToCopy = pRawScan->numberOfAScansInBScan < numberOfBufferRows ? pRawScan->numberOfAScansInBScan : numberOfBufferRows ;
	numberOfColumnsToCopy = numberOfBScans < numberOfBufferColumns ? numberOfBScans : numberOfBufferColumns ;

	for (i = 0 ; i < numberOfColumnsToCopy ; i++)
	{
		for (j = 0 ; j < numberOfRowsToCopy ; j++)
		{
			unsigned char *pAScanGatesBaseAddress = pBScanGatesBaseAddress + pRawInternal->gateOffsetThisStep[pRawInternal->RPPS[j]] - 4;
			unsigned long cScanHeader = *(unsigned long *) (pAScanGatesBaseAddress) ; 
			NDTS_Boolean isCScanHeaderIDPresent ;
			NDTS_Boolean isCScanHeaderOK = RQ_CheckCScanHeader(cScanHeader, (unsigned long) pRawInternal->RPPS[j], &isCScanHeaderIDPresent) ;

			if (!isCScanHeaderOK)
			{
				if (isCScanHeaderIDPresent)
				{
					RQ_HandleRQError(RQOffline_ExtractCScan_CScanDesynchronisation);
					return NDTS_BFalse;
				}
				else
				{
					RQ_HandleRQError(RQOffline_ExtractCScan_InternalDataFormatError);
					return NDTS_BFalse;
				}
			}

			stackIndex = 0 ;
			seqIndex = 0 ;

			isComplete = NDTS_BFalse ;
			isInvalidSequence = NDTS_BFalse ;

			do
			{
				if (RQ_BOAdd <= pRPNSequence[seqIndex] && pRPNSequence[seqIndex] <= RQ_BONop)
				{
					// Next symbol is operator. Pop two metric values off stack and apply.

					op = (RQ_BinaryOperator) pRPNSequence[seqIndex] ;

					stackIndex-- ;
					oprndB = pStack[stackIndex] ;

					stackIndex-- ;
					oprndA = pStack[stackIndex] ;

					if (oprndA == 0 || oprndB == 0)
					{
						// If either operand is non-triggered then we should return non-triggered as the output value
						metric = 0;
					}
					else
					{
						switch (op)
						{
						case RQ_BOAdd :
							metric = oprndA + oprndB ;
							break ;
						case RQ_BOSubtract :
							metric = oprndA - oprndB ;
							break ;
						case RQ_BODivide :
							oprndB >>= unityShift ;
							if (oprndB != 0)
							{
								metric = oprndA / oprndB ;
							}
							else
							{
								// oprndB likely to be 0 due to dropout so
								// return 0 rather than max
								metric = 0 ;
							}
							break ;
						case RQ_BOMultiply :
							oprndB >>= unityShift ;
							metric = oprndA * oprndB ;
							break ;
						default :						
							isInvalidSequence = NDTS_BTrue ;
							metric = 0 ;
						}
					}

					pStack[stackIndex] = metric ;
					stackIndex++ ;
				}
				else if (RQ_GMAbsDistance <= pRPNSequence[seqIndex] && pRPNSequence[seqIndex] <= RQ_GMNull)
				{
					// Next symbol is a metric value. Push onto stack.
					internalGateIndex = rgGateIndices[(pRPNSequence[seqIndex] - RQ_GM0Distance) >> 1] ;

					if (internalGateIndex == 0xffffffff)
					{
						RQ_HandleRQError(RQOffline_ExtractCScan_InvalidGate) ;
						return NDTS_BFalse ;
					}

					gateOutput = *(RQ_GateOutput *) (pAScanGatesBaseAddress + 4 + 4 * internalGateIndex) ; 

// To make this work with gate outputs value all the time - 
// sort out gate 2 output value all the time and triggers off gate 1, what happens if gate 1 doesn't trigger 

					metric = (pRPNSequence[seqIndex] & 1) == 0 ? gateOutput.wasDistanceMeasured ? (long) gateOutput.distanceMetric : 0 :
					                                             gateOutput.wasAmplitudeMeasured ? (long) gateOutput.amplitudeMetric : 0 ;
					metric <<= unityShift ;

					pStack[stackIndex] = metric ;
					stackIndex++ ;
				}
				else if (pRPNSequence[seqIndex] == -1)
					isComplete = NDTS_BTrue ;
				else
					isInvalidSequence = NDTS_BTrue ;

				seqIndex++ ;
			}
			while (!isComplete && !isInvalidSequence && seqIndex < RQ_MAXIMUM_RPN_SEQUENCE_LENGTH) ;

			if (isInvalidSequence)
			{
				// free(pStack);
				RQ_HandleRQError(RQOffline_ExtractCScan_InvalidRPNSequence) ;
				return NDTS_BFalse ;
			} 

			stackIndex-- ;
			pCScan->ppValues[j][i] = pStack[stackIndex] ;
		}

		pBScanGatesBaseAddress += pRawInternal->gateBytesPerBScan;
	}

	if (numberOfRowsToCopy < numberOfBufferRows)
		for (i = 0 ; i < numberOfBufferColumns ; i++)
			for (j = numberOfRowsToCopy ; j < numberOfBufferRows ; j++)
				pCScan->ppValues[j][i] = 0 ;

	if (numberOfColumnsToCopy < numberOfBufferColumns)
		for (i = numberOfColumnsToCopy ; i < numberOfBufferColumns ; i++)
			for (j = 0 ; j < numberOfBufferRows ; j++)
				pCScan->ppValues[j][i] = 0 ;


	pCScan->numberOfAScansInBScan = pRawScan->numberOfAScansInBScan ;
	pCScan->numberOfBScans = pRawScan->numberOfBScans ;
	pCScan->unity = unity ;

	// free(pStack) ;
	return NDTS_BTrue ;
}

// This function is used by Windows RapidScan to efficiently extract the packed gate values.
// It is only applicable to non sequenced applications.
// WinRS takes care of the unpacking once the scan is complete.
// Other applications may be better using a function that unpacks the gates, such as RQ_ExtractGateValuesSequenced

RQ_API NDTS_Boolean CALL_CONV RQ_ExtractGateValues(unsigned long bScanIndex, 
												   unsigned long aScanIndex, 
												   RQ_RawScan *pRawScan, 
												   RQ_GateOutput *pGateOutputs,
												   unsigned long numberOfBufferValues)
{
	unsigned long i, numberOfValuesToCopy ;
	unsigned char *pAScanGatesBaseAddress ;
	RQ_RawScanProtected *pRawInternal = RQ_ReturnRawScanInternal(pRawScan) ;
	RQ_MemoryHandle *pGateValuesBody ;
	NDTS_Boolean isOK ;
	RQ_GateOutput blankGateOutput = {0, 0, 0, 0, 0, 0} ;

	// To make this function sequence compatible, the RQ_GetGatesBScanScanNumberOfStrideBytes would need to be able
	// to cope with different numbers of gates per sequence step.
	// Or rewrite calling RQ_ExtractReturnGateValue multiple times
	if (pRawScan->isSequence)
	{
		RQ_HandleRQError(RQOffline_ExtractGateValues_InvalidBScanIndex) ;
		return NDTS_BFalse ;
	}

	if (pRawInternal->pDataB== 0)
	{
		RQ_HandleRQError(RQOffline_ExtractGateValues_RawScanDoesNotContainGateData) ;
		return NDTS_BFalse ;
	}

	if (bScanIndex >= pRawScan->numberOfBScans)
	{
		RQ_HandleRQError(RQOffline_ExtractGateValues_InvalidBScanIndex) ;
		return NDTS_BFalse ;
	}

	if (aScanIndex >= pRawScan->numberOfAScansInBScan)
	{
		RQ_HandleRQError(RQOffline_ExtractGateValues_InvalidAScanIndex) ;
		return NDTS_BFalse ;
	}

	// Get the gate result body.
	isOK = RQ_GetRawScanGateValuesBody(pRawScan, bScanIndex, &pGateValuesBody) ;
	if(!isOK) 
		return NDTS_BFalse ; 

	pAScanGatesBaseAddress = pGateValuesBody->p ;
	pAScanGatesBaseAddress += pRawInternal->gateOffsetThisStep[pRawInternal->RPPS[aScanIndex]];

	numberOfValuesToCopy = pRawScan->numberOfGates[pRawInternal->RPPS[aScanIndex]] < numberOfBufferValues ? pRawScan->numberOfGates[pRawInternal->RPPS[aScanIndex]] : numberOfBufferValues ;

	for (i = 0 ; i < numberOfValuesToCopy ; i++)
		pGateOutputs[i] = *(RQ_GateOutput *) (pAScanGatesBaseAddress + 4 * i) ; 

	for (i = numberOfValuesToCopy ; i < numberOfBufferValues ; i++)
		pGateOutputs[i] = blankGateOutput ;

	return NDTS_BTrue ;
}

RQ_API NDTS_Boolean CALL_CONV RQ_ExtractEncoderValue(unsigned long bScanIndex, unsigned long encoderIndex, RQ_RawScan *pRawScan, long *pEncoderValue)
{
	RQ_RawScanProtected *pRawInternal = RQ_ReturnRawScanInternal(pRawScan) ;
	long *pEncoderBlock ;
	NDTS_Boolean isOK ;
	RQ_MemoryHandle *pGateValuesTrailer ;

	// Preconditions
	_ASSERTE(pRawScan) ;
	_ASSERTE(pEncoderValue) ;

	if (pRawInternal->pDataB == 0)
	{
		RQ_HandleRQError(RQOffline_ExtractEncoderValue_RawScanDoesNotContainEncoderData) ;
		return NDTS_BFalse ;
	}

	if (bScanIndex >= pRawScan->numberOfBScans)
	{
		RQ_HandleRQError(RQOffline_ExtractEncoderValue_InvalidBScanIndex) ;
		return NDTS_BFalse ;
	}

	if (encoderIndex > RQ_NUMBER_OF_ENCODERS)
	{
		RQ_HandleRQError(RQOffline_ExtractEncoderValue_InvalidEncoderIndex) ;
		return NDTS_BFalse ;
	}

	// FT for dual encoder
	if (encoderIndex == RQ_NUMBER_OF_ENCODERS)
	{
		encoderIndex = 0;
	}
	
	isOK = RQ_GetRawScanGateValuesTrailer(pRawScan, bScanIndex, &pGateValuesTrailer) ;
	if(!isOK) 
		return NDTS_BFalse ; 

	pEncoderBlock = (long *)(pGateValuesTrailer->p) ;
	_ASSERTE(pEncoderBlock) ;

	*pEncoderValue = *(pEncoderBlock + encoderIndex);
	
	return NDTS_BTrue;
}

RQ_API NDTS_Boolean CALL_CONV RQ_ExtractSnapshotOutput(unsigned long cardIndex, RQ_RawScan *pRawScan, unsigned long step, unsigned long gateIndex, short *pBuffer, unsigned long numberOfBufferValues, short *pAmplitude, short *pDepth)
{
	if (pDepth != NULL)
	{
        RQ_ExtractGateDepthValues(pRawScan, step, pAmplitude, pDepth);
	}
	if (pAmplitude != NULL)
	{
		RQ_ExtractGateAmplitudeValues(pRawScan, step, pAmplitude, pDepth);
	}
	
	if (pBuffer != NULL)
	{
		// Ignore the return code - if the A Scan was corrupted, the gate data is still likely to be OK
		RQ_ExtractSpecificAScan(cardIndex, pRawScan, step, gateIndex, pBuffer, numberOfBufferValues, pAmplitude, pDepth);
	}

	return NDTS_BTrue;
}

// ***************************************************************************************************

RQ_API NDTS_Boolean CALL_CONV RQ_ExtractAScanPtr(unsigned long bScanIndex, unsigned long aScanIndex, RQ_RawScan *pRawScan, short **ppAScan, unsigned long *pNumberOfAScanSamples)
{
	NDTS_Boolean isAScanHeaderOK, isAScanHeaderIDPresent;
	RQ_MemoryHandle *pAScanHeader, *pAScanBody ;
	NDTS_Boolean isOK ;
	RQ_RawScanProtected *pRawInternal ;

	// Preconditions
	_ASSERTE(pRawScan) ;
	_ASSERTE(ppAScan) ;

	pRawInternal = RQ_ReturnRawScanInternal(pRawScan) ;
	_ASSERTE(pRawInternal) ;

	if (pRawInternal->pDataA == 0)
	{
		RQ_HandleRQError(RQOffline_ExtractAScan_RawScanDoesNotContainAScanData) ;
		return NDTS_BFalse;
	}

	if (bScanIndex >= pRawScan->numberOfBScans)
	{
		RQ_HandleRQError(RQOffline_ExtractAScan_InvalidBScanIndex) ;
		return NDTS_BFalse ;
	}

	if (aScanIndex >= pRawScan->numberOfAScansInBScan)
	{
		RQ_HandleRQError(RQOffline_ExtractAScan_InvalidAScanIndex) ;
		return NDTS_BFalse ;
	}

	// Check the a-scan header.
	isOK = RQ_GetRawScanAScanHeader(pRawScan, pRawInternal->RPPS[aScanIndex], bScanIndex, &pAScanHeader) ;
	if(!isOK) 
		return NDTS_BFalse ; 

	isAScanHeaderOK = RQ_CheckAScanHeader(*(unsigned long *)(pAScanHeader->p), pRawInternal->RPPS[aScanIndex], &isAScanHeaderIDPresent) ;
	if (!isAScanHeaderOK)
	{
		if (isAScanHeaderIDPresent)
			RQ_HandleRQError(RQOffline_ExtractAScan_AScanDesynchronisation);
		else
			RQ_HandleRQError(RQOffline_ExtractAScan_InternalDataFormatError);

		return NDTS_BFalse;
	}

	// Copy the a-scan body.
	isOK = RQ_GetRawScanAScanBody(pRawScan, pRawInternal->RPPS[aScanIndex], bScanIndex, &pAScanBody) ;
	if(!isOK) 
		return NDTS_BFalse ; 

	*ppAScan = (short *)(pAScanBody->p) ;

	// The caller nows owns the a-scan data and is responsible to returning it to the RapiDAQ DLL.
	pAScanBody->p		= 0 ;
	pAScanBody->owner	= 0 ;
#ifdef _DEBUG
	pAScanBody->size	= 0 ;
#endif

	*pNumberOfAScanSamples = pRawScan->aScanSamplesThisStep[aScanIndex] ;

	return NDTS_BTrue ;
}

RQ_API NDTS_Boolean CALL_CONV RQ_ExtractBScanPtrs(unsigned long bScanIndex, RQ_RawScan *pRawScan, short **ppBScan, unsigned long numberOfAScanPointers, unsigned long *pNumberOfAScanSamples)
{
	unsigned long aScanIndex ;
	NDTS_Boolean isAScanHeaderOK, isAScanHeaderIDPresent ;
	RQ_RawScanProtected *pRawInternal ;
	NDTS_Boolean isOK ;
	unsigned long numberOfAScanPointersToCopy ;

	// Preconditions
	_ASSERTE(pRawScan) ;
	_ASSERTE(ppBScan) ;

	pRawInternal = RQ_ReturnRawScanInternal(pRawScan) ;
	_ASSERTE(pRawInternal) ;

	if (pRawInternal->pDataA == 0)
	{
		RQ_HandleRQError(RQOffline_ExtractBScan_RawScanDoesNotContainBScanData) ;
		return NDTS_BFalse ;
	}

	if (bScanIndex >= pRawScan->numberOfBScans)
	{
		RQ_HandleRQError(RQOffline_ExtractBScan_InvalidBScanIndex) ;
		return NDTS_BFalse ;
	}

	numberOfAScanPointersToCopy = numberOfAScanPointers < pRawScan->numberOfAScansInBScan ? numberOfAScanPointers : pRawScan->numberOfAScansInBScan ;

	for (aScanIndex = 0 ; aScanIndex < numberOfAScanPointersToCopy ; aScanIndex++)
	{
		RQ_MemoryHandle *pAScanHeader, *pAScanBody ;

		// Check the a-scan header.
		isOK = RQ_GetRawScanAScanHeader(pRawScan, pRawInternal->RPPS[aScanIndex], bScanIndex, &pAScanHeader) ;
		if(!isOK) 
			return NDTS_BFalse ; 

		isAScanHeaderOK = RQ_CheckAScanHeader(*(unsigned long *)(pAScanHeader->p), pRawInternal->RPPS[aScanIndex], &isAScanHeaderIDPresent) ;
		if (!isAScanHeaderOK)
		{
			if (isAScanHeaderIDPresent)
				RQ_HandleRQError(RQOffline_ExtractAScan_AScanDesynchronisation);
			else
				RQ_HandleRQError(RQOffline_ExtractAScan_InternalDataFormatError);

			return NDTS_BFalse;
		}

		// Copy the a-scan body.
		isOK = RQ_GetRawScanAScanBody(pRawScan, pRawInternal->RPPS[aScanIndex], bScanIndex, &pAScanBody) ;
		if(!isOK) 
			return NDTS_BFalse ; 

		ppBScan[aScanIndex] = (short *)(pAScanBody->p) ;

		// The caller nows owns the a-scan data and is responsible to returning it to the RapiDAQ DLL.
		pAScanBody->p		= 0 ;
		pAScanBody->owner	= 0 ;
#ifdef _DEBUG
		pAScanBody->size	= 0 ;
#endif

		pNumberOfAScanSamples[aScanIndex] = pRawScan->aScanSamplesThisStep[aScanIndex] ;
	}

	for (aScanIndex = numberOfAScanPointersToCopy ; aScanIndex < numberOfAScanPointers ; aScanIndex++)
	{
		ppBScan[aScanIndex] = 0 ;
		pNumberOfAScanSamples[aScanIndex] = 0 ;
	}

	return NDTS_BTrue ;
}

