#include <windows.h>
#include <crtdbg.h>

#include "NDTSError.h"
#include "RQ.h"
#include "Collect.h"

#include "PingPongTable.h"
#include "TCG.h"
#include "NDTSRS2AInternal.h"
#include "NDTSRS2AErrors.h"
#include "PropertiesInterface.h"
#include "PropertyInternal.h"
#include "SDKPropertiesCollection.h"
#include "SDKUtilities.h"

using namespace NDTS::RS2A;
using namespace NDTS::SCO;

BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{ 
	switch (ul_reason_for_call)
	{ 
	case DLL_PROCESS_ATTACH :
		RS2A_DLLInitialise();
		break ;
	case DLL_PROCESS_DETACH :
		RS2A_DLLUninitialise();
		break ;
	case DLL_THREAD_ATTACH :
		break ;
	case DLL_THREAD_DETACH :
		break ;
	}

	return TRUE ;
}

NDTS_Boolean CALL_CONV RS2A_DLLInitialise()
{
	rs2aGlobalState.isInitialised = false;
	// Register our GetCodeString function
	NDTS_RegisterGetCodeStringFunction(NDTS_ERROR_RS2A_SDK_INDEX, &RS2A_GetCodeString) ;
	return NDTS_BTrue ;
}

NDTS_Boolean CALL_CONV RS2A_DLLUninitialise()
{
	return NDTS_BTrue;
}

NDTS_Boolean CALL_CONV RS2A_InitialiseInternal()
{
	if (rs2aGlobalState.isInitialised)
	{
		// ToDo: Maybe set an error and return false once IW calls Init as the first thing.
		return NDTS_BTrue;
	}

	NDTS_Boolean isOK ;


	rs2aGlobalState.cardIndex = 0 ;

	if (RQ_ReturnNumberOfRapiDAQBoards() <= rs2aGlobalState.cardIndex)
	{
		RS2A_HandleRS2AError(RS2A_NoHardwareFound);
		return NDTS_BFalse;
	}
	RawScan_InitialiseRawScan(&rs2aGlobalState.rawScan);

	unsigned long dspVer, perVer;

	RQ_GetRS2AFirmwareRevisions(&dspVer, &perVer);

	NDTS_LogLine(NDTS_LLNotice, "Running Rs2A SDK revision %s.  Dsp %x, Per %x", SDKUtilities_GetSubversionRevisionString(), dspVer, perVer) ;

	isOK = RQ_InitialiseNonSequenced(rs2aGlobalState.cardIndex) ;
	if (!isOK)
		return NDTS_BFalse ;


	return isOK ;
}


FactoryInternal::FactoryInternal()
{
	_ASSERTE(!rs2aGlobalState.isInitialised);
	pPropertiesCollection = new SDKPropertiesCollection(&tcg);
}

FactoryInternal::~FactoryInternal()
{

	if (rs2aGlobalState.isInitialised)
	{
		uninitialise();
	}
	delete pPropertiesCollection;
	return;
}

Snapshot *FactoryInternal::getSnapshot()
{
	_ASSERTE(rs2aGlobalState.isInitialised);
	return &snapshot;
}
ErrorLog *FactoryInternal::getErrorLog()
{
	return &errorLog;
}
TCG *FactoryInternal::getTcg()
{
	_ASSERTE(rs2aGlobalState.isInitialised);
	return &tcg;
}

NDTS::SCO::Property *FactoryInternal::getRootProperty()
{
	return dynamic_cast<NDTS::SCO::Property*>(pPropertiesCollection->getRoot());
}

bool FactoryInternal::initialise()
{
	if (!rs2aGlobalState.isInitialised )
	{
		if (pPropertiesCollection->initialise())
		{
			NDTS_LogLine(NDTS_LLNotice, "\n\nInitialisation OK \n\n");
			rs2aGlobalState.isInitialised = true;
		}
	}
	return rs2aGlobalState.isInitialised;
}
void FactoryInternal::uninitialise()
{
	pPropertiesCollection->uninitialise();
}

bool SnapshotInternal::getAScan(int bScanIndex, int aScanIndex, short *pSamples, int numberOfBufferValues)
{
	if (!rs2aGlobalState.isInitialised)
	{
		RS2A_HandleRS2AError(RS2A_NotInitialised) ;
		return false ;
	}

	// Just pass this through to the RQ SDK with our raw-scan
	NDTS_Boolean isOK = RawScan_ExtractAScan(bScanIndex, aScanIndex, &rs2aGlobalState.rawScan, pSamples, numberOfBufferValues) ;
	return 	(isOK) ? true : false;
}

unsigned long SnapshotInternal::returnNumberOfSamples()
{
	return rs2aGlobalState.rawScan.aScanSampleValuesThisStep[0];
}

void SnapshotInternal::resetEncoder(int encoderIndex)
{
	RQ_SetResetEncoderOutput(0, encoderIndex);
}

