/// ****************************************************************************
/// @file   
/// @author Dr.Neeton
/// @date   2008/X/X - 2014/01/27
/// 
/// @brief  DirectSound周り
/// 
/// ****************************************************************************
#pragma once

#include "CPL_DX_Sound.h"


namespace CPL {
namespace DX {
namespace Audio {


// _____________________________________________________________________________
/// @brief SoundPlayer実体定義
///
// *****************************************************************************
class baseSoundPlayer::Entity_
  : public Core::SoundPlayer_i_
{
  friend class SoundPlayerEntity_for_Decompressed;
  friend class SoundPlayerEntity_for_Stream;
protected:
  static const int dECOMPRESSED_mAX_sIZE = 64*Ki;

  // 
  CpDXSound                     m_cp_dsound;
  CpDX8SoundBuffer              m_cp_dsound_buf;
  sdptr<Core::BaseSoundManager> m_sp_snd_mng;

  Entity_(){}

public:
  // _____________________________________________________________________________
  /// @brief DirectSoundバッファを取得。
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  CpDX8SoundBuffer GetDSB() const { return m_cp_dsound_buf; }

  // _____________________________________________________________________________
  /// @brief データ書き込み
  /// 
  /// @param[in] in_pos     : 書き込み位置を指定します。
  /// @param[in] in_ptr_buf : 書き込むデータのアドレスを指定します。
  /// @param[in] in_size    : 書き込むサイズを指定します。
  /// 
  /// @return 実際に書き込まれたサイズ。
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  SDEF
    WriteDsBuffer
      ( _in<SDEF     > in_pos
      , _in<void_cptr> in_ptr_buf
      , _in<SDEF     > in_size
      )
  {
    // ロック
    LPVOID ptr_mem1, ptr_mem2;
    DWORD size1, size2;
    HRESULT hr =
      m_cp_dsound_buf->Lock
      ( static_cast<DWORD>(in_pos),
        static_cast<DWORD>(in_size),
        &ptr_mem1, &size1,
        &ptr_mem2, &size2,
        0 );
    if( FAILED(hr) )
    {
      CPL_OUTPUT(SwS("サウンドバッファのロックに失敗"));
      switch( hr )
      {
      case DSERR_BUFFERLOST:      OutputDebugString( SwS("DSERR_BUFFERLOST\n") ); break;
      case DSERR_INVALIDCALL:     OutputDebugString( SwS("DSERR_INVALIDCALL\n") ); break;
      case DSERR_INVALIDPARAM:    OutputDebugString( SwS("DSERR_INVALIDPARAM\n") ); break;
      case DSERR_PRIOLEVELNEEDED: OutputDebugString( SwS("DSERR_PRIOLEVELNEEDED\n") ); break;
      }
      return 0;
    }
    // 書き込み
    ::memcpy( ptr_mem1, in_ptr_buf, size1 );

    //ロック解除
    m_cp_dsound_buf->Unlock( ptr_mem1, size1, ptr_mem2, size2 );

    return size1;// + size2;
  }


  // _____________________________________________________________________________
  /// @brief データ書き込み
  /// 
  /// @param[in] in_pos
  ///   : 書き込み位置を指定します。
  /// @param[in] in_sound_stream
  ///   : 書き込むサウンドストリームオブジェクトを指定します。
  /// @param[in] in_size
  ///   : 書き込むサイズを指定します。
  /// 
  /// @return 実際に書き込まれたサイズ。
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  SDEF
    WriteDsBuffer
      ( _in<SDEF                  > in_pos
      , _in<Core::BaseSoundManager> in_sound_stream
      , _in<SDEF                  > in_size
      )
  {
    // ロック
    LPVOID ptr_mem1, ptr_mem2;
    DWORD size1, size2;
    HRESULT hr =
      m_cp_dsound_buf->Lock
      ( static_cast<DWORD>(in_pos),
        static_cast<DWORD>(in_size),
        &ptr_mem1, &size1,
        &ptr_mem2, &size2, 0 );
    if( FAILED(hr) )
    {
      CPL_THROW("サウンドバッファのロックに失敗");
      switch( hr )
      {
      case DSERR_BUFFERLOST:      OutputDebugString( SwS("DSERR_BUFFERLOST\n") ); break;
      case DSERR_INVALIDCALL:     OutputDebugString( SwS("DSERR_INVALIDCALL\n") ); break;
      case DSERR_INVALIDPARAM:    OutputDebugString( SwS("DSERR_INVALIDPARAM\n") ); break;
      case DSERR_PRIOLEVELNEEDED: OutputDebugString( SwS("DSERR_PRIOLEVELNEEDED\n") ); break;
      }
      return 0;
    }
    // 書き込み
    auto read_size = in_sound_stream.ReadPCM( ptr_mem1, size1 );
    if( ptr_mem2 )
    {
      read_size += in_sound_stream.ReadPCM( ptr_mem2, size2 );
    }

    //ロック解除
    m_cp_dsound_buf->Unlock( ptr_mem1, size1, ptr_mem2, size2 );

    return read_size; // size1;// + size2;
  }

  // _____________________________________________________________________________
  /// @brief 停止
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  void Stop() const
  {
    m_cp_dsound_buf->Stop();
  }

  // _____________________________________________________________________________
  /// @brief 現在のカーソル位置を取得（バイト単位）
  /// 
  /// @return 現在のカーソル位置をバイト単位で返します。
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  size_t GetPos() const
  {
    DWORD play_cursor;
    m_cp_dsound_buf->GetCurrentPosition( &play_cursor, nullptr );
    return play_cursor;
  }

  // _____________________________________________________________________________
  /// @brief ステータスを取得
  /// 
  /// @return
  /// 現在のステータス情報を返します。\n
  /// 詳しくはIDirectSoundBuffer8::GetStatusを検索してください。
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  U32 GetStatus() const
  {
    DWORD f_status;
    m_cp_dsound_buf->GetStatus( &f_status );
    return f_status;
  }

  // _____________________________________________________________________________
  /// @brief 再生中かどうか取得
  /// 
  /// @retval true  : 再生中
  /// @retval false : 停止中
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  bool IsPlaying() const
  {
    return (GetStatus() & DSBSTATUS_PLAYING) != 0;
  }

  // _____________________________________________________________________________
  /// @brief ループ中かどうか取得
  /// 
  /// @retval true  : ループＯＮ
  /// @retval false : ループＯＦＦ
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  bool IsLooping() const
  {
    return (GetStatus() & DSBSTATUS_LOOPING) != 0;
  }

  // _____________________________________________________________________________
  /// @brief 再生。
  /// 
  /// @param[in] in_flag : 0 or DSBPLAY_LOOPING
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  void Play( _in<U32> in_flag ) const
  {
    if( m_cp_dsound_buf )
    {
      m_cp_dsound_buf->Play( 0, -1, in_flag );
    }
  }
};





/// @copydoc baseSoundPlayer::Entity_::GetDSB
CpDX8SoundBuffer baseSoundPlayer::GetDSB() const
{
  return m_sp_entity->GetDSB();
}

///// @copydoc baseSoundPlayer::Entity_::WriteDsBuffer(SDEF,void_cptr,SDEF)
//SDEF
//  baseSoundPlayer::WriteDsBuffer
//    ( SDEF in_pos, void_cptr in_ptr_buffer, SDEF in_size )
//{
//  return m_sp_entity->WriteDsBuffer( in_pos, in_ptr_buffer, in_size );
//}
//
///// @copydoc baseSoundPlayer::Entity_::WriteDsBuffer(SDEF,void*,SDEF)
//SDEF
//  baseSoundPlayer::WriteDsBuffer
//    ( SDEF in_pos, Stream_cref in_stream, SDEF in_size )
//{
//  return m_sp_entity->WriteDsBuffer( in_pos, in_stream, in_size );
//}

/// @copydoc baseSoundPlayer::Entity_::Stop
void baseSoundPlayer::Stop() const
{
  m_sp_entity->Stop();
}

/// @copydoc baseSoundPlayer::Entity_::GetPos
size_t baseSoundPlayer::GetPos() const
{
  return m_sp_entity->GetPos();
}

/// @copydoc baseSoundPlayer::Entity_::GetStatus
U32 baseSoundPlayer::GetStatus() const
{
  return m_sp_entity->GetStatus();
}

/// @copydoc baseSoundPlayer::Entity_::IsPlaying
bool baseSoundPlayer::IsPlaying() const
{
  return m_sp_entity->IsPlaying();
}

/// @copydoc baseSoundPlayer::Entity_::IsLooping
bool baseSoundPlayer::IsLooping() const
{
  return m_sp_entity->IsLooping();
}

/// @copydoc baseSoundPlayer::Entity_::Play
void baseSoundPlayer::Play( _in<U32> in_flag ) const
{
  return m_sp_entity->Play( in_flag );
}


} // namespace Audio
} // namespace DX
} // namespace CPL
