#include "RQRawScanInternal.h"

#include <crtdbg.h>
#include "RQInternal.h"
#include "RQError.h"
#include "RQMemoryFactoryInternal.h"
#include "RQRawScanPools.h"


RQ_API NDTS_Boolean CALL_CONV RQ_InitialiseRawScan(RQ_RawScan *pRawScan)
{
	// Preconditions.
	_ASSERTE(pRawScan) ;

	pRawScan->pInternal = RQ_AllocFromPool(RQ_ReturnRawScanProtectedMemoryPool()) ;
	if(!pRawScan->pInternal)
		return NDTS_BFalse ;

	RQ_ClearRawScan(pRawScan) ;

	return NDTS_BTrue;
}

RQ_API NDTS_Boolean CALL_CONV RQ_UninitialiseRawScan(RQ_RawScan *pRawScan)
{
	RQ_RawScanProtected *pInternal ;

	// Preconditions.
	_ASSERTE(pRawScan) ;

	if(pRawScan->pInternal)
	{
		pInternal = RQ_ReturnRawScanInternal(pRawScan) ;

		// Ensure they have freed the data memory.
		_ASSERTE(pInternal->pDataA == 0) ;
		_ASSERTE(pInternal->pDataB == 0) ;

		RQ_FreeToPool(RQ_ReturnRawScanProtectedMemoryPool(), pRawScan->pInternal) ;
	}

	return NDTS_BTrue ;
}

RQ_API NDTS_Boolean CALL_CONV RQ_ClearRawScan(RQ_RawScan *pRawScan)
{
	RQ_RawScanProtected *pInternal ;
	int i;
	// Preconditions.
	_ASSERTE(pRawScan) ;

	pRawScan->cardIndex				= 0 ; 
	memset(pRawScan->aScanSamplesThisStep, sizeof pRawScan->aScanSamplesThisStep, 0) ;
	pRawScan->numberOfAScansInBScan	= 0 ; 
	pRawScan->numberOfBScans		= 0 ;
	pRawScan->isSequence			= NDTS_BFalse ; // Default to non sequenced
	pRawScan->scanStorage			= RQ_SSMultipleAScanMultipleCScan ;
	pRawScan->sampleSize			= RQ_SS12Bit ;
	memset(pRawScan->ppGates, sizeof pRawScan->ppGates, 0) ;
	memset(pRawScan->numberOfGates, sizeof pRawScan->numberOfGates, 0) ;

	// Internal.
	pInternal = RQ_ReturnRawScanInternal(pRawScan) ;
	_ASSERTE(pInternal) ;

	memset(pInternal->RPPS, sizeof pInternal->RPPS, 0) ;
	pInternal->numberToCheckEitherSide	= 0 ;
	pInternal->proportionToTriggerOff	= 0 ;
	for (i = 0; i < RQ_MAXIMUM_NUMBER_OF_STEPS; i++)
	{
		pInternal->oversampleCountThisStep[i] = 1;
	}
	memset(pInternal->aScanOffsetThisStep, sizeof pInternal->aScanOffsetThisStep, 0) ;
	pInternal->aScanBytesPerBScan		= 0 ;
	memset(pInternal->gateOffsetThisStep, sizeof pInternal->gateOffsetThisStep, 0) ;
	memset(pInternal->gateCountThisStep, sizeof pInternal->gateCountThisStep, 0) ;
	memset(pInternal->storeGateValue, sizeof pInternal->storeGateValue, 0) ;
	pInternal->gateBytesPerBScan		= 0 ;
	pInternal->isCRCCheckPresent		= NDTS_BFalse ;
	pInternal->pDataA					= 0 ;
	pInternal->pDataB					= 0 ;

	return NDTS_BTrue;
}

