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

#include "NDTSError.h"
#include "RawScanError.h"
#include "RawScanPools.h"
#include "RawScanInternal.h"
#include "SDKUtilities.h"

void CALL_CONV RawScan_DLLInitialise()
{
	NDTS_InitErrorService() ;
	NDTS_RegisterGetCodeStringFunction(NDTS_ERROR_RAW_SCAN_SDK_INDEX, &RawScan_GetCodeString) ;
  
	RawScan_InitRawScanMemoryPools() ;
}

void CALL_CONV RawScan_DLLUninitialise()
{
}

BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
	lpReserved; hModule; // Keep compiler happy
	switch (ul_reason_for_call)
	{
		case DLL_PROCESS_ATTACH :
			RawScan_DLLInitialise() ;
			break ;
		case DLL_PROCESS_DETACH :
			RawScan_DLLUninitialise() ;
			break ;
		case DLL_THREAD_ATTACH :
			break ;
		case DLL_THREAD_DETACH :
			break ;
    }

    return TRUE ;
}

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


RAW_SCAN_API NDTS_Boolean CALL_CONV RawScan_InitialiseRawScan(NDTS_RawScan *pRawScan)
{
	// Preconditions.
	_ASSERTE(pRawScan) ;

	pRawScan->pInternal = MemoryFactory_AllocFromPool(RawScan_ReturnRawScanProtectedMemoryPool()) ;
	if(!pRawScan->pInternal)
		return NDTS_BFalse ;

	RawScan_ClearRawScan(pRawScan) ;

	return NDTS_BTrue;
}

RAW_SCAN_API NDTS_Boolean CALL_CONV RawScan_UninitialiseRawScan(NDTS_RawScan *pRawScan)
{
	RawScan_RawScanProtected *pInternal ;

	// Preconditions.
	_ASSERTE(pRawScan) ;

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

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

		MemoryFactory_FreeToPool(RawScan_ReturnRawScanProtectedMemoryPool(), pRawScan->pInternal) ;
	}

	return NDTS_BTrue ;
}

RAW_SCAN_API NDTS_Boolean CALL_CONV RawScan_ClearRawScan(NDTS_RawScan *pRawScan)
{
	RawScan_RawScanProtected *pInternal ;
	int i;
	// Preconditions.
	_ASSERTE(pRawScan) ;

	pRawScan->cardIndex				= 0 ; 
	memset(pRawScan->aScanSampleValuesThisStep, 0, sizeof pRawScan->aScanSampleValuesThisStep) ;
	pRawScan->numberOfAScansInBScan	= 0 ; 
	pRawScan->numberOfBScans		= 0 ;
	pRawScan->samplesDivider		= 1;
	pRawScan->isSequence			= NDTS_BFalse ; // Default to non sequenced
	pRawScan->scanStorage			= NDTS_SSMultipleAScanMultipleCScan ;
	pRawScan->sampleSize			= NDTS_SS12Bit ;
	memset(pRawScan->ppGateSettings, 0, sizeof pRawScan->ppGateSettings) ;
	memset(pRawScan->numberOfGates, 0, sizeof pRawScan->numberOfGates) ;

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

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

	// Default to RapiDAQ values
	pInternal->numberOfEncodersSupportedByHardware = 7;
	pInternal->dualEncoderWritesToEnc0and1 = NDTS_BTrue;
	pInternal->isLargeGateOutputData = NDTS_BFalse;

	return NDTS_BTrue;
}

RAW_SCAN_API NDTS_Boolean CALL_CONV RawScan_CopyRawScan(NDTS_RawScan *pDestination, NDTS_RawScan *pSource)
{
	RawScan_RawScanProtected *pInternalSource, *pInternalDestination ;

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

	pDestination->cardIndex				= pSource->cardIndex ; 
	memcpy(pDestination->aScanSampleValuesThisStep, pSource->aScanSampleValuesThisStep, sizeof pSource->aScanSampleValuesThisStep) ;
	pDestination->samplesDivider        = pSource->samplesDivider;
	pDestination->numberOfAScansInBScan	= pSource->numberOfAScansInBScan ; 
	pDestination->numberOfBScans		= pSource->numberOfBScans ;
	pDestination->numberOfFullBScans	= pSource->numberOfFullBScans ;
	pDestination->isSequence			= pSource->isSequence ; 
	pDestination->scanStorage			= pSource->scanStorage ;
	pDestination->sampleSize			= pSource->sampleSize ;
	memcpy(pDestination->ppGateSettings, pSource->ppGateSettings, sizeof pSource->ppGateSettings) ;
	memcpy(pDestination->numberOfGates, pSource->numberOfGates, sizeof pSource->numberOfGates) ;
	//memcpy(pDestination->oversampleCountThisStep, pSource->oversampleCountThisStep, sizeof pSource->oversampleCountThisStep) ;

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

	pInternalDestination = RawScan_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) ;
	pInternalDestination->gateBytesPerBScan			= pInternalSource->gateBytesPerBScan ;
	pInternalDestination->isCRCCheckPresent			= pInternalSource->isCRCCheckPresent ;
	pInternalDestination->pDataA					= pInternalSource->pDataA ;
	pInternalDestination->pDataB					= pInternalSource->pDataB ;

	return NDTS_BTrue;
}


