#include "Mix/Private/Graphics/DX9/IndexBuffer.h"
#include "Mix/Private/Graphics/DX9/Device.h"

namespace Mix{ namespace Graphics{ namespace DX9{

////////////////////////////////////////////////////////////////////////////////////////////////////
// Mix::Graphics::DX9::IndexBuffer
////////////////////////////////////////////////////////////////////////////////////////////////////

IndexBuffer* IndexBuffer::CreateInstance(	Mix::Graphics::DX9::Device* pDevice,
											Mix::Graphics::INDEX_FORMAT format,
											D3DFORMAT d3dFormat,
											UInt32 count,
											Boolean bDynamic,
											UInt32 resizeStep,
											IDirect3DIndexBuffer9* pIndexBuffer )
{
	return MIX_LIB_NEW_T( Mix::Memory::SECTION_GRAPHICS, IndexBuffer, pDevice, format, d3dFormat, count, bDynamic, resizeStep, pIndexBuffer );
}

IndexBuffer::IndexBuffer(	Mix::Graphics::DX9::Device* pDevice,
							Mix::Graphics::INDEX_FORMAT format,
							D3DFORMAT d3dFormat,
							UInt32 count,
							Boolean bDynamic,
							UInt32 resizeStep,
							IDirect3DIndexBuffer9* pIndexBuffer ) :
DeviceObject( pDevice ),
m_pDevice( pDevice ),
m_pIndexBuffer( pIndexBuffer ),
m_pBuffer( NULL ),
m_Format( format ),
m_D3DFormat( d3dFormat ),
m_Stride( ( format == Mix::Graphics::INDEX_USHORT )? 2 : 4 ),
m_Capacity( count ),
m_bDynamic( bDynamic ),
m_ResizeStep( resizeStep ),
m_LockFlags( 0 ),
m_Count( 0 ),
m_pSource( NULL ),
m_bResize( False )
{
	MIX_ADD_REF( m_pDevice );

	m_LockFlags = ( m_bDynamic == True )? D3DLOCK_DISCARD : 0;
}

IndexBuffer::~IndexBuffer( void )
{
	MIX_LIB_FREE( m_pBuffer );
	MIX_RELEASE( m_pIndexBuffer );
	MIX_RELEASE( m_pDevice );
}

HRESULT IndexBuffer::Initialize( const void* pInitialData )
{
	UInt32 buffSize = m_Stride * m_Capacity;

	m_pBuffer = static_cast<UInt8*>( MIX_LIB_MALLOC( Mix::Memory::SECTION_GRAPHICS, buffSize ) );
	if( m_pBuffer == NULL )
	{
		return E_OUTOFMEMORY;
	}

	if( pInitialData != NULL )
	{
		HRESULT ret;
		void* pDst;

		Mix::Memory::Copy( m_pBuffer, pInitialData, buffSize );

		ret = m_pIndexBuffer->Lock( 0, 0, &pDst, m_LockFlags );
		if( ret == D3D_OK )
		{
			Mix::Memory::Copy( pDst, m_pBuffer, buffSize );
			m_pIndexBuffer->Unlock();
		}
		else
		{
			return ret;
		}

		m_Count = m_Capacity;
	}
	else
	{
		Mix::Memory::Zero( m_pBuffer, buffSize );
	}

	return D3D_OK;
}

IDirect3DIndexBuffer9* IndexBuffer::GetInterface( void )
{
	return m_pIndexBuffer;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Mix::Graphics::Common::DeviceObject
////////////////////////////////////////////////////////////////////////////////////////////////////

void IndexBuffer::Invalidate( void )
{
	MIX_RELEASE( m_pIndexBuffer );
}

void IndexBuffer::Validate( void )
{
	if( ( m_pDevice == NULL ) ||
		( m_pIndexBuffer != NULL ) )
	{
		return;
	}

	HRESULT ret;

	//インデックスバッファ作成
	ret = m_pDevice->CreateIndexBuffer( m_D3DFormat, m_Capacity, m_bDynamic, &m_pIndexBuffer );
	if( ret == D3D_OK )
	{
		void* pDst = NULL;

		if( m_pIndexBuffer->Lock( 0, 0, &pDst, m_LockFlags ) == D3D_OK )
		{
			::CopyMemory( pDst, m_pBuffer, ( m_Stride * m_Capacity ) );
			m_pIndexBuffer->Unlock();
		}
	}
	else
	{
		MIX_LOG_ERROR( L"インデックスバッファの復帰に失敗 : Result[%s]", m_pDevice->GetD3DResultText( ret ) );
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Mix::Graphics::IIndexBuffer
////////////////////////////////////////////////////////////////////////////////////////////////////

Boolean IndexBuffer::Lock( void )
{
	if( ( m_bDynamic == False ) ||
		( m_pIndexBuffer == NULL ) )
	{
		return False;
	}

	m_pSource = &( m_pBuffer[0] );
	m_Count = 0;

	return True;
}

void IndexBuffer::Unlock( void )
{
	if( ( m_pIndexBuffer == NULL ) ||
		( m_pSource == NULL ) )
	{
		return;
	}

	void* pDst = NULL;

	if( m_bResize == True )
	{
		HRESULT ret;
		IDirect3DIndexBuffer9* pTempIB = NULL;

		ret = m_pDevice->CreateIndexBuffer( m_D3DFormat, m_Capacity, m_bDynamic, &pTempIB );
		if( ret == D3D_OK )
		{
			MIX_RELEASE( m_pIndexBuffer );
			m_pIndexBuffer = pTempIB;
#ifdef _DEBUG
			MIX_LOG_INFO( L"インデックスバッファのサイズを変更 : %s[%s] : %s[%d]", Mix::STR_DEBUGNAME, GetDebugName(), Mix::STR_CAPACITY, m_Capacity );
#endif //_DEBUG
		}
		else
		{
#ifdef _DEBUG
			MIX_LOG_ERROR( L"インデックスバッファのサイズ変更に失敗 : %s[%s] %s[%s]", Mix::STR_DEBUGNAME, GetDebugName(), Mix::STR_RESULT, m_pDevice->GetD3DResultText( ret ) );
#else //_DEBUG
			MIX_LOG_ERROR( L"インデックスバッファのサイズ変更に失敗 : %s[%s]", Mix::STR_RESULT, m_pDevice->GetD3DResultText( ret ) );
#endif //_DEBUG
		}

		m_bResize = False;
	}

	if( m_pIndexBuffer->Lock( 0, 0, &pDst, m_LockFlags ) == D3D_OK )
	{
		UInt32 copyCount = ( m_Capacity > m_Count )? m_Count : m_Capacity;
		Mix::Memory::Copy( pDst, m_pBuffer, ( m_Stride * copyCount ) );
		m_pIndexBuffer->Unlock();
	}

	m_pSource = NULL;
}

Boolean IndexBuffer::Push( const void* pData, UInt32 count )
{
	void* pDst = Push( count );
	if( pDst == NULL )
	{
		return False;
	}

	::CopyMemory( pDst, pData, ( m_Stride * count ) );

	return True;
}

void* IndexBuffer::Push( UInt32 count )
{
	if( m_pSource == NULL )
	{
		return NULL;
	}

	UInt32 size;
	UInt32 preCur;

	size = ( m_Count + count );

	if( m_ResizeStep > 0 )
	{
		if( size <= m_Capacity )
		{
			preCur = m_Count;
			m_Count += count;
		}
		else
		{
			UInt32 newCount;
			void* pTempBuff;
			
			newCount = ( size / m_ResizeStep );
			if( ( size % m_ResizeStep ) > 0 )
			{
				newCount++;
			}

			newCount *= m_ResizeStep;

			pTempBuff = MIX_LIB_REALLOC( m_pBuffer, ( newCount * 2 ) );
			if( pTempBuff != NULL )
			{
				m_pBuffer = reinterpret_cast<UInt8*>( pTempBuff );

				m_pSource = &( m_pBuffer[0] );
				m_Capacity = newCount;

				preCur = m_Count;
				m_Count += count;

				m_bResize = True;
			}
			else
			{
				return NULL;
			}
		}
	}
	else
	{
		if( size <= m_Capacity )
		{
			preCur = m_Count;
			m_Count += count;
		}
		else
		{
			return NULL;
		}
	}

	return ( m_pSource + ( preCur * m_Stride ) );
}

void IndexBuffer::Clear( void )
{
	m_Count = 0;
}

UInt32 IndexBuffer::GetCount( void ) const
{
	return m_Count;
}

UInt32 IndexBuffer::GetCapacity( void ) const
{
	return m_Capacity;
}

Mix::Graphics::INDEX_FORMAT IndexBuffer::GetFormat( void ) const
{
	return m_Format;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Mix::Graphics::IDeviceObject
////////////////////////////////////////////////////////////////////////////////////////////////////

Mix::Graphics::IDeviceObject::TYPE IndexBuffer::GetType( void ) const
{
	return Mix::Graphics::IDeviceObject::INDEX_BUFFER;
}

}}}
