#include <windows.h>
#include "RegisterBasedHardwareAccess.h"
#include "NDTS2100Internal.h"
#include "NDTSError.h"
#include "NDTSCommonHal.h"

using namespace NDTS::SCO;
using namespace NDTS::NDTS2100;

Reg::Reg(int registerBase, int card, int step, int gate)
{
	this->card = card;
	this->step = step;
	this->gate = gate;
	registerAddress = calcRegAddr(registerBase);
}

Reg::~Reg(void)
{
}



// Implementation of the HardwareAccessClass interface
static int skipWrite = false;
bool Reg::setValue(int newValue)
{
	if (card >= globalState.numberOfCards)
	{
		return false;
	}


	StackBasedLock lock(card);
	if (globalState.isRealHardwareFound)
	{
		if (registerAddress & 0x10000000)
		{
			NDTS_LogLine(NDTS_LLErr, "Skipping Write %x value %x", registerAddress, newValue);
			return true;
		}
		_ASSERTE(card == 0);
	    NDTS_HAL_SetWord(NDTS_SID2100, card, registerAddress, newValue);
	//	if (registerAddress < 0x00305000)
//			NDTS_LogLine(NDTS_LLDebug, "Write reg %x value %x", registerAddress, newValue);
	}
	else
	{
		if (registerAddress != 0x305050)
			NDTS_LogLine(NDTS_LLDebug, "Write reg %x value %x", registerAddress, newValue);
	}

	return true;
}

int Reg::getValue()
{
	StackBasedLock lock(card);

	if (globalState.isRealHardwareFound)
	{
		if (card >= globalState.numberOfCards)
			return false;
		unsigned long retVal = 0;
		//		if (registerAddress != 0x00300004 && registerAddress != 0x300000 && registerAddress != 0x305000 )
		//			NDTS_LogLine(NDTS_LLErr, "About to Read reg %x", registerAddress);

		if (registerAddress & 0x10000000)
		{
			NDTS_LogLine(NDTS_LLErr, "Skipping read %x ", registerAddress);
			return 0;
		}
		_ASSERTE(card == 0);

		NDTS_HAL_GetWord(NDTS_SID2100, card, registerAddress, &retVal);
		return retVal;
	}
	else
	{
		int retVal = rand() % (1<<31);

		if (registerAddress != 0x30504c)
			NDTS_LogLine(NDTS_LLDebug, "Read reg %x set to random value %x", registerAddress, retVal);
		
		return retVal;
	}
}

int Reg::calcRegAddr(int base)
{
	return base + step*stepRegisterIncrement + gate*gateRegisterIncrement;
}

int Reg::getCardNumber()
{
	return card;
}

int Reg::getStepNumber()
{
	return step;
}

int Reg::getGateNumber()
{
	return gate;
}

bool Reg::waitForBit(bool valueToWaitFor, int bit, int timeOutMicroSec)
{
	// Start the timeout timer
	StackBasedLock lock(card);

	LARGE_INTEGER timerFreq, timeCountA, timeCountB ;
	QueryPerformanceFrequency(&timerFreq) ;
	QueryPerformanceCounter(&timeCountA) ;
	int micros = 0;
	// Busy wait loop 
	do
	{
		// Has value been set 
		// Reg::getValue()  - Get the value from this class not a child class as we want the value directly from the hardware
		// E.g. SerialBusRegister calls waitForBit in its getValue()
		bool currentBitValue = (Reg::getValue() & (1 << bit)) ? true : false;
		if (currentBitValue == valueToWaitFor)
		{
			//static int debugWait = true;
			//if (debugWait)
			//	NDTS_LogLine(NDTS_LLErr, "Waited for %duS", micros); 
			return true;
		}
		// Check for timeout
		QueryPerformanceCounter(&timeCountB) ;
		micros = (int) (1000000 * (timeCountB.QuadPart - timeCountA.QuadPart) / timerFreq.QuadPart) ;
	}
	while (micros < timeOutMicroSec);
	return false;
}

