//-----------------------------------------------------------------------------------------------
// Grid2D.hpp
//
#pragma once
#ifndef INCLUDED_Grid2D
#define INCLUDED_Grid2D

//-----------------------------------------------------------------------------------------------
#include "Engine/Core/EngineBase.hpp"


/////////////////////////////////////////////////////////////////////////////////////////////////
// PowerOfTwoGrid2D
//
// Instance this class template for whatever game-specific tile class/struct/type you want,
//	packed tightly by value, in a fixed-capacity sizeX-by-sizeY grid.
//
// Note: sizeX and sizeY can be different (e.g. 32x8), but they must both be powers of two.
//	This is because the template uses bit-shifting and bit-masking tricks to do stupid-fast
//	tileIndex-to-tileCoords conversions, et al.
//
// If your game wishes to use a non-power-of-two-sized grid, you can still use this template,
//	but merely choose not to use all of the allocated physical space (which you should round
//	up to the nearest power of two).
//
template< typename T_TileType, int sizeX, int sizeY >
class PowerOfTwoGrid2D
{
private:
public:
	PowerOfTwoGrid2D();
	PowerOfTwoGrid2D( const PowerOfTwoGrid2D& copy );
	PowerOfTwoGrid2D& operator = ( const PowerOfTwoGrid2D& copy );
	int GetTileIndexForTileCoords( const IntVector2& tileCoords ) const;
	int GetTileIndexForTileCoords( int tileX, int tileY ) const;
	const IntVector2 GetTileCoordsForIndex( int tileIndex ) const;
	void GetTileCoordsForIndex( int& out_tileX, int& out_tileY, int tileIndex ) const;
	void GetTileCoordsForIndex( IntVector2& out_tileCoords, int tileIndex ) const;
	T_TileType& GetTileAtIndex( int tileIndex );
	const T_TileType& GetTileAtIndex( int tileIndex ) const;
	T_TileType& GetTileAtCoords( const IntVector2& tileCoords );
	const T_TileType& GetTileAtCoords( const IntVector2& tileCoords ) const;
	T_TileType& GetTileAtCoords( int tileX, int tileY );
	const T_TileType& GetTileAtCoords( int tileX, int tileY ) const;

public:
	const IntVector2	m_size;						// e.g. 32,8 (32 x 8 tiles in grid)
	const IntVector2	m_sizeBits;					// e.g. 5,3 (for 2^5 x 2^3 tiles in grid)
	const IntVector2	m_componentBitMasks;		// e.g. 00000000000000000000000000011111 (x is bottom 5 bits, or 31), 00000000000000000000000011100000 (y is next 3 bits, or 224)
	const int			m_numTiles;					// e.g. 256 (32 x 8 tiles total)
	T_TileType			m_tiles[ sizeX * sizeY ];
};


//-----------------------------------------------------------------------------------------------
template< typename T_TileType, int sizeX, int sizeY >
PowerOfTwoGrid2D< T_TileType, sizeX, sizeY >::PowerOfTwoGrid2D()
	: m_size( sizeX, sizeY )
	, m_numTiles( sizeX * sizeY )
	, m_sizeBits( CalcLogBase2( sizeX ), CalcLogBase2( sizeY ) )
	, m_componentBitMasks( sizeX - 1, (sizeY - 1) << m_sizeBits.x )
{
	FATAL_GUARANTEE( IsPowerOfTwo( sizeX ), Stringf( "ERROR: PowerOfTwoGrid2D declared with non-power-of-two width %i!", sizeX ) );
	FATAL_GUARANTEE( IsPowerOfTwo( sizeY ), Stringf( "ERROR: PowerOfTwoGrid2D declared with non-power-of-two height %i!", sizeY ) );
}


//-----------------------------------------------------------------------------------------------
template< typename T_TileType, int sizeX, int sizeY >
PowerOfTwoGrid2D< T_TileType, sizeX, sizeY >::PowerOfTwoGrid2D( const PowerOfTwoGrid2D& copy )
	: m_size( copy.m_size )
	, m_numTiles( copy.m_numTiles )
	, m_sizeBits( 42, 42 ) // copy.m_sizeBits )
	, m_componentBitMasks( copy.m_componentBitMasks )
{
	// TODO: is there a better way to invoke copy constructors en masse?  (We'd like to simple memcpy but need to defer deep-vs-shallow to implementor...)
	for( int tileIndex = 0; tileIndex < m_numTiles; ++ tileIndex )
	{
		m_tiles[ tileIndex ] = copy.m_tiles[ tileIndex ];
	}
}


