#include "VertexBuffer.h"

#include <vector>

#include <d3d8.h>
#include <d3dx8.h>

template < typename P > //- P wie Pointer
void SafeRelease( P& p )
{
  if ( p )
  {
    p->Release();
    p = 0; //- oder NULL
  }
}




CVertexBuffer::~CVertexBuffer()
{
  if ( m_pData )
  {
    delete[] m_pData;
    m_pData = NULL;
  }
  tVectVertexBuffers::iterator    it( m_vectVertexBuffers.begin() );
  while ( it != m_vectVertexBuffers.end() )
  {
    SafeRelease( *it );

    ++it;
  }
  m_vectVertexBuffers.clear();
}



void CVertexBuffer::Invalidate()
{
  tVectVertexBuffers::iterator    it( m_vectVertexBuffers.begin() );
  while ( it != m_vectVertexBuffers.end() )
  {
    SafeRelease( *it );

    ++it;
  }
  m_vectVertexBuffers.clear();
}



bool CVertexBuffer::CreateBuffers( LPDIRECT3DDEVICE8 pd3dDevice )
{
  if ( pd3dDevice == NULL )
  {
    return false;
  }
  D3DCAPS8    d3dCaps;
  
  if ( FAILED( pd3dDevice->GetDeviceCaps( &d3dCaps ) ) )
  {
    return false;
  }

  m_dwPrimitivePartCount = d3dCaps.MaxPrimitiveCount;

  DWORD   dwPrimitives = m_dwPrimitiveCount;

  while ( dwPrimitives )
  {
    DWORD     dwPrimitivesThisBuffer = dwPrimitives;

    if ( dwPrimitivesThisBuffer > m_dwPrimitivePartCount )
    {
      dwPrimitivesThisBuffer = m_dwPrimitivePartCount;
    }

    IDirect3DVertexBuffer8*   pVertexBuffer = NULL;

    HRESULT hr = pd3dDevice->CreateVertexBuffer( dwPrimitivesThisBuffer * m_dwPrimitiveSize * 3, 
                                                 m_dwUsage, 
                                                 m_dwFVF, 
                                                 m_d3dPool, 
                                                 &pVertexBuffer );

    if ( hr != D3D_OK )
    {
      //dh::Log( "CD3DViewer::CreateVertexBuffer: CreateVertexBuffer failed" );
      OutputDebugString( "uhoh" );
      Invalidate();
      return false;
    }

    m_vectVertexBuffers.push_back( pVertexBuffer );

    dwPrimitives -= dwPrimitivesThisBuffer;
  }
  return true;
}



BOOL CVertexBuffer::Restore( LPDIRECT3DDEVICE8 pd3dDevice )
{
  if ( !pd3dDevice )
  {
    return FALSE;
  }

  IDirect3DVertexBuffer8  *pVertexBuffer = NULL;

  if ( !m_vectVertexBuffers.empty() )
  {
    tVectVertexBuffers::iterator    it( m_vectVertexBuffers.begin() );
    while ( it != m_vectVertexBuffers.end() )
    {
      SafeRelease( *it );

      ++it;
    }
    m_vectVertexBuffers.clear();
  }

  if ( !CreateBuffers( pd3dDevice ) )
  {
    return FALSE;
  }

  UpdateData();

  return TRUE;
}



void CVertexBuffer::UpdateData()
{
  BYTE*   pData = (BYTE*)m_pData;

  DWORD   dwPrimitivesLeft = m_dwPrimitiveCount;

  tVectVertexBuffers::iterator    it( m_vectVertexBuffers.begin() );
  while ( it != m_vectVertexBuffers.end() )
  {
    IDirect3DVertexBuffer8*   pVB = *it;

    DWORD   dwPrimitivesToCopy = dwPrimitivesLeft;
    if ( dwPrimitivesToCopy > m_dwPrimitivePartCount )
    {
      dwPrimitivesToCopy = m_dwPrimitivePartCount;
    }

    BYTE*   pPos;

    if ( FAILED( pVB->Lock( 0, 0, &pPos, 0 ) ) )
    {
      OutputDebugString( "uhoh lock" );
    }
    else
    {
      memcpy( pPos, pData, dwPrimitivesToCopy * m_dwPrimitiveSize * 3 );
      pVB->Unlock();
    }

    pData += dwPrimitivesToCopy * m_dwPrimitiveSize * 3;

    dwPrimitivesLeft -= dwPrimitivesToCopy;

    ++it;
  }
}



bool CVertexBuffer::Display( LPDIRECT3DDEVICE8 pd3dDevice, DWORD dwIndex, DWORD dwCount )
{
  if ( pd3dDevice == NULL )
  {
    return false;
  }

  pd3dDevice->SetVertexShader( m_dwFVF );

  DWORD     dwPrimitives = m_dwPrimitiveCount,
            dwPrimCopied = 0;

  if ( dwCount == 0 )
  {
    dwCount = m_dwPrimitiveCount;
  }

  tVectVertexBuffers::iterator    it( m_vectVertexBuffers.begin() );
  while ( it != m_vectVertexBuffers.end() )
  {
    IDirect3DVertexBuffer8*   iVB = *it;

    DWORD   dwPrimitivesToCopy = dwPrimitives;
    if ( dwPrimitivesToCopy > m_dwPrimitivePartCount )
    {
      dwPrimitivesToCopy = m_dwPrimitivePartCount;
    }

    if ( ( dwIndex >= 3 * dwPrimCopied )
    &&   ( dwIndex < 3 * ( dwPrimCopied + dwPrimitivesToCopy ) ) )
    {
      DWORD   dwCopyNow = dwCount;
      if ( dwCopyNow > dwPrimitivesToCopy )
      {
        dwCopyNow = dwPrimitivesToCopy;
      }
      if ( FAILED( pd3dDevice->SetStreamSource( 0, iVB, m_dwPrimitiveSize ) ) )
      {
        OutputDebugString( "uhoh sss" );
      }

      if ( FAILED( pd3dDevice->DrawPrimitive( m_d3dPrimType, dwIndex - dwPrimCopied, dwCopyNow ) ) )
      {
        OutputDebugString( "uhoh dp" );
      }

      dwCount -= dwCopyNow;
      dwPrimCopied += dwCopyNow;
    }

    if ( dwCount == 0 )
    {
      break;
    }

    ++it;
  }
  return true;
}



bool CVertexBuffer::SetData( void* pData )
{
  memcpy( m_pData, pData, m_dwCompleteBufferSize );

  UpdateData();
  return true;
}



