/// ****************************************************************************
/// @file   
/// @author Dr.Neeton
/// @date   2014/02/12 - 2014/12/31
/// 
/// @brief  ファイルストリームセル
/// 
/// 2014/02/12 CPL_Core_FileStream.cppから分離。
/// 
/// ****************************************************************************

#include "../CPL_Core.h"

namespace CPL {
namespace Core {




// _____________________________________________________________________________
/// @brief コンストラクタ。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
FileStreamCell::FileStreamCell
( pin<FileStreamBase> in_file_stm
, _in<size_t        > in_pos_base
, _in<size_t        > in_size
) : FileStreamBase( in_file_stm->GetFileObject() )
{
  m_pos = m_pos_base = in_pos_base;
  auto file_end = in_file_stm->GetPosEnd();
  m_pos_end = m_pos_base + in_size;
  if( m_pos_end > file_end )
  {
    m_pos_end = file_end;
  }
}


// _____________________________________________________________________________
/// @brief スペースをスキップする（マルチバイト仕様）
///
/// @return スキップしたバイト数（終端に達した場合ー１を返す）
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int FileStreamCell::SpaceSkipA() const
{
  SeekCur( 0 );

  char data;
  int num = -1;
  do {
    if( Read( &data, sizeof(data) ) == EOF ) return EOF;
    num++;
  } while( data == ' '  ||  data == '\n'  ||  data == '\t' );
  SeekCur(-1); // １つ戻す

  return num;
}


// _____________________________________________________________________________
/// @brief スペースをスキップする（UNICODE仕様）
///
/// @return スキップした文字数（終端に達した場合ー１を返す）
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int FileStreamCell::SpaceSkipW() const
{
  SeekCur( 0 );

  wchar_t data;
  int num = -1;
  do {
    if( Read( &data, sizeof(data) ) == EOF ) return EOF;
    num++;
  } while( data == ' '  ||  data == '\n'  ||  data == '\t' );
  SeekCur(-1); // １つ戻す

  return num;
}


// _____________________________________________________________________________
/// @brief 指定キャラクタの次の文字まで進める（UNICODE仕様）
///
/// @return ヒットした文字の位置を返す。ヒットしない場合ー１を返す。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int FileStreamCell::FindCharW( _in<int> in_C ) const
{
  SeekCur( 0 );

  int  num = 0;
  SDEF pos = ::ftell( GetFile() ); // 現在位置取得
  wchar_t buf;
  while( Read( &buf, sizeof(buf) ) != EOF )
  {
    num += sizeof(buf);
    if( (int)buf == in_C )
    {
      return num;
    }
  }

  // 見つからなかったのでポインタを戻す
  Seek( pos, SEEK_SET );
  return -1;
}


// _____________________________________________________________________________
/// @brief 指定キャラクタの次の文字まで進める（マルチバイト仕様）
///
/// @return ヒットした文字の位置を返す。ヒットしない場合ー１を返す。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int FileStreamCell::FindCharA( _in<int> in_C ) const
{
  SeekCur( 0 );

  int  num = 0;
  SDEF pos = ::ftell( GetFile() ); // 現在位置取得
  char buf;
  while( Read( &buf, sizeof(buf) ) != EOF )
  {
    num += sizeof(buf);

    if( (int)buf == in_C )
    {
      return num;
    }
  }

  // 見つからなかったのでポインタを戻す
  Seek( pos, SEEK_SET );
  return -1;
}


// _____________________________________________________________________________
/// @brief シーク。
///
/// @param[in] in_offset
///   : オフセット（バイト単位）
/// @param[in] in_origin
///   : オフセット基準位置を表すフラグ。\n
///   : SEEK_SET : 先頭                 \n
///   : SEEK_CUR : 現在位置             \n
///   : SEEK_END : 終端
///
/// @return 成功すると0、それ以外の場合は0以外を返す。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int FileStreamCell::Seek( _in<S64> in_offset, _in<int> in_origin ) const
{
  switch( in_origin )
  {
  case SEEK_SET: m_pos = m_pos_base + in_offset; break;
  case SEEK_CUR: m_pos = m_pos      + in_offset; break;
  case SEEK_END: m_pos = m_pos_end  + in_offset; break;
  }

  if     ( m_pos < m_pos_base ){ m_pos = m_pos_base; }
  else if( m_pos > m_pos_end  ){ m_pos = m_pos_end; }

#if defined(CPL_PLATFORM_64)
  return _fseeki64( GetFile(), m_pos, SEEK_SET );
#else
  return fseek( GetFile(), static_cast<long>(m_pos), SEEK_SET );
#endif
}


// _____________________________________________________________________________
/// @brief 読み込み。
///
/// @param[out] out_ptr_buf : 読み出し先のアドレスを指定します。
/// @param[in]  in_size     : 読み込むサイズをバイト単位で指定します。
///
/// @return
/// 読み取ったバイト数。失敗するとそれ以外。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
U64 FileStreamCell::Read( pout<void> out_ptr_buf, _in<U64> in_size ) const
{
  auto size = in_size;
  if( m_pos + size > m_pos_end )
  {
    size = m_pos_end - m_pos;
  }
  Seek( GetPos(), SEEK_SET );
  auto read_size =
    ::fread( (void_ptr)out_ptr_buf, 1, static_cast<size_t>(size), GetFile() );
  m_pos += read_size;

  return read_size;
}


// _____________________________________________________________________________
/// @brief メモリストリームへの読み込み。
///
/// @param[out] out_buf_stm
///   : 読み込み先となる、ストリームインターフェイスオブジェクトへの参照。
///
/// @return 読み取ったバイト数。失敗するとそれ以外。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
U64 FileStreamCell::operator >> ( rout<Stream_i_> out_buf_stm ) const
{
  auto file_size = GetSize();
  Array::Constant<U8> temp_buf( static_cast<size_t>(file_size) );
  SeekSet( 0 );
  auto ret = Read( temp_buf.GetTop(), file_size );
  out_buf_stm.Write( temp_buf.GetTop(), ret );
  return ret;
}


// _____________________________________________________________________________
/// @brief 書き出し。
///
/// @param[in] in_ptr_buf : 書き込まれるデータを指すアドレスを指定します。
/// @param[in] in_size    : 書き込むサイズをバイト単位で指定します。
///
/// @return 書き込んだバイト数。失敗するとそれ以外。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
U64 FileStreamCell::Write( pin<void> in_ptr_buf, _in<U64> in_size )
{
  auto size = in_size;
  SeekCur( 0 );
  if( size > m_pos_end - m_pos )
  {
    size = m_pos_end - m_pos;
  }
  auto write_size =
    ::fwrite( in_ptr_buf, 1, static_cast<size_t>(size), GetFile() );
  m_pos += write_size;

  return write_size;
}




// _____________________________________________________________________________
/// @brief 部分ファイルストリームの生成。
///
/// @param[in] in_pos_offs : 先頭オフセットバイト単位で指定します。
/// @param[in] in_size     : 部分ファイルストリームのサイズをバイト単位で指定します。
///
/// @return 部分ファイルストリームのポインタを返す。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
sdptr<FileStreamCell>
  FileStreamCell::CreateStreamCell
    ( _in<U64> in_pos_offs
    , _in<U64> in_size
    ) const
{
  auto size = in_size;
  if( GetFile() )
  {
    if( size > GetSize() - in_pos_offs )
    {
      size = GetSize() - in_pos_offs;
    }
    return
      CPL_MAKE_SHARED
        ( FileStreamCell
        , this
        , static_cast<size_t>(m_pos_base + in_pos_offs)
        , static_cast<size_t>(size)
        );
  }
  else
  {
    return nullptr;
  }
}





// _____________________________________________________________________________
/// @brief テキストファイルから整数値を読み込む（UNICODE仕様）
///
/// @param[out] out_ptr_int : 読み込み先のint領域へのポインタを指定します。
///
/// @return 読み込んだバイト数。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int FileStreamCell::ReadIntegerW( pout<int> out_ptr_int ) const
{
  SeekCur( 0 );

  FILE* fp = GetFile();
  if( out_ptr_int == nullptr ){ return -1; }
  int old_pos = ::ftell( fp ); // 以前の位置を保存
  if( ::fwscanf_s( fp, L"%d", out_ptr_int ) == EOF ){ return -1; }
  int new_pos = ::ftell( fp );
  int read_size = new_pos - old_pos;
  m_pos += read_size;
  return read_size;
}


// _____________________________________________________________________________
/// @brief テキストファイルから整数値を読み込む（マルチバイト仕様）
///
/// @param[out] out_ptr_int : 読み込み先のint領域へのポインタを指定します。
///
/// @return 読み込んだバイト数。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int FileStreamCell::ReadIntegerA( pout<int> out_ptr_int ) const
{
  SeekCur( 0 );

  FILE* fp = GetFile();
  if( out_ptr_int == nullptr ){ return -1; }
  int old_pos = ::ftell( fp ); // 以前の位置を保存
  if( ::fscanf_s( fp, "%d", out_ptr_int ) == EOF ){ return -1; }
  int new_pos = ::ftell( fp );
  int read_size = new_pos - old_pos;
  m_pos += read_size;
  return read_size;
}



// _____________________________________________________________________________
/// @brief テキストファイルから実数値を読み込む（UNICODE仕様）
///
/// @param[out] out_ptr_float : 読み込み先のfloat領域へのポインタを指定します。
///
/// @return 読み込んだ文字数。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int FileStreamCell::ReadFloatW( pout<float> out_ptr_float ) const
{
  SeekCur( 0 );

  FILE* fp = GetFile();

  if( out_ptr_float == nullptr ) return -1;
  int old_pos = ::ftell( fp );
  if( ::fwscanf_s( fp, L"%f", out_ptr_float ) == EOF ) return -1;
  int new_pos = ::ftell( fp );
  int read_size = new_pos - old_pos;
  m_pos += read_size;
  return read_size;
}


// _____________________________________________________________________________
/// @brief テキストファイルから実数値を読み込む（マルチバイト仕様）
///
/// @param[out] out_ptr_float : 読み込み先のfloat領域へのポインタを指定します。
///
/// @return 読み込んだ文字数。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int FileStreamCell::ReadFloatA( pout<float> out_ptr_float ) const
{
  SeekCur( 0 );

  FILE* fp = GetFile();

  if( out_ptr_float == nullptr ) return -1;
  int old_pos = ::ftell( fp );
  if( ::fscanf_s( fp, "%f", out_ptr_float ) == EOF ) return -1;
  int new_pos = ::ftell( fp );
  int read_size = new_pos - old_pos;
  m_pos += read_size;
  return read_size;
}



// _____________________________________________________________________________
/// @brief テキストファイルから文字列を読み込む（マルチバイト仕様）
///
/// @param[out] out_ptr_str   : 取得用のバッファを指すポインタを指定します。
/// @param[in]  in_buf_length : バッファサイズバイト単位で指定します。
/// @param[in]  in_flag_line  : １行単位で読み込むかどうかを指定します。
///
/// @return 読み込んだバイト数。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int
  FileStreamCell::ReadString
    ( pout<char> out_ptr_str
    , _in <int > in_buf_length
    , _in <bool> in_flag_line
    ) const
{
  StringA get_str;
  const int read_size = ReadString( get_str, in_flag_line );
  const int copy_size = Min( read_size, in_buf_length - 1 );

  StringA::str_ncpy_( out_ptr_str, get_str, copy_size );

  return copy_size;
}


// _____________________________________________________________________________
/// @brief テキストファイルから文字列を読み込む（マルチバイト仕様）
///
/// @param[out] out_ptr_str   : 取得用のバッファを指すポインタを指定します。
/// @param[in]  in_buf_length : バッファサイズをバイト単位で指定します。
/// @param[in]  in_flag_line  : １行単位で読み込むかどうかを指定します。
///
/// @return 読み込んだバイト数。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int
  FileStreamCell::ReadString
    ( pout<wchar_t> out_ptr_str
    , _in <int    > in_buf_length
    , _in <bool   > in_flag_line
    ) const
{
  StringW get_str;
  const int read_size = ReadString( get_str, in_flag_line );
  const int copy_size = Min( read_size, in_buf_length - 1 );

  StringW::str_ncpy_( out_ptr_str, get_str, copy_size );

  return copy_size;
}



// _____________________________________________________________________________
/// @brief テキストファイルから文字列を読み込む。
///
/// @param[out] out_str      : 取得用の文字列クラスへの参照を指定します。
/// @param[in]  in_flag_line : １行単位で読み込むかどうかを指定します。
///
/// @return 読み込んだバイト数。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int
  FileStreamCell::ReadString
    ( rout<StringA> out_str
    , _in <bool   > in_flag_line
    ) const
{
  SeekCur( 0 );

  FILE* fp = GetFile();

  int old_pos = ::ftell( fp );
  Array::Constant<S8> buf(16*1024); // 16KB （このサイズは適当な値なので注意）
  if( in_flag_line )
  {
    if( ::fgets( buf.GetTop(), static_cast<int>(buf.GetNum())-1, fp ) == nullptr )
    {
      return -1;
    }
  }
  else
  {
    if( ::fscanf_s( fp, "%s", buf.GetTop(), static_cast<U32>(buf.GetSize()) ) == EOF )
    {
      return -1;
    }
  }
  int new_pos = ::ftell( fp );

  out_str = char_ptr(buf.GetTop());

  int read_size = new_pos - old_pos;
  m_pos += read_size;
  return read_size;
}


// _____________________________________________________________________________
/// @brief テキストファイルから文字列を読み込む。
///
/// @param[out] out_str      : 取得用の文字列クラスへの参照を指定します。
/// @param[in]  in_flag_line : １行単位で読み込むかどうかを指定します。
///
/// @return 読み込んだバイト数。
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int
  FileStreamCell::ReadString
    ( rout<StringW> out_str
    , _in <bool   > in_flag_line
    ) const
{
  SeekCur( 0 );

  FILE* fp = GetFile();

  int old_pos = ::ftell( fp );
  Array::Constant<wchar_t> buf(8*1024); // 16KB （このサイズは適当な値なので注意）
  if( in_flag_line )
  {
    if( ::fgetws( buf.GetTop(), static_cast<int>(buf.GetNum())-1, fp ) == nullptr )
    {
      return -1;
    }
//    if( ::fscanf( fp, "%s", buf.GetTop() ) == EOF ){
//      return -1;
//    }
  }
  else
  {
    if( ::fwscanf_s( fp, L"%s", buf.GetTop(), static_cast<U32>(buf.GetSize()) ) == EOF )
    {
      return -1;
    }
  }
  int new_pos = ::ftell( fp );

  out_str = (wchar_t*)buf.GetTop();

  int read_size = new_pos - old_pos;
  m_pos += read_size;
  return read_size;
}




} // namespace Core
} // namespace CPL

