// =====================================================================
//  $Id: TInputObjectFile.cc,v 1.9 2004/03/07 10:30:32 goiwai Exp $
//  $Name: CLDAQ-1-14-06 $
//  $Log: TInputObjectFile.cc,v $
//  Revision 1.9  2004/03/07 10:30:32  goiwai
//  ROOTに組みこむためのおまじないマクロを埋めこみました。
//  全てにおいて完全に動作するわけではありません。
//
//  Revision 1.8  2003/12/06 10:56:19  goiwai
//  フィルタ関連の修整と Read(),Write()の戻り値の修整です.
//  Read()もWrite()も処理したレコードサイズを返します.
//  フィルタがある場合も同様です.
//  それぞれのストリームのDataSizeの扱いですが,これは扱ったレコードサイズ
//  でなく扱ったファイルやソケットなどのサイズになります.レコードサイズと
//  一致するとは限りません.
//
//  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:06:23  goiwai
//  operator[]( const Tstring& id ) と operator[]( Tint index ) を実装しま
//  した.
//  stream["TAG"] や stream[index] でレコードが取りだせます.
//
//  Revision 1.4  2003/10/12 13:04:56  goiwai
//  ファイルの終端に到達するか読みこみに失敗したときには 0 を返すようにし
//  ました.オフラインではこれで十分でしょう.
//  while ( stream -> Read( record ) ) {
//    ;
//  }
//
//  Revision 1.3  2003/10/06 17:02:42  goiwai
//  *** empty log message ***
//
//  Revision 1.2  2003/07/30 16:19:30  goiwai
//  ファイルにコミットログをつけることにしました.
//
// =====================================================================
#include "TInputObjectFile.hh"
#include "TStreamableObject.hh"
#include "TDataRecord.hh"
#include "TDataSection.hh"
#include "TDataSegment.hh"
#include "TDataElement.hh"
#include "TInputObjectFilter.hh"

TInputObjectFile::TInputObjectFile( const Tstring& filename, const Tstring& mode )
  : TObjectFile( filename, mode ), 
    TInputObjectStream( tFileStream )
{;}

TInputObjectFile::TInputObjectFile( TInputObjectFilter* filter, const Tstring& filename, const Tstring& mode )
  : TObjectFile( filename, mode ), 
    TInputObjectStream( filter, tFileStream )
{;}

TInputObjectFile::~TInputObjectFile()
{;}

Tint TInputObjectFile::Read( TStreamableObject& object )
{
  if ( HasFilter() ) {
    return theObjectFilter -> Filtering( object, this );
  }  

  theLastDataSize = theDataSize;
  theDataSize = 0;

  static Tobject_t objecttype = object.GetObjectType();

  switch ( objecttype ) {
    case tObjectDataRecord:
      *( (TDataRecord*)(&object) ) = getDataRecord();
      break;
    case tObjectDataSection:
      *( (TDataSection*)(&object) ) = getDataSection();
      break;
    case tObjectDataSegment:
      *( (TDataSegment*)(&object) ) = getDataSegment();
      break;
    case tObjectDataElement:
      *( (TDataElement*)(&object) ) = getDataElement();
      break;
    default:
      break;
  }

  if ( theDataSize <= 0 ) {
    SetOffset( theTotalDataSize, TObjectFile::tSeekBegin );
    theDataSize = 0;
  } else {
    theTotalDataSize += theDataSize;
  }

  return theDataSize;
}

TDataRecord TInputObjectFile::getDataRecord()
{
  Tint recsize;
  Tobject_t type;
  Tstring id;
  Tsize_t entries;

  readProperties( recsize, type, id, entries );

  TDataRecord record( id );
  record.SetObjectType( type );
  if ( theDataSize == 0 ) {
    return record;
  }

  for ( Tsize_t i = 0; i < entries; i ++ ) {
    record.push_back( getDataSection() );
  }

  return record;
}

TDataSection TInputObjectFile::getDataSection()
{
  Tint recsize;
  Tobject_t type;
  Tstring id;
  Tsize_t entries;
  readProperties( recsize, type, id, entries );

  TDataSection section( id );
  section.SetObjectType( type );
  if ( theDataSize == 0 ) {
    return section;
  }

  for ( Tsize_t i = 0; i < entries; i ++ ) {
    section.push_back( getDataSegment() );
  }

  return section;
}

