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

namespace Mix{ namespace Graphics{ namespace DX9{

////////////////////////////////////////////////////////////////////////////////////////////////////
// Mix::Graphics::DX9::VertexBuffer
////////////////////////////////////////////////////////////////////////////////////////////////////

VertexBuffer* VertexBuffer::CreateInstance( Mix::Graphics::DX9::Device* pDevice,
											UInt32 count, UInt32 stride,
											Boolean bDynamic, UInt32 resizeStep,
											IDirect3DVertexBuffer9* pVertexBuffer )
{
	return MIX_LIB_NEW_T( Mix::Memory::SECTION_GRAPHICS, VertexBuffer, pDevice, count, stride, bDynamic, resizeStep, pVertexBuffer );
}

VertexBuffer::VertexBuffer(	Mix::Graphics::DX9::Device* pDevice,
							UInt32 count, UInt32 stride,
							Boolean bDynamic, UInt32 resizeStep,
							IDirect3DVertexBuffer9* pVertexBuffer ) :
DeviceObject( pDevice ),
m_pDevice( pDevice ),
m_pVertexBuffer( pVertexBuffer ),
m_pBuffer( NULL ),
m_Stride( stride ),
m_Capacity( count ),
m_Count( 0 ),
m_bDynamic( bDynamic ),
m_ResizeStep( resizeStep ),
m_LockFlags( 0 ),
m_pSource( NULL ),
m_bResize( False )
{
	MIX_ADD_REF( m_pDevice );

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

VertexBuffer::~VertexBuffer( void )
{
	MIX_LIB_FREE( m_pBuffer );
	MIX_RELEASE( m_pVertexBuffer );
	MIX_RELEASE( m_pDevice );
}

HRESULT VertexBuffer::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_pVertexBuffer->Lock( 0, 0, &pDst, m_LockFlags );
		if( ret == D3D_OK )
		{
			Mix::Memory::Copy( pDst, m_pBuffer, buffSize );
			m_pVertexBuffer->Unlock();
		}
		else
		{
			return ret;
		}

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

	return D3D_OK;
}

IDirect3DVertexBuffer9* VertexBuffer::GetInterface( void )
{
	return m_pVertexBuffer;
}

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

void VertexBuffer::Invalidate( void )
{
	MIX_RELEASE( m_pVertexBuffer );
}

void VertexBuffer::Validate( void )
{
	if( ( m_pDevice == NULL ) ||
		( m_pVertexBuffer != NULL ) )
	{
		return;
	}

	HRESULT ret;

	//頂点バッファ作成
	ret = m_pDevice->CreateVertexBuffer( m_Capacity, m_Stride, m_bDynamic, &m_pVertexBuffer );
	if( ret == D3D_OK )
	{
		void* pDst = NULL;

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

////////////////////////////////////////////////////////////////////////////////////////////////////
// Mix::Graphics::IVertexBuffer
////////////////////////////////////////////////////////////////////////////////////////////////////

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

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

	return True;
}

void VertexBuffer::Unlock( void )
{
	if( ( m_pVertexBuffer == NULL ) ||
		( m_pSource == NULL ) )
	{
		return;
	}

	void* pDst = NULL;

	if( m_bResize == True )
	{
		//頂点バッファのリサイズ

		IDirect3DVertexBuffer9* pTempVB = NULL;
		HRESULT hRet;

		hRet = m_pDevice->CreateVertexBuffer( m_Capacity, m_Stride, m_bDynamic, &pTempVB );
		if( hRet == D3D_OK )
		{
			MIX_RELEASE( m_pVertexBuffer );
			m_pVertexBuffer = pTempVB;
#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( hRet ) );
#else //_DEBUG
			MIX_LOG_ERROR( L"バーテックスバッファのサイズの変更に失敗 : %s[%s]", Mix::STR_RESULT, m_pDevice->GetD3DResultText( hRet ) );
#endif //_DEBUG
		}

		m_bResize = False;
	}

	//コピー
	if( m_pVertexBuffer->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_pVertexBuffer->Unlock();
	}

	m_pSource = NULL;
}

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

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

	return True;
}

void* VertexBuffer::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 * m_Stride ) );
			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 + ( m_Stride * preCur ) );
}

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

UInt32 VertexBuffer::GetStride( void ) const
{
	return m_Stride;
}

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

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

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

Mix::Graphics::IDeviceObject::TYPE VertexBuffer::GetType( void ) const
{
	return Mix::Graphics::IDeviceObject::VERTEX_BUFFER;
}

}}}
