//---------------------------------------------------
//	Provides a simple typeless
//	static fixed array template class
//  The template code is all compile time template generation
//  So in a release build:
//    Asura_FixedArray<int,10> aiArray;
//  is identical to
//	  int aiArray[10];
//  But in debug builds you get an assert if you access outside the array limits
//
// Created: JAT 15/03/06 
//
//---------------------------------------------------

#ifdef ASURA_USE_PRAGMA_ONCE
#pragma once
#endif

#ifndef __ASURA_FIXEDARRAY_H__
#define __ASURA_FIXEDARRAY_H__

#include "Debugging/Asura_Assert.h"

//---------------------------------------------------
//	Asura_FixedArray class


template<class T, u_int uNumber> 
class Asura_FixedArray
{
public:
	Asura_FixedArray( void );
	~Asura_FixedArray( void );
	T& operator [] ( const u_int uIndex );
	const T& operator [] ( const u_int uIndex ) const;
	// (MH - 09.01.2008) added casting operators so you can write e.g. memset(m_axData, 0, m_axData.GetSize())
	operator void*();
	operator const void*() const;
	u_int GetSize( void ) const;
private:
	T m_axData[uNumber];
};

template<class T, u_int uNumber> 
inline Asura_FixedArray<T, uNumber>::Asura_FixedArray( void )
{
}

template<class T, u_int uNumber> 
inline Asura_FixedArray<T, uNumber>::~Asura_FixedArray( void )
{
}

template<class T, u_int uNumber> 
inline T& Asura_FixedArray<T, uNumber>::operator [] ( const u_int uIndex ) 
{ 
	Asura_Assert( ( uIndex < uNumber ), "Asura_FixedArray::[] illegal index" );
#if _DEBUG
	// Assumes instantiated with at least uNumber > 0
	if ( uIndex >= uNumber ) return m_axData[0];
#endif // #if _DEBUG
	return m_axData[uIndex];
}

template<class T, u_int uNumber> 
inline const T& Asura_FixedArray<T, uNumber>::operator [] ( const u_int uIndex )  const
{ 
	Asura_Assert( ( uIndex < uNumber ), "Asura_FixedArray:: const [] illegal index" );
#if _DEBUG
	// Assumes instantiated with at least uNumber > 0
	if ( uIndex >= uNumber ) return m_axData[0];
#endif // #if _DEBUG
	return m_axData[uIndex]; 
}

template<class T, u_int uNumber> 
inline Asura_FixedArray<T, uNumber>::operator void*()
{
	return &m_axData[0];
}

template<class T, u_int uNumber> 
inline Asura_FixedArray<T, uNumber>::operator const void*() const
{
	return &m_axData[0];
}

template<class T, u_int uNumber> 
inline u_int Asura_FixedArray<T, uNumber>::GetSize( void ) const 
{ 
	return uNumber; 
}

//---------------------------------------------------
//	Asura_FixedArrayOfBools class
//
// Support for an arbitrarily sized array if bits use this rather
// than Asura_FixedArray<bool>

template<u_int uNumber> class Asura_FixedArrayBoolHelper;
template<u_int uNumber> class Asura_FixedArrayBoolHelperConst;


// Unfortunately not all platforms support partial specialisation so use this one if you want an array of bits/bools
template<u_int uNumber> class Asura_FixedArrayOfBools
{
	friend class Asura_FixedArrayBoolHelper<uNumber>;
public:

	Asura_FixedArrayOfBools( void ) {};
	~Asura_FixedArrayOfBools( void ) {};

	Asura_FixedArrayBoolHelper<uNumber> operator [] (  u_int uIndex );
	Asura_FixedArrayBoolHelperConst<uNumber> operator [] ( u_int uIndex ) const;

	// (MH - 09.01.2008) added casting operators so you can write e.g. memset(m_axData, 0, m_axData.GetSize())
	operator void*();
	operator const void*() const;
	u_int GetSize( void ) const;
	u_int GetNumberOfBools( void ) const;

	bool Get(u_int uIndex) const;
	void Set(u_int uIndex, bool bValue);

private:
	u_char m_axData[(uNumber+7) / 8];
};

template<u_int uNumber> class Asura_FixedArrayBoolHelper
{
	friend class Asura_FixedArrayOfBools<uNumber>;

	Asura_FixedArrayOfBools<uNumber> &m_xArray;
	const u_int m_uIndex;

	Asura_FixedArrayBoolHelper<uNumber>(Asura_FixedArrayOfBools<uNumber> &xArray, u_int uIndex);
public:

	Asura_FixedArrayBoolHelper<uNumber> &operator=(bool);

	Asura_FixedArrayBoolHelper<uNumber> &operator^=(bool);

	Asura_FixedArrayBoolHelper<uNumber> &operator|=(bool);

	Asura_FixedArrayBoolHelper<uNumber> &operator&=(bool);

	operator bool() const;
	operator Asura_FixedArrayBoolHelperConst<uNumber>() const;
};

template<u_int uNumber> class Asura_FixedArrayBoolHelperConst
{
	friend class Asura_FixedArrayOfBools<uNumber>;

	const Asura_FixedArrayOfBools<uNumber> &m_xArray;
	const u_int m_uIndex;

	Asura_FixedArrayBoolHelperConst<uNumber>(const Asura_FixedArrayOfBools<uNumber> &xArray, u_int uIndex);
public:
	operator bool() const;
};



template<u_int uNumber> 
inline Asura_FixedArrayOfBools<uNumber>::operator void*()
{
	return &m_axData[0];
}