TDataSegment TInputObjectFile::getDataSegment()
{
  Tint recsize;
  Tobject_t type;
  Tstring id;
  Tsize_t entries;
  readProperties( recsize, type, id, entries );


  TDataSegment segment( id );
  segment.SetObjectType( type );
  if ( theDataSize == 0 ) {
    return segment;
  }

  for ( Tsize_t i = 0; i < entries; i ++ ) {
    segment.push_back( getDataElement() );
  }

  return segment;
}

TDataElement TInputObjectFile::getDataElement()
{
  static const Tsize_t nmemb = 1;
  static const Tstring head = "TInputObjectFile::getDataElement";
  Tint recsize;
  Tobject_t otype;
  Tstring id;
  Telement_t etype;
  Tint nprvs;
  TDataElement element;


  // read record size
  if ( ( fread( &recsize, Tsizeof( Tint ), nmemb, theFileStream ) != nmemb ) ) {
    //perror( head.c_str() );
    SetOffset( 0, TObjectFile::tSeekEnd );
    theDataSize = 0;
    return element;
  } else {
    theDataSize += Tsizeof( Tint );
  }


  // read object type
  if ( ( fread( &otype, Tsizeof( Tobject_t ), nmemb, theFileStream ) != nmemb ) ) {
    //perror( head.c_str() );
    SetOffset( 0, TObjectFile::tSeekEnd );
    theDataSize = 0;
    return element;
  } else {
    theDataSize += Tsizeof( Tobject_t );
    element.SetObjectType( otype );
  }


  // read object id
  Tsize_t nchar;
  Tchar* charbuf = 0;
  if ( fread( &nchar, Tsizeof( Tsize_t ), nmemb, theFileStream ) != nmemb ) {
    //perror( head.c_str() );
    SetOffset( 0, TObjectFile::tSeekEnd );
    theDataSize = 0;
    return element;
  } else {
    theDataSize += Tsizeof( Tsize_t );
    charbuf = new Tchar[ nchar ];
  }
  if ( fread( charbuf, Tsizeof( Tchar ) * nchar, nmemb, theFileStream ) != nmemb ) {
    //perror( head.c_str() );
    SetOffset( 0, TObjectFile::tSeekEnd );
    theDataSize = 0;
    delete [] charbuf;
    return element;
  } else {
    theDataSize += Tsizeof( Tchar ) * nchar;
    id = charbuf;
    delete [] charbuf;
    element.SetID( id );
  }



  // read element type
  if ( ( fread( &etype, Tsizeof( Telement_t ), nmemb, theFileStream ) != nmemb ) ) {
    //perror( head.c_str() );
    SetOffset( 0, TObjectFile::tSeekEnd );
    theDataSize = 0;
    return element;
  } else {
    theDataSize += Tsizeof( Telement_t );
    element.SetElementType( etype );
  }


  // read number of primitives
  if ( ( fread( &nprvs, Tsizeof( Tint ), nmemb, theFileStream ) != nmemb ) ) {
    //perror( head.c_str() );
    SetOffset( 0, TObjectFile::tSeekEnd );
    theDataSize = 0;
    return element;
  } else {
    theDataSize += Tsizeof( Tint );
    element.SetNumberOfPrimitives( nprvs );
  }


  Tstring* strbuf = 0;
  Tchar* cc = 0;
  Tint* ii = 0;
  TUint* ui = 0;
  Tdouble* dd = 0;
  TUshort* us = 0;
  Tshort* ss = 0;
  Tfloat* ff = 0;
  Tlong* ll = 0;
  TUlong* ul = 0;



  switch ( etype ) {

    case tTypeInt:
      ii = new Tint[ nprvs ];
      if ( fread( ii, Tsizeof( Tint ) * nprvs, nmemb, theFileStream ) != nmemb ) {
	//perror( head.c_str() );
	SetOffset( 0, TObjectFile::tSeekEnd );
	theDataSize = 0;
	delete [] ii;
	return element;
      } else {
	element.FillData( ii, nprvs );
	theDataSize += (Tint)( Tsizeof( Tint ) * nprvs );
	delete [] ii;
      }
      break;


    case tTypeUnsignedInt:
      ui = new TUint[ nprvs ];
      if ( fread( ui, Tsizeof( TUint ) * nprvs, nmemb, theFileStream ) != nmemb ) {
	//perror( head.c_str() );
	SetOffset( 0, TObjectFile::tSeekEnd );
	theDataSize = 0;
	delete [] ui;
	return element;
      } else {
	element.FillData( ui, nprvs );
	theDataSize += (Tint)( Tsizeof( TUint ) * nprvs );
	delete [] ui;
      }
      break;


    case tTypeWord:
    case tTypeUnsignedShort:
      us = new TUshort[ nprvs ];
      if ( fread( us, Tsizeof( TUshort ) * nprvs, nmemb, theFileStream ) != nmemb ) {
	//perror( head.c_str() );
	SetOffset( 0, TObjectFile::tSeekEnd );
	theDataSize = 0;
	delete [] us;
	return element;
      } else {
	element.FillData( us, nprvs );
	theDataSize += (Tint)( Tsizeof( TUshort ) * nprvs );
	delete [] us;
      }
      break;

    case tTypeDouble:
      dd = new Tdouble[ nprvs ];
      if ( fread( dd, Tsizeof( Tdouble ) * nprvs, nmemb, theFileStream ) != nmemb ) {
	//perror( head.c_str() );
	SetOffset( 0, TObjectFile::tSeekEnd );
	theDataSize = 0;
	delete [] dd;
	return element;
      } else {
	element.FillData( dd, nprvs );
	theDataSize += (Tint)( Tsizeof( Tdouble ) * nprvs );
	delete [] dd;
      }
      break;

    case tTypeFloat:
      ff = new Tfloat[ nprvs ];

      if ( fread( ff, Tsizeof( Tfloat ) * nprvs, nmemb, theFileStream ) != nmemb ) {
	//perror( head.c_str() );
	SetOffset( 0, TObjectFile::tSeekEnd );
	theDataSize = 0;
	delete [] ff;
	return element;
      } else {
	element.FillData( ff, nprvs );
	theDataSize += (Tint)( Tsizeof( Tfloat ) * nprvs );
	delete [] ff;
      }
      break;


    case tTypeShort:
      ss = new Tshort[ nprvs ];
      if ( fread( ss, Tsizeof( Tshort ) * nprvs, nmemb, theFileStream ) != nmemb ) {
	//perror( head.c_str() );
	SetOffset( 0, TObjectFile::tSeekEnd );
	theDataSize = 0;
	delete [] ss;
	return element;
      } else {
	element.FillData( ss, nprvs );
	theDataSize += (Tint)( Tsizeof( Tshort ) * nprvs );
	delete [] ss;
      }
      break;

    case tTypeLong:
      ll = new Tlong[ nprvs ];
      if ( fread( ll, Tsizeof( Tlong ) * nprvs, nmemb, theFileStream ) != nmemb ) {
	//perror( head.c_str() );
	SetOffset( 0, TObjectFile::tSeekEnd );
	theDataSize = 0;
	delete [] ll;
	return element;
      } else {
	element.FillData( ll, nprvs );
	theDataSize += (Tint)( Tsizeof( Tlong ) * nprvs );
	delete [] ll;
      }
      break;

    case tTypeUnsignedLong:
      ul = new TUlong[ nprvs ];
      if ( fread( ul, Tsizeof( TUlong ) * nprvs, nmemb, theFileStream ) != nmemb ) {
	//perror( head.c_str() );
	SetOffset( 0, TObjectFile::tSeekEnd );
	theDataSize = 0;
	delete [] ul;
	return element;
      } else {
	element.FillData( ul, nprvs );
	theDataSize += (Tint)( Tsizeof( TUlong ) * nprvs );
	delete [] ul;
      }
      break;

    case tTypeString:
      strbuf = new Tstring[ nprvs ];
      for ( Tint i = 0; i < nprvs; i ++ ) {
	Tsize_t nchar;
	if ( fread( &nchar, Tsizeof( Tsize_t ), nmemb, theFileStream ) != nmemb ) {
	  //perror( head.c_str() );
	  SetOffset( 0, TObjectFile::tSeekEnd );
	  theDataSize = 0;
	  delete [] strbuf;
	  return element;
	} else {
	  theDataSize += (Tint)( Tsizeof( Tsize_t ) * nmemb );
	  cc = new Tchar[ nchar ];
	}
	if ( fread( cc, Tsizeof( Tchar ) * nchar, nmemb, theFileStream ) != nmemb ) {
	  //perror( head.c_str() );
	  SetOffset( 0, TObjectFile::tSeekEnd );
	  theDataSize = 0;
	  delete [] cc;
	  delete [] strbuf;
	  return element;
	} else {
	  theDataSize += (Tint)( Tsizeof( Tchar ) * nchar );
	}
	strbuf[ i ] = cc;
	delete [] cc;
      }
      element.FillData( strbuf, nprvs );
      delete [] strbuf;
      break;

    case tTypeUnknown:
    case tTypeObject:
    default:
      break;
  }

  return element;
}