RQ_API NDTS_Boolean CALL_CONV RQ_CopyRawScan(RQ_RawScan *pDestination, RQ_RawScan *pSource)
{
	RQ_RawScanProtected *pInternalSource, *pInternalDestination ;

	// Preconditions.
	_ASSERTE(pSource) ;
	_ASSERTE(pDestination) ;

	pDestination->cardIndex				= pSource->cardIndex ; 
	memcpy(pDestination->aScanSamplesThisStep, pSource->aScanSamplesThisStep, sizeof pSource->aScanSamplesThisStep) ;
	pDestination->numberOfAScansInBScan	= pSource->numberOfAScansInBScan ; 
	pDestination->numberOfBScans		= pSource->numberOfBScans ;
	pDestination->isSequence			= pSource->isSequence ; 
	pDestination->scanStorage			= pSource->scanStorage ;
	pDestination->sampleSize			= pSource->sampleSize ;
	memcpy(pDestination->ppGates, pSource->ppGates, sizeof pSource->ppGates) ;
	memcpy(pDestination->numberOfGates, pSource->numberOfGates, sizeof pSource->numberOfGates) ;
	//memcpy(pDestination->oversampleCountThisStep, pSource->oversampleCountThisStep, sizeof pSource->oversampleCountThisStep) ;

	// Internal.
	pInternalSource = RQ_ReturnRawScanInternal(pSource) ;
	_ASSERTE(pInternalSource) ;

	pInternalDestination = RQ_ReturnRawScanInternal(pDestination) ;
	_ASSERTE(pInternalDestination) ;

	memcpy(pInternalDestination->RPPS, pInternalSource->RPPS, sizeof pInternalSource->RPPS) ;
	pInternalDestination->numberToCheckEitherSide	= pInternalSource->numberToCheckEitherSide ;
	pInternalDestination->proportionToTriggerOff	= pInternalSource->proportionToTriggerOff ;
	memcpy(pInternalDestination->oversampleCountThisStep, pInternalSource->oversampleCountThisStep, sizeof(pInternalSource->oversampleCountThisStep));
	memcpy(pInternalDestination->aScanOffsetThisStep, pInternalSource->aScanOffsetThisStep, sizeof pInternalSource->aScanOffsetThisStep) ;
	pInternalDestination->aScanBytesPerBScan		= pInternalSource->aScanBytesPerBScan ;
	memcpy(pInternalDestination->gateOffsetThisStep, pInternalSource->gateOffsetThisStep, sizeof pInternalSource->gateOffsetThisStep) ;
	memcpy(pInternalDestination->gateCountThisStep, pInternalSource->gateCountThisStep, sizeof pInternalSource->gateCountThisStep) ;
	memcpy(pInternalDestination->storeGateValue, pInternalSource->storeGateValue, sizeof pInternalSource->storeGateValue) ;
	pInternalDestination->gateBytesPerBScan			= pInternalSource->gateBytesPerBScan ;
	pInternalDestination->isCRCCheckPresent			= pInternalSource->isCRCCheckPresent ;
	pInternalDestination->pDataA					= pInternalSource->pDataA ;
	pInternalDestination->pDataB					= pInternalSource->pDataB ;

	return NDTS_BTrue;
}


NDTS_Boolean CALL_CONV RQ_AllocRawScanDataAMemoryBlockNode(RQ_RawScan *pRawScan)
{
	NDTS_Boolean isOK ;
	RQ_DataAHeader *pHeader ;
	RQ_MemoryBlockNode *pMemoryBlockNode ;
	RQ_MemoryHandle *pIt ;
	void *pMemoryBlock ;
	unsigned long *pAScanHeaders ;
	unsigned long aScanIndex, bScanIndex ;
	unsigned long blockCount, bytesPerAScanSample, aScanHeaderSize, bScanTrailerSize ;
	RQ_RawScanProtected *pRawScanProtected ;
	// Assume we are not sequenced.
	TPoolHandle aScanBodyPool = 0, bScanTrailerPool = 0 ;

	// Preconditions.
	_ASSERTE(pRawScan) ;

	pRawScanProtected = RQ_ReturnRawScanInternal(pRawScan) ;

	// Remember the a-scan pool handle. 
	if(!pRawScan->isSequence)
		aScanBodyPool = RQ_ReturnRawScanAScanBodyMemoryPool(pRawScan->cardIndex, 0) ;

	bScanTrailerPool = RQ_ReturnRawScanBScanTrailerMemoryPool(pRawScan->cardIndex) ;

	blockCount = 
		1 + // Raw scan header.
		pRawScan->numberOfBScans * RQ_ReturnRawScanBScanMemoryBlockListSize(pRawScan) ;

	// Alloc memory block node.
	isOK = RQ_AllocMemoryBlockNode(blockCount, &pMemoryBlockNode) ;
	if(!isOK) 
		return NDTS_BFalse ; 

	// Store the memory block node in the raw scan.
	RQ_ReturnRawScanInternal(pRawScan)->pDataA = pMemoryBlockNode ;

	// Get the first memory block pointer in the list. This happens to point to the raw scan header.
	isOK = RQ_GetMemoryBlockList(pMemoryBlockNode, &pIt) ;
	if(!isOK) 
		return NDTS_BFalse ; 

	// Alloc the raw scan header.
	pMemoryBlock = RQ_AllocFromPool(RQ_ReturnRawScanDataAHeaderMemoryPool()) ;
	if(!pMemoryBlock) 
	{
		RQ_HandleRQError(RQ_Alloc_OutOfMemory) ;
		return NDTS_BFalse ; 
	}

	// Fill in the header.
	pHeader = (RQ_DataAHeader *)pMemoryBlock ;

	pIt->p = pMemoryBlock ;
	pIt->owner = 1 ;
#ifdef _DEBUG
	pIt->size = sizeof(RQ_DataAHeader) ;
#endif
	pIt++ ;

	aScanHeaderSize = RQ_ReturnRawScanAScanHeaderSize(pRawScan) ;
	bScanTrailerSize = RQ_ReturnRawScanBScanTrailerSize(pRawScan) ;
	bytesPerAScanSample = RQ_ReturnBytesPerSample(pRawScan->sampleSize) ;
	pAScanHeaders = 0 ;

	for(bScanIndex = 0; bScanIndex < pRawScan->numberOfBScans; ++bScanIndex)
	{
		for(aScanIndex = 0; aScanIndex < pRawScan->numberOfAScansInBScan; ++aScanIndex)
		{
			// A-scan header.
			if(bScanIndex == 0 && aScanIndex == 0)
			{
				// All of the a-scan headers will be written in one memory block that is attached to the very first a-scan.
				pMemoryBlock = RQ_Alloc(pRawScan->numberOfBScans * pRawScan->numberOfAScansInBScan * aScanHeaderSize) ;
				if(!pMemoryBlock) 
				{
					RQ_HandleRQError(RQ_Alloc_OutOfMemory) ;
					return NDTS_BFalse ; 
				}

				pAScanHeaders = (unsigned long *)pMemoryBlock ;
				pIt->owner = 1 ;
#ifdef _DEBUG
				pIt->size = pRawScan->numberOfBScans * pRawScan->numberOfAScansInBScan * aScanHeaderSize ;
#endif
			}
			else
			{
				_ASSERTE(pAScanHeaders) ;
				pIt->owner = 0 ;
#ifdef _DEBUG
				pIt->size = 0 ;
#endif
			}

			pIt->p = (void *)pAScanHeaders++ ;
			pIt++ ;

			// A-scan body memory pool.
			if(pRawScan->isSequence)
				aScanBodyPool = RQ_ReturnRawScanAScanBodyMemoryPool(pRawScan->cardIndex, aScanIndex) ;

			// A-scan body.
			pMemoryBlock = RQ_AllocFromPool(aScanBodyPool) ;
			if(!pMemoryBlock) 
			{
				RQ_HandleRQError(RQ_Alloc_OutOfMemory) ;
				return NDTS_BFalse ; 
			}

			/*
			// *** Mark
			{
				unsigned long numberOfSamples, _numberOfSamples ;
				int numberOfBytes ;

				RQ_GetNumberOfSamples(pRawScan->cardIndex, &numberOfSamples) ;
				RQ_GetMemoryBlockSize(pMemoryBlock, &numberOfBytes) ;

				_numberOfSamples = pRawScan->aScanSamplesThisStep[0] ;

				if ((unsigned long) numberOfBytes != 2 * numberOfSamples ||
					(unsigned long) numberOfBytes != 2 * _numberOfSamples)
				{
					int test = 0 ;
					test = test ;
				}
			}
			*/

			pIt->p = pMemoryBlock ;
			pIt->owner = 1 ;
#ifdef _DEBUG
			pIt->size = pRawScan->aScanSamplesThisStep[aScanIndex] * bytesPerAScanSample ;
			_ASSERTE(pIt->size > 0); // zero samples per step is not allowed
#endif
			pIt++ ;
		}

		// B-scan trailer (encoder values + CRC (which is ignored)).
		pMemoryBlock = RQ_AllocFromPool(bScanTrailerPool) ;
		if(!pMemoryBlock) 
		{
			RQ_HandleRQError(RQ_Alloc_OutOfMemory) ;
			return NDTS_BFalse ; 
		}

		pIt->p = pMemoryBlock ;
		pIt->owner = 1 ;
#ifdef _DEBUG
		pIt->size = bScanTrailerSize ;
#endif
		pIt++ ;
	}

	return NDTS_BTrue ;
}