template<u_int uNumber> 
inline Asura_FixedArrayOfBools<uNumber>::operator const void*() const
{
	return &m_axData[0];
}

template<u_int uNumber> 
inline u_int Asura_FixedArrayOfBools<uNumber>::GetSize( void ) const 
{ 
	return (uNumber+7) / 8; 
}

template<u_int uNumber> 
inline u_int Asura_FixedArrayOfBools<uNumber>::GetNumberOfBools( void ) const 
{ 
	return uNumber; 
}

template<u_int uNumber>  
inline bool Asura_FixedArrayOfBools<uNumber>::Get(u_int uIndex) const
{
	Asura_Assert( ( uIndex < GetNumberOfBools() ), "Asura_FixedArrayOfBools::Get illegal index" );
#if _DEBUG
	// Assumes instantiated with at least uNumber > 0
	if ( uIndex >= GetNumberOfBools() ) return false;
#endif // #if _DEBUG
	u_int uOffset = uIndex / 8;
	u_int uBit = 1<<(uIndex & 7);

	return (m_axData[uOffset] & uBit) != 0;
}

template<u_int uNumber>  
inline void Asura_FixedArrayOfBools<uNumber>::Set(u_int uIndex, bool bValue)
{
	Asura_Assert( ( uIndex < GetNumberOfBools() ), "Asura_FixedArrayOfBools::Set illegal index" );
#if _DEBUG
	// Assumes instantiated with at least uNumber > 0
	if ( uIndex >= GetNumberOfBools() ) return;
#endif // #if _DEBUG

	u_int uOffset = uIndex / 8;
	u_int uBitIndex = uIndex & 7;

	m_axData[uOffset] = static_cast<u_char>((m_axData[uOffset] & ~(1u<<uBitIndex)) | (bValue << uBitIndex));
}

template<u_int uNumber>  
inline Asura_FixedArrayBoolHelper<uNumber> Asura_FixedArrayOfBools<uNumber>::operator [] (  u_int uIndex )
{
	Asura_Assert( ( uIndex < GetNumberOfBools() ), "Asura_FixedArrayOfBools::operator [] const illegal index" );
#if _DEBUG
	// Assumes instantiated with at least uNumber > 0
	if ( uIndex >= GetNumberOfBools() ) return Asura_FixedArrayBoolHelper<uNumber>(*this,0);
#endif // #if _DEBUG
	 return Asura_FixedArrayBoolHelper<uNumber>(*this,uIndex);
}

template<u_int uNumber>  
inline Asura_FixedArrayBoolHelperConst<uNumber> Asura_FixedArrayOfBools<uNumber>::operator [] (  u_int uIndex ) const
{
	Asura_Assert( ( uIndex < GetNumberOfBools() ), "Asura_FixedArrayOfBools::operator [] illegal index" );
#if _DEBUG
	// Assumes instantiated with at least uNumber > 0
	if ( uIndex >= GetNumberOfBools() ) return Asura_FixedArrayBoolHelper<uNumber>(*this,0);
#endif // #if _DEBUG
	return Asura_FixedArrayBoolHelper<uNumber>(*this,uIndex);
}


template<u_int uNumber>  
inline Asura_FixedArrayBoolHelper<uNumber>::Asura_FixedArrayBoolHelper(Asura_FixedArrayOfBools<uNumber> &xArray, u_int uIndex) 
: m_xArray(xArray), m_uIndex(uIndex)
{

}

template<u_int uNumber>  
inline Asura_FixedArrayBoolHelper<uNumber> &Asura_FixedArrayBoolHelper<uNumber>::operator=(bool bValue)
{
	m_xArray.Set(m_uIndex, bValue);
	return *this;
}

template<u_int uNumber>  
inline Asura_FixedArrayBoolHelper<uNumber> &Asura_FixedArrayBoolHelper<uNumber>::operator^=(bool bValue)
{
	u_int uOffset = m_uIndex / 8;
	u_int uBitIndex = m_uIndex & 7;

	m_xArray.m_axData[uOffset] ^= (bValue << uBitIndex);
	return *this;
}

template<u_int uNumber>  
inline Asura_FixedArrayBoolHelper<uNumber> &Asura_FixedArrayBoolHelper<uNumber>::operator|=(bool bValue)
{
	u_int uOffset = m_uIndex / 8;
	u_int uBitIndex = m_uIndex & 7;

	m_xArray.m_axData[uOffset] |= (bValue << uBitIndex);
	return *this;
}

template<u_int uNumber>  
inline Asura_FixedArrayBoolHelper<uNumber> &Asura_FixedArrayBoolHelper<uNumber>::operator&=(bool bValue)
{
	u_int uOffset = m_uIndex / 8;
	u_int uBitIndex = m_uIndex & 7;

	m_xArray.m_axData[uOffset] &= (0xff & (bValue << uBitIndex));
	return *this;
}

template<u_int uNumber>  
inline Asura_FixedArrayBoolHelper<uNumber>::operator bool() const
{
	return m_xArray.Get(m_uIndex);
}
template<u_int uNumber>  
inline Asura_FixedArrayBoolHelper<uNumber>::operator Asura_FixedArrayBoolHelperConst<uNumber>() const
{
	return Asura_FixedArrayBoolHelperConst<uNumber>(m_xArray, m_uIndex);

}


template<u_int uNumber>  
inline Asura_FixedArrayBoolHelperConst<uNumber>::operator bool() const
{
	return m_xArray.Get(m_uIndex);
}



#endif // #ifndef __ASURA_FIXEDARRAY_H__
