#if !defined( _IOAAD9824_H )
#define _IOAAD9824_H

#include "BitArray.h"

// Enumerate options for ChannelSelect variable contained in the Operation register
enum IoAAD9824ChannelSelectEnum
{
    eCCDMode,
    eAUX1Mode,
    eAUX2Mode,
    eTestOnlyMode
};

// Enumerate options for PowerDownMode variable contained in the Operation register
enum IoAAD9824PowerDownModeEnum
{
    eNormalPower,
    eTestOnly,
    eStandy,
    eTotalPowerDown
};

// Enumerate options for ColourSteeringMode variable contained in the Control register
enum IoAAD9824ColourSteeringModeEnum
{
    eSteeringDisabled,
    eMosaicSeparate,
    eInterlace,
    eThreeColour,
    eFourColour,
    eVDSelected,
    eMosaicRepeat,
    eUserSpecified
};

// Enumerate registers
enum IoAAD9824RegisterEnum
{
    eOperationRegister,
    eVGAGainRegister,
    eClampLevelRegister,
    eControlRegister,
    ePxGAGainRegister0,
    ePxGAGainRegister1,
    ePxGAGainRegister2,
    ePxGAGainRegister3,

    // Total number of registers = 8
    nAD9824RegisterCount,

    eAD9824InvalidRegister = -1
};

// Enumerate variable index relative to given control register
enum IoAAD9824VariableIndex { };

// Enumerate register variables
enum IoAAD9824VariableEnum
{ 
    // Operation register
    eChannelSelect, ePowerDownMode, eRegisterReset, eOpticalBlackClampDisable,

    // VGA Gain register
    eVGAGain, 

    // Clamp Level register
    eClampLevel, 

    // Control register
    eColourSteeringMode, ePxGAEnable, eSHPAndSHDActiveHigh, eCLPAndPBLKActiveHigh, eTriggerOnDataClockFallingEdge,
    eDataOutDisable,

    // PxGAGain0 register
    ePxGAGain0,

    // PxGAGain0 register
    ePxGAGain1,

    // PxGAGain2 register
    ePxGAGain2,

    // PxGAGain3 register
    ePxGAGain3,

    // Total number of variables = 16
    nAD9824VariableCount,

    eInvalidAD9824Variable = -1
};

// Enumerate bit offsets and counts
enum IoAAD9824BitEnum
{
    iAD9824RegistersBitOffset = 0,

    nAD9824RegistersBitCount = 63,
};

// Enumerate byte counts
enum IoAAD9824ByteEnum
{
    nAD9824RegistersByteCount = nAD9824RegistersBitCount / 8,
};

class IoAAD9824Data
{
    friend class IoAAD9824RegisterArray;  // for benefit of IoAAD9824RegisterArray( ) constructor 

protected:
    IoABitArray& BitArray( ) const { return m_BitArray; }

    IoAAD9824Data( IoABitArray& BitArray ) : m_BitArray( BitArray ) { }

    int IsValid( ) const { return m_BitArray.IsAllocated( ); }

    int Size( ) const { return m_BitArray.Size( ); }

    // Set/get bitfield value
    void SetValue( int Offset, int Count, int Value ) { if ( Count > 0 ) m_BitArray.SetValue( Value, Offset, Count ); }
    int  GetValue( int Offset, int Count ) const { return ( Count > 0 ) ? m_BitArray.Value( Offset, Count ) : 0; }

    // Set/get bit value
    void SetBit( int Offset, int Count, int Value ) { if ( Count == 1 ) m_BitArray.SetBit( Offset, Value ); }
    int  GetBit( int Offset, int Count ) const { return ( Count == 1 ) ? m_BitArray.Bit( Offset ) : 0; }

    // Test bitfield/bit for equality
    int IsBitFieldEqual( int Offset, int Count, const IoAAD9824Data& Other, int OtherOffset, int OtherCount ) const
    {
        return Count == OtherCount && Count > 0 && m_BitArray.IsBitFieldEqual( Offset, Count, Other.m_BitArray, OtherOffset );
    }

    int IsBitEqual( int Offset, int Count, const IoAAD9824Data& Other, int OtherOffset, int OtherCount ) const
    {
        return Count == OtherCount && Count == 1 && m_BitArray.IsBitEqual( Offset, Other.m_BitArray, OtherOffset );
    }