NDTS_Boolean CALL_CONV RQ_FreeRawScanDataAMemoryBlockNode(RQ_RawScan *pRawScan)
{
	NDTS_Boolean isOK ;
	RQ_RawScanProtected *pRawScanProtected ;
	RQ_MemoryBlockNode *pMemoryBlockNode ;
	TPoolHandle aScanBodyPool = 0, bScanTrailerPool = 0 ;

	// Preconditions.
	_ASSERTE(pRawScan) ;

	/*
	Note: Do NOT use the RQ_ReturnRawScanxxxMemoryPool() functions to free memory blocks as the memory 
	pools references in the g_poolHandles and g_poolSized structure is dependent on the current scan settings.
	*/

	pRawScanProtected = RQ_ReturnRawScanInternal(pRawScan) ;

	// First free all the memory blocks that are referenced by all the memory block lists 
	// that are referenced by the (chain of) memory block node(s) referenced by the raw scan. Phew!
	pMemoryBlockNode = pRawScanProtected->pDataA ;
	while(pMemoryBlockNode != 0)
	{
		RQ_MemoryHandle *pList ;

		// Iterate through the memory block list, freeing memory blocks as we go.
		isOK = RQ_GetMemoryBlockList(pMemoryBlockNode, &pList) ;
		if(!isOK) 
			return NDTS_BFalse ; 

		if(pList != 0)
		{
			// The number of b-scans in the memory block list.
			unsigned long numberOfBScans = (pMemoryBlockNode->size - 1) / RQ_ReturnRawScanBScanMemoryBlockListSize(pRawScan) ;
			unsigned long bScanIndex = 0 ;
			unsigned long blockIndex = 0 ;

			// Free the raw scan header.
			if(pList[blockIndex].p)
				RQ_FreeToPool(RQ_ReturnRawScanDataAHeaderMemoryPool(), pList[blockIndex].p) ;
			++blockIndex ;

			for(bScanIndex = 0; bScanIndex < numberOfBScans; ++bScanIndex)
			{
				unsigned long aScanIndex = 0 ;

				for(aScanIndex = 0; aScanIndex < pRawScan->numberOfAScansInBScan; ++aScanIndex)
				{
					// A-scan header.
					if(pList[blockIndex].p && pList[blockIndex].owner)
						RQ_Free(pList[blockIndex].p) ;
					++blockIndex ;

					// A-scan body.
					if(pList[blockIndex].p)
					{
						// A-scan body pool. 
						if(pRawScan->isSequence || aScanBodyPool == 0)
							// The a-scan bodies in this (sequenced) raw scan may come from different pools.
							aScanBodyPool = RQ_FindPool(pList[blockIndex].p) ;

						if (aScanBodyPool != 0)
							RQ_FreeToPool(aScanBodyPool, pList[blockIndex].p) ;
					}
					++blockIndex ;
				}

				// B-scan trailer.
				if(pList[blockIndex].p)
				{
					// B-scan trailer pool. All b-scan trailers in this raw scan come from the same pool as the first b-scan trailer.
					if(bScanTrailerPool == 0)
						bScanTrailerPool = RQ_FindPool(pList[blockIndex].p) ;

					RQ_FreeToPool(bScanTrailerPool, pList[blockIndex].p) ;
				}
				++blockIndex ;
			}
		}

		// Move to the next memory block node in the chain.
		pMemoryBlockNode = pMemoryBlockNode->pNext ;
	}

	// Then free the (chain of) memory block node(s).
	isOK = RQ_FreeMemoryBlockNode(pRawScanProtected->pDataA) ;
	if(!isOK) 
		return NDTS_BFalse ; 

	pRawScanProtected->pDataA = 0 ;

	return NDTS_BTrue ;
}

