#include <crtdbg.h>
#include "SnapshotInternal.h"
#include "SnapshotProtected.h"
#include "ThreadInternal.h"
#include "DataCollectionErrorInternal.h"
#include "RawScanProtected.h"
#include "RawScanOffline.h"
#include "DataSamplingStatusInternal.h"

// Called on data available.  DC_Lock and threadCriticalSection lock are already held.
NDTS_Boolean DC_UpdateSnapshot(unsigned long cardIndex)
{
	unsigned long numberOfBScansInBank, step, bank, numberOfDescriptions;
	DC_State *pThreadState = DC_ReturnThreadStatePointer(cardIndex) ;
	NDTS_RawScan tempRawScan ;
	NDTS_RawScan *pRawScan = &tempRawScan;
	RawScan_CardBlockDescription *pDataBCardBlockDescriptions  = 0 ; 
	NDTS_Boolean isOk = NDTS_BTrue;
	if (!pThreadState) return NDTS_BFalse; 

	bank = (pThreadState->snapshotState.snapshotBeingRead) ? 0 : 1 ;
	if (pThreadState->snapshotState.pRawScans[bank].numberOfAScansInBScan == 0) return NDTS_BTrue; // Not got valid sample parameters yet.

	isOk = pThreadState->associatedSDK.fpGetNumberOfBScansInSRAM(cardIndex, &numberOfBScansInBank);
	if (!isOk && !DC_IsErrorNonFatal())
		goto breakout2;

	// NDTS2100 - both the bank being filled and the bank that is to be read from are mapped into PCI memory
	// We need to swap the window on the RAM to point to the correct bank.
	// Do nothing on RapiDAQ / PRX
	if (pThreadState->associatedSDK.fpSetRAMWindow)
	{
		isOk = pThreadState->associatedSDK.fpSetRAMWindow(cardIndex);
		if (!isOk && !DC_IsErrorNonFatal())
			goto breakout2;
	}

	pThreadState->snapshotState.bScansThisSnapshot += numberOfBScansInBank;

	// Get the data from the card in a temporary pRawScan.
	RawScan_InitialiseRawScan(pRawScan) ;

	isOk = pThreadState->associatedSDK.fpCopyStateToRawScan(cardIndex, pRawScan, NDTS_BTrue);
	if (!isOk && !DC_IsErrorNonFatal())
		goto breakout1;

	if (pThreadState->snapshotState.doFullACapture)
	{
		//////////////////////////////////////
		// Full A Scan data capture
		//////////////////////////////////////
		RawScan_CardBlockDescription *pDataACardBlockDescriptions  = 0 ; 

		// Create a-scan data card buffer descriptions.
		isOk = RawScan_CreateRawScanBScanDescriptions(pRawScan, &pDataACardBlockDescriptions, &numberOfDescriptions);
		if (!isOk && !DC_IsErrorNonFatal())
			goto breakout1;

		// Allocate raw scan a-scan data memory.
		isOk = RawScan_AllocRawScanDataAMemoryBlockNode(pRawScan);
		if (!isOk && !DC_IsErrorNonFatal())
			goto breakout1;
		// Get a-scan data.

		if (pThreadState->associatedSDK.isRealHardwareFound)
		{
			isOk = DC_ReadRawScanBScans(cardIndex, pRawScan, pDataACardBlockDescriptions, numberOfDescriptions, pThreadState->crcState.pCRCTable);
			if (!isOk && !DC_IsErrorNonFatal())
				goto breakout1;
		}
		else
		{
			RawScan_FillRawScanWithTestAScanData(pRawScan);
		}
		RawScan_FreeRawDescriptions(pDataACardBlockDescriptions) ;
	}

	//////////////////////////////////////
	// Full gate data capture
	//////////////////////////////////////
	// Create descriptions of raw gate result data. 
	isOk = RawScan_CreateRawGateValueDescriptions(pRawScan, &pDataBCardBlockDescriptions, &numberOfDescriptions);
	if (!isOk && !DC_IsErrorNonFatal())
		goto breakout1;

	isOk = RawScan_AllocRawScanDataBMemoryBlockNode(pRawScan);
	if (!isOk && !DC_IsErrorNonFatal())
		goto breakout1;

	if (pThreadState->associatedSDK.isRealHardwareFound)
	{
		isOk = DC_ReadRawScanGateValues(cardIndex, pRawScan, pDataBCardBlockDescriptions, numberOfDescriptions);
		if (!isOk && !DC_IsErrorNonFatal())
			goto breakout1;
	}
	else
	{
		RawScan_FillRawScanWithTestCScanData(pRawScan);
	}

#ifdef _DEBUG
	RawScan_ValidateRawScan(pRawScan);
#endif


	RawScan_FreeRawDescriptions(pDataBCardBlockDescriptions) ;

	//////////////////////////////////////
	// Summary A Scan and gate data selected from snapshot
	//////////////////////////////////////
	// Update the selected data for this bank using only the new gate data
	if 	(!RawScan_ReturnRawScanInternal(pRawScan)->isLargeGateOutputData)
	{
		for (step=0; step<pRawScan->numberOfAScansInBScan; step++)
		{
			unsigned long aScanSelectionGateIndex = pThreadState->snapshotState.selectionParameters[step].gateIndex;
			short *pBuffer = pThreadState->snapshotState.aScanSamples[bank][step];
			short *pAmplitude = pThreadState->snapshotState.gateAmplitudes[bank][step];
			short *pDepth = pThreadState->snapshotState.gateDepths[bank][step];
			isOk = DC_ExtractSnapshotOutput(cardIndex, pRawScan, step, aScanSelectionGateIndex, pBuffer, NDTS_MAXIMUM_NUMBER_OF_SAMPLES, pAmplitude, pDepth);
			if (!isOk && !DC_IsErrorNonFatal())
				goto breakout1;
		}
	}

	// Append the new gate / encoder data to the current bank's raw scan


	if (RawScan_MoveRawScanDataToRawScan(&(pThreadState->snapshotState.pRawScans[bank]), pRawScan))
	{
		// RawScan_MoveRawScanDataToRawScan will fail if over 10,000 scans are collected to prevent excessive memory usage
		// This will happen if the app stops calling takeSnapshot when free running

		// Work around for firmware problems here!
		//_ASSERTE(pThreadState->snapshotState.bScansThisSnapshot == pThreadState->snapshotState.pRawScans[bank].numberOfBScans);
		if (pThreadState->snapshotState.bScansThisSnapshot != pThreadState->snapshotState.pRawScans[bank].numberOfBScans)
			NDTS_LogLine(NDTS_LLErr, "B in bank not consistent snap %d raw %d", pThreadState->snapshotState.bScansThisSnapshot, pThreadState->snapshotState.pRawScans[bank].numberOfBScans);

	}
	else
	{
		pThreadState->snapshotState.bScansThisSnapshot = pThreadState->snapshotState.pRawScans[bank].numberOfBScans;
		DC_FreeRawScanData(pRawScan, DC_FRSOAll);
	}


	// Finished with the temporary raw scan
breakout1:
	RawScan_UninitialiseRawScan(pRawScan) ;

breakout2:
	isOk = DC_IndicateSampleDataDownloaded(cardIndex);

	return isOk;
}