    int IsEqual( const IoAAD9824Data& Other ) const { return m_BitArray.IsEqual( Other.m_BitArray ); }

    // Clear data
    int Clear( ) const { return m_BitArray.Clear( ); }

    int ClearBitField( int Offset, int Count ) const
    {
       return Count > 0 && m_BitArray.ClearBitField( Offset, Count );
    }

    // Copy data
    int Copy( const IoAAD9824Data& Other ) const { return m_BitArray.Copy( Other.m_BitArray ); }

    int CopyBitField( int Offset, int Count, const IoAAD9824Data& Other, int OtherOffset, int OtherCount ) const
    {
       return Count == OtherCount && Count > 0 && m_BitArray.CopyBitField( Offset, Count, Other.m_BitArray, OtherOffset );
    }

private:
    IoABitArray& m_BitArray;
};

class IoAAD9824Bit : public IoAAD9824Data
{
public:
    int IsDefined( ) const { return m_Count >= 0; }

    // Set and get
    void Set( int Value ) { IoAAD9824Data::SetBit( m_Offset, m_Count, Value ); }
    int Value( ) const { return IoAAD9824Data::GetBit( m_Offset, m_Count ); }

    // Assignment and cast operator functions to simplify set and get
    IoAAD9824Bit& operator=( int Value ) { Set( Value ); return *this; }
    operator int( ) const { return Value( ); }

    // Test for equality
    int IsEqual( const IoAAD9824Bit& Other ) const
    {
        return IoAAD9824Data::IsBitEqual( m_Offset, m_Count, Other, Other.m_Offset, Other.m_Count );
    }

    int operator==( const IoAAD9824Bit& Other ) const { return   IsEqual( Other ); }
    int operator!=( const IoAAD9824Bit& Other ) const { return ! IsEqual( Other ); }

protected:
    IoAAD9824Bit( IoABitArray& BitArray ) : IoAAD9824Data( BitArray ), m_Offset( 0 ), m_Count( 0 ) { }

    // Bit reference (located relative to start of parent object)
    IoAAD9824Bit& SetIndex( int Index, int Offset, int Count )
    {
        m_Offset = 0;
        m_Count  = 0;

        if ( 0 <= Index && Index < Count )
        {
            m_Offset = Offset + Index;  // relative offset
            m_Count  = 1;
        }

        return *this;
    }

private:
    int m_Offset, m_Count;
};

class IoAAD9824Byte : public IoAAD9824Bit
{
public:
    int IsDefined( ) const { return m_Count > 0; }
    int BitCount( ) const { return m_Count; }

    // Array operator for accessing individual bits
    IoAAD9824Bit& operator[]( IoAAD9824BitEnum Index ) { return IoAAD9824Bit::SetIndex( Index, m_Offset, m_Count ); }

    // Overload bit array operator with one taking (int) argument
    IoAAD9824Bit& operator[]( int Index ) { return operator[]( (IoAAD9824BitEnum) Index ); }

    // Same array operators modified to work with const source objects and returning const reference
    const IoAAD9824Bit& operator[]( IoAAD9824BitEnum Index ) const { return This( )->operator[]( Index ); }
    const IoAAD9824Bit& operator[]( int              Index ) const { return This( )->operator[]( Index ); }

    // Set and get
    void Set( char Value ) { IoAAD9824Data::SetValue( m_Offset, m_Count, (int) Value ); }
    char Value( ) const { return (char) IoAAD9824Data::GetValue( m_Offset, m_Count ); }

    // Assignment and cast operator functions to simplify set and get
    IoAAD9824Byte& operator=( char Value ) { Set( Value ); return *this; }
    operator char( ) const { return Value( ); }

    // Test for equality
    int IsEqual( const IoAAD9824Byte& Other ) const
    {
        return IoAAD9824Data::IsBitFieldEqual( m_Offset, m_Count, Other, Other.m_Offset, Other.m_Count );
    }

    int operator==( const IoAAD9824Byte& Other ) const { return   IsEqual( Other ); }
    int operator!=( const IoAAD9824Byte& Other ) const { return ! IsEqual( Other ); }

protected:
    IoAAD9824Byte( IoABitArray& BitArray ) : IoAAD9824Bit( BitArray ), m_Offset( 0 ), m_Count( 0 ) { }