NDTS_Boolean CALL_CONV RQ_AllocRawScanDataBMemoryBlockNode(RQ_RawScan *pRawScan)
{
	NDTS_Boolean isOK ;
	RQ_DataBHeader *pHeader ;
	unsigned long blockCount ;
	RQ_MemoryBlockNode *pMemoryBlockNode ;
	RQ_MemoryHandle *pIt ;
	void *pMemoryBlock ;
	unsigned long bScanIndex ;
	unsigned long trailerSize ;
	RQ_RawScanProtected *pRawScanProtected ;
	TPoolHandle gateValuesBodyPool = 0, gateValuesTrailerPool = 0 ;

	// Preconditions.
	_ASSERTE(pRawScan) ;

	blockCount = 
		1 + // Raw scan header.
		pRawScan->numberOfBScans * RQ_ReturnRawScanGateValuesMemoryBlockListSize(pRawScan) ;

	// Alloc memory block node.
	isOK = RQ_AllocMemoryBlockNode(blockCount, &pMemoryBlockNode) ;
	if(!isOK) 
		return NDTS_BFalse ; 

	pRawScanProtected = RQ_ReturnRawScanInternal(pRawScan) ;
	_ASSERT(pRawScanProtected) ;

	// Store the memory block node in the raw scan.
	pRawScanProtected->pDataB = pMemoryBlockNode ;

	// Get the first memory block pointer in the list. This happens to point to the raw scan header.
	isOK = RQ_GetMemoryBlockList(pMemoryBlockNode, &pIt) ;
	if(!isOK) 
		return NDTS_BFalse ; 

	// Alloc the raw scan header.
	pMemoryBlock = RQ_AllocFromPool(RQ_ReturnRawScanDataBHeaderMemoryPool()) ;
	if(!pMemoryBlock) 
	{
		RQ_HandleRQError(RQ_Alloc_OutOfMemory) ;
		return NDTS_BFalse ; 
	}

	// Fill in the header.
	pHeader = (RQ_DataBHeader *)pMemoryBlock ;

	gateValuesBodyPool = RQ_ReturnRawScanGateValuesBodyMemoryPool(pRawScan->cardIndex) ;
	gateValuesTrailerPool = RQ_ReturnRawScanGateValuesTrailerMemoryPool(pRawScan->cardIndex) ;

	pIt->p = pMemoryBlock ;
	pIt->owner = 1 ;
#ifdef _DEBUG
	pIt->size = sizeof(RQ_DataBHeader) ;
#endif
	pIt++ ;

	trailerSize = RQ_ReturnRawScanGateValuesTrailerSize(pRawScan) ;

	for(bScanIndex = 0; bScanIndex < pRawScan->numberOfBScans; ++bScanIndex)
	{
		// Gate values body.
		pMemoryBlock = RQ_AllocFromPool(gateValuesBodyPool) ;
		if(!pMemoryBlock) 
		{
			RQ_HandleRQError(RQ_Alloc_OutOfMemory) ;
			return NDTS_BFalse ; 
		}

		pIt->p = pMemoryBlock ;
		pIt->owner = 1 ;
#ifdef _DEBUG
		pIt->size = pRawScanProtected->gateBytesPerBScan - trailerSize ;
#endif
		pIt++ ;

		// Gate values trailer.
		pMemoryBlock = RQ_AllocFromPool(gateValuesTrailerPool) ;
		if(!pMemoryBlock) 
		{
			RQ_HandleRQError(RQ_Alloc_OutOfMemory) ;
			return NDTS_BFalse ; 
		}

		pIt->p = pMemoryBlock ;
		pIt->owner = 1 ;
#ifdef _DEBUG
		pIt->size = trailerSize ;
#endif
		pIt++ ;
	}

	return NDTS_BTrue ;
}

