// =====================================================================
//  $Id: TObjectFile.cc,v 1.8 2004/03/07 10:30:32 goiwai Exp $
//  $Name: CLDAQ-1-14-03 $
//  $Log: TObjectFile.cc,v $
//  Revision 1.8  2004/03/07 10:30:32  goiwai
//  ROOTに組みこむためのおまじないマクロを埋めこみました。
//  全てにおいて完全に動作するわけではありません。
//
//  Revision 1.7  2003/11/27 07:34:54  goiwai
//  TInputObjectFile::operator[](index)の挙動がおかしかったので直しました.
//  そしてちょっとだけ高速化しました.
//  これであらかたバグはつぶしたと思うんですけど...
//
//  Revision 1.6  2003/11/24 22:38:50  goiwai
//  Size(),operator[int]を使ったときに異常に遅くなる問題を解消しました.
//  ストリームからの読みだしサイズが実際のファイルサイズと一致しない問題を
//  解決しました.
//
//  Revision 1.5  2003/11/24 07:04:37  goiwai
//  以下のメンバ関数を追加しました.
//  このへんはオフラインむけの関数と考えて下さい.
//  ソケットやメモリに対してこのへんを実装するのは骨が折れそうなので,ファ
//  イルのみへの実装とします.
//
//  Tint GetStreamSize() const;
//  ストリームの全長をバイト単位で返します.
//
//  Tint Size() const;
//  ストリームに含まれるレコードの数を返します.
//
//  Tint FindDataRecord( const Tstring& id, Tint offset = 0 ) const;
//  Tint Find( const Tstring& id, Tint offset = 0 ) const;
//  IDに一致するレコード位置を返します.そのときオフセットもその位置になります.
//
//  Tint FindDataRecord( Tint index, Tint offset = 0 ) const;
//  Tint Find( Tint index, Tint offset = 0 ) const;
//  index番目のレコード位置を返します.そのときオフセットもその位置になります.
//
//  Revision 1.4  2003/10/12 13:03:43  goiwai
//  最近できた FileProperty を持たせています.またEOFとエラーの検知のために
//  IsEnd()とIsError()を追加しました.
//
//  Revision 1.3  2003/10/06 17:02:42  goiwai
//  *** empty log message ***
//
//  Revision 1.2  2003/07/30 16:19:30  goiwai
//  ファイルにコミットログをつけることにしました.
//
// =====================================================================
#include "TObjectFile.hh"
#include "TFileProperty.hh"

TObjectFile::TObjectFile( const Tstring& filename, const Tstring& mode )
  : theFileName( filename ), 
    theMode( mode ),
    theFileStream( 0 ),
    theFileProperty()
{
  theFileStream = fopen( theFileName.c_str(), theMode.c_str() );
  theFileProperty.SetPathName( theFileName );
}

TObjectFile::~TObjectFile()
{
  fclose( theFileStream );
}

Tint TObjectFile::GetStreamSize() const
{
  if ( !isexist( theFileName ) ) {
    return ERROR;
  }

  TFileProperty p( theFileName );
  if ( !p.IsReadable() ) {
    return ERROR;
  }

  TFileStream* fs = fopen( theFileName.c_str(), "r" );
  if ( !fs ) {
    return ERROR;
  }

  Tint retval = 0;
  Tlong posbuf = 0;
  Tint recsizebuf = -1;
  static const Tsize_t nread = 1;

  while ( !feof(fs) ) {
    // record size を取得
    // DataRecordが入ってるものだと決め打ちでいきましょう
    posbuf = ftell( fs );
    if ( posbuf == -1 ) {
      break;
    }
    Tsize_t result = fread( &recsizebuf, Tsizeof( Tint ), nread, fs );
    if ( result != nread || ferror( fs ) != 0 ) {
      break;
    } else {
      if ( fseek( fs, posbuf+recsizebuf, SEEK_SET ) != 0 ) {
        break;
      }
      retval += recsizebuf;
    }
  }
  fclose( fs );

  return retval;
}

Tint TObjectFile::Size() const
{
  static Tint lastmod = -1;
  static Tint lastresult = 0;

  if ( !isexist( theFileName ) ) {
    return ERROR;
  }

  TFileProperty p( theFileName );
  if ( !p.IsReadable() ) {
    return ERROR;
  }

  if ( lastmod == theFileProperty.GetLastModification() ) {
    return lastresult;
  }

  TFileStream* fs = fopen( theFileName.c_str(), "r" );
  if ( !fs ) {
    return ERROR;
  }

  Tint retval = 0;
  Tlong posbuf = 0;
  Tint recsizebuf = -1;
  static const Tsize_t nread = 1;

  while ( !feof(fs) ) {
    // record size を取得
    // DataRecordが入ってるものだと決め打ちでいきましょう
    posbuf = ftell( fs );
    if ( posbuf == -1 ) {
      break;
    }
    Tsize_t result = fread( &recsizebuf, Tsizeof( Tint ), nread, fs );
    if ( result != nread || ferror( fs ) != 0 ) {
      break;
    } else {
      if ( fseek( fs, posbuf+recsizebuf, SEEK_SET ) != 0 ) {
        break;
      }
      retval ++;
    }
  }
  fclose( fs );

  if ( retval > 0 ) {
    lastmod = theFileProperty.GetLastModification();
    lastresult = retval;
  }
  return retval;
}