BaseForPartReg::BaseForPartReg(int registerBase, int card, int step, int gate) : Reg(registerBase, card, step, gate)
{
	// Unused bits are set to zero
	cachedValue = 0;
}

bool BaseForPartReg::setValue(int newValue)
{

	cachedValue = newValue;
	return Reg::setValue(newValue);
}

int BaseForPartReg::getCachedValue()
{
	return cachedValue;
}



PartReg::PartReg(BaseForPartReg *pBaseRegister, int startBit, int endBit)
{
	_ASSERTE(startBit >= 0 && startBit <= 31);
	_ASSERTE(endBit >= 0 && endBit <= 31);
	_ASSERTE(startBit <= endBit);

	this->startBit = startBit;
	this->endBit = endBit;
	this->pBaseRegister = pBaseRegister;
}

PartReg::~PartReg()
{
}

bool PartReg::setValue(int newValue)
{
	_ASSERTE(startBit >= 0 && startBit <= 31);
	_ASSERTE(endBit >= 0 && endBit <= 31);
	_ASSERTE(startBit <= endBit);
	_ASSERTE(pBaseRegister);

	int mask = 0;

	for (int i=startBit; i<=endBit; i++)
	{
		mask |= (1 << i);
	}

	int	valueToWrite = pBaseRegister->getCachedValue() & ~mask;
	valueToWrite |= (newValue << startBit);
	pBaseRegister->setValue(valueToWrite);
	return true;
}

// Should never read from a part range - read from the full register
int PartReg::getValue()
{
	_ASSERTE(0);
	return -1;
}

int PartReg::getCardNumber()
{
	return pBaseRegister->getCardNumber();
}

int PartReg::getStepNumber()
{
	return pBaseRegister->getStepNumber();
}

int PartReg::getGateNumber()
{
	return pBaseRegister->getGateNumber();
}


HVDac::HVDac(int card)
{
	this->card = card;
	pDataReg = new Reg(0x0305010, card, 0, 0);
	pDacControlReg = new Reg(0x00305014, card, 0, 0);
}

HVDac::~HVDac(void)
{
	delete pDataReg;
	delete pDacControlReg;
}

bool HVDac::setValue(int newValue)
{
	// Write the value to the data reg
	if (!pDataReg->setValue(newValue))
		return false;

	// Set control reg bit zero to 1
	if (!pDacControlReg->setValue(1))
		return false;

	// Wait for control reg bit 31 to go to zero
	if (!pDacControlReg->waitForBit(false, 31, 10))
		return false;

	return true;
}

// Should never read from HV DAC - should use cached values ? maybe do want to read if it is slow to respond or doesn't reach the set value ?
int HVDac::getValue()
{
	_ASSERTE(0);
	return -1;
}

int HVDac::getCardNumber()
{
	return card;
}

int HVDac::getStepNumber()
{
	return 0;
}
int HVDac::getGateNumber()
{
	return 0;
}

SerialBusRegister::SerialBusRegister(IntProperty *pAddressProp, int registerBase, int card, int step, int gate) : Reg(registerBase, card, step, gate)
{
	this->pAddressProp = pAddressProp;
}
SerialBusRegister::~SerialBusRegister(void)
{
}