NDTS_Boolean CALL_CONV RQ_FreeRawScanDataBMemoryBlockNode(RQ_RawScan *pRawScan)
{
	NDTS_Boolean isOK ;
	RQ_RawScanProtected *pRawScanProtected ;
	RQ_MemoryBlockNode *pMemoryBlockNode ;
	TPoolHandle gateValuesBodyPool = 0, gateValuesTrailerPool = 0;

	// Preconditions.
	_ASSERTE(pRawScan) ;

	pRawScanProtected = RQ_ReturnRawScanInternal(pRawScan) ;

	// First free all the memory blocks that are referenced by all the memory block lists 
	// that are referenced by the (chain of) memory block node(s) referenced by the raw scan. Phew!
	pMemoryBlockNode = pRawScanProtected->pDataB ;
	while(pMemoryBlockNode != 0)
	{
		RQ_MemoryHandle *pList ;

		// Iterate through the memory block list, freeing memory blocks as we go.
		isOK = RQ_GetMemoryBlockList(pMemoryBlockNode, &pList) ;
		if(!isOK) 
			return NDTS_BFalse ; 

		if(pList != 0)
		{
			// The number of gate values in the memory block list.
			unsigned long numberOfGateValues = (pMemoryBlockNode->size - 1) / RQ_ReturnRawScanGateValuesMemoryBlockListSize(pRawScan) ;
			unsigned long bScanIndex = 0 ;
			unsigned long blockIndex = 0 ;

			// Free the raw scan header.
			if(pList[blockIndex].p)
				RQ_FreeToPool(RQ_ReturnRawScanDataBHeaderMemoryPool(), pList[blockIndex].p) ;
			++blockIndex ;

			for(bScanIndex = 0; bScanIndex < numberOfGateValues; ++bScanIndex)
			{
				// Gate values body.
				if(pList[blockIndex].p)
				{
					// Gate values body memory pool.
					if(gateValuesBodyPool == 0)
						gateValuesBodyPool = RQ_FindPool(pList[blockIndex].p) ;

					RQ_FreeToPool(gateValuesBodyPool, pList[blockIndex].p) ;
				}
				++blockIndex ;

				// Gate values trailer.
				if(pList[blockIndex].p)
				{
					// Gate values trailer memory pool.
					if(gateValuesTrailerPool == 0)
						gateValuesTrailerPool = RQ_FindPool(pList[blockIndex].p) ;

					RQ_FreeToPool(gateValuesTrailerPool, pList[blockIndex].p) ;
				}
				++blockIndex ;
			}
		}

		// Move to the next memory block node in the chain.
		pMemoryBlockNode = pMemoryBlockNode->pNext ;
	}

	// Then free the (chain of) memory block node(s).
	isOK = RQ_FreeMemoryBlockNode(pRawScanProtected->pDataB) ;
	if(!isOK) 
		return NDTS_BFalse ; 

	pRawScanProtected->pDataB = 0 ;

	return NDTS_BTrue ;
}

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


unsigned long CALL_CONV RQ_ReturnRawScanCRCSize(RQ_RawScan *pRawScan)
{
	// Preconditions.
	_ASSERTE(pRawScan) ;

	return RQ_ReturnRawScanInternal(pRawScan)->isCRCCheckPresent ? 4 : 0 ; // 32 bit CRC (which we ignore).
}

unsigned long CALL_CONV RQ_ReturnRawScanAScanHeaderSize(RQ_RawScan *pRawScan)
{
	pRawScan ;

	// Preconditions.
	_ASSERTE(pRawScan) ;

	return 4 ;
}

unsigned long CALL_CONV RQ_ReturnRawScanAScanBodySize(RQ_RawScan *pRawScan, unsigned long aScanIndex)
{
	// Preconditions.
	_ASSERTE(pRawScan) ;

	return pRawScan->aScanSamplesThisStep[aScanIndex] * RQ_ReturnBytesPerSample(pRawScan->sampleSize) ;
}

unsigned long CALL_CONV RQ_ReturnRawScanEncoderValuesSize(RQ_RawScan *pRawScan)
{
	// Preconditions.
	_ASSERTE(pRawScan) ; 
	pRawScan ; // Keep release build compiler happy

	return RQ_NUMBER_OF_ENCODERS * 4 ;
}

unsigned long CALL_CONV RQ_ReturnRawScanBScanTrailerSize(RQ_RawScan *pRawScan)
{
	// Preconditions.
	_ASSERTE(pRawScan) ;

	// The CRC is ignored.
	return RQ_ReturnRawScanCRCSize(pRawScan) + RQ_ReturnRawScanEncoderValuesSize(pRawScan) ;
}

