/// ****************************************************************************
/// @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 {

  
/// @cond
namespace zzz_ {
  // _____________________________________________________________________________
  /// @brief エンコード。
  /// 
  /// @param[out] out_buf
  ///   : 変換データを格納するバッファクラスの参照。
  /// @param[in]  in_quality_level
  ///   : クオリティレベル。\n
  ///   : -0.1 to 1.0 (lo to hi).
  /// 
  /// @return
  /// 変換後のサイズを返す。\n
  /// エラーの場合、エラーコードを返す。
  ///  OV_EFAULT | 内部ロジック障害。バグまたはスタック/ヒープの破損。
  ///  OV_EINVAL | 無効なセットアップ要求。例えば、引数が範囲外など。
  ///  OV_EIMPL  | 未実装モード。品質レベルの要求に応じることができない。
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  int Encode( rout<Buffer_i> out_buf, _in<float> in_quality_level )
  {
    return 0;
#if 0
    //***** Encoding workflow *****//
    // 
    // 1. Initialize a vorbis_info structure by calling vorbis_info_init and then functions from libvorbisenc on it.
    //    libvorbisencのvorbis_info_init関数により、vorbis_info構造体を初期化します。（かな？）

    // 2. Initialize a vorbis_dsp_state for encoding based on the parameters in the vorbis_info by using vorbis_analysis_init.
    //    既に設定されたvorbis_infoパラメタを用いて、vorbis_analysis_init関数によりvorbis_dsp_stateを初期化します。（かな？）

    // 3. Initialize a vorbis_comment structure using vorbis_comment_init,
    //    populate it with any comments you wish to store in the stream,
    //    and call vorbis_analysis_headerout to get the three Vorbis stream header packets.
    //    Output the packets.

    // 4. Initialize a vorbis_block structure using vorbis_block_init.
    //    vorbis_block_init関数によりvorbis_block構造体を初期化。

    // 5. While there is more audio to encode:
    //    1. Submit a chunk of audio data using vorbis_analysis_buffer and vorbis_analysis_wrote.
    //    2. Obtain all available blocks using vorbis_analysis_blockout in a loop. For each block obtained:
    //      1. Encode the block into a packet (or prepare it for bitrate management) using vorbis_analysis. (It's a good idea to always pass the blocks through the bitrate management mechanism; more information is on the vorbis_analysis page. It does not affect the resulting packets unless you are actually using a bitrate-managed mode.)
    //      2. If you are using bitrate management, submit the block using vorbis_bitrate_addblock and obtain packets using vorbis_bitrate_flushpacket.
    //      3. Output any obtained packets.

    // 6. Submit an empty buffer to indicate the end of input; this will result in an end-of-stream packet after all encoding steps are done to it.
    // 7. Destroy the structures using the appropriate vorbis_*_clear routines.

    ogg_stream_state os; /* take physical pages, weld into a logical stream of packets */
    ogg_page         og; /* one Ogg bitstream page.  Vorbis packets are inside */
    ogg_packet       op; /* one raw packet of data for decode */
  

    try {
      // WAVEの基本情報を得るため、クロスキャスト。
      auto p_wd = dynamic_cast<BaseWaveData const*>(&*m_sp_stream);
      if( p_wd == nullptr )
      {
        return 0;
      }
      auto const* wfex = p_wd->GetWaveInfo().GetWaveFormatEX();
      const int ch = wfex->nChannels;
      const int hz = wfex->nSamplesPerSec;
      size_t write_pos = 0;
      // バッファ予約（バッファオブジェクトの場合。ファイルストリームでは何もされない）
      out_buf.Reserve( p_wd->GetWaveInfo().GetSize() );

      // vorbis_infoの初期化。（1.辺り）
      ov::VorbisInfo vi( ch, hz, in_quality_level );

      //
      ov::VorbisComment vc;

      // vorbis_dsp_stateの初期化。（2.辺り）
      ov::VorbisDspState vds( vi );

      // vorbis_blockの初期化。(4.辺り)
      ov::VorbisBlock vb( vds );
  

      // たぶん、ここら辺から3.かな？
      ov::OggStreamState os;

      {
        ogg_packet header;
        ogg_packet header_comm;
        ogg_packet header_code;

        vorbis_analysis_headerout
        ( &vds,
          &vc,
          &header,
          &header_comm,
          &header_code );

        // automatically placed in its own page
        ogg_stream_packetin( &os, &header );
        ogg_stream_packetin( &os, &header_comm );
        ogg_stream_packetin( &os, &header_code );

        // This ensures the actual audio data will start on a new page, as per spec
        while(true)
        {
          int result = ogg_stream_flush( &os, &og );
          if( result == 0 )
            break;
          out_buf.Insert( write_pos, og.header, og.header_len );
          write_pos += og.header_len;
          out_buf.Insert( write_pos, og.body, og.body_len );
          write_pos += og.body_len;
        }

      }
  

      // たぶん、ここから5.の処理。
      int eos = 0;
      while(!eos)
      {
      //  long bytes=fread(readbuffer,1,READ*4,infile); /* stereo hardwired here */
        U8 tmp_buf[ 4096 ];
        size_t read_size = m_sp_stream->Read( tmp_buf, sizeof(tmp_buf) );

        if( read_size == -1 )
        {
          break;
        }
        if( read_size == 0 )
        {
          /* end of file.  this can be done implicitly in the mainline,
              but it's easier to see here in non-clever fashion.
              Tell the library we're at end of stream so that it can handle
              the last frame and mark end of stream in the output properly */
          vorbis_analysis_wrote( &vds, 0 );

        } else {
          /* data to encode */

          /* expose the buffer to submit data */
          float **buffer = vorbis_analysis_buffer( &vds, 1024 );
        
          /* uninterleave samples */
          for( int i = 0; i < read_size / (ch*2); i++ )
          {
            for( int n = 0; n < ch; n++ )
            {
              buffer[n][i] = 
                ((tmp_buf[i*(ch*2)+n*2+1]<<8)|
                (0x00ff&(int)tmp_buf[i*(ch*2)+n*2]))/32768.f;
            }
          }
      
          /* tell the library how much we actually submitted */
          vorbis_analysis_wrote( &vds, i );
        }

        /* vorbis does some data preanalysis, then divvies up blocks for
            more involved (potentially parallel) processing.  Get a single
            block for encoding now */
        while(vorbis_analysis_blockout(&vds,&vb)==1){

          /* analysis, assume we want to use bitrate management */
          vorbis_analysis(&vb,NULL);
          vorbis_bitrate_addblock(&vb);

          while(vorbis_bitrate_flushpacket(&vds,&op)){
      
        /* weld the packet into the bitstream */
        ogg_stream_packetin(&os,&op);
      
        /* write out pages (if any) */
        while(!eos){
          int result=ogg_stream_pageout(&os,&og);
          if(result==0)break;
          fwrite(og.header,1,og.header_len,outfile);
          fwrite(og.body,1,og.body_len,outfile);
        
          /* this could be set above, but for illustrative purposes, I do
              it here (to show that vorbis does know where the stream ends) */
        
          if(ogg_page_eos(&og))eos=1;
        }
          }
        }
      }

      ///* clean up and exit.  vorbis_info_clear() must be called last */
  
      //ogg_stream_clear(&os);
      //vorbis_block_clear(&vb);
      //vorbis_dsp_clear(&vd);
      //vorbis_comment_clear(&vc);
      //vorbis_info_clear(&vi);
  
      ///* ogg_page and ogg_packet structs always point to storage in
      //    libvorbis.  They're never freed or manipulated directly */
  
      //fprintf(stderr,"\n終わり\n");
      //return(0);
    }
    catch(int e)
    {
      return e;
    }
    catch(...)
    {
      return OV_FALSE;
    }
    return out_buf.GetSize();
#endif
  }
}//namespace zzz_
/// @endcond




} // namespace Core
} // namespace CPL

#endif // CPL_USE_OGG_VORBIS