Tint TObjectFile::FindDataRecord( const Tstring& id, Tint offset ) const
{
  if ( !isexist( theFileName ) ) {
    return ERROR;
  }

  TFileProperty p( theFileName );
  if ( !p.IsReadable() ) {
    return ERROR;
  }

  TFileStream* fs = fopen( theFileName.c_str(), "r" );  
  if ( !fs ) {
    return ERROR;
  }

  if ( fseek( fs, offset, SEEK_SET ) != 0 ) {
    fclose( fs );
    return ERROR;
  }

  Tint retval = ERROR;
  Tlong posbuf = 0;
  Tint recsizebuf = -1;
  Tobject_t typebuf = tObjectUnknown;
  Tsize_t ncharbuf = 0;
  static const Tsize_t nread = 1;

  while ( !feof(fs) ) {
    // 現在位置設定
    posbuf = ftell( fs );
    if ( posbuf == -1 ) {
      break;
    }

    // record size
    Tsize_t result = fread( &recsizebuf, Tsizeof( Tint ), nread, fs );
    if ( result != nread || ferror( fs ) != 0 ) {
      break;
    }

    // object type
    result = fread( &typebuf, Tsizeof( Tobject_t ), nread, fs );
    if ( result != nread || ferror( fs ) != 0 || typebuf != tObjectDataRecord ) {
      break;
    }

    // read number of ID string
    result = fread( &ncharbuf, Tsizeof( Tsize_t ), nread, fs );
    if ( result != nread || ferror( fs ) != 0 ) {
      break;
    }

    Tchar* charbuf = new Tchar[ ncharbuf ];
    result = fread( charbuf, Tsizeof(Tchar) * ncharbuf, nread, fs );

    if ( id == charbuf ) {
      retval = posbuf;
      delete [] charbuf;
      break;
    } else {
      if ( fseek( fs, posbuf+recsizebuf, SEEK_SET ) != 0 ) {
        break;
      }
      delete [] charbuf;
    }
  }
  fclose( fs );

  return retval;
}

Tint TObjectFile::FindDataRecord( Tint index, Tint offset ) const
{
  static Tint lastindex = -1;
  static Tint lastresult = -1;

  if ( index < 0 ) {
    return ERROR;
  }

  Tint maxsize = Size();
  if ( index >= maxsize ) {
    return ERROR;
  }

  if ( !isexist( theFileName ) ) {
    return ERROR;
  }

  TFileProperty p( theFileName );
  if ( !p.IsReadable() ) {
    return ERROR;
  }

  TFileStream* fs = fopen( theFileName.c_str(), "r" );
  if ( !fs ) {
    return ERROR;
  }

  if ( fseek( fs, offset, SEEK_SET ) != 0 ) {
    fclose( fs );
    return ERROR;
  }


  Tint start = 0;
  if ( lastindex >= 0 && lastresult >= 0 ) {
    if ( index > lastindex ) {
      if ( fseek( fs, lastresult, SEEK_SET ) != 0 ) {
        fclose( fs );
        return ERROR;
      }
      start = lastindex;
    } else if ( lastindex == index ) {
      // 前回と同じindexを呼び出したときも高速化
      return lastresult;
    } else {
      ;
    }
  }

  Tint retval = 0;
  Tlong posbuf = 0;
  Tint recsizebuf = -1;
  static const Tsize_t nread = 1;

  for ( Tint i = start; i <= index; i ++ ) {
    // record size を取得
    // DataRecordが入ってるものだと決め打ちでいきましょう
    posbuf = ftell( fs );
    if ( posbuf == -1 ) {
      return ERROR;
    } else if ( i == index ) {
      retval = posbuf;
      break;
    }

    Tsize_t result = fread( &recsizebuf, Tsizeof( Tint ), nread, fs );
    if ( result != nread || ferror( fs ) != 0 ) {
      return ERROR;
    } else if ( fseek( fs, posbuf+recsizebuf, SEEK_SET ) != 0 ) {
      return ERROR;
    }
  }
  fclose( fs );

  if ( retval >= 0 ) {
    lastindex = index;
    lastresult = retval;
  }

  return retval;
}

#ifdef __CLDAQ_ROOT_DLL
    ClassImp(TObjectFile)
#endif