unsigned long CALL_CONV RQ_ReturnRawScanGateValuesBodySize(RQ_RawScan *pRawScan)
{
	RQ_RawScanProtected *pRawInternal ;

	// Preconditions.
	_ASSERTE(pRawScan) ;

	pRawInternal = RQ_ReturnRawScanInternal(pRawScan) ;

	return pRawInternal->gateBytesPerBScan - RQ_ReturnRawScanGateValuesTrailerSize(pRawScan) ;
}

unsigned long CALL_CONV RQ_ReturnRawScanGateValuesTrailerSize(RQ_RawScan *pRawScan)
{
	// Preconditions.
	_ASSERTE(pRawScan) ;

	return RQ_ReturnRawScanEncoderValuesSize(pRawScan) ;
}

unsigned long CALL_CONV RQ_ReturnBytesPerSample(RQ_SampleSize sampleSize)
{
	return sampleSize == RQ_SS12Bit ? 2 : 1 ;
}

unsigned long CALL_CONV RQ_ReturnRawScanAScanMemoryBlockListSize(RQ_RawScan *pRawScan)
{
	pRawScan ;

	// Header + data.
	return 2 ;
}

unsigned long CALL_CONV RQ_ReturnRawScanBScanMemoryBlockListSize(RQ_RawScan *pRawScan)
{
	_ASSERTE(pRawScan) ;

	return pRawScan->numberOfAScansInBScan * RQ_ReturnRawScanAScanMemoryBlockListSize(pRawScan) + 1;
}

unsigned long CALL_CONV RQ_ReturnRawScanGateValuesMemoryBlockListSize(RQ_RawScan *pRawScan)
{
	pRawScan; // keep compiler happy
	// Gate data + encoder.
	return 2 ;
}

RQ_API RQ_RawScanProtected * CALL_CONV RQ_ReturnRawScanInternal(RQ_RawScan *pRawScan)
{
	// Preconditions.
	_ASSERTE(pRawScan) ;

	return (RQ_RawScanProtected *)(pRawScan->pInternal) ;
}

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

NDTS_Boolean CALL_CONV RQ_GetRawScanDataAHeader(RQ_RawScan *pRawScan, RQ_DataAHeader **ppHeader)
{
	NDTS_Boolean isOK ;
	RQ_MemoryHandle *pMemoryBlock ;

	// Preconditions.
	_ASSERTE(pRawScan) ;
	_ASSERTE(ppHeader) ;

	isOK = RQ_GetMemoryBlockList(RQ_ReturnRawScanInternal(pRawScan)->pDataA, &pMemoryBlock) ;
	if(!isOK) 
		return NDTS_BFalse ; 

	*ppHeader = (RQ_DataAHeader *)pMemoryBlock->p ;

	return NDTS_BTrue ;
}

NDTS_Boolean CALL_CONV RQ_GetRawScanDataBHeader(RQ_RawScan *pRawScan, RQ_DataBHeader **ppHeader)
{
	NDTS_Boolean isOK ;
	RQ_MemoryHandle *pMemoryBlock ;

	// Preconditions.
	_ASSERTE(pRawScan) ;
	_ASSERTE(ppHeader) ;

	// First memory block is header.
	isOK = RQ_GetMemoryBlockList(RQ_ReturnRawScanInternal(pRawScan)->pDataB, &pMemoryBlock) ;
	if(!isOK) 
		return NDTS_BFalse ; 

	*ppHeader = (RQ_DataBHeader *)pMemoryBlock->p ;

	return NDTS_BTrue ;
}

NDTS_Boolean CALL_CONV RQ_GetRawScanBScanMemoryBlockList(RQ_RawScan *pRawScan, unsigned long bScanIndex, RQ_MemoryHandle **ppData)
{
	RQ_MemoryBlockNode *pMemoryBlockNode ;
	NDTS_Boolean isOK ;
	RQ_MemoryHandle *pMemoryBlock ;
	unsigned long bScanMBLSize ;

	// Preconditions.
	_ASSERTE(pRawScan) ;
	_ASSERTE(RQ_ReturnRawScanInternal(pRawScan)->pDataA) ;
	_ASSERTE(bScanIndex < pRawScan->numberOfBScans) ;

	bScanMBLSize = RQ_ReturnRawScanBScanMemoryBlockListSize(pRawScan) ;
	pMemoryBlockNode = RQ_ReturnRawScanInternal(pRawScan)->pDataA ;

	*ppData = 0 ;
	do
	{
		unsigned long numberOfBScans ;

		// How many b-scans are referenced by the current node's memory block list?
		numberOfBScans = (pMemoryBlockNode->size - 1 /* RSH */) / bScanMBLSize ;

		if(bScanIndex > numberOfBScans - 1)
		{
			pMemoryBlockNode = pMemoryBlockNode->pNext ;
			_ASSERTE(pMemoryBlockNode) ;

			bScanIndex -= numberOfBScans ;
		}
		else
		{
			isOK = RQ_GetMemoryBlockList(pMemoryBlockNode, &pMemoryBlock) ;
			if(!isOK) 
				return NDTS_BFalse ; 

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

			*ppData = pMemoryBlock + bScanIndex * bScanMBLSize ;
		}
	}
	while(*ppData == 0) ;
	
	return NDTS_BTrue ;
}