bool SnapshotInternal::getPackedGateData(int bScanIndex, int aScanIndex, GateOutput *pGateOutputs, int numberOfBufferValues)
{
	if (!rs2aGlobalState.isInitialised)
	{
		RS2A_HandleRS2AError(RS2A_NotInitialised) ;
		return false ;
	}

	// Just pass this through to the RQ SDK with our raw-scan
	NDTS_Boolean isOK =  RawScan_ExtractGateValues(bScanIndex, aScanIndex, &rs2aGlobalState.rawScan, (NDTS_GateOutput*)pGateOutputs, numberOfBufferValues) ;
	return 	(isOK) ? true : false;
}

bool SnapshotInternal::getUnPackedGateData(int bScanIndex, int aScanIndex, GateOutput *pGateOutputs, int numberOfBufferValues)
{
	int packedGate = 0;
	int unpackedGate = 0;
    NDTS_RawScan *pRawScan;
	pRawScan = &rs2aGlobalState.rawScan;
	static NDTS_GateOutput pPackedResults[NDTS_MAXIMUM_NUMBER_OF_GATES_COMPILE_TIME];
    
	if (!rs2aGlobalState.isInitialised)
	{
		RS2A_HandleRS2AError(RS2A_NotInitialised) ;
		return false ;
	}

	// Just pass this through to the RQ SDK with our raw-scan
	NDTS_Boolean isOK = RawScan_ExtractGateValues(bScanIndex, aScanIndex, pRawScan, pPackedResults, numberOfBufferValues);
	if (!isOK)
	    return 	(isOK) ? true : false;
    
	packedGate = 0;
	for (unpackedGate=0; unpackedGate < numberOfBufferValues; unpackedGate++)
	{
		if (pRawScan->ppGateSettings[bScanIndex][unpackedGate].isGateEnabled)
		{
			if (unpackedGate < NDTS_MAXIMUM_NUMBER_OF_GATES_COMPILE_TIME && pPackedResults[packedGate].wasAmplitudeMeasured)
				pGateOutputs[unpackedGate].amplitudeMetric = pPackedResults[packedGate].amplitudeMetric;
			else
				pGateOutputs[unpackedGate].amplitudeMetric = 0;

	 		if (unpackedGate < NDTS_MAXIMUM_NUMBER_OF_GATES_COMPILE_TIME && pPackedResults[packedGate].wasDistanceMeasured)
				pGateOutputs[unpackedGate].distanceMetric= pPackedResults[packedGate].distanceMetric;
			else
				pGateOutputs[unpackedGate].distanceMetric = 0;

			packedGate++;
		}
		else
		{
			pGateOutputs[unpackedGate].amplitudeMetric = 0;
			pGateOutputs[unpackedGate].distanceMetric = 0;
		}
	}
    
	//for (int i=0; i < numberOfBufferValues; i++)
	//{
	//	if (i < NDTS_MAXIMUM_NUMBER_OF_GATES_COMPILE_TIME && pPackedResults[i].wasAmplitudeMeasured)
	//		pGateOutputs[i].amplitudeMetric = pPackedResults[i].amplitudeMetric;
	//	else
	//		pGateOutputs[i].amplitudeMetric = 0;

	// 	if (i < NDTS_MAXIMUM_NUMBER_OF_GATES_COMPILE_TIME && pPackedResults[i].wasDistanceMeasured)
	//		pGateOutputs[i].distanceMetric= pPackedResults[i].distanceMetric;
	//	else
	//		pGateOutputs[i].distanceMetric = 0;
	//}

	return true;
}


bool SnapshotInternal::getEncoderValues(int bScanIndex, int numberOfEncoders, int *pEncoderValue)
{
	if (!rs2aGlobalState.isInitialised)
	{
		RS2A_HandleRS2AError(RS2A_NotInitialised) ;
		return false ;
	}

	// Just pass this through to the RQ SDK with our raw-scan
	NDTS_Boolean isOK =  RawScan_ExtractEncoderValues(bScanIndex, &rs2aGlobalState.rawScan, (long*)pEncoderValue, numberOfEncoders);
	return 	(isOK) ? true : false;
}



bool SnapshotInternal::getEncoderValue(int bScanIndex, int encoderIndex, int *pEncoderValue)
{
	if (!rs2aGlobalState.isInitialised)
	{
		RS2A_HandleRS2AError(RS2A_NotInitialised) ;
		return false ;
	}

	// Just pass this through to the RQ SDK with our raw-scan
	NDTS_Boolean isOK =  RawScan_ExtractEncoderValue(bScanIndex, encoderIndex, &rs2aGlobalState.rawScan, (long*)pEncoderValue);
	return 	(isOK) ? true : false;
}

bool ErrorLogInternal::getErrorLogLine(int lineNumber, char *pMessageBuffer, int numberOfBytesInMessageBuffer)
{
	NDTS_Boolean isOK = NDTS_GetErrorLogLine(lineNumber, pMessageBuffer, numberOfBytesInMessageBuffer);
	return 	(isOK) ? true : false;
}


