#include <crtdbg.h>
#include "TCG.h"
#include "NDTS2100Errors.h"
#include "NDTS2100Internal.h"
#include "NDTS2100SDK.h"
#include "RegisterBasedHardwareAccess.h"
#include "NDTSCommonHal.h"
#include "NDTSError.h"

using namespace NDTS::NDTS2100;

const int tcgRamSizeSamples = 256000;
const int tcgRamBaseAddress = 0;

TCGInternal::TCGInternal()
{
	for (int card=0; card < maxCards; card++)
	{
		steps[card] = 1;
		for (int step=0; step < maxSteps; step++)
		{
			isInterfaceTriggered[card][step] = false;
			isSimpleGain[card][step] = false;
			simpleGain[card][step] = 0xffff;  // Set a simple gain different to the default value so that when the default value is set for the first time, the card is updated.
			samplesPerAScan[card][step] = 2;
			samplesDropPerAScan[card][step] = 0;
			for (int samp=0; samp<NDTS_MAXIMUM_NUMBER_OF_SAMPLES; samp++)
				pShadowCurve[card][step][samp] = 0;
		}
	}
	isInitialised = false;
}

TCGInternal::~TCGInternal()
{

}

void TCGInternal::initialise()
{
	isInitialised = true;
	// Do we need to send the curves now or should we wait until 
	// later once the default values such as the range and drop samples have been applied to the card?
//	for (int card=0; card < globalState.numberOfCards; card++)
//	{
//		sendCurve(card, step);
//	}
}

bool TCGInternal::setCurve(int card, int step, int *pTCGCurve, int pointsInCurve)
{
	
	if (pointsInCurve >= maxPointsPerCurve ||
		pointsInCurve < 1)
	{
		NDTS2100_HandleNDTS2100Error(NDTS2100_TCG_ValueOutOfRange);	
		return false;
	}

	// Range check the values in the curve and check there is actually a change in value
	bool identical = true;
	for (int i=0; i < pointsInCurve; i++)
	{
		if (pTCGCurve[i] > tcgMaxValue || pTCGCurve[i] < 0)
		{
			NDTS2100_HandleNDTS2100Error(NDTS2100_TCG_ValueOutOfRange);	
			return false;
		}
		if (pShadowCurve[card][step][i] != pTCGCurve[i])
		{
			pShadowCurve[card][step][i] = pTCGCurve[i] ;
			identical = false;
		}
	}

	if (identical)
		return true;

	return sendCurve(card, step);
	
}
void TCGInternal::setIsInterfaceTriggered(int card, int step, bool isInterfaceTriggered)
{
	if (this->isInterfaceTriggered[card][step] == isInterfaceTriggered)
		return;

	this->isInterfaceTriggered[card][step] = isInterfaceTriggered;
	sendCurve(card, step);
}


void TCGInternal::setIsSimpleGain(int card, int step, bool isSimpleGain)
{
	if (this->isSimpleGain[card][step] == isSimpleGain)
		return;

	this->isSimpleGain[card][step] = isSimpleGain;
	sendCurve(card, step);
}

void TCGInternal::setSimpleGain(int card, int step, int simpleGain)
{
	if (this->simpleGain[card][step] == simpleGain)
		return;
	this->simpleGain[card][step]  = simpleGain;
	sendCurve(card, step);
}

void TCGInternal::setSamplesPerAScan(int card, int step, int samplesPerAScan)
{
	_ASSERTE(samplesPerAScan > 0);
	if (this->samplesPerAScan[card][step] == samplesPerAScan)
		return;

	this->samplesPerAScan[card][step] = samplesPerAScan;
	sendCurve(card, step);
}

void TCGInternal::setSamplesDropPerAScan(int card, int step, int numberOfSamplesDrop)
{
	if (this->samplesDropPerAScan[card][step] == numberOfSamplesDrop)
		return;

	this->samplesDropPerAScan[card][step] = numberOfSamplesDrop;

	// Samples drop isn't needed on the 2100 as the sample rate is divided on both the TCG curve and the A Scan data.
	//sendCurve(card, step);
}

void TCGInternal::setNumberOfSteps(int card, int steps)
{
	_ASSERTE(steps > 0 && steps < maxSteps);
	if (this->steps[card] == steps)
		return;

	// The step parameter is only used to check that the step is active.  
	// The curve for all steps must be re-written each time setCurve is called
	// So calling with step0 ensures the curve is always sent if the number of steps is changed.
	sendCurve(card, 0);
}
	
bool TCGInternal::sendCurve(int card, int step)
{
	_ASSERTE(isInitialised);

	if (card >= globalState.numberOfCards)
		return true;

	if (step >= steps[card])
		return true;

	// TODO: This gets called multiple times on a commit group and that may make things slower than they need to be
	// as the curve for all steps has to be is written on each call here!
	// In the NDTS2100, the curve for each step immediately follows that of the previous step.

	int totalAScanSamples = 0;
	for (int step=0; step < maxSteps; step++)
	{
		Reg tcgControlReg(0x00301020, card, step);
		totalAScanSamples += this->samplesPerAScan[card][step];

		// Currently no support for BEA gates - these would need to work with the TCG Control reg here!
		// Disable TCG
		tcgControlReg.setValue(0);

	}

	// Temp debugging fix here!
	if (this->isInterfaceTriggered[card][0])
	  return true;

	// TCG RAM size is 256K - allow some safety
	int holdSamples = (totalAScanSamples / tcgRamSizeSamples) + 1;

	// pReSampledShadowCurve contains the curve ready to DMA to TCG RAM
	// The RAM is arranged with each 32 bit word containing one sample of TCG information in the lowest 12 bits.
	int pReSampledShadowCurve[tcgRamSizeSamples];
	int resampledCurveIndex = 0;
	for (int step=0; step < maxSteps; step++)
	{
		int pointsThisStep = samplesPerAScan[card][step] / holdSamples;
		Reg tcgPointsReg(0x00301024, card, step);
		tcgPointsReg.setValue(pointsThisStep);
		Reg tcgHoldSamples(0x00301028, card, step);
		tcgHoldSamples.setValue(holdSamples);

		for (int point = 0; point < pointsThisStep; point++)
		{
			if (isSimpleGain[card][step])
			{
				pReSampledShadowCurve[resampledCurveIndex++] = simpleGain[card][step];
			}
			else
			{
				pReSampledShadowCurve[resampledCurveIndex++] = pShadowCurve[card][step][point / holdSamples];
			}
		}
	}

	int tcgRAMBaseAddress = 0;
	//NDTS_HAL_PerformDMATransfer(NDTS_SID2100, card, pReSampledShadowCurve, tcgRAMBaseAddress, resampledCurveIndex*2, NDTS_BFalse) ;
	for (int i=0; i < resampledCurveIndex; i++)
	{
		NDTS_HAL_SetWord(NDTS_SID2100, card, i*4, pReSampledShadowCurve[i]);
/*		NDTS_LogLine(NDTS_LLErr, "TCG addr %x, samp %x", 
			i*4, 
			pReSampledShadowCurve[i]);,
		);
*/
	}
	// Re enable the TCG
	for (int step=0; step < maxSteps; step++)
	{
		Reg tcgControlReg(0x00301020, card, step);

		// Currently no support for BEA gates - these would need to work with the TCG Control reg here!
		// TCG Trigger control
		int tcgData = this->isInterfaceTriggered[card][step] ? 0 : 1;
		// TCG Enable
		tcgData |= 0x02;
		tcgControlReg.setValue(tcgData);
	}

	return true;
}