NDTS_Boolean DC_InitSnapshotDataBank(unsigned long cardIndex, unsigned long bank, NDTS_Boolean full)
{
	unsigned long numSteps, step, gate, sample ;
	DC_State *pThreadState = DC_ReturnThreadStatePointer(cardIndex) ;
	if (!pThreadState) return NDTS_BFalse; 


	
	// Daniel changed July 08 - should be OK 
	// numSteps = (full) ? NDTS_MAXIMUM_NUMBER_OF_STEPS : pBoardState->numberOfAScansPerBScan;
	numSteps = (full) ? NDTS_MAXIMUM_NUMBER_OF_STEPS : pThreadState->snapshotState.pRawScans[bank].numberOfAScansInBScan;

	for (step=0; step<numSteps; step++)
	{
		for(gate=0; gate< NDTS_MAXIMUM_NUMBER_OF_GATES_COMPILE_TIME; gate++)
		{
			pThreadState->snapshotState.gateAmplitudes[bank][step][gate] = 0;
			pThreadState->snapshotState.gateDepths[bank][step][gate] = 0;

//			switch(pBoardState->gates[step][gate].snapshotSelection)
			switch(pThreadState->associatedSDK.fpReturnSnapshotSelectionForGate(cardIndex, step, gate))
			{
			case NDTS_PDSMin:
				// Set to max so first scan to arrive will trigger a new min
				pThreadState->snapshotState.gateAmplitudes[bank][step][gate] = 0xfff;
				break;
			case NDTS_PDSNearest:
				pThreadState->snapshotState.gateDepths[bank][step][gate] = 0x7fff;
				break;
			}
		}
		if (full && pThreadState->snapshotState.clearAScanOnUpdate)
		{
			// Clear down the sample array so if changing range, get zeros at end or if 
			// something goes wrong return flat line rather than garbage.
			short *pSamples = pThreadState->snapshotState.aScanSamples[bank][step];

			for (sample = 0 ; sample < NDTS_MAXIMUM_NUMBER_OF_SAMPLES ; sample++)
			{
				pSamples[sample] = 0;
			}
		}
	}

	{
		NDTS_RawScan *pRawScan = &(pThreadState->snapshotState.pRawScans[bank]);

		// Free the raw scan data at the start of each new snapshot
		if (pRawScan->numberOfAScansInBScan != 0)
			DC_FreeRawScanData(pRawScan, DC_FRSOAll);
	}
	pThreadState->snapshotState.bScansThisSnapshot = 0;

	return NDTS_BTrue;
}