bool SerialBusRegister::setValue(int newValue)
{
	// 8 bit data 
	_ASSERTE(newValue >= 0 && newValue <= 0xff);
	// 8 bit address for the AD9230 - the Analog Devices SPI bus spec has 13 address bits (bits 0:12).  Bits 13:14 allow a data size of larger than 8 bits and bit 15 is read / write.
	_ASSERTE(pAddressProp->getValue() >= 0 && pAddressProp->getValue() <= 0xff);


	// Set bit 25 serial reset
	//Reg::setValue(0x02000000);
	// Wait for serial write active to go to zero
	//if (!Reg::waitForBit(false, 31, 10))
	//	return false;

	// Bottom 8 bits (0:7) are data
	int regValue = newValue;  

	// Bits 8:15 are set from our 8 bit address
	regValue |= (pAddressProp->getValue() << 8);

	// Bit 23 is the data direction - 0 for write so leave at zero

	// Bit 24 is start transfer - set to 1
	regValue |= (1 << 24);

	// Bit 25 - serial reset is left as zero

	// Write the above bits
	Reg::setValue(regValue);

	// Wait for serial write active to go to zero
	if (!Reg::waitForBit(false, 31, 10))
		return false;

	// Write 1 to device update register at address xff
	Reg::setValue(0x0100ff01);

	// Wait for serial write active to go to zero
	if (!Reg::waitForBit(false, 31, 10))
		return false;

	return true;
}

int SerialBusRegister::getValue()
{
	// 8 bit address for the AD9230  - the Analog Devices SPI bus spec has 13 address bits (bits 0:12).  Bits 13:14 allow a data size of larger than 8 bits and bit 15 is read / write.
	_ASSERTE(pAddressProp->getValue() >= 0 && pAddressProp->getValue() <= 0xff);


	// Set bit 25 serial reset
	//Reg::setValue(0x02000000);
	// Wait for serial write active to go to zero
	//if (!Reg::waitForBit(false, 31, 10))
	//	return false;

	// Bottom 8 bits (0:7) are data - set to zero for read
	int regValue = 0;  

	// Bits 8:15 are set from our 8 bit address
	regValue |= (pAddressProp->getValue() << 8);

	// Bit 23 is the data direction - 1 for read
	regValue |= (1 << 23);

	// Bit 24 is start transfer - set to 1
	regValue |= (1 << 24);

	// Bit 25 - serial reset is left as zero

	// Write the above bits
	Reg::setValue(regValue);

	// Wait for serial write active to go to zero
	if (!Reg::waitForBit(false, 31, 10))
		return false;

	// Read the result
	int result = Reg::getValue();
	// Result is in the bottom 8 bits of the register
	// We put the result in the associated data property 
	result &= 0xff;

	return result;
}



AccessAnyAddressRegister::AccessAnyAddressRegister(IntProperty *pAddressProp, int registerBase, int card, int step, int gate) : Reg(registerBase, card, step, gate)
{
	this->pAddressProp = pAddressProp;
}
AccessAnyAddressRegister::~AccessAnyAddressRegister(void)
{
}

bool AccessAnyAddressRegister::setValue(int newValue)
{
	// On start up - the address is set to zero by default
//	if (pAddressProp->getValue() < 0x00300000)
//		return false;
	// Always work with card 0
	Reg actualReg(pAddressProp->getValue(), 0);
	return actualReg.setValue(newValue);
}

int AccessAnyAddressRegister::getValue()
{
	// On start up - the address is set to zero by default
//	if (pAddressProp->getValue() < 0x00300000)
//		return false;
	// Always work with card 0
	Reg actualReg(pAddressProp->getValue(), 0);
	return actualReg.getValue();
}

DDSControlReg::DDSControlReg(int registerBase, int card, int step, int gate) : Reg(registerBase, card, step, gate)
{
	cachedValue = 0;
}

// Implementation of the HardwareAccessClass interface
bool DDSControlReg::setValue(int newValue)
{
	cachedValue = newValue;
	// Set bit 16 - dds reset
	Reg::setValue(0x00010000);

	// Wait for bit 31 - write active - to go low
	if (!Reg::waitForBit(false, 31, 10))
		return false;

	// Write the value
	Reg::setValue(newValue);

	// Wait for bit 31 - write active - to go low
	if (!Reg::waitForBit(false, 31, 10))
		return false;

	return true;
}

int DDSControlReg::getValue()
{
	return cachedValue;
}