Tvoid TInputObjectFile::readProperties( Tint& recsize, Tobject_t& type, Tstring& id, Tsize_t& entries )
{
  static const Tsize_t nmemb = 1;
  static const Tstring head = "TInputObjectFile::readProperties";

  // read record size
  if ( ( fread( &recsize, Tsizeof( Tint ), nmemb, theFileStream ) != nmemb ) ) {
    //perror( head.c_str() );
    SetOffset( 0, TObjectFile::tSeekEnd );
    theDataSize = 0;
    return;
  } else {
    theDataSize += Tsizeof( Tint );
  }


  // read object type
  if ( ( fread( &type, Tsizeof( Tobject_t ), nmemb, theFileStream ) != nmemb ) ) {
    //perror( head.c_str() );
    SetOffset( 0, TObjectFile::tSeekEnd );
    theDataSize = 0;
    return;
  } else {
    theDataSize += Tsizeof( Tobject_t );
  }

  // read object id
  Tsize_t nchar;
  Tchar* charbuf = 0;
  if ( fread( &nchar, Tsizeof( Tsize_t ), nmemb, theFileStream ) != nmemb ) {
    //perror( head.c_str() );
    SetOffset( 0, TObjectFile::tSeekEnd );
    theDataSize = 0;
    return;
  } else {
    theDataSize += Tsizeof( Tsize_t );
    charbuf = new Tchar[ nchar ];
  }
  if ( fread( charbuf, Tsizeof( Tchar ) * nchar, nmemb, theFileStream ) != nmemb ) {
    //perror( head.c_str() );
    SetOffset( 0, TObjectFile::tSeekEnd );
    theDataSize = 0;
    delete [] charbuf;
    return;
  } else {
    theDataSize += Tsizeof( Tchar ) * nchar;
  }
  id = charbuf;
  delete [] charbuf;

  // read entries
  if ( ( fread( &entries, Tsizeof( Tsize_t ), nmemb, theFileStream ) != nmemb ) ) {
    //perror( head.c_str() );
    SetOffset( 0, TObjectFile::tSeekEnd );
    theDataSize = 0;
    return;
  } else {
    theDataSize += Tsizeof( Tsize_t );
  }

  return;
}

TDataRecord TInputObjectFile::operator[]( const Tstring& id )
{
  TDataRecord retval;
  Tint pos = Find( id, GetOffset() );
  //Tint pos = Find( id );
  if ( pos >= 0 ) {
    theLastDataSize = theDataSize;
    theDataSize = 0;  
    retval = getDataRecord();
    theTotalDataSize += theDataSize;
  }
  return retval;
}

TDataRecord TInputObjectFile::operator[]( Tint index )
{
  TDataRecord retval;
  Tint pos = Find( index );

  if ( pos >= 0 ) {
    SetOffset( pos, tSeekBegin );
    theLastDataSize = theDataSize;
    theDataSize = 0;
    retval = getDataRecord();
    theTotalDataSize += theDataSize;
  }
  return retval;
}

#ifdef __CLDAQ_ROOT_DLL
    ClassImp(TInputObjectFile)
#endif