    // Byte reference (located relative to start of parent object)
    IoAAD9824Byte& SetIndex( int Index, int Offset, int Count )
    {
        m_Offset = 0;
        m_Count  = 0;

        if ( 0 <= Index && Index < Count / 8 )
        {
            m_Offset = Offset + Index * 8;  // relative offset
            m_Count  = 8;
        }

        return *this;
    }

private:
    IoAAD9824Byte *This( ) const { return (IoAAD9824Byte *) this; }  // helper function to cast-away const

    int m_Offset, m_Count;
};

class IoAAD9824Variable : public IoAAD9824Byte
{
public:
    int IsDefined( ) const { return m_Count > 0; }
    int BitCount( ) const { return m_Count; }

    const char *Name( ) const { return m_Name; }

    // Array operator for accessing individual bits
    IoAAD9824Bit& operator[]( IoAAD9824BitEnum Index ) { return IoAAD9824Bit::SetIndex( Index, m_Offset, m_Count ); }

    // Overload bit array operator with one taking (int) argument
    IoAAD9824Bit& operator[]( int Index ) { return operator[]( (IoAAD9824BitEnum) Index ); }

    // Same array operators modified to work with const source objects and returning const reference
    const IoAAD9824Bit& operator[]( IoAAD9824BitEnum Index ) const { return This( )->operator[]( Index ); }
    const IoAAD9824Bit& operator[]( int              Index ) const { return This( )->operator[]( Index ); }

    // Set and get
    void Set( int Value ) { IoAAD9824Data::SetValue( m_Offset, m_Count, Value ); }
    int Value( ) const { return IoAAD9824Data::GetValue( m_Offset, m_Count ); }

    // Assignment and cast operator to simplify set and get
    IoAAD9824Variable& operator=( int Value ) { Set( Value ); return *this; }
    operator int( ) const { return Value( ); }

    // Test for equality
    int IsEqual( const IoAAD9824Variable& Other ) const
    {
        return IoAAD9824Data::IsBitFieldEqual( m_Offset, m_Count, Other, Other.m_Offset, Other.m_Count );
    }

    int operator==( const IoAAD9824Variable& Other ) const { return   IsEqual( Other ); }
    int operator!=( const IoAAD9824Variable& Other ) const { return ! IsEqual( Other ); }

    // Comparison operator functions (==) and (!=) accepting (int) as second argument
    int IsEqual( int Integer ) const { return Value( ) == Integer; }

    int operator==( int Integer ) const { return   IsEqual( Integer ); }
    int operator!=( int Integer ) const { return ! IsEqual( Integer ); }

    // Clear data => Set(0)
    int Clear( ) { return IoAAD9824Data::ClearBitField( m_Offset, m_Count ); }

    // Copy data => Set( Get( ) )
    int Copy( const IoAAD9824Variable& Other )
    {
        return IoAAD9824Data::CopyBitField( m_Offset, m_Count, Other, Other.m_Offset, Other.m_Count );
    }

    // Assignment operator => Set( Get( ) )
    IoAAD9824Variable& operator=( const IoAAD9824Variable& Other ) { Copy( Other ); return *this; }

protected:
    IoAAD9824Variable( IoABitArray& BitArray ) : IoAAD9824Byte( BitArray ), m_Offset( 0 ), m_Count( 0 ) { }

    // Variable reference (located relative to start of parent object)
    IoAAD9824Variable& SetIndex( int Index, int Offset, int Count )
    {
        m_Offset = 0;
        m_Count  = 0;
        m_Name   = NULL;

        if ( 0 <= Index && Index < nAD9824VariableCount )
        {
            ASSERT( Offset <= m_OffsetLUT[ Index ] && Offset + Count >= m_OffsetLUT[ Index ] + m_CountLUT[ Index ] );

            m_Offset = m_OffsetLUT[ Index ];  // absolute offset
            m_Count  = m_CountLUT[ Index ];
            m_Name   = m_NameLUT[ Index ];
        }

        return *this;
    }

private:
    IoAAD9824Variable *This( ) const { return (IoAAD9824Variable *) this; }  // helper function to cast-away const

