#include "ThreadInternal.h"
#include "SnapshotInternal.h"
#include "DataCollectionErrorInternal.h"
#include "RawScanOffline.h"
#include "DataSamplingStatusInternal.h"


DC_API NDTS_Boolean DC_SnapshotInitialise(DC_AssociatedSDK *pAssociatedSDK)
{
	unsigned long step, bank, cardIndex;
	DC_State *pThreadState; 

	if (!DC_Initialise(pAssociatedSDK)) return NDTS_BFalse;
	for (cardIndex=0; cardIndex < pAssociatedSDK->numberOfBoards; cardIndex++)
	{

		pThreadState = DC_ReturnThreadStatePointer(cardIndex) ;
		if (!pThreadState) return NDTS_BFalse; 
		DC_Lock(cardIndex);

		pThreadState->snapshotState.snapshotBeingRead = 0;
		pThreadState->snapshotState.bScansThisSnapshot = 0;
		for (bank=0; bank<NUMBER_OF_SNAPSHOT_BANKS; bank++)
		{
			RawScan_InitialiseRawScan(&(pThreadState->snapshotState.pRawScans[bank]));
			pThreadState->snapshotState.pDataBCardBlockDescriptions[bank] = 0;
			pThreadState->snapshotState.numDataBCardBlockDescriptions[bank] = 0;
		}

		for (step=0; step<NDTS_MAXIMUM_NUMBER_OF_STEPS; step++)
		{
			pThreadState->snapshotState.selectionParameters[step].gateIndex = 0;
		}
		pThreadState->snapshotState.clearAScanOnUpdate = NDTS_BTrue;
		pThreadState->snapshotState.doFullACapture = NDTS_BFalse;

		pThreadState->associatedSDK.fpUnlock(cardIndex);
		if (!DC_SetThreadState(cardIndex, DC_SMSSnapshot))
			return NDTS_BFalse;
	}
	return NDTS_BTrue;
}

DC_API NDTS_Boolean DC_SnapshotUninitialise()
{
	NDTS_Boolean isOK;
	unsigned long bank;
	DC_AssociatedSDK *pAssociatedSDK = &(DC_ReturnThreadStatePointer(0)->associatedSDK);
	unsigned long cardIndex;

	for (cardIndex=0; cardIndex < pAssociatedSDK->numberOfBoards; cardIndex++)
	{
		DC_State *pThreadState; 
		pThreadState = DC_ReturnThreadStatePointer(cardIndex) ;
		if (!pThreadState) return NDTS_BFalse; 

		DC_Lock(cardIndex);
		for (bank=0; bank<NUMBER_OF_SNAPSHOT_BANKS; bank++)
		{
			NDTS_RawScan *pRawScan = &(pThreadState->snapshotState.pRawScans[bank]);
			if (!DC_FreeRawScanData(pRawScan, DC_FRSOAll)) 
			{
				if (!DC_IsErrorNonFatal())
				{
					pThreadState->associatedSDK.fpUnlock(cardIndex);
					return NDTS_BFalse;
				}
			}
			RawScan_UninitialiseRawScan(pRawScan);
		}

		//TODO: Remove DC_UpdateSnapshotSampleStorage from UpdateParametes hook 
		pThreadState->associatedSDK.fpUnlock(cardIndex);
	}
	isOK = DC_Uninitialise();
	if (!isOK)
	{
		return NDTS_BFalse;
	}

	return NDTS_BTrue;
}


DC_API NDTS_Boolean DC_SetAScanSelectionGate(unsigned long cardIndex, unsigned long stepIndex, unsigned long gateIndex)
{
	DC_State *pThreadState = DC_ReturnThreadStatePointer(cardIndex) ;
	if (!pThreadState) return NDTS_BFalse; 
	DC_Lock(cardIndex);

	pThreadState->snapshotState.selectionParameters[stepIndex].gateIndex = gateIndex;

	DC_Unlock(cardIndex);
	return NDTS_BTrue;
}


DC_API NDTS_Boolean DC_GetAScanAndGateDataFromSnapshot(unsigned long cardIndex, unsigned long step, short *pBuffer, unsigned long numberOfBufferValues, short *pAmplitude, short *pDepth) 
{
	DC_State *pThreadState = DC_ReturnThreadStatePointer(cardIndex) ;
	unsigned long unpackedGate, sample, bank;
	if (!pThreadState) return NDTS_BFalse; 
	DC_Lock(cardIndex);

	bank = pThreadState->snapshotState.snapshotBeingRead;

	for (unpackedGate = 0; unpackedGate < pThreadState->associatedSDK.maximumNumberOfGates; unpackedGate++)
	{
		pAmplitude[unpackedGate] = pThreadState->snapshotState.gateAmplitudes[bank][step][unpackedGate];
		pDepth[unpackedGate] = pThreadState->snapshotState.gateDepths[bank][step][unpackedGate];
	}

	for (sample=0; sample<numberOfBufferValues; sample++)
	{
		pBuffer[sample] = pThreadState->snapshotState.aScanSamples[bank][step][sample];
	}

	DC_Unlock(cardIndex);
	return NDTS_BTrue;
}



DC_API NDTS_Boolean DC_GetFullBScanGateDataFromSnapshot(unsigned long cardIndex, unsigned long bScanIndex, NDTS_GateOutput *pGateData, int nSteps, int nGates)
{
	DC_State *pThreadState = DC_ReturnThreadStatePointer(cardIndex) ;
	NDTS_RawScan *pRawScan;
	unsigned long bank;
	if (!pThreadState) return NDTS_BFalse; 

	bank = (pThreadState->snapshotState.snapshotBeingRead);
	pRawScan = &(pThreadState->snapshotState.pRawScans[bank]);

	return RawScan_ExtractFullBScanGateValuesSequenced(bScanIndex, pRawScan, pGateData, nSteps, nGates);
}


DC_API NDTS_Boolean DC_GetOneStepGateDataFromSnapshot(unsigned long cardIndex, unsigned long bScanIndex, unsigned long stepIndex, NDTS_GateOutput *pGateData, int nGates)
{
	DC_State *pThreadState = DC_ReturnThreadStatePointer(cardIndex) ;
	NDTS_RawScan *pRawScan;
	unsigned long bank;
	if (!pThreadState) return NDTS_BFalse; 

	bank = (pThreadState->snapshotState.snapshotBeingRead);
	pRawScan = &(pThreadState->snapshotState.pRawScans[bank]);

	return RawScan_ExtractOneStepGateValuesSequenced(bScanIndex, stepIndex, pRawScan, pGateData, nGates);
}

DC_API NDTS_Boolean DC_GetOneStepLargeGateDataFromSnapshot(unsigned long cardIndex, unsigned long bScanIndex, unsigned long stepIndex, NDTS_GateOutputLarge *pGateData, int nGates)
{
	DC_State *pThreadState = DC_ReturnThreadStatePointer(cardIndex) ;
	NDTS_RawScan *pRawScan;
	unsigned long bank;
	if (!pThreadState) return NDTS_BFalse; 

	bank = (pThreadState->snapshotState.snapshotBeingRead);
	pRawScan = &(pThreadState->snapshotState.pRawScans[bank]);

	return RawScan_ExtractOneStepLargeGateValuesSequenced(bScanIndex, stepIndex, pRawScan, pGateData, nGates);
}

DC_API NDTS_Boolean DC_GetOneStepLargeUnPackedGateDataFromSnapshot(unsigned long cardIndex, unsigned long bScanIndex, unsigned long stepIndex, NDTS_GateOutputLarge *pGateData, int nGates)
{
	DC_State *pThreadState = DC_ReturnThreadStatePointer(cardIndex) ;
	int packedGate = 0;
	int unpackedGate = 0;
	NDTS_RawScan *pRawScan;
	unsigned long bank;
	NDTS_GateOutputLarge pPackedGateData[NDTS_MAXIMUM_NUMBER_OF_GATES_COMPILE_TIME];
	NDTS_GateOutputLarge blank;
	memset(&blank, 0, sizeof(blank));

	if (!pThreadState) return NDTS_BFalse; 

	bank = (pThreadState->snapshotState.snapshotBeingRead);
	pRawScan = &(pThreadState->snapshotState.pRawScans[bank]);

	if (!RawScan_ExtractOneStepLargeGateValuesSequenced(bScanIndex, stepIndex, pRawScan, pPackedGateData, NDTS_MAXIMUM_NUMBER_OF_GATES_COMPILE_TIME))
		return NDTS_BFalse;

	packedGate = 0;
	for (unpackedGate=0; unpackedGate < nGates; unpackedGate++)
	{
		if (pRawScan->ppGateSettings[stepIndex][unpackedGate].isGateEnabled)
		{
			pGateData[unpackedGate] = pPackedGateData[packedGate++];
		}
		else
		{
			pGateData[unpackedGate] = blank;
		}
	}
	return NDTS_BTrue;
}

DC_API NDTS_Boolean DC_GetEncoderDataFromSnapshot(unsigned long cardIndex, unsigned long bScanIndex, long *pEncoders, int nEncoders)
{
	DC_State *pThreadState = DC_ReturnThreadStatePointer(cardIndex) ;
	NDTS_RawScan *pRawScan;
	unsigned long bank;
	if (!pThreadState) return NDTS_BFalse; 

	bank = (pThreadState->snapshotState.snapshotBeingRead);
	pRawScan = &(pThreadState->snapshotState.pRawScans[bank]);
	return RawScan_ExtractEncoderValuesProtected(bScanIndex, pRawScan, pEncoders, nEncoders);
}

