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

#include "SDKUtilities.h"
#include "RawScanOfflineInternal.h"
#include "RawScanInternal.h"
#include "RawScanError.h"
#include "RawScan.h"


RAW_SCAN_API NDTS_Boolean CALL_CONV RawScan_ExtractAScan(unsigned long bScanIndex, unsigned long aScanIndex, NDTS_RawScan *pRawScan, short *pAScan, unsigned long numberOfBufferValues)
{
	NDTS_Boolean isAScanHeaderOK, isAScanHeaderIDPresent;
	RawScan_MemoryHandle *pAScanHeader, *pAScanBody ;
	NDTS_Boolean isOK ;
	RawScan_RawScanProtected *pRawInternal ;
	unsigned long numberOfAScanBytes, numberOfBytesToCopy, numberOfBufferBytes ;

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

	pRawInternal = RawScan_ReturnRawScanInternal(pRawScan) ;
	_ASSERTE(pRawInternal) ;

	if (pRawInternal->pDataA == 0)
	{
		RawScan_HandleError(RawScan_ExtractAScan_RawScanDoesNotContainAScanData) ;
		return NDTS_BFalse;
	}


	if (bScanIndex >= pRawScan->numberOfFullBScans)
	{
		if (pRawScan->scanStorage == NDTS_SSOneBScanMultipleCScan)
		{
			// Fix the B Scan index to 0 when in NDTS_SSOneBScanMultipleCScan - done for IW integration 
			bScanIndex = 0;
		}
		else
		{
			RawScan_HandleError(RawScan_ExtractAScan_InvalidBScanIndex) ;
			return NDTS_BFalse ;
		}
	}

	if (aScanIndex >= pRawScan->numberOfAScansInBScan)
	{
		RawScan_HandleError(RawScan_ExtractAScan_InvalidAScanIndex) ;
		return NDTS_BFalse ;
	}

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

	isAScanHeaderOK = RawScan_CheckAScanHeader(*(unsigned long *)(pAScanHeader->p), pRawInternal->RPPS[aScanIndex], &isAScanHeaderIDPresent) ;
	if (!isAScanHeaderOK)
	{
		if (isAScanHeaderIDPresent)
			RawScan_HandleError(RawScan_ExtractAScan_AScanDesynchronisation);
		else
			RawScan_HandleError(RawScan_ExtractAScan_InternalDataFormatError);

		return NDTS_BFalse;
	}

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

	numberOfBufferBytes = numberOfBufferValues << 1 ;

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

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

RAW_SCAN_API NDTS_Boolean CALL_CONV RawScan_ExtractBScan(unsigned long bScanIndex, NDTS_RawScan *pRawScan, short **ppBScan, unsigned long numberOfAScanPointers, unsigned long numberOfAScanBufferValues)
{
	unsigned long aScanIndex, numberOfAScansToCopy ;
	NDTS_Boolean isAScanHeaderOK, isAScanHeaderIDPresent ;
	RawScan_RawScanProtected *pRawInternal ;
	NDTS_Boolean isOK ;
	unsigned long numberOfAScanBytes, numberOfBytesToCopy, numberOfAScanBufferBytes ;

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

	pRawInternal = RawScan_ReturnRawScanInternal(pRawScan) ;
	_ASSERTE(pRawInternal) ;

	if (pRawInternal->pDataA == 0)
	{
		RawScan_HandleError(RawScan_ExtractBScan_RawScanDoesNotContainBScanData) ;
		return NDTS_BFalse ;
	}

	if (bScanIndex >= pRawScan->numberOfBScans)
	{
		RawScan_HandleError(RawScan_ExtractBScan_InvalidBScanIndex) ;
		return NDTS_BFalse ;
	}

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

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

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

		isAScanHeaderOK = RawScan_CheckAScanHeader(*(unsigned long *)(pAScanHeader->p), pRawInternal->RPPS[aScanIndex], &isAScanHeaderIDPresent) ;
		if (!isAScanHeaderOK)
		{
			if (isAScanHeaderIDPresent)
				RawScan_HandleError(RawScan_ExtractAScan_AScanDesynchronisation);
			else
				RawScan_HandleError(RawScan_ExtractAScan_InternalDataFormatError);

			return NDTS_BFalse;
		}

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

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

		numberOfAScanBufferBytes = numberOfAScanBufferValues << 1 ;

		SDKUtilities_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.
*/

RAW_SCAN_API NDTS_Boolean CALL_CONV RawScan_ExtractCScan(int *pRPNSequence, 
											  unsigned long bScanIndex, 
											  unsigned long numberOfBScans, 
											  NDTS_RawScan *pRawScan, 
											  NDTS_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 ;
	RawScan_BinaryOperator op ;
	NDTS_GateOutput gateOutput ;
	unsigned long rgGateIndices[NDTS_MAXIMUM_NUMBER_OF_GATES_COMPILE_TIME] ;
	NDTS_Boolean isScaled ;
	unsigned char *pBScanGatesBaseAddress ;
	RawScan_RawScanProtected *pRawInternal ;
	RawScan_MemoryHandle *pGateValuesBody ;
	NDTS_Boolean isOK ;
	long stack[2 * RawScan_MAXIMUM_RPN_SEQUENCE_LENGTH] ;
	unsigned long numberOfRowsToCopy, numberOfColumnsToCopy ;

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

	pRawInternal = RawScan_ReturnRawScanInternal(pRawScan) ;
	_ASSERTE(pRawInternal) ;
	_ASSERTE(!pRawInternal->isLargeGateOutputData);

	if (pRawInternal->pDataB == 0)
	{
		RawScan_HandleError(RawScan_ExtractCScan_RawScanDoesNotContainGateData) ;
		return NDTS_BFalse ;
	}

	if (bScanIndex + numberOfBScans > pRawScan->numberOfBScans)
	{
		RawScan_HandleError(RawScan_ExtractCScan_InvalidBScanBounds) ;
		return NDTS_BFalse ;
	}

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

	for (i = 0 ; i < NDTS_MAXIMUM_NUMBER_OF_GATES_COMPILE_TIME; i++)
		rgGateIndices[i] = 0xffffffff ;
	
	if (pRawScan->isSequence)
	{
		RawScan_HandleError(RawScan_ExtractCScan_NotCompatibleWithSequencedFPGA) ;
		_ASSERTE(0);
		return NDTS_BFalse ;
	}

	packedGate = 0;
	for (unpackedGate = 0; unpackedGate < NDTS_MAXIMUM_NUMBER_OF_GATES_COMPILE_TIME; unpackedGate++)
	{
		// ppGates now stores the data for all the gates not just the active ones
		if (pRawScan->ppGateSettings[0][unpackedGate].isGateEnabled)
		{
			rgGateIndices[unpackedGate] = packedGate++;
		}
		if (!pRawScan->ppGateSettings[0][unpackedGate].storeGateOnlyWhenTriggered)
		{
			// To make this work with gate outputs value all the time - use something like RawScan_ExtractIsGateValueValid
			// search for wasAmplitudeMeasured below
			_ASSERTE(0);
			RawScan_HandleError(RawScan_ExtractCScan_InvalidGate) ;
			return NDTS_BFalse ;
		}
	}

	if (pRawScan->scanStorage == NDTS_SSMultipleAScanNoCScan)
	{
		RawScan_HandleError(RawScan_ExtractCScan_MissingCScan) ;
		return NDTS_BFalse ;
	}

	i = 0 ;
	isScaled = NDTS_BFalse ;

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

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

	pStack = stack ;

	isOK = RawScan_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 = RawScan_CheckCScanHeader(cScanHeader, (unsigned long) pRawInternal->RPPS[j], &isCScanHeaderIDPresent) ;

			if (!isCScanHeaderOK)
			{
				if (isCScanHeaderIDPresent)
				{
					RawScan_HandleError(RawScan_ExtractCScan_CScanDesynchronisation);
					return NDTS_BFalse;
				}
				else
				{
					RawScan_HandleError(RawScan_ExtractCScan_InternalDataFormatError);
					return NDTS_BFalse;
				}
			}

			stackIndex = 0 ;
			seqIndex = 0 ;

			isComplete = NDTS_BFalse ;
			isInvalidSequence = NDTS_BFalse ;

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

					op = (RawScan_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 RawScan_BOAdd :
							metric = oprndA + oprndB ;
							break ;
						case RawScan_BOSubtract :
							metric = oprndA - oprndB ;
							break ;
						case RawScan_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 RawScan_BOMultiply :
							oprndB >>= unityShift ;
							metric = oprndA * oprndB ;
							break ;
						default :						
							isInvalidSequence = NDTS_BTrue ;
							metric = 0 ;
						}
					}

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

					if (internalGateIndex == 0xffffffff)
					{
						RawScan_HandleError(RawScan_ExtractCScan_InvalidGate) ;
						return NDTS_BFalse ;
					}

					gateOutput = *(NDTS_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 < RawScan_MAXIMUM_RPN_SEQUENCE_LENGTH) ;

			if (isInvalidSequence)
			{
				// free(pStack);
				RawScan_HandleError(RawScan_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 NDTS_ExtractGateValuesSequenced

RAW_SCAN_API NDTS_Boolean CALL_CONV RawScan_ExtractGateValues(unsigned long bScanIndex, 
												   unsigned long aScanIndex, 
												   NDTS_RawScan *pRawScan, 
												   NDTS_GateOutput *pGateOutputs,
												   unsigned long numberOfBufferValues)
{
	unsigned long i, numberOfValuesToCopy ;
	unsigned char *pAScanGatesBaseAddress ;
	RawScan_RawScanProtected *pRawInternal = RawScan_ReturnRawScanInternal(pRawScan) ;
	RawScan_MemoryHandle *pGateValuesBody ;
	NDTS_Boolean isOK ;
	NDTS_GateOutput blankGateOutput = {0, 0, 0, 0, 0, 0} ;

	_ASSERTE(!pRawInternal->isLargeGateOutputData);

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

	if (pRawInternal->pDataB== 0)
	{
		RawScan_HandleError(RawScan_ExtractGateValues_RawScanDoesNotContainGateData) ;
		return NDTS_BFalse ;
	}

	if (bScanIndex >= pRawScan->numberOfBScans)
	{
		RawScan_HandleError(RawScan_ExtractGateValues_InvalidBScanIndex) ;
		return NDTS_BFalse ;
	}

	if (aScanIndex >= pRawScan->numberOfAScansInBScan)
	{
		RawScan_HandleError(RawScan_ExtractGateValues_InvalidAScanIndex) ;
		return NDTS_BFalse ;
	}

	// Get the gate result body.
	isOK = RawScan_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] = *(NDTS_GateOutput *) (pAScanGatesBaseAddress + 4 * i) ; 

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

	return NDTS_BTrue ;
}

RAW_SCAN_API NDTS_Boolean CALL_CONV RawScan_ExtractEncoderValue(unsigned long bScanIndex, unsigned long encoderIndex, NDTS_RawScan *pRawScan, long *pEncoderValue)
{
	RawScan_RawScanProtected *pRawInternal = RawScan_ReturnRawScanInternal(pRawScan) ;
	long *pEncoderBlock ;
	NDTS_Boolean isOK ;
	RawScan_MemoryHandle *pGateValuesTrailer ;

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

	if (pRawInternal->pDataB == 0)
	{
		RawScan_HandleError(RawScan_ExtractEncoderValue_RawScanDoesNotContainEncoderData) ;
		return NDTS_BFalse ;
	}

	if (bScanIndex >= pRawScan->numberOfBScans)
	{
		RawScan_HandleError(RawScan_ExtractEncoderValue_InvalidBScanIndex) ;
		return NDTS_BFalse ;
	}

	// FT for dual encoder
	// On the RQ, when in dual encoder mode, the output of the dual encoder 
	// is written to both encoder 0 and encoder 1.  The encoders appear to the users of the .dlls as 0..5 and 6 is the dual encoder
	// This means that when dual encoder (6) is extracted, we return the output from encoder 0.
	// On the 2100, the dual encoder has its own output.
	if ((encoderIndex == pRawInternal->numberOfEncodersSupportedByHardware - 1) && pRawInternal->dualEncoderWritesToEnc0and1)
	{
		encoderIndex = 0;
	}

	if (encoderIndex >= pRawInternal->numberOfEncodersSupportedByHardware)
	{
		RawScan_HandleError(RawScan_ExtractEncoderValue_InvalidEncoderIndex) ;
		return NDTS_BFalse ;
	}
	
	isOK = RawScan_GetRawScanGateValuesTrailer(pRawScan, bScanIndex, &pGateValuesTrailer) ;
	if(!isOK) 
		return NDTS_BFalse ; 

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

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


RAW_SCAN_API NDTS_Boolean CALL_CONV RawScan_ExtractEncoderValues(unsigned long bScanIndex, NDTS_RawScan *pRawScan, long *pEncoderValues, int numberOfEncodersInBuffer)
{
	int i;

	RawScan_RawScanProtected *pRawInternal = RawScan_ReturnRawScanInternal(pRawScan) ;
	long *pEncoderBlock ;
	NDTS_Boolean isOK ;
	RawScan_MemoryHandle *pGateValuesTrailer ;

	// Preconditions
	_ASSERTE(pRawScan) ;
	_ASSERTE(pEncoderValues) ;

	if (pRawInternal->pDataB == 0)
	{
		RawScan_HandleError(RawScan_ExtractEncoderValue_RawScanDoesNotContainEncoderData) ;
		return NDTS_BFalse ;
	}

	if (bScanIndex >= pRawScan->numberOfBScans)
	{
		RawScan_HandleError(RawScan_ExtractEncoderValue_InvalidBScanIndex) ;
		return NDTS_BFalse ;
	}
   
	isOK = RawScan_GetRawScanGateValuesTrailer(pRawScan, bScanIndex, &pGateValuesTrailer) ;
	if(!isOK) 
		return NDTS_BFalse ; 

	pEncoderBlock = (long *)(pGateValuesTrailer->p) ;
	_ASSERTE(pEncoderBlock) ;
    
	for (i = 0; i < numberOfEncodersInBuffer; i++)
	{
		if (i < (int)pRawInternal->numberOfEncodersSupportedByHardware)
			pEncoderValues[i] = *(pEncoderBlock + i);
		else
			pEncoderValues[i] = 0;
	}

	return NDTS_BTrue;
}

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

RAW_SCAN_API NDTS_Boolean CALL_CONV RawScan_ExtractAScanPtr(unsigned long bScanIndex, unsigned long aScanIndex, NDTS_RawScan *pRawScan, short **ppAScan, unsigned long *pNumberOfAScanSamples)
{
	NDTS_Boolean isAScanHeaderOK, isAScanHeaderIDPresent;
	RawScan_MemoryHandle *pAScanHeader, *pAScanBody ;
	NDTS_Boolean isOK ;
	RawScan_RawScanProtected *pRawInternal ;

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

	pRawInternal = RawScan_ReturnRawScanInternal(pRawScan) ;
	_ASSERTE(pRawInternal) ;

	if (pRawInternal->pDataA == 0)
	{
		RawScan_HandleError(RawScan_ExtractAScan_RawScanDoesNotContainAScanData) ;
		return NDTS_BFalse;
	}

	if (bScanIndex >= pRawScan->numberOfBScans)
	{
		RawScan_HandleError(RawScan_ExtractAScan_InvalidBScanIndex) ;
		return NDTS_BFalse ;
	}

	if (aScanIndex >= pRawScan->numberOfAScansInBScan)
	{
		RawScan_HandleError(RawScan_ExtractAScan_InvalidAScanIndex) ;
		return NDTS_BFalse ;
	}

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

	isAScanHeaderOK = RawScan_CheckAScanHeader(*(unsigned long *)(pAScanHeader->p), pRawInternal->RPPS[aScanIndex], &isAScanHeaderIDPresent) ;
	if (!isAScanHeaderOK)
	{
		if (isAScanHeaderIDPresent)
			RawScan_HandleError(RawScan_ExtractAScan_AScanDesynchronisation);
		else
			RawScan_HandleError(RawScan_ExtractAScan_InternalDataFormatError);

		return NDTS_BFalse;
	}

	// Copy the a-scan body.
	isOK = RawScan_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->aScanSampleValuesThisStep[aScanIndex] ;

	return NDTS_BTrue ;
}

RAW_SCAN_API NDTS_Boolean CALL_CONV RawScan_ExtractBScanPtrs(unsigned long bScanIndex, NDTS_RawScan *pRawScan, short **ppBScan, unsigned long numberOfAScanPointers, unsigned long *pNumberOfAScanSamples)
{
	unsigned long aScanIndex ;
	NDTS_Boolean isAScanHeaderOK, isAScanHeaderIDPresent ;
	RawScan_RawScanProtected *pRawInternal ;
	NDTS_Boolean isOK ;
	unsigned long numberOfAScanPointersToCopy ;

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

	pRawInternal = RawScan_ReturnRawScanInternal(pRawScan) ;
	_ASSERTE(pRawInternal) ;

	if (pRawInternal->pDataA == 0)
	{
		RawScan_HandleError(RawScan_ExtractBScan_RawScanDoesNotContainBScanData) ;
		return NDTS_BFalse ;
	}

	if (bScanIndex >= pRawScan->numberOfBScans)
	{
		RawScan_HandleError(RawScan_ExtractBScan_InvalidBScanIndex) ;
		return NDTS_BFalse ;
	}

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

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

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

		isAScanHeaderOK = RawScan_CheckAScanHeader(*(unsigned long *)(pAScanHeader->p), pRawInternal->RPPS[aScanIndex], &isAScanHeaderIDPresent) ;
		if (!isAScanHeaderOK)
		{
			if (isAScanHeaderIDPresent)
				RawScan_HandleError(RawScan_ExtractAScan_AScanDesynchronisation);
			else
				RawScan_HandleError(RawScan_ExtractAScan_InternalDataFormatError);

			return NDTS_BFalse;
		}

		// Copy the a-scan body.
		isOK = RawScan_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->aScanSampleValuesThisStep[aScanIndex] ;
	}

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

	return NDTS_BTrue ;
}


NDTS_Boolean CALL_CONV RawScan_CheckAScanHeader(unsigned long aScanHeader, unsigned long expectedAScanIndex, NDTS_Boolean *pIsIdentifierPresent)
{
	NDTS_Boolean isOK ;

	isOK = aScanHeader == (0x41534e00 | expectedAScanIndex) ;

	if (pIsIdentifierPresent != 0)
		*pIsIdentifierPresent = (aScanHeader & 0xffffff00) == 0x41534e00 ;

	return isOK ;
}

NDTS_Boolean CALL_CONV RawScan_CheckCScanHeader(unsigned long cScanHeader, unsigned long expectedCScanIndex, NDTS_Boolean *pIsIdentifierPresent)
{
	NDTS_Boolean isOK ;

	isOK = cScanHeader == (0x43534e00 | expectedCScanIndex) ;

	if (pIsIdentifierPresent != 0)
		*pIsIdentifierPresent = (cScanHeader & 0xffffff00) == 0x43534e00 ;

	return isOK ;
}

RAW_SCAN_API NDTS_Boolean CALL_CONV RawScan_Initialise()
{
	pOfflineState = malloc(sizeof(RawScanOffline_State));
	pOfflineState->pIsThisSampleNoise = 0 ;
	pOfflineState->sampleNoiseSize  = 0 ;
	return NDTS_BTrue;
}


RAW_SCAN_API NDTS_Boolean CALL_CONV RawScan_Uninitialise()
{
	free(pOfflineState->pIsThisSampleNoise);
	free(pOfflineState);
	return NDTS_BTrue;
}

RAW_SCAN_API void RawScan_FillRawScanWithTestAScanData(NDTS_RawScan *pRawScan)
{
	RawScan_MemoryHandle *pAScanHeader, *pAScanBody ;
	NDTS_Boolean isOK ;
	RawScan_RawScanProtected *pRawInternal ;
	unsigned long aScanIndex, bScanIndex, i;

	// Preconditions
	_ASSERTE(pRawScan) ;
	pRawInternal = RawScan_ReturnRawScanInternal(pRawScan) ;
	_ASSERTE(pRawInternal) ;
	_ASSERTE(pRawInternal->pDataA);

	for (bScanIndex=0; bScanIndex < pRawScan->numberOfFullBScans; bScanIndex++)
	{
		for (aScanIndex=0; aScanIndex < pRawScan->numberOfAScansInBScan; aScanIndex++)
		{
			isOK = RawScan_GetRawScanAScanHeader(pRawScan, pRawInternal->RPPS[aScanIndex], bScanIndex, &pAScanHeader) ;
			_ASSERTE(isOK);
			*(unsigned long*)(pAScanHeader->p) = (0x41534e00 | pRawInternal->RPPS[aScanIndex]) ;
				isOK = RawScan_GetRawScanAScanBody(pRawScan, pRawInternal->RPPS[aScanIndex], bScanIndex, &pAScanBody) ;
			_ASSERTE(isOK);
			_ASSERTE(RawScan_ReturnBytesPerSample(pRawScan->sampleSize) == 2);
			for (i=0; i < pRawScan->aScanSampleValuesThisStep[pRawInternal->RPPS[aScanIndex]] ; i++)
			{
				int echoMult = (i > 100 && i < 150) ? 30 : 5;  // Magnify the signal at certain point(s) to emulate an echo
				*((short*)pAScanBody->p + i) = (short)((i % 50) * echoMult // Sawtooth wave
					+ rand() % 200);  // Random noise;
			}
		}
	}
}

RAW_SCAN_API void RawScan_FillRawScanWithTestCScanData(NDTS_RawScan *pRawScan)
{
	int packedGateIndex, activeGatesThisStep, encoderIndex;
	unsigned long bScanIndex, stepIndex;
	unsigned char *pGatesBaseAddressThisStep ;
	RawScan_RawScanProtected *pRawInternal = RawScan_ReturnRawScanInternal(pRawScan) ;
	RawScan_MemoryHandle *pGateValuesBody ;
	RawScan_MemoryHandle *pGateValuesTrailer ;
	static int encoderClicks = 0;

	NDTS_Boolean isOK ;

	// Preconditions
	_ASSERTE(pRawScan) ;
	pRawInternal = RawScan_ReturnRawScanInternal(pRawScan) ;
	_ASSERTE(pRawInternal) ;
	_ASSERTE(pRawInternal->pDataB);
	_ASSERTE(pRawScan->isSequence);
	_ASSERTE(pRawInternal->isLargeGateOutputData);  // This function is for NDTS2100 with large gates

	for (bScanIndex=0; bScanIndex < pRawScan->numberOfFullBScans; bScanIndex++)
	{
		for (stepIndex=0; stepIndex < pRawScan->numberOfAScansInBScan; stepIndex++)
		{
			// Get the gate result body.
			isOK = RawScan_GetRawScanGateValuesBody(pRawScan, bScanIndex, &pGateValuesBody) ;
			_ASSERTE(isOK);

			pGatesBaseAddressThisStep = pGateValuesBody->p + pRawInternal->gateOffsetThisStep[pRawInternal->RPPS[stepIndex]];
			activeGatesThisStep = (int)pRawScan->numberOfGates[pRawInternal->RPPS[stepIndex]];
			for (packedGateIndex = 0; packedGateIndex < activeGatesThisStep; packedGateIndex++)
			{
				NDTS_GateOutputLarge gateOut;
				gateOut.amplitudeMetric = 1200 + rand() % 300;
				gateOut.distanceMetric = 120 + rand() %30;  // Distance between 120 and 150 close to the first peak of the fake A Scan data
				gateOut.wasAmplitudeMeasured = 1;
				gateOut.wasDistanceMeasured = 1;
				*(NDTS_GateOutputLarge *) (pGatesBaseAddressThisStep + 4 * packedGateIndex)  = gateOut;
			}
		}
		isOK = RawScan_GetRawScanGateValuesTrailer(pRawScan, bScanIndex, &pGateValuesTrailer) ;
		_ASSERTE(isOK) ;

		for(encoderIndex = 0; encoderIndex < (int)pRawInternal->numberOfEncodersSupportedByHardware; encoderIndex++)
		{
			((long *)(pGateValuesTrailer->p))[encoderIndex] = encoderClicks++;
			encoderClicks = encoderClicks % 5000;
		}

	}
}