    int m_Offset, m_Count;
    const char *m_Name;

    // Bit offset for each variable
    static const int m_OffsetLUT[ nAD9824VariableCount ];

    // Numbers of bits in each variable
    static const int m_CountLUT[ nAD9824VariableCount ];

    // String to identify each variable
    static const char *m_NameLUT[ nAD9824VariableCount ];
};

// Comparison operator functions (==) and (!=) accepting (int) as first argument
inline int operator==( int Integer, const IoAAD9824Variable& Other ) { return   Other.IsEqual( Integer ); }
inline int operator!=( int Integer, const IoAAD9824Variable& Other ) { return ! Other.IsEqual( Integer ); }

class IoAAD9824Register : public IoAAD9824Variable
{
public:
    int IsDefined( ) const { return m_Count > 0; }
    int BitCount( ) const { return m_Count; }

    const char *Name( ) const { return m_Name; }

    int Number( ) const { return IsDefined( ) ? m_Index : -1; }

    IoAAD9824RegisterEnum Enum( ) const { return (IoAAD9824RegisterEnum) Number( ); }

    int VariableCount( ) const { return m_VariableCount; }

    int ByteCount( ) const { return m_Count / 8; }

    // Array operator for accessing individual variables, bytes and bits
    IoAAD9824Variable& operator[]( IoAAD9824VariableEnum Index ) { return IoAAD9824Variable::SetIndex( Index, m_Offset, m_Count ); }
    IoAAD9824Byte&     operator[]( IoAAD9824ByteEnum     Index ) { return IoAAD9824Byte    ::SetIndex( Index, m_Offset, m_Count ); }
    IoAAD9824Bit&      operator[]( IoAAD9824BitEnum      Index ) { return IoAAD9824Bit     ::SetIndex( Index, m_Offset, m_Count ); }

    // Overload variable array operator with one taking index relative to given register
    IoAAD9824Variable& operator[]( IoAAD9824VariableIndex Index )
    {
        int AbsoluteIndex = ( 0 <= Index && Index < m_VariableCount ) ? m_VariableOffset + Index : eInvalidAD9824Variable;

        return IoAAD9824Variable::SetIndex( AbsoluteIndex, m_Offset, m_Count );
    }

    // Overload bit array operator with one taking (int) argument
    IoAAD9824Bit& operator[]( int Index ) { return operator[]( (IoAAD9824BitEnum) Index ); }

    // Same array operators modified to work with const source objects and returning const reference
    const IoAAD9824Variable& operator[]( IoAAD9824VariableIndex Index ) const { return This( )->operator[]( Index ); }
    const IoAAD9824Variable& operator[]( IoAAD9824VariableEnum  Index ) const { return This( )->operator[]( Index ); }
    const IoAAD9824Byte&     operator[]( IoAAD9824ByteEnum      Index ) const { return This( )->operator[]( Index ); }
    const IoAAD9824Bit&      operator[]( IoAAD9824BitEnum       Index ) const { return This( )->operator[]( Index ); }
    const IoAAD9824Bit&      operator[]( int                    Index ) const { return This( )->operator[]( Index ); }

    // Set and get
    void Set( int Value );
    int Value( ) const { return IoAAD9824Data::GetValue( m_Offset, m_Count ); }

    // Assignment and cast operator to simplify set and get
    IoAAD9824Register& operator=( int Value ) { Set( Value ); return *this; }
    operator int( ) const { return Value( ); }

    // Test for equality
    int IsEqual( const IoAAD9824Register& Other ) const
    {
        return IoAAD9824Data::IsBitFieldEqual( m_Offset, m_Count, Other, Other.m_Offset, Other.m_Count );
    }

    int operator==( const IoAAD9824Register& Other ) const { return   IsEqual( Other ); }
    int operator!=( const IoAAD9824Register& Other ) const { return ! IsEqual( Other ); }

    // Clear data
    int Clear( ) { return IoAAD9824Data::ClearBitField( m_Offset, m_Count ); }

    // Copy data
    int Copy( const IoAAD9824Register& Other )
    {
        return m_Offset == Other.m_Offset && IoAAD9824Data::CopyBitField( m_Offset, m_Count, Other, Other.m_Offset, Other.m_Count );
    }