NDTS_Boolean CALL_CONV RQ_GetRawScanAScanHeader(RQ_RawScan *pRawScan, 
												unsigned long aScanIndex, 
												unsigned long bScanIndex, 
												RQ_MemoryHandle **ppData)
{
	NDTS_Boolean isOK ;
	RQ_MemoryHandle *pMemoryBlock ;

	// Preconditions.
	_ASSERTE(pRawScan) ;
	_ASSERTE(ppData) ;
	_ASSERTE(aScanIndex < pRawScan->numberOfAScansInBScan) ;

	isOK = RQ_GetRawScanBScanMemoryBlockList(pRawScan, bScanIndex, &pMemoryBlock) ;
	if(!isOK) 
		return NDTS_BFalse ; 

	*ppData = pMemoryBlock + aScanIndex * RQ_ReturnRawScanAScanMemoryBlockListSize(pRawScan) ;

	return NDTS_BTrue ;
}

NDTS_Boolean CALL_CONV RQ_GetRawScanAScanBody(RQ_RawScan *pRawScan, 
											  unsigned long aScanIndex, 
											  unsigned long bScanIndex, 
											  RQ_MemoryHandle **ppData)
{
	NDTS_Boolean isOK ;
	RQ_MemoryHandle *pMemoryBlock ;

	// Preconditions.
	_ASSERTE(pRawScan) ;
	_ASSERTE(ppData) ;

	isOK = RQ_GetRawScanAScanHeader(pRawScan, aScanIndex, bScanIndex, &pMemoryBlock) ;
	if(!isOK) 
		return NDTS_BFalse ; 

	// The a-scan body is immediately after the header in the b-scan memory block list.
	*ppData = pMemoryBlock + 1 ;

	return NDTS_BTrue ;
}

NDTS_Boolean CALL_CONV RQ_GetRawScanBScanTrailer(RQ_RawScan *pRawScan, 
												 unsigned long bScanIndex, 
												 RQ_MemoryHandle **ppData) 
{
	NDTS_Boolean isOK ;
	RQ_MemoryHandle *pMemoryBlock ;

	// Preconditions.
	_ASSERTE(pRawScan) ;

	isOK = RQ_GetRawScanBScanMemoryBlockList(pRawScan, bScanIndex, &pMemoryBlock) ;
	if(!isOK) 
		return NDTS_BFalse ; 

	// The b-scan trailer is the last block in the b-scan memory block list.
	*ppData = pMemoryBlock + RQ_ReturnRawScanBScanMemoryBlockListSize(pRawScan) - 1 ;

	return NDTS_BTrue ;
}

NDTS_Boolean CALL_CONV RQ_GetRawScanGateValuesMemoryBlockList(RQ_RawScan *pRawScan, 
															  unsigned long bScanIndex, 
															  RQ_MemoryHandle **ppData) 
{
	RQ_MemoryBlockNode *pMemoryBlockNode ;
	NDTS_Boolean isOK ;
	RQ_MemoryHandle *pMemoryBlock ;
	unsigned long gateValuesMBLSize ;

	// Preconditions.
	_ASSERTE(pRawScan) ;
	_ASSERTE(RQ_ReturnRawScanInternal(pRawScan)->pDataB) ;
	_ASSERTE(bScanIndex < pRawScan->numberOfBScans) ;

	gateValuesMBLSize = RQ_ReturnRawScanGateValuesMemoryBlockListSize(pRawScan) ;
	pMemoryBlockNode = RQ_ReturnRawScanInternal(pRawScan)->pDataB ;

	*ppData = 0 ;
	do
	{
		unsigned long numberOfBScans ;

		// How many b-scans are are referenced by the current node's memory block list?
		numberOfBScans = (pMemoryBlockNode->size - 1 /* RSH */) / gateValuesMBLSize ;
		if(bScanIndex > numberOfBScans - 1)
		{
			pMemoryBlockNode = pMemoryBlockNode->pNext ;
			_ASSERTE(pMemoryBlockNode) ;

			bScanIndex -= numberOfBScans ;
		}
		else
		{
			isOK = RQ_GetMemoryBlockList(pMemoryBlockNode, &pMemoryBlock) ;
			if(!isOK) 
				return NDTS_BFalse ; 

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

			*ppData = pMemoryBlock + bScanIndex * gateValuesMBLSize ;
		}
	}
	while(*ppData == 0) ;
	
	return NDTS_BTrue ;
}

NDTS_Boolean CALL_CONV RQ_GetRawScanGateValuesBody(RQ_RawScan *pRawScan, 
													unsigned long bScanIndex, 
													RQ_MemoryHandle **ppData)
{
	NDTS_Boolean isOK ;
	RQ_MemoryHandle *pMemoryBlock ;

	// Preconditions.
	_ASSERTE(pRawScan) ;
	_ASSERTE(ppData) ;

	isOK = RQ_GetRawScanGateValuesMemoryBlockList(pRawScan, bScanIndex, &pMemoryBlock) ;
	if(!isOK) 
		return NDTS_BFalse ; 

	*ppData = pMemoryBlock ;

	return NDTS_BTrue ;
}