// Called from UpdateSampleParameters in rq.dll.  DC_Lock() will be on before this is called.
// Does not set the last error because the fuction is called via RQ.dll.
void DC_UpdateSnapshotSampleStorage(unsigned long cardIndex)
{
	unsigned long bank;
	NDTS_RawScan *pRawScan;
	DC_State *pThreadState = DC_ReturnThreadStatePointer(cardIndex);
	if (!pThreadState) return ;
	
	for (bank=0; bank<NUMBER_OF_SNAPSHOT_BANKS; bank++)
	{
		pRawScan = &(pThreadState->snapshotState.pRawScans[bank]);

		// Free the raw scan now before the copy state and re-init to prevent problems when the number of steps is changed
		DC_FreeRawScanData(pRawScan, DC_FRSOAll);

		if (!pThreadState->associatedSDK.fpCopyStateToRawScan(cardIndex, pRawScan, NDTS_BFalse)) 
			return ;

		if (bank == 0)
		{
			// Only need to allocate the memory pools once
//			if (!DC_AllocRawScanMemoryPools(pRawScan)) 
			if (!DC_AllocCardMemoryPools(cardIndex, NDTS_BTrue)) 
				return;
		}
	 ;

		if (!DC_InitSnapshotDataBank(cardIndex, bank, NDTS_BTrue)) 
			return ;
	}
}


DC_API NDTS_Boolean CALL_CONV DC_ExtractSnapshotOutput(unsigned long cardIndex, NDTS_RawScan *pRawScan, unsigned long step, unsigned long gateIndex, short *pBuffer, unsigned long numberOfBufferValues, short *pAmplitude, short *pDepth)
{
	if (pDepth != NULL)
	{
        RawScan_ExtractGateDepthValues(pRawScan, step, pAmplitude, pDepth);
	}
	if (pAmplitude != NULL)
	{
		RawScan_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
		DC_ExtractSpecificAScan(cardIndex, pRawScan, step, gateIndex, pBuffer, numberOfBufferValues, pAmplitude, pDepth);
	}

	return NDTS_BTrue;
}


// Only extract if the selection is a better match than the existing contents of *pAmplitude / *pDepth
NDTS_Boolean DC_ExtractSpecificAScan(unsigned long cardIndex, 
									 NDTS_RawScan *pRawScan, 
									 unsigned long step, 
									 unsigned long gateIndex, 
									 short *pBuffer, 
									 unsigned long numberOfBufferValues, 
									 short *pAmplitude, 
									 short *pDepth)
{
	int bScanIndex ;
	NDTS_SnapshotDataSelection selection;

	_ASSERTE(pRawScan) ;
	_ASSERTE(pBuffer) ;
	_ASSERTE(pAmplitude) ;
	_ASSERTE(pDepth) ;

	selection = pRawScan->ppGateSettings[step][gateIndex].snapshotSelection;
	//  If the gate is not enabled and user has asked for a gate dependent AScan
	// give them a flat line as this is not a valid thing to do.
	if (RawScan_ReturnIsSelectionGateDependent(selection) && pRawScan->ppGateSettings[step][gateIndex].isGateEnabled== NDTS_BFalse)
	{
		unsigned long i;
		for (i=0; i< numberOfBufferValues; i++)
		{
			pBuffer[i] = 0;
		}
		return NDTS_BTrue;
	}

	bScanIndex = RawScan_ExtractSelectSpecificBScanIndex(pRawScan, step, selection, gateIndex, pAmplitude, pDepth) ;

	if (bScanIndex == -1)
	{
		return NDTS_BTrue; // Old scan is best fit so OK to do nothing
	}

	// Bug 786 - DSP_124 has the A Scan data as sign and magnitude.  If this function is used in
	// non sequenced applications, the A Scans will need to be converted back.
//	_ASSERTE(RawScan_ReturnBoardStatePointer(cardIndex)->isSequence);
	_ASSERTE(pRawScan->isSequence);  // Changed during creation of RawScan.dll - not yet tested with PRX
	
	// Internal gets the AScan from this bank.  No  lock or wait for bank swap.
	return DC_GetAScanNoBankSwap(cardIndex, pBuffer, numberOfBufferValues, step, bScanIndex) ;
}



// Bug 838 - gate depth is one sample too late on peak metric
// I suppose this is also likely to be true on other boards such as the 2100 and so it is always applied
void DC_AdjustGateDepths(RawScanGateSettings *pGateSettings, unsigned char *pOurCScanRecordAddress)
{
	int gateIndex = 0, activeGateIndex = 0;

	for (gateIndex = 0; gateIndex < NDTS_MAXIMUM_NUMBER_OF_GATES_COMPILE_TIME; gateIndex++)
	{
		if (pGateSettings[gateIndex].isGateEnabled)
		{
			activeGateIndex++;
			if (pGateSettings[gateIndex].isDistanceMetricPeak)
			{
				NDTS_GateOutput* pGateOutput = (NDTS_GateOutput*)(pOurCScanRecordAddress + activeGateIndex * sizeof(NDTS_GateOutput));
				if (pGateOutput->wasDistanceMeasured)
				{
					// This sometimes goes off with the Sonateset Serial Mux and the Chang An workspace
				//	_ASSERTE(pGateOutput->distanceMetric > 0);
					pGateOutput->distanceMetric--;
				}
			}
		}
	}
}