    // Assignment operator
    IoAAD9824Register& operator=( const IoAAD9824Register& Other ) { Copy( Other ); return *this; }

    // Register type
    int IsOperationRegister( ) const { return Enum( ) == eOperationRegister; }
    int IsControlRegister  ( ) const { return Enum( ) == eControlRegister;   }

    // Register Reset
    void EnableRegisterReset( ) { if ( IsOperationRegister( ) ) ( *this )[ eRegisterReset ] = 1; }

    void DisableRegisterReset( ) { if ( IsOperationRegister( ) ) ( *this )[ eRegisterReset ] = 0; }

    int IsRegisterResetEnabled( ) const { return IsOperationRegister( ) && (int) ( *this )[ eRegisterReset ]; }

protected:
    IoAAD9824Register( IoABitArray& BitArray ) : IoAAD9824Variable( BitArray ), m_Offset( 0 ), m_Count( 0 ) { }

    // Register (located at fixed absolute location in data array)
    IoAAD9824Register& SetIndex( int Index, int Offset, int Count )
    {
        m_Offset = 0;
        m_Count  = 0;
        m_Name   = NULL;

        m_VariableOffset = 0;
        m_VariableCount  = 0;

        if ( 0 <= Index && Index < nAD9824RegisterCount )
        {
            // Offset and count must encompass entire data array
            ASSERT( Offset == 0 && Count > 0 && Count == IoAAD9824Data::Size( ) );

            m_Offset = m_OffsetLUT[ Index ];  // absolute offset
            m_Count  = m_CountLUT[ Index ];
            m_Name   = m_NameLUT[ Index ];

            m_VariableOffset = m_VariableOffsetLUT[ Index ];
            m_VariableCount  = m_VariableCountLUT[ Index ];

            m_Index = Index;
        }

        return *this;
    }

private:
    IoAAD9824Register *This( ) const { return (IoAAD9824Register *) this; }  // helper function to cast-away const

    int m_Offset, m_Count, m_Index;
    const char *m_Name;
    int m_VariableOffset, m_VariableCount;

    // Bit offset for each register
    static const int m_OffsetLUT[ nAD9824RegisterCount ];

    // Numbers of bits in each register
    static const int m_CountLUT[ nAD9824RegisterCount ];

    // String to identify each register
    static const char *m_NameLUT[ nAD9824RegisterCount ];

    // Variable offset for each register
    static const int m_VariableOffsetLUT[ nAD9824RegisterCount ];

    // Numbers of variables in each register
    static const int m_VariableCountLUT[ nAD9824RegisterCount ];
};

class IoAAD9824RegisterArray : public IoAAD9824Register
{
public:
    IoAAD9824RegisterArray( IoABitArray& BitArray ) : IoAAD9824Register( BitArray ) { }

    // Provides valid conversion from any base class object (direct or indirect down to IoAAD9824Data base class)
    // Should also function as copy constructor (be careful and beware: new object only has reference to source object's data)
    IoAAD9824RegisterArray( const IoAAD9824Data& Other ) : IoAAD9824Register( Other.BitArray( ) ) { }

    int IsValid( ) const { return IoAAD9824Data::IsValid( ); }

    int BitCount( ) const { return IoAAD9824Data::Size( ); }

    int ByteCount( ) const { return BitCount( ) / 8; }

    int IsDefined( ) const { return BitCount( ) == nAD9824RegistersBitCount; }

    int RegisterCount( )   const { return IsDefined( ) ? nAD9824RegisterCount : 0; }

    int VariableCount( ) const { return IsDefined( ) ? nAD9824VariableCount : 0; }

    // Array operators for accessing individual registers, variables, bytes and bits
    IoAAD9824Register& operator[]( IoAAD9824RegisterEnum  Index ) { return IoAAD9824Register::SetIndex( Index, 0, BitCount( ) ); }
    IoAAD9824Variable& operator[]( IoAAD9824VariableIndex Index ) { return IoAAD9824Variable::SetIndex( Index, 0, BitCount( ) ); }
    IoAAD9824Variable& operator[]( IoAAD9824VariableEnum  Index ) { return IoAAD9824Variable::SetIndex( Index, 0, BitCount( ) ); }
    IoAAD9824Byte&     operator[]( IoAAD9824ByteEnum      Index ) { return IoAAD9824Byte    ::SetIndex( Index, 0, BitCount( ) ); }
    IoAAD9824Bit&      operator[]( IoAAD9824BitEnum       Index ) { return IoAAD9824Bit     ::SetIndex( Index, 0, BitCount( ) ); }