NDTS_Boolean CALL_CONV RawScan_AllocRawScanDataAMemoryBlockNode(NDTS_RawScan *pRawScan)
{
	NDTS_Boolean isOK ;
	RawScan_DataAHeader *pHeader ;
	RawScan_MemoryBlockNode *pMemoryBlockNode ;
	RawScan_MemoryHandle *pIt ;
	void *pMemoryBlock ;
	unsigned long *pAScanHeaders ;
	unsigned long aScanIndex, bScanIndex ;
	unsigned long blockCount, bytesPerAScanSample, aScanHeaderSize, bScanTrailerSize ;
	RawScan_RawScanProtected *pRawScanProtected ;
	// Assume we are not sequenced.
	TPoolHandle aScanBodyPool = 0, bScanTrailerPool = 0 ;

	// Preconditions.
	_ASSERTE(pRawScan) ;

	pRawScanProtected = RawScan_ReturnRawScanInternal(pRawScan) ;

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

	bScanTrailerPool = RawScan_ReturnRawScanBScanTrailerMemoryPool(pRawScan->cardIndex) ;

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

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

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

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

	// Alloc the raw scan header.
	pMemoryBlock = MemoryFactory_AllocFromPool(RawScan_ReturnRawScanDataAHeaderMemoryPool()) ;
	if(!pMemoryBlock) 
	{
		RawScan_HandleError(RawScan_Alloc_OutOfMemory) ;
		return NDTS_BFalse ; 
	}

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

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

	aScanHeaderSize = RawScan_ReturnRawScanAScanHeaderSize(pRawScan) ;
	bScanTrailerSize = RawScan_ReturnRawScanBScanTrailerSize(pRawScan) ;
	bytesPerAScanSample = RawScan_ReturnBytesPerSample(pRawScan->sampleSize) ;
	pAScanHeaders = 0 ;

	for(bScanIndex = 0; bScanIndex < pRawScan->numberOfFullBScans; ++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 = MemoryFactory_Alloc(pRawScan->numberOfFullBScans * pRawScan->numberOfAScansInBScan * aScanHeaderSize) ;
				if(!pMemoryBlock) 
				{
					RawScan_HandleError(RawScan_Alloc_OutOfMemory) ;
					return NDTS_BFalse ; 
				}

				pAScanHeaders = (unsigned long *)pMemoryBlock ;
				pIt->owner = 1 ;
#ifdef _DEBUG
				pIt->size = pRawScan->numberOfFullBScans * 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 = RawScan_ReturnRawScanAScanBodyMemoryPool(pRawScan->cardIndex, aScanIndex) ;

			// A-scan body.
			pMemoryBlock = MemoryFactory_AllocFromPool(aScanBodyPool) ;
			if(!pMemoryBlock) 
			{
				RawScan_HandleError(RawScan_Alloc_OutOfMemory) ;
				return NDTS_BFalse ; 
			}

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

				RawScan_GetNumberOfSamples(pRawScan->cardIndex, &numberOfSamples) ;
				RawScan_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->aScanSampleValuesThisStep[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 = MemoryFactory_AllocFromPool(bScanTrailerPool) ;
		if(!pMemoryBlock) 
		{
			RawScan_HandleError(RawScan_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 RawScan_FreeRawScanDataAMemoryBlockNode(NDTS_RawScan *pRawScan)
{
	NDTS_Boolean isOK ;
	RawScan_RawScanProtected *pRawScanProtected ;
	RawScan_MemoryBlockNode *pMemoryBlockNode ;
	TPoolHandle aScanBodyPool = 0, bScanTrailerPool = 0 ;

	// Preconditions.
	_ASSERTE(pRawScan) ;

	/*
	Note: Do NOT use the RawScan_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 = RawScan_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)
	{
		RawScan_MemoryHandle *pList ;

		// Iterate through the memory block list, freeing memory blocks as we go.
		isOK = RawScan_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) / RawScan_ReturnRawScanBScanMemoryBlockListSize(pRawScan) ;
			unsigned long bScanIndex = 0 ;
			unsigned long blockIndex = 0 ;

			// Free the raw scan header.
			if(pList[blockIndex].p)
				MemoryFactory_FreeToPool(RawScan_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)
						MemoryFactory_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 = MemoryFactory_FindPool(pList[blockIndex].p) ;

						if (aScanBodyPool != 0)
							MemoryFactory_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 = MemoryFactory_FindPool(pList[blockIndex].p) ;

					MemoryFactory_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 = RawScan_FreeMemoryBlockNode(pRawScanProtected->pDataA) ;
	if(!isOK) 
		return NDTS_BFalse ; 

	pRawScanProtected->pDataA = 0 ;

	return NDTS_BTrue ;
}

NDTS_Boolean CALL_CONV RawScan_AllocRawScanDataBMemoryBlockNode(NDTS_RawScan *pRawScan)
{
	NDTS_Boolean isOK ;
	RawScan_DataBHeader *pHeader ;
	unsigned long blockCount ;
	RawScan_MemoryBlockNode *pMemoryBlockNode ;
	RawScan_MemoryHandle *pIt ;
	void *pMemoryBlock ;
	unsigned long bScanIndex ;
	unsigned long trailerSize ;
	RawScan_RawScanProtected *pRawScanProtected ;
	TPoolHandle gateValuesBodyPool = 0, gateValuesTrailerPool = 0 ;

	// Preconditions.
	_ASSERTE(pRawScan) ;

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

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

	pRawScanProtected = RawScan_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 = RawScan_GetMemoryBlockList(pMemoryBlockNode, &pIt) ;
	if(!isOK) 
		return NDTS_BFalse ; 

	// Alloc the raw scan header.
	pMemoryBlock = MemoryFactory_AllocFromPool(RawScan_ReturnRawScanDataBHeaderMemoryPool()) ;
	if(!pMemoryBlock) 
	{
		RawScan_HandleError(RawScan_Alloc_OutOfMemory) ;
		return NDTS_BFalse ; 
	}

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

	gateValuesBodyPool = RawScan_ReturnRawScanGateValuesBodyMemoryPool(pRawScan->cardIndex) ;
	gateValuesTrailerPool = RawScan_ReturnRawScanGateValuesTrailerMemoryPool(pRawScan->cardIndex) ;

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

	trailerSize = RawScan_ReturnRawScanGateValuesTrailerSize(pRawScan) ;

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

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

		// Gate values trailer.
		pMemoryBlock = MemoryFactory_AllocFromPool(gateValuesTrailerPool) ;
		if(!pMemoryBlock) 
		{
			RawScan_HandleError(RawScan_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 RawScan_FreeRawScanDataBMemoryBlockNode(NDTS_RawScan *pRawScan)
{
	NDTS_Boolean isOK ;
	RawScan_RawScanProtected *pRawScanProtected ;
	RawScan_MemoryBlockNode *pMemoryBlockNode ;
	TPoolHandle gateValuesBodyPool = 0, gateValuesTrailerPool = 0;

	// Preconditions.
	_ASSERTE(pRawScan) ;

	pRawScanProtected = RawScan_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)
	{
		RawScan_MemoryHandle *pList ;

		// Iterate through the memory block list, freeing memory blocks as we go.
		isOK = RawScan_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) / RawScan_ReturnRawScanGateValuesMemoryBlockListSize(pRawScan) ;
			unsigned long bScanIndex = 0 ;
			unsigned long blockIndex = 0 ;

			// Free the raw scan header.
			if(pList[blockIndex].p)
				MemoryFactory_FreeToPool(RawScan_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 = MemoryFactory_FindPool(pList[blockIndex].p) ;

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

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

					MemoryFactory_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 = RawScan_FreeMemoryBlockNode(pRawScanProtected->pDataB) ;
	if(!isOK) 
		return NDTS_BFalse ; 

	pRawScanProtected->pDataB = 0 ;

	return NDTS_BTrue ;
}

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


unsigned long CALL_CONV RawScan_ReturnRawScanCRCSize(NDTS_RawScan *pRawScan)
{
	// Preconditions.
	_ASSERTE(pRawScan) ;

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

unsigned long CALL_CONV RawScan_ReturnRawScanAScanHeaderSize(NDTS_RawScan *pRawScan)
{
	pRawScan ;

	// Preconditions.
	_ASSERTE(pRawScan) ;

	return 4 ;
}

unsigned long CALL_CONV RawScan_ReturnRawScanAScanBodySize(NDTS_RawScan *pRawScan, unsigned long aScanIndex)
{
	// Preconditions.
	_ASSERTE(pRawScan) ;

	return pRawScan->aScanSampleValuesThisStep[aScanIndex] * RawScan_ReturnBytesPerSample(pRawScan->sampleSize) ;
}

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

	return RawScan_ReturnRawScanInternal(pRawScan)->numberOfEncodersSupportedByHardware  * 4 ;
}

unsigned long CALL_CONV RawScan_ReturnRawScanBScanTrailerSize(NDTS_RawScan *pRawScan)
{
	// Preconditions.
	_ASSERTE(pRawScan) ;

	// The CRC is ignored.
	return RawScan_ReturnRawScanCRCSize(pRawScan) + RawScan_ReturnRawScanEncoderValuesSize(pRawScan) ;
}

unsigned long CALL_CONV RawScan_ReturnRawScanGateValuesBodySize(NDTS_RawScan *pRawScan)
{
	RawScan_RawScanProtected *pRawInternal ;

	// Preconditions.
	_ASSERTE(pRawScan) ;

	pRawInternal = RawScan_ReturnRawScanInternal(pRawScan) ;

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

unsigned long CALL_CONV RawScan_ReturnRawScanGateValuesTrailerSize(NDTS_RawScan *pRawScan)
{
	// Preconditions.
	_ASSERTE(pRawScan) ;

	return RawScan_ReturnRawScanEncoderValuesSize(pRawScan) ;
}

unsigned long CALL_CONV RawScan_ReturnBytesPerSample(NDTS_SampleSize sampleSize)
{
	return sampleSize == NDTS_SS12Bit ? 2 : 1 ;
}

unsigned long CALL_CONV RawScan_ReturnRawScanAScanMemoryBlockListSize(NDTS_RawScan *pRawScan)
{
	pRawScan ;

	// Header + data.
	return 2 ;
}

unsigned long CALL_CONV RawScan_ReturnRawScanBScanMemoryBlockListSize(NDTS_RawScan *pRawScan)
{
	_ASSERTE(pRawScan) ;

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

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

RAW_SCAN_API RawScan_RawScanProtected * CALL_CONV RawScan_ReturnRawScanInternal(NDTS_RawScan *pRawScan)
{
	// Preconditions.
	_ASSERTE(pRawScan) ;

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

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

NDTS_Boolean CALL_CONV RawScan_GetRawScanDataAHeader(NDTS_RawScan *pRawScan, RawScan_DataAHeader **ppHeader)
{
	NDTS_Boolean isOK ;
	RawScan_MemoryHandle *pMemoryBlock ;

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

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

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

	return NDTS_BTrue ;
}

NDTS_Boolean CALL_CONV RawScan_GetRawScanDataBHeader(NDTS_RawScan *pRawScan, RawScan_DataBHeader **ppHeader)
{
	NDTS_Boolean isOK ;
	RawScan_MemoryHandle *pMemoryBlock ;

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

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

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

	return NDTS_BTrue ;
}

NDTS_Boolean CALL_CONV RawScan_GetRawScanBScanMemoryBlockList(NDTS_RawScan *pRawScan, unsigned long bScanIndex, RawScan_MemoryHandle **ppData)
{
	RawScan_MemoryBlockNode *pMemoryBlockNode ;
	NDTS_Boolean isOK ;
	RawScan_MemoryHandle *pMemoryBlock ;
	unsigned long bScanMBLSize ;

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

	bScanMBLSize = RawScan_ReturnRawScanBScanMemoryBlockListSize(pRawScan) ;
	pMemoryBlockNode = RawScan_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 = RawScan_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 RawScan_GetRawScanAScanHeader(NDTS_RawScan *pRawScan, 
												unsigned long aScanIndex, 
												unsigned long bScanIndex, 
												RawScan_MemoryHandle **ppData)
{
	NDTS_Boolean isOK ;
	RawScan_MemoryHandle *pMemoryBlock ;

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

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

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

	return NDTS_BTrue ;
}

NDTS_Boolean CALL_CONV RawScan_GetRawScanAScanBody(NDTS_RawScan *pRawScan, 
											  unsigned long aScanIndex, 
											  unsigned long bScanIndex, 
											  RawScan_MemoryHandle **ppData)
{
	NDTS_Boolean isOK ;
	RawScan_MemoryHandle *pMemoryBlock ;

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

	isOK = RawScan_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 RawScan_GetRawScanBScanTrailer(NDTS_RawScan *pRawScan, 
												 unsigned long bScanIndex, 
												 RawScan_MemoryHandle **ppData) 
{
	NDTS_Boolean isOK ;
	RawScan_MemoryHandle *pMemoryBlock ;

	// Preconditions.
	_ASSERTE(pRawScan) ;

	isOK = RawScan_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 + RawScan_ReturnRawScanBScanMemoryBlockListSize(pRawScan) - 1 ;

	return NDTS_BTrue ;
}

NDTS_Boolean CALL_CONV RawScan_GetRawScanGateValuesMemoryBlockList(NDTS_RawScan *pRawScan, 
															  unsigned long bScanIndex, 
															  RawScan_MemoryHandle **ppData) 
{
	RawScan_MemoryBlockNode *pMemoryBlockNode ;
	NDTS_Boolean isOK ;
	RawScan_MemoryHandle *pMemoryBlock ;
	unsigned long gateValuesMBLSize ;

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

	gateValuesMBLSize = RawScan_ReturnRawScanGateValuesMemoryBlockListSize(pRawScan) ;
	pMemoryBlockNode = RawScan_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 = RawScan_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 RawScan_GetRawScanGateValuesBody(NDTS_RawScan *pRawScan, 
													unsigned long bScanIndex, 
													RawScan_MemoryHandle **ppData)
{
	NDTS_Boolean isOK ;
	RawScan_MemoryHandle *pMemoryBlock ;

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

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

	*ppData = pMemoryBlock ;

	return NDTS_BTrue ;
}

NDTS_Boolean CALL_CONV RawScan_GetRawScanGateValuesTrailer(NDTS_RawScan *pRawScan, 
													   unsigned long bScanIndex, 
													   RawScan_MemoryHandle **ppData) 
{
	NDTS_Boolean isOK ;
	RawScan_MemoryHandle *pMemoryBlock ;

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

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

	*ppData = pMemoryBlock + 1 ;

	return NDTS_BTrue ;
}

NDTS_Boolean CALL_CONV RawScan_MoveRawScanDataToRawScan(NDTS_RawScan *pDestination, NDTS_RawScan *pSource)
{
	NDTS_Boolean isOK ;
	RawScan_RawScanProtected *pInternalSource, *pInternalDestination ;

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

	if (pDestination->numberOfBScans > 100000)
	{
		RawScan_HandleError(RawScan_MoveData_TooManyBScans) ;
		return NDTS_BFalse;
	}

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

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

	// DataB
	if(pInternalSource->pDataB)
	{
		if(pInternalDestination->pDataB == 0)
			pInternalDestination->pDataB = pInternalSource->pDataB ;
		else
		{
			isOK = RawScan_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 ;
	pDestination->numberOfFullBScans += pSource->numberOfFullBScans;
	pSource->numberOfBScans = 0 ;

	return NDTS_BTrue ;
}

RAW_SCAN_API NDTS_Boolean CALL_CONV RawScan_ValidateRawScan(NDTS_RawScan *pRawScan)
{
	RawScan_RawScanProtected *pRawInternal ;
	unsigned long aScanIndex, bScanIndex ; 
	
	// Preconditions.
	_ASSERTE(pRawScan) ;

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

	for(bScanIndex = 0; bScanIndex < pRawScan->numberOfFullBScans; ++bScanIndex)
	{
		RawScan_MemoryHandle *pData ;

		// Check b-scans. There may not be any B Scans, e.g. sequenced SFD.
		if (RawScan_ReturnRawScanInternal(pRawScan)->pDataA)
		{
			for(aScanIndex = 0; aScanIndex < pRawScan->numberOfAScansInBScan; ++aScanIndex)
			{
				if(RawScan_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(RawScan_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(RawScan_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(RawScan_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 ;
}
void RawScan_ConvertRawScanFromSignMagToTwosComp(NDTS_RawScan *pRawScan)
{
	unsigned long aScanIndex = 0;
	unsigned long bScanIndex = 0;
	for (bScanIndex = 0; bScanIndex < pRawScan->numberOfFullBScans; bScanIndex++)
	{
		for (aScanIndex = 0; aScanIndex < pRawScan->numberOfAScansInBScan; aScanIndex++)
		{
			RawScan_MemoryHandle *pAScanBody;
			RawScan_GetRawScanAScanBody(pRawScan, aScanIndex, bScanIndex, &pAScanBody);
			SDKUtilities_SignAndMagToTwosComp(pAScanBody->p, pRawScan->aScanSampleValuesThisStep[aScanIndex]);
		}
	}
}