DC_API NDTS_Boolean DC_SwapSnapshotDataBank(unsigned long cardIndex, unsigned long *numberOfBScans)
{
	DC_State *pThreadState = DC_ReturnThreadStatePointer(cardIndex) ;
	unsigned long samplingStatus;
	if (!pThreadState) return NDTS_BFalse; 

	if (!DC_GetDataSamplingStatus(cardIndex, &samplingStatus))
		return NDTS_BFalse;
	if (samplingStatus != 0)
	{
		// Must be called outside DC_Lock so thread can run
		if (!DC_BankSwapAndUpdate(cardIndex))
			return NDTS_BFalse;
	} 

	DC_Lock(cardIndex);

	if (numberOfBScans) *numberOfBScans = pThreadState->snapshotState.bScansThisSnapshot;

	if (!DC_InitSnapshotDataBank(cardIndex, pThreadState->snapshotState.snapshotBeingRead, NDTS_BFalse))
	{
		DC_Unlock(cardIndex);
		return NDTS_BFalse;
	}
	pThreadState->snapshotState.snapshotBeingRead = (pThreadState->snapshotState.snapshotBeingRead) ? 0 : 1;

	DC_Unlock(cardIndex);
	return NDTS_BTrue;
}

DC_API unsigned long DC_ReturnNumberOfBScansInSnapshot(unsigned long cardIndex)
{
	DC_State *pThreadState = DC_ReturnThreadStatePointer(cardIndex) ;
	int bank = (pThreadState->snapshotState.snapshotBeingRead);
	return (pThreadState->snapshotState.pRawScans[bank].numberOfBScans);

}

DC_API NDTS_Boolean DC_SetClearAScanOnUpdate(unsigned long cardIndex, NDTS_Boolean clearAScanOnUpdate)
{
	DC_State *pThreadState = DC_ReturnThreadStatePointer(cardIndex) ;
	pThreadState->snapshotState.clearAScanOnUpdate = clearAScanOnUpdate;
	return NDTS_BTrue;
}

DC_API NDTS_Boolean DC_SetFullAScanCapture(unsigned long cardIndex, NDTS_Boolean doFullACapture)
{
	DC_State *pThreadState = DC_ReturnThreadStatePointer(cardIndex) ;

	DC_Lock(cardIndex);

	pThreadState->snapshotState.doFullACapture = doFullACapture;
	// Set the current bank back to empty
	if (!DC_InitSnapshotDataBank(cardIndex, pThreadState->snapshotState.snapshotBeingRead, NDTS_BFalse))
	{
		DC_Unlock(cardIndex);
		return NDTS_BFalse;
	}
	pThreadState->snapshotState.snapshotBeingRead = (pThreadState->snapshotState.snapshotBeingRead) ? 0 : 1;

	DC_Unlock(cardIndex);

	return NDTS_BTrue;
}


DC_API int CALL_CONV DC_ReturnNumberOfAScanSamples(unsigned long cardIndex, unsigned long bScanIndex)
{
	DC_State *pThreadState = DC_ReturnThreadStatePointer(cardIndex) ;
	NDTS_RawScan *pRawScan;
	unsigned long bank;

	if (!pThreadState) return NDTS_BFalse; 


	if (!pThreadState->snapshotState.doFullACapture)
	{
		DC_HandleError(DC_NotInDoFullACaptureMode) ;
		return NDTS_BFalse;
	}


	bank = (pThreadState->snapshotState.snapshotBeingRead);
	pRawScan = &(pThreadState->snapshotState.pRawScans[bank]);

	return pRawScan->aScanSampleValuesThisStep[bScanIndex];
}

DC_API NDTS_Boolean CALL_CONV DC_GetFullAScanDataFromSnapshot(unsigned long cardIndex, unsigned long bScanIndex, unsigned long step, short *pBuffer, unsigned long numberOfBufferValues)
{
	DC_State *pThreadState = DC_ReturnThreadStatePointer(cardIndex) ;
	NDTS_RawScan *pRawScan;
	unsigned long bank;

	if (!pThreadState) return NDTS_BFalse; 


	if (!pThreadState->snapshotState.doFullACapture)
	{
		DC_HandleError(DC_NotInDoFullACaptureMode) ;
		return NDTS_BFalse;
	}


	bank = (pThreadState->snapshotState.snapshotBeingRead);
	pRawScan = &(pThreadState->snapshotState.pRawScans[bank]);

	return RawScan_ExtractAScan(bScanIndex, step, pRawScan, pBuffer, numberOfBufferValues);
}