    // Overload bit array operator with one taking (int) argument
    IoAAD9824Bit& operator[]( int Index ) { return operator[]( (IoAAD9824BitEnum) Index ); }

    // Same array operators modified to work with const source objects and returning const reference
    const IoAAD9824Register& operator[]( IoAAD9824RegisterEnum  Index ) const { return This( )->operator[]( Index ); }
    const IoAAD9824Variable& operator[]( IoAAD9824VariableIndex Index ) const { return This( )->operator[]( Index ); }
    const IoAAD9824Variable& operator[]( IoAAD9824VariableEnum  Index ) const { return This( )->operator[]( Index ); }
    const IoAAD9824Byte&     operator[]( IoAAD9824ByteEnum      Index ) const { return This( )->operator[]( Index ); }
    const IoAAD9824Bit&      operator[]( IoAAD9824BitEnum       Index ) const { return This( )->operator[]( Index ); }
    const IoAAD9824Bit&      operator[]( int                    Index ) const { return This( )->operator[]( Index ); }

    // Test for equality
    int IsEqual( const IoAAD9824RegisterArray& Other ) const { return IoAAD9824Data::IsEqual( Other ); }

    int operator==( const IoAAD9824RegisterArray& Other ) const { return   IsEqual( Other ); }
    int operator!=( const IoAAD9824RegisterArray& Other ) const { return ! IsEqual( Other ); }

    // Clear data
    int Clear( ) { return IoAAD9824Data::Clear( ); }

    // Copy data
    int Copy( const IoAAD9824RegisterArray& Other ) { return IoAAD9824Data::Copy( Other ); }

    // Assignment operator
    IoAAD9824RegisterArray& operator=( const IoAAD9824RegisterArray& Other ) { Copy( Other ); return *this; }

private:
    IoAAD9824RegisterArray *This( ) const { return (IoAAD9824RegisterArray *) this; }  // helper function to cast-away const
};

// Register data storage class
class IoAAD9824Registers : public IoAAD9824RegisterArray
{
public:
    // Default constructor + default register settings
    IoAAD9824Registers( ) : IoAAD9824RegisterArray( m_BitArray )
    {
        // Allocate memory for bit array to hold register bits
        if ( m_BitArray.Allocate( nAD9824RegistersBitCount ) )
        {
            SetDefaults( );
        }
    }

    // Copy constructor
    IoAAD9824Registers( const IoAAD9824Registers& Other ) : IoAAD9824RegisterArray( m_BitArray )
    {
        IoAAD9824Data::Copy( Other );

        SetMandatoryBits( );  // enforces mandatory bit settings
    }

    // Assignment operator
    IoAAD9824Registers& operator=( const IoAAD9824Registers& Other ) { IoAAD9824Data::Copy( Other ); return *this; }

    // Default settings
    void SetDefaults( );

    void SetMandatoryBits( );

    // Operation register = Channel Select, Powerdown Mode, Software Reset, Optical Black Clamp

    // Channel Select
    int SetChannelSelect( IoAAD9824ChannelSelectEnum ChannelSelect )
    {
        if ( ChannelSelect < 0 || ChannelSelect > 3 ) return FALSE;

        ( *this )[ eChannelSelect ] = ChannelSelect;

        return TRUE;
    }

    IoAAD9824ChannelSelectEnum ChannelSelect( ) const { return (IoAAD9824ChannelSelectEnum) ( (int) ( *this )[ eChannelSelect ] ); }

    // Powerdown mode
    int SetPowerDownMode( IoAAD9824PowerDownModeEnum PowerDownMode )
    {
        if ( PowerDownMode < 0 || PowerDownMode > 3 ) return FALSE;

        ( *this )[ ePowerDownMode ] = PowerDownMode;

        return TRUE;
    }

