/// ****************************************************************************
/// @file   
/// @author Dr.Neeton
/// @date   2008/X/X - 2015/11/11
/// 
/// @brief  WAVE関連。
/// 
/// ****************************************************************************



#include "CPL_Core_WAVE.h"


namespace CPL {
namespace Core {


// _____________________________________________________________________________
/// @brief コンストラクタ（WAVEファイルパス指定バージョン）。
/// 
/// @param[in] in_path : WAVEファイルのパス。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
WaveInfo::WaveInfo( str_cptr in_path )
{
  try {
    // ファイルオープン
    Core::FileStream
      fs( in_path, Core::FileStream::kREAD_BINARY );

    Set( &fs );
  }
  catch(...)
  {
    CPL_THROW("WAVEファイル読み込みに失敗" );
    return;
  }
}

// _____________________________________________________________________________
/// @brief コンストラクタ（WAVEデータアドレス指定バージョン）。
/// 
/// @param[in] in_wave_data_addr : WAVEデータが格納されたメモリアドレス。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
WaveInfo::WaveInfo( void_cptr in_wave_data_addr )
{
  Set( in_wave_data_addr );
}

// _____________________________________________________________________________
/// @brief コンストラクタ（ストリーム指定バージョン）。
/// 
/// @param[in] in_stream : ストリームクラスへの参照。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
WaveInfo::WaveInfo( _in<Stream_i_> in_stream )
{
  Set( in_stream );
}

// _____________________________________________________________________________
/// コピーコンストラクタ。
/// 
/// @param[in] in_wave_info : WaveInfo構造体への参照。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
WaveInfo::WaveInfo( cref<WaveInfo> in_wave_info )
{
  m_head = in_wave_info.m_head;
  m_wfex = in_wave_info.m_wfex;
  if( in_wave_info.m_ptr_chunk_list )
  {
    m_ptr_chunk_list = CPL_NEW(ChunkList)( *in_wave_info.m_ptr_chunk_list );
  }
  else
  {
    m_ptr_chunk_list = nullptr;
  }
}


// _____________________________________________________________________________
/// @brief デストラクタ。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
WaveInfo::~WaveInfo()
{
  zzf_release();
}


// _____________________________________________________________________________
/// @brief 指定したアドレスからWAVEデータをセットアップする。
/// 
/// @param[in] in_wave_data_addr : WAVEデータを指すメモリアドレス。
/// 
/// @retval true  : 成功。
/// @retval false : 失敗。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool WaveInfo::Set( void_cptr in_wave_data_addr )
{
  zzf_release();

  char* buf = (char*)in_wave_data_addr;
  int pos = 0;
  // ヘッダ読み込み
  ::memcpy( &m_head, buf, sizeof(m_head) );
  int size = m_head.size;
  pos += sizeof(m_head);

  ChunkList** pp_chunk = &m_ptr_chunk_list;
  // チャンク分まわす
  while( pos < size )
  {
    // チャンク作成
    if( *pp_chunk == nullptr )
    {
      // 初期チャンク。
      *pp_chunk = CPL_NEW(ChunkList);
    }
    else
    {
      (*pp_chunk)->m_ptr_next_chunk = CPL_NEW(ChunkList);
      pp_chunk = &(*pp_chunk)->m_ptr_next_chunk;
    }
    // チャンク読み込み
    ::memcpy( &(*pp_chunk)->m_chunk, buf + pos, sizeof(ChunkInfo) );
    pos += sizeof(ChunkInfo);
    // オフセット設定
    (*pp_chunk)->m_chunk.offset = pos;
    // 次のチャンクへ位置へ
    pos += (*pp_chunk)->m_chunk.size;
  }

  // フォーマット読み込み
  ChunkOffs_cptr ptr_chunk_fmt = FindChunk( "fmt " );
  if( ptr_chunk_fmt )
  {
    memcpy
    ( &m_wfex,
      (char*)in_wave_data_addr + ptr_chunk_fmt->offset,
      sizeof(WAVEFORMATEX) );
  }

  return true;
}


// _____________________________________________________________________________
/// @brief 指定したストリームからWAVEデータをセットアップする。
/// 
/// @param[in] in_stream : WAVEデータを格納したストリームクラスへの参照。
/// 
/// @retval true  : 成功。
/// @retval false : 失敗。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool WaveInfo::Set( _in<Stream_i_> in_stream )
{
  zzf_release();

  SDEF //pos0,
       pos1,
       pos2,
       pos3;
  SDEF pos = 0;
  //pos0 = in_stream.GetPos();
  // ヘッダ読み込み
  in_stream.Read( &m_head, sizeof(m_head) );
  int size = m_head.size;
  pos += sizeof(m_head);

  pos1 = static_cast<SDEF>(in_stream.GetPos());

  ChunkList** pp_chunk = &m_ptr_chunk_list;
  // チャンク分まわす
  while( pos < size )
  {
    // チャンク作成
    if( *pp_chunk == nullptr )
    {
      // 初期チャンク。
      *pp_chunk = CPL_NEW(ChunkList);
    }
    else
    {
      (*pp_chunk)->m_ptr_next_chunk = CPL_NEW(ChunkList);
      pp_chunk = &(*pp_chunk)->m_ptr_next_chunk;
    }
    // チャンク読み込み
    in_stream.Read( (void*)&(*pp_chunk)->m_chunk, (long)sizeof(ChunkInfo) );
    pos2 = static_cast<SDEF>(in_stream.GetPos());
    pos += sizeof(ChunkInfo);
    // オフセット設定
    (*pp_chunk)->m_chunk.offset = static_cast<int>(pos);
    // 次のチャンクへ
    pos += (*pp_chunk)->m_chunk.size;
    in_stream.SeekCur( (*pp_chunk)->m_chunk.size );
    pos3 = static_cast<SDEF>(in_stream.GetPos());
  }
  
  // フォーマット読み込み
  WaveInfo::ChunkOffs const* ptr_chunk_fmt = FindChunk( "fmt " );
  if( ptr_chunk_fmt )
  {
    in_stream.SeekSet( ptr_chunk_fmt->offset );
    in_stream.Read( &m_wfex, sizeof(m_wfex) );
  }

  return true;
}


// _____________________________________________________________________________
/// @brief チャンクの検索。
/// 
/// @param[in] in_name : チャンク名。
/// 
/// @return 
/// ヒットした場合、そのチャンクのポインタを返す。\n
/// ヒットしなかった場合、nullptrを返す。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
const WaveInfo::ChunkOffs* WaveInfo::FindChunk( char_cptr in_name ) const
{
  ChunkList* ptr_chunk = m_ptr_chunk_list;
  while( ptr_chunk )
  {
    if( ::strncmp( ptr_chunk->m_chunk.tag, in_name, 4 ) == 0 )
    {
      return &ptr_chunk->m_chunk;
    }
    ptr_chunk = ptr_chunk->m_ptr_next_chunk;
  }

  return nullptr;
}


// _____________________________________________________________________________
/// @brief コピー
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
WaveInfo& WaveInfo::operator =( _in<WaveInfo> in_wave_info )
{
  m_head           = in_wave_info.m_head;
  m_wfex           = in_wave_info.m_wfex; // なぜかこれが無かったのだが、意図的か？とりあえず追加。（2014/02/04）
  m_ptr_chunk_list = CPL_NEW(ChunkList)( *in_wave_info.m_ptr_chunk_list );
  return *this;
}



} // namespace Core
} // namespace CPL
