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

namespace Mix{ namespace Graphics{ namespace DX11{

////////////////////////////////////////////////////////////////////////////////////////////////////
// Mix::Graphics::DX11::IndexBuffer
////////////////////////////////////////////////////////////////////////////////////////////////////

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

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

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;

	HRESULT ret = m_pDevice->CreateIndexBuffer( m_Format, m_Capacity, m_bDynamic, pInitialData, &m_pIndexBuffer );
	if( ret != S_OK )
	{
		return ret;
	}

	if( ( m_ResizeStep > 0 ) &&
		( m_bDynamic == True ) )
	{
		m_pBuffer = static_cast<UInt8*>( MIX_LIB_MALLOC( Mix::Memory::SECTION_GRAPHICS, buffSize ) );
		if( m_pBuffer != NULL )
		{
			if( pInitialData != NULL )
			{
				Mix::Memory::Copy( m_pBuffer, pInitialData, buffSize );
			}
			else
			{
				Mix::Memory::Zero( m_pBuffer, buffSize );
			}
		}
		else
		{
			return E_OUTOFMEMORY;
		}
	}

	if( pInitialData != NULL )
	{
		m_Count = m_Capacity;
	}

	return S_OK;
}

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

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

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

	if( m_ResizeStep > 0 )
	{
		m_pSource = &( m_pBuffer[0] );
	}
	else
	{
		if( m_pDevice->Map( m_pIndexBuffer, m_bDynamic, m_Mapped ) == S_OK )
		{
			m_pSource = static_cast<UInt8*>( m_Mapped.pData );
		}
		else
		{
			return False;
		}
	}

	m_Count = 0;
	m_bLock = True;

	return True;
}

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

	if( m_ResizeStep > 0 )
	{
		if( m_bResize == True )
		{
			HRESULT ret;
			ID3D11Buffer* pTempBuffer;

			ret = m_pDevice->CreateIndexBuffer( m_Format, m_Capacity, m_bDynamic, m_pBuffer, &pTempBuffer );
			if( ret == S_OK )
			{
				MIX_RELEASE( m_pIndexBuffer );
				m_pIndexBuffer = pTempBuffer;
#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_pDevice->Map( m_pIndexBuffer, m_bDynamic, m_Mapped ) == S_OK )
		{
			UInt32 copyCount = ( m_Capacity > m_Count )? m_Count : m_Capacity;
			Mix::Memory::Copy( m_Mapped.pData, m_pBuffer, ( copyCount * m_Stride ) );
			m_pDevice->Unmap( m_pIndexBuffer );
		}
	}
	else
	{
		m_pDevice->Unmap( m_pIndexBuffer );
	}

	m_bLock = False;
}

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

	Mix::Memory::Copy( pDst, pData, ( 2 * count ) );

	return True;
}

void* IndexBuffer::Push( UInt32 count )
{
	if( m_bLock == False )
	{
		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;
}

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

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

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

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

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

}}}