    IoAAD9824PowerDownModeEnum PowerDownMode( ) const { return (IoAAD9824PowerDownModeEnum) ( (int) ( *this )[ ePowerDownMode ] ); }

    // Register Reset
    void EnableRegisterReset( ) { ( *this )[ eRegisterReset ] = 1; }

    void DisableRegisterReset( ) { ( *this )[ eRegisterReset ] = 0; }

    int IsRegisterResetEnabled( ) const { return ( *this )[ eRegisterReset ]; }

    // Optical Black Clamp
    void EnableOpticalBlackClamp ( ) { ( *this )[ eOpticalBlackClampDisable ] = 0; }
    void DisableOpticalBlackClamp( ) { ( *this )[ eOpticalBlackClampDisable ] = 1; }

    int IsOpticalBlackClampEnabled ( ) const { return ( *this )[ eOpticalBlackClampDisable ] == 0; }
    int IsOpticalBlackClampDisabled( ) const { return ( *this )[ eOpticalBlackClampDisable ] == 1; }

    // VGA Gain register

    int SetVGAGain( double dBValue )
    {
        if ( dBValue < 0.0 || dBValue > 36.0 ) return FALSE;

        ( *this )[ eVGAGain ] = (int) ( ( 1023.0 / 36.0 ) * dBValue + 0.5 );

        return TRUE;
    }

    double VGAGain( ) const { return (double) ( (int) ( *this )[ eVGAGain ] ) * ( 36.0 / 1023.0 ); }

    // Clamp Level register

    int SetClampLevel( int ClampLevel )
    {
        if ( ClampLevel < 0 || ClampLevel > 1020 ) return FALSE;

        ( *this )[ eClampLevel ] = ( ClampLevel + 2 ) / 4;

        return TRUE;
    }

    int ClampLevel( ) const { return ( (int) ( *this )[ eClampLevel ] ) * 4; }

    // Control Register = Colour Steering Mode, PxGA Control, Data Clock Polarity, Data Output Control

    // Colour Steering Mode
    int SetColourSteeringMode( IoAAD9824ColourSteeringModeEnum ColourSteeringMode )
    {
        if ( ColourSteeringMode < 0 || ColourSteeringMode > 7 ) return FALSE;

        ( *this )[ eColourSteeringMode ] = ColourSteeringMode;

        return TRUE;
    }

    IoAAD9824ColourSteeringModeEnum ColourSteeringMode( ) const { return (IoAAD9824ColourSteeringModeEnum) ( (int) ( *this )[ eColourSteeringMode ] ); }

    // PxGA Control
    void EnablePxGA ( ) { ( *this )[ ePxGAEnable ] = 1; }
    void DisablePxGA( ) { ( *this )[ ePxGAEnable ] = 0; }

    int IsPxGAEnabled ( ) const { return ( *this )[ ePxGAEnable ] == 1; }
    int IsPxGADisabled( ) const { return ( *this )[ ePxGAEnable ] == 0; }

    // SHP and SHD polarity
    void SetSHPAndSHDActiveHigh( ) { ( *this )[ eSHPAndSHDActiveHigh ] = 1; }
    void SetSHPAndSHDActiveLow ( ) { ( *this )[ eSHPAndSHDActiveHigh ] = 0; }

    int IsSHPAndSHDActiveHigh( ) const { return ( *this )[ eSHPAndSHDActiveHigh ] == 1; }
    int IsSHPAndSHDActiveLow ( ) const { return ( *this )[ eSHPAndSHDActiveHigh ] == 0; }

    // CLP and PBLK polarity
    void SetCLPAndPBLKActiveHigh( ) { ( *this )[ eCLPAndPBLKActiveHigh ] = 1; }
    void SetCLPAndPBLKActiveLow ( ) { ( *this )[ eCLPAndPBLKActiveHigh ] = 0; }

    int IsCLPAndPBLKActiveHigh( ) const { return ( *this )[ eCLPAndPBLKActiveHigh ] == 1; }
    int IsCLPAndPBLKActiveLow ( ) const { return ( *this )[ eCLPAndPBLKActiveHigh ] == 0; }