//-----------------------------------------------------------------------------------------------
template< typename T_TileType, int sizeX, int sizeY >
PowerOfTwoGrid2D< T_TileType, sizeX, sizeY >& PowerOfTwoGrid2D< T_TileType, sizeX, sizeY >::operator = ( const PowerOfTwoGrid2D& copy )
{
	// TODO: is there a better way to invoke copy constructors en masse?  (We'd like to simple memcpy but need to defer deep-vs-shallow to implementor...)
	for( int tileIndex = 0; tileIndex < m_numTiles; ++ tileIndex )
	{
		m_tiles[ tileIndex ] = copy.m_tiles[ tileIndex ];
	}
	return *this;
}


//-----------------------------------------------------------------------------------------------
template< typename T_TileType, int sizeX, int sizeY >
int PowerOfTwoGrid2D< T_TileType, sizeX, sizeY >::GetTileIndexForTileCoords( const IntVector2& tileCoords ) const
{
	return tileCoords.x | (tileCoords.y << m_sizeBits.y);
}


//-----------------------------------------------------------------------------------------------
template< typename T_TileType, int sizeX, int sizeY >
int PowerOfTwoGrid2D< T_TileType, sizeX, sizeY >::GetTileIndexForTileCoords( int tileX, int tileY ) const
{
	return tileX | (tileY << m_sizeBits.y);
}


//-----------------------------------------------------------------------------------------------
template< typename T_TileType, int sizeX, int sizeY >
const IntVector2 PowerOfTwoGrid2D< T_TileType, sizeX, sizeY >::GetTileCoordsForIndex( int tileIndex ) const
{
	return IntVector2( tileIndex & m_componentBitMasks.x, (tileIndex & m_componentBitMasks.y) >> m_sizeBits.x );
}


//-----------------------------------------------------------------------------------------------
template< typename T_TileType, int sizeX, int sizeY >
void PowerOfTwoGrid2D< T_TileType, sizeX, sizeY >::GetTileCoordsForIndex( IntVector2& out_tileCoords, int tileIndex ) const
{
	out_tileCoords.x = tileIndex & m_componentBitMasks.x;
	out_tileCoords.y = (tileIndex & m_componentBitMasks.y) >> m_sizeBits.x;
}


//-----------------------------------------------------------------------------------------------
template< typename T_TileType, int sizeX, int sizeY >
void PowerOfTwoGrid2D< T_TileType, sizeX, sizeY >::GetTileCoordsForIndex( int& out_tileX, int& out_tileY, int tileIndex ) const
{
	out_tileX = tileIndex & m_componentBitMasks.x;
	out_tileY = (tileIndex & m_componentBitMasks.y) >> m_sizeBits.x;
}


//-----------------------------------------------------------------------------------------------
template< typename T_TileType, int sizeX, int sizeY >
T_TileType& PowerOfTwoGrid2D< T_TileType, sizeX, sizeY >::GetTileAtIndex( int tileIndex )
{
	return m_tiles[ tileIndex ];
}


//-----------------------------------------------------------------------------------------------
template< typename T_TileType, int sizeX, int sizeY >
const T_TileType& PowerOfTwoGrid2D< T_TileType, sizeX, sizeY >::GetTileAtIndex( int tileIndex ) const
{
	return m_tiles[ tileIndex ];
}


//-----------------------------------------------------------------------------------------------
template< typename T_TileType, int sizeX, int sizeY >
T_TileType& PowerOfTwoGrid2D< T_TileType, sizeX, sizeY >::GetTileAtCoords( const IntVector2& tileCoords )
{
	int tileIndex = GetTileIndexForTileCoords( tileCoords );
	return m_tiles[ tileIndex ];
}


//-----------------------------------------------------------------------------------------------
template< typename T_TileType, int sizeX, int sizeY >
const T_TileType& PowerOfTwoGrid2D< T_TileType, sizeX, sizeY >::GetTileAtCoords( const IntVector2& tileCoords ) const
{
	int tileIndex = GetTileIndexForTileCoords( tileCoords );
	return m_tiles[ tileIndex ];
}


//-----------------------------------------------------------------------------------------------
template< typename T_TileType, int sizeX, int sizeY >
T_TileType& PowerOfTwoGrid2D< T_TileType, sizeX, sizeY >::GetTileAtCoords( int tileX, int tileY )
{
	int tileIndex = GetTileIndexForTileCoords( tileX, tileY );
	return m_tiles[ tileIndex ];
}


//-----------------------------------------------------------------------------------------------
template< typename T_TileType, int sizeX, int sizeY >
const T_TileType& PowerOfTwoGrid2D< T_TileType, sizeX, sizeY >::GetTileAtCoords( int tileX, int tileY ) const
{
	int tileIndex = GetTileIndexForTileCoords( tileX, tileY );
	return m_tiles[ tileIndex ];
}



#endif // INCLUDED_Grid2D
