#pragma once
#include "IntPropertyInternal.h"
#include "NonRegisterHardwareAccessClass.h"

namespace NDTS
{
namespace NDTS2100
{
	
using namespace SCO;

class Reg : public NDTS2100HardwareAccess 
{
public:

	Reg(int registerBase, int card, int step=0, int gate=0);
	~Reg(void);

	// Implementation of the HardwareAccessClass interface
	virtual bool setValue(int newValue);
	virtual int getValue();
	virtual int getCardNumber();
	virtual int getStepNumber();
	virtual int getGateNumber();

	// Does a busy wait for a bit of a register to be set / clear
	// valueToWaitFor is false for wait for clear (i.e. bit == zero), true for wait for set (i.e. bit == 1)
	// bit is the bit of the register to check, 0..31.
	// timeOutMicroSec is the time to wait before giving up
	// Returns true if the bit is set to valueToWaitFor on entry or before timeOutMicroSec
	// returns false if after timeOutMicroSec, the bit has never been set to valueToWaitFor
	bool waitForBit(bool valueToWaitFor, int bit, int timeOutMicroSec);

private:
	int calcRegAddr(int regBase);
	int registerAddress;
	int card;
	int step;
	int gate;

};

class BaseForPartReg : public Reg
{
public:
	BaseForPartReg(int registerBase, int card, int step=0, int gate=0);
	virtual bool setValue(int newValue);
	int BaseForPartReg::getCachedValue();
private:
	int cachedValue;
};

class PartReg : public NDTS2100HardwareAccess
{
public:

	PartReg(BaseForPartReg *pBaseRegister, int startBit, int endBit);
	~PartReg(void);

	// Implementation of the HardwareAccessClass interface
	virtual bool setValue(int newValue);
	virtual int getValue();

	virtual int getCardNumber();
	virtual int getStepNumber();
	virtual int getGateNumber();

private:
	bool writeBitRange(int newValue);
	int startBit;
	int endBit;
	// The baseRegister is used in conjunction with the startBit and endBit.
	// It contains the full value of the register and is normally pointed to by several
	// Reg objects, each describing a part of the register.
	BaseForPartReg *pBaseRegister;

};

class ValueMinus1Reg : public Reg
{
public:

	ValueMinus1Reg(int registerBase, int card, int step=0, int gate=0) : Reg(registerBase, card, step, gate) {};
	~ValueMinus1Reg(void){};

	// Implementation of the HardwareAccessClass interface
	virtual bool setValue(int newValue) { return Reg::setValue(newValue - 1); };
	virtual int getValue() {_ASSERTE(0); return -1; };

};

// The sequence control reg contains the number of steps and a sequence enable bit.
// The sequence enable bit should always be true, otherwise the sequence will not run
// The lowest 5 bits contain the number of sequence steps - 1.
class SequenceControlReg: public Reg
{
public:

	SequenceControlReg(int registerBase, int card, int step=0, int gate=0) : Reg(registerBase, card, step, gate) {};
	~SequenceControlReg(void){};

	// Implementation of the HardwareAccessClass interface
	virtual bool setValue(int newValue) { return Reg::setValue(0x80000000 | (newValue - 1)); };
	virtual int getValue() {_ASSERTE(0); return -1; };

};

class HVDac : public NDTS2100HardwareAccess
{
public:

	HVDac(int card);
	~HVDac(void);

	// Implementation of the HardwareAccessClass interface
	virtual bool setValue(int newValue);
	virtual int getValue();

	virtual int getCardNumber();
	virtual int getStepNumber();
	virtual int getGateNumber();
private:
	int card;
	Reg *pDataReg;
	Reg *pDacControlReg;
	Reg *pHVControlReg;
};

// Wrapper for a serial bus such as that used to access the ADC.
// If this is to be exposed to the user long term, each register on the serial bus should map to a property
// This class gives a quick mechanism to access devices on the bus but if the app saves and reloads
// settings, the various registers on the bus won't all be set correctly from the settings alone.
// The property associated with this register represents the data.  There is a separate address property that
// must be set to the address to acccess on the bus before this register is read or written to.
class SerialBusRegister : public Reg
{
public:
	SerialBusRegister(IntProperty *pAddressProp, int registerBase, int card, int step=0, int gate=0);
	~SerialBusRegister(void);

	// Implementation of the HardwareAccessClass interface
	virtual bool setValue(int newValue);
	virtual int getValue();
private:
	IntProperty *pAddressProp;
};


// Wrapper to give direct access to any register on card 0 for initial development
// The property associated with this register represents the data.  There is a separate address property that
// must be set to the address to acccess before this register is read or written to.
class AccessAnyAddressRegister : public Reg
{
public:
	AccessAnyAddressRegister(IntProperty *pAddressProp, int registerBase, int card, int step=0, int gate=0);
	~AccessAnyAddressRegister(void);

	// Implementation of the HardwareAccessClass interface
	virtual bool setValue(int newValue);
	virtual int getValue();
private:
	IntProperty *pAddressProp;
};

class DDSControlReg : public Reg
{
public:
	DDSControlReg(int registerBase, int card, int step=0, int gate=0);
	~DDSControlReg(void);

	// Implementation of the HardwareAccessClass interface
	virtual bool setValue(int newValue);
	virtual int getValue();
private:
	int cachedValue;
};

// A Scan range - bottom bit must be zero
// Range is also adjusted in ChangeHook::updateRangeDelayRepMax
class AScanRangeReg : public Reg
{
public:

	AScanRangeReg(int registerBase, int card, int step=0, int gate=0) : Reg(registerBase, card, step, gate) {};
	~AScanRangeReg(void){};

	// Implementation of the HardwareAccessClass interface
	virtual bool setValue(int newValue) 
	{ 
		return Reg::setValue(getAdjustedRange(newValue)); 
	};

	static int getAdjustedRange(int newValue)
	{
		newValue &= 0xfffffffe;
		if (newValue == 0)
			newValue = 0x02;
		return newValue;

	}

	virtual int getValue() {_ASSERTE(0); return -1; };

};



}
}