NDTS_Boolean CALL_CONV RQ_GetRawScanGateValuesTrailer(RQ_RawScan *pRawScan, 
													   unsigned long bScanIndex, 
													   RQ_MemoryHandle **ppData) 
{
	NDTS_Boolean isOK ;
	RQ_MemoryHandle *pMemoryBlock ;

	// Preconditions.
	_ASSERTE(pRawScan) ;
	_ASSERTE(ppData) ;

	isOK = RQ_GetRawScanGateValuesMemoryBlockList(pRawScan, bScanIndex, &pMemoryBlock) ;
	if(!isOK) 
		return NDTS_BFalse ; 

	*ppData = pMemoryBlock + 1 ;

	return NDTS_BTrue ;
}

NDTS_Boolean CALL_CONV RQ_MoveRawScanDataToRawScan(RQ_RawScan *pDestination, RQ_RawScan *pSource)
{
	NDTS_Boolean isOK ;
	RQ_RawScanProtected *pInternalSource, *pInternalDestination ;

	// Preconditions.
	_ASSERTE(pDestination) ;
	_ASSERTE(pSource) ;

	pInternalSource = RQ_ReturnRawScanInternal(pSource) ;
	pInternalDestination = RQ_ReturnRawScanInternal(pDestination) ;

	// DataA
	if(pInternalSource->pDataA)
	{
		if(pInternalDestination->pDataA == 0)
			pInternalDestination->pDataA = pInternalSource->pDataA ;
		else
		{
			isOK = RQ_JoinMemoryBlockNodes(pInternalDestination->pDataA, pInternalSource->pDataA) ;
			if(!isOK)
				return NDTS_BFalse ;
		}
	}

	// DataB
	if(pInternalSource->pDataB)
	{
		if(pInternalDestination->pDataB == 0)
			pInternalDestination->pDataB = pInternalSource->pDataB ;
		else
		{
			isOK = RQ_JoinMemoryBlockNodes(pInternalDestination->pDataB, pInternalSource->pDataB) ;
			if(!isOK)
				return NDTS_BFalse ;
		}
	}

	// The raw scan data has now been 'moved'.
	pInternalSource->pDataA = pInternalSource->pDataB = 0 ;
	pDestination->numberOfBScans += pSource->numberOfBScans ;
	pSource->numberOfBScans = 0 ;

	return NDTS_BTrue ;
}

RQ_API NDTS_Boolean CALL_CONV RQ_ValidateRawScan(RQ_RawScan *pRawScan)
{
	RQ_RawScanProtected *pRawInternal ;
	unsigned long aScanIndex, bScanIndex ; 
	
	// Preconditions.
	_ASSERTE(pRawScan) ;

	pRawInternal = RQ_ReturnRawScanInternal(pRawScan) ;
	if(pRawInternal == 0)
	{
		_ASSERTE("Invalid raw scan" && 0) ;
		return NDTS_BFalse ;
	}

	for(bScanIndex = 0; bScanIndex < pRawScan->numberOfBScans; ++bScanIndex)
	{
		RQ_MemoryHandle *pData ;

		// Check b-scans. There may not be any B Scans, e.g. sequenced SFD.
		if (RQ_ReturnRawScanInternal(pRawScan)->pDataA)
		{
			for(aScanIndex = 0; aScanIndex < pRawScan->numberOfAScansInBScan; ++aScanIndex)
			{
				if(RQ_GetRawScanAScanHeader(pRawScan, aScanIndex, bScanIndex, &pData) == NDTS_BFalse)
				{
					_ASSERTE("Invalid raw scan" && 0) ;
					return NDTS_BFalse ;
				}

				if(pData->p == 0)
				{
					_ASSERTE("Invalid raw scan" && 0) ;
					return NDTS_BFalse ;
				}
#ifdef _DEBUG
				if(pData->owner && pData->size == 0)
				{
					_ASSERTE("Invalid raw scan" && 0) ;
					return NDTS_BFalse ;
				}
#endif
				if(RQ_GetRawScanAScanBody(pRawScan, aScanIndex, bScanIndex, &pData) == NDTS_BFalse)
				{
					_ASSERTE("Invalid raw scan" && 0) ;
					return NDTS_BFalse ;
				}

				if(pData->p == 0)
				{
					_ASSERTE("Invalid raw scan" && 0) ;
					return NDTS_BFalse ;
				}
			}
		}
		// Check gate values
		if(RQ_GetRawScanGateValuesBody(pRawScan, bScanIndex, &pData) == NDTS_BFalse)
		{
			_ASSERTE("Invalid raw scan" && 0) ;
			return NDTS_BFalse ;
		}

		if(pData->p == 0)
		{
			_ASSERTE("Invalid raw scan" && 0) ;
			return NDTS_BFalse ;
		}

		if(RQ_GetRawScanGateValuesTrailer(pRawScan, bScanIndex, &pData) == NDTS_BFalse)
		{
			_ASSERTE("Invalid raw scan" && 0) ;
			return NDTS_BFalse ;
		}

		if(pData->p == 0)
		{
			_ASSERTE("Invalid raw scan" && 0) ;
			return NDTS_BFalse ;
		}
	}

	return NDTS_BTrue ;
}