    // Polarity of data clock trigger
    void SetTriggerOnDataClockRisingEdge ( ) { ( *this )[ eTriggerOnDataClockFallingEdge ] = 0; }
    void SetTriggerOnDataClockFallingEdge( ) { ( *this )[ eTriggerOnDataClockFallingEdge ] = 1; }

    int IsTriggerOnDataClockRisingEdge ( ) const { return ( *this )[ eTriggerOnDataClockFallingEdge ] == 0; }
    int IsTriggerOnDataClockFallingEdge( ) const { return ( *this )[ eTriggerOnDataClockFallingEdge ] == 1; }

    // Data Output Control
    void EnableDataOut ( ) { ( *this )[ eDataOutDisable ] = 0; }
    void DisableDataOut( ) { ( *this )[ eDataOutDisable ] = 1; }

    int IsDataOutEnabled ( ) const { return ( *this )[ eDataOutDisable ] == 0; }
    int IsDataOutDisabled( ) const { return ( *this )[ eDataOutDisable ] == 1; }

    // PxGAGain registers
    int SetPxGAGain( int Index, double dBValue )
    {
        if ( Index < 0 || Index > 3 ) return FALSE;
        if ( dBValue < -2.5 || dBValue > 9.5 ) return FALSE;

        IoAAD9824VariableEnum Variable = (IoAAD9824VariableEnum) ( (int) ePxGAGain0 + Index );

        int Value = (int) ( ( 63.0 / 12.0 ) * ( dBValue + 2.5 ) + 0.5 ) - 32;

        if ( Value < 0 ) Value += 64;

        ( *this )[ Variable ] = Value;

        return TRUE;
    }

    double PxGAGain( int Index ) const
    {
        if ( Index < 0 || Index > 3 ) return 0.0;

        IoAAD9824VariableEnum Variable = (IoAAD9824VariableEnum) ( (int) ePxGAGain0 + Index );

        int Value = ( *this )[ Variable ];

        if ( Value >= 32 ) Value -= 64;

        return ( 12.0 / 63.0 ) * (double) ( Value + 32 ) - 2.5;
    }

private:
    IoAAD9824Registers *This( ) const { return (IoAAD9824Registers *) this; }  // helper function to cast-away const

    // Register data
    IoABitArray m_BitArray;
};

#include "Device.h"

class IoAAD9824 : public IoADevice
{
public:
    IoAAD9824( int Index = 0 ) : IoADevice( "AD9824", Index ) { }
    IoAAD9824( const IoAAD9824Registers& Registers, int Index = 0 ) : IoADevice( "AD9824", Index ),
                                                                      m_Registers( Registers ) { }

//    ~IoAAD9824( ) { Close( ); }  // not required if IoAAD9824::Close( ) is not defined

    const IoAAD9824Registers& Registers( ) const { return m_Registers; }
    operator const IoAAD9824Registers&( ) const { return Registers( ); }

    void Detach( ) { Close( ); IoADevice::Detach( ); }  // have to override IoADevice::Detach( )

    // Open + close
    int Open( int /* Verify */ = FALSE );  // have to override IoADevice::Open( )

//    void Close( int = 0 ) { IoADevice::Close( ) }  // no need to override IoADevice::Close( )

    // Synchronize specific register
    int Synchronize( const IoAAD9824Register& Register );

    // Synchronize everything
    int Synchronize( const IoAAD9824Registers& Registers );

    // Verify specific register
//    int Verify( const IoAAD9824Register& Register );

//    int Verify( const IoAAD9824Register& Register )
//    {
//        return IsOpen( ) && Read( m_Registers[ Register.Enum( ) ] ) && Register == m_Registers[ Register.Enum( ) ];
//    }

    // Verify everything
//    int Verify( const IoAAD9824Registers& Registers );

//    int Verify( const IoAAD9824Registers& Registers )
//    {
//        return IsOpen( ) && Read( m_Registers ) && Registers == m_Registers;
//    }

    int RegisterReset( );

private:
    // Unconditional read/write of register contents
//    int Read( IoAAD9824Register& Register ) const;
    int Write( const IoAAD9824Register& Register ) const;

    // Unconditional read/write of contents of all registers
//    int Read( IoAAD9824Registers& Registers ) const;
    int Write( const IoAAD9824Registers& Registers ) const;

    // Register data
    IoAAD9824Registers m_Registers;
};

#endif
