/// ****************************************************************************
/// @file   
/// @author Dr.Neeton
/// @date   2014/02/25 - 2014/02/25
/// 
/// @brief  OggVorbisデコーダー。
/// 
/// ※ 2014/02/25 CPL_Core_OvBase.cppより分離。
/// 
/// ****************************************************************************

#include "CPL_Core_Ogg.h"

#ifdef CPL_USE_OGG_VORBIS

#include <vorbis/vorbisfile.h>
#include <vorbis/vorbisenc.h>

namespace CPL {
namespace Core {



// _____________________________________________________________________________
/// @brief デコード用の基本関数。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int
  OvDecodePCM
    ( void_ptr&            io_buffer
    , cptr<OggVorbis_File> in_ov_file
    , size_t               in_req_size
    )
{
  char tmp_buffer[ 4096 ];
  int  bitstream = 0;
  size_t count   = 0;
  U8_ptr write_addr = static_cast<U8_ptr>(io_buffer);
  while( true )
  {
    size_t req_size = in_req_size ? in_req_size - count : 0;
    if( (req_size == 0)  ||  (req_size > sizeof(tmp_buffer)) )
    {
      req_size = sizeof(tmp_buffer);
    }
    auto read_size =
      ov_read
      ( const_cast<OggVorbis_File*>(in_ov_file),
        tmp_buffer,
        static_cast<int>(req_size),
        0,           // リトルエンディアン
        2,           // WORDは16bit
        1,           // 符号有り
        &bitstream );

    if( read_size == 0 )
    {
      io_buffer = write_addr;
      return static_cast<int>(count);
    }

    count += read_size;

    // バッファにコピー。
    ::memcpy( write_addr, tmp_buffer, read_size );

    write_addr += read_size;

    if( count >= in_req_size )
    {
      io_buffer = write_addr;
      return static_cast<int>(count);
    }
  }
}

// _____________________________________________________________________________
/// @brief デコード用の基本関数。
/// 
/// @param[out] out_stream
///   : デコードしたデータを格納するバッファへの参照。\n
///   : 末尾に追記されていく。
/// @param[in]  in_req_size
///   : 要求する変換後のサイズ。\n
///   : 0を指定すると、全てのデータの変換を試みる。
/// 
/// @return 読み込まれたバイト数。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int
  OvDecodePCM
    ( Buffer_i&           out_stream
    , cptr<OggVorbis_File> in_ov_file
    , size_t               in_req_size
    )
{
  char tmp_buffer[ 4096 ];
  int  bitstream = 0;
  size_t count = 0;
  size_t write_pos = static_cast<size_t>(out_stream.GetSize());
  out_stream.Reserve( write_pos + in_req_size );
  while( true )
  {
    size_t req_size = in_req_size ? in_req_size - count : 0;
    if( (req_size == 0)  ||  (req_size > sizeof(tmp_buffer)) )
    {
      req_size = sizeof(tmp_buffer);
    }
    auto read_size =
      ov_read
      ( const_cast<OggVorbis_File*>(in_ov_file),
        tmp_buffer,
        static_cast<int>(req_size),
        0,           // リトルエンディアン
        2,           // WORDは16bit
        1,           // 符号有り
        &bitstream );

    if( read_size == 0 ) // 終端に達した。
    {
      return static_cast<int>(count);
    }
    else
    if( read_size < 0 ) // エラー。
    {
      return static_cast<int>(read_size);
    }

    count += read_size;

    // ストリームに追記。
    out_stream.Insert( write_pos, tmp_buffer, read_size );

    if( count >= in_req_size )
    {
      return static_cast<int>(count);
    }
  }
}


// _____________________________________________________________________________
/// @brief Wave形式へのデコード。
///
/// @param[out] out_wave_buffer
///   : デコードしたデータを格納するバッファへの参照。\n
///   : 末尾に追記されていく。
/// @param[in] in_ov_buffer
///   : OvManagerクラスへの参照。\n
///   : 事前にSetupSoundData()を呼んでおくこと。
/// 
/// @retval 0  : 成功。
/// @retval !0 : 失敗。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int OvDecode( rout<Buffer_i> out_wave_buffer, _in<OvManager> in_ov_mng )
{
  //***** WAVE用ヘッダの作成 *****//
  out_wave_buffer.Release();
  // ※ ↓渡されるクラスがストリームとは限らないので、バッファクラスとして頑張って処理。
  size_t pos  = 0;
  U32    size = 0;
  out_wave_buffer.Insert( pos, "RIFF", 4 ); pos += 4; // "RIFF"
  out_wave_buffer.Insert( pos, &size,  4 ); pos += 4; // サイズ（あとから設定しなおす。）
  out_wave_buffer.Insert( pos, "WAVE", 4 ); pos += 4; // "WAVE"
  out_wave_buffer.Insert( pos, "fmt ", 4 ); pos += 4; // "fmt "
  out_wave_buffer.Insert( pos, in_ov_mng.GetWaveFormatEx(), 16 ); pos += 16;
  out_wave_buffer.Insert( pos, "data", 4 ); pos += 4; // "data"

  auto res =
    OvDecodePCM
      ( out_wave_buffer
      , (OggVorbis_File*)in_ov_mng.GetOggVorbisFile()
      , static_cast<size_t>(in_ov_mng.GetSize())
      );

  // サイズ補正。
  U32* ptr_size = out_wave_buffer.GetDataAddrT<U32*>( 1 );
  *ptr_size = (U32)out_wave_buffer.GetSize() - 8;

  return 0;
}


// _____________________________________________________________________________
/// @brief Wave形式へのデコード。
///
/// @param[out] out_wave_buffer
///   : デコードしたデータを格納するバッファへの参照。\n
///   : 末尾に追記されていく。
/// @param[in] in_ov_buffer
///   : OggVorbisデータを指すアドレス。
/// @param[in]
///   : OggVorbisデータのサイズ（バイト単位）。
/// 
/// @retval 0  : 成功。
/// @retval !0 : 失敗。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int
  OvDecode
    ( ref<Buffer_i> out_wave_buffer
    , pin<void     > in_ov_buffer
    , _in<size_t   > in_ov_size
    )
{
  auto sp_mstm = CPL_MAKE_SHARED( MemoryStream, in_ov_buffer, in_ov_size );
  OvManager ov_mng( sp_mstm );

  return OvDecode( out_wave_buffer, ov_mng );
}



// _____________________________________________________________________________
/// @brief Wave形式へのデコード。
///
/// @param[out] out_wave_buffer
///   : デコードしたデータを格納するバッファへの参照。\n
///   : 末尾に追記されていく。
/// @param[in] in_ov_buffer
///   : OggVorbisデータを保持するバッファクラスへの参照。
/// 
/// @retval 0  : 成功。
/// @retval !0 : 失敗。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int
  OvDecode
    ( rout<Buffer_i> out_wave_buffer
    , _in <Buffer_i> in_ov_buffer
    )
{
  if( auto* povm = dynamic_cast<cptr<OvManager>>(&in_ov_buffer) )
  {
    return OvDecode( out_wave_buffer, *povm );
  }
  else
  {
    return
      OvDecode
        ( out_wave_buffer
        , in_ov_buffer.GetDataAddr()
        , static_cast<size_t>(in_ov_buffer.GetSize())
        );
  }
}



} // namespace Core
} // namespace CPL

#endif // CPL_USE_OGG_VORBIS
