// =====================================================================
//  $Id: TDataRecord.cc,v 1.1.1.1 2003/06/27 02:56:41 goiwai Exp $
//  $Name: CLDAQ-1-06-02 $
// =====================================================================
#include "TDataRecord.hh"
#include "TOutputObjectStream.hh"
#include "TOutputObjectFile.hh"
#include "TOutputObjectSocket.hh"
#include "TOutputObjectSharedMemory.hh"

TDataRecord::TDataRecord( const Tstring& id )
  : TStreamableObject( tObjectDataRecord, id ), TDataSectionList()
{;}

TDataRecord::TDataRecord( const TDataRecord& right )
  : TStreamableObject( right ), TDataSectionList( right )
{;}

TDataRecord::~TDataRecord()
{;}

Tint TDataRecord::GetRecordSize()
{
  Tsize_t nchar = theID.size() + 1;
  // record size + object type + object id + number of entries
  Tint total = Tsizeof( Tint ) + Tsizeof( Tobject_t ) + Tsizeof( Tsize_t ) + ( Tsizeof( Tchar ) * nchar ) + Tsizeof( Tsize_t );
  for ( Tsize_t i = 0; i < size(); i ++ ) {
    total += ( (*this)[ i ] ).GetRecordSize();
  }
  return( total );
}

Tint TDataRecord::Record( TOutputObjectStream* output )
{
  Tint datasize = 0;
  Tstream_t streamtype = output -> GetStreamType();

  switch ( streamtype ) {
    case tFileStream:
      datasize = record( (TOutputObjectFile*)output );
      break;
    case tSocketStream:
      datasize = record( (TOutputObjectSocket*)output );
      break;
    case tSharedMemoryStream:
      datasize = record( (TOutputObjectSharedMemory*)output );
      break;
    case tUnknownStream:
    default:
      Tcerr << "TDataRecord::Record: unknown stream type." << Tendl;
      return( 0 );
  }

  for ( Tsize_t i = 0; i < size(); i ++ ) {
    datasize += ( (*this)[ i ] ).Record( output );
  }

  return( datasize );
}

Tostream& operator<<( Tostream& tos, const TDataRecord& right )
{
  tos << "* Data Record(" << right.theObjectType << "), ";
  tos << "ID: " << right.theID;
  tos << Twspace << "Capacity: " << right.capacity();
  tos << Twspace << "Entry: " << right.size();

  if ( right.empty() )
    return( tos );
  
  tos << Tendl;

  for ( Tsize_t i = 0; i < right.size(); i ++ ) {
    tos << right[ i ];
    if ( i != right.size() - 1 )
      tos << Tendl;
  }

  return( tos );
}

// iwai@mebius:~/work/ccd-daq-1.3.0/tests> ../bin/ofile 100000
// Real=269.29s User(parent/child)=228.92s/0s Sys(parent/child)=5.33s/0s
Tint TDataRecord::record( TOutputObjectFile* ofile )
{
  Tsize_t datasize = 0;
  static const Tsize_t nmemb = 1;
  Tsize_t ss = 0;
  static const Tstring head = "TDataRecord::record";

  // write object type
  Tint recsize = GetRecordSize();
  ss = fwrite( &recsize, Tsizeof( Tint ), nmemb, ofile -> GetFileStream() );
  if ( ss != nmemb ) {
    perror( head.c_str() );
  } else {
    datasize += ss * Tsizeof( Tint );
  }


  // write object type
  ss = fwrite( &theObjectType, Tsizeof( Tobject_t ), nmemb, ofile -> GetFileStream() );
  if ( ss != nmemb ) {
    perror( head.c_str() );
  } else {
    datasize += ss * Tsizeof( Tobject_t );
  }


  // write object ID
  Tchar charbuf;
  Tsize_t nchar = theID.size() + 1;
  ss = fwrite( &nchar, Tsizeof( Tsize_t ), nmemb, ofile -> GetFileStream() );
  if ( ss != nmemb ) {
    perror( head.c_str() );
  } else {
    datasize += ss * Tsizeof( Tsize_t );
  }
  for ( Tsize_t i = 0; i < nchar; i ++ ) {
    if ( i == ( nchar - 1 ) ) {
      charbuf = '\0';
    } else {
      charbuf = theID[ i ];
    }
    ss = fwrite( &charbuf, Tsizeof( Tchar ), nmemb, ofile -> GetFileStream() );
    if ( ss != nmemb ) {
      perror( head.c_str() );
    } else {
      datasize += ss * Tsizeof( Tchar );
    }
  }

   
  // write number of entries
  Tsize_t entry = size();
  ss = fwrite( &entry, Tsizeof( Tsize_t ), nmemb, ofile -> GetFileStream() );
  if ( ss != nmemb ) {
    perror( head.c_str() );
  } else {
    datasize += ss * Tsizeof( Tsize_t );
  }

  return( (Tint)datasize );
}

// iwai@mebius:~/work/ccd-daq-1.3.0/tests> ../bin/ofile 100000
// Real=94.08s User(parent/child)=84.95s/0s Sys(parent/child)=4.86s/0s
// static Tvoid* _buffer = new Tbyte[ 1024 * 1024 ];
// Tint TDataRecord::writeToFile( TOutputObjectFile* ofile )
// {
//   Tvoid* buffer = _buffer;
//   Tint datasize = Serialize( buffer );
//   fwrite( buffer, datasize, 1, ofile -> GetFileStream() );
//   return( datasize );
// }

Tint TDataRecord::record( TOutputObjectSocket* osocket )
{
  Tsize_t datasize = 0;
  static const Tstring head = "TDataRecord::record";

  // send record size
  Tint recsize = GetRecordSize();
  if ( send( osocket -> GetServerDescriptor(), &recsize, Tsizeof( Tint ), 0 ) != Tsizeof( Tint ) ) {
    perror( head.c_str() );
  } else {
    datasize += Tsizeof( Tint );
  }


  // send object type
  if ( send( osocket -> GetServerDescriptor(), &theObjectType, Tsizeof( Tobject_t ), 0 ) != Tsizeof( Tobject_t ) ) {
    perror( head.c_str() );
  } else {
    datasize += Tsizeof( Tobject_t );
  }


  // send object ID
  Tchar charbuf;
  Tsize_t nchar = theID.size() + 1;
  if ( send( osocket -> GetServerDescriptor(), &nchar, Tsizeof( Tsize_t ), 0 ) != Tsizeof( Tsize_t ) ) {
    perror( head.c_str() );
  } else {
    datasize += Tsizeof( Tsize_t );
  }
  for ( Tsize_t i = 0; i < nchar; i ++ ) {
    if ( i == ( nchar - 1 ) ) {
      charbuf = '\0';
    } else {
      charbuf = theID[ i ];
    }
    if ( send( osocket -> GetServerDescriptor(), &charbuf, Tsizeof( Tchar ), 0 ) != Tsizeof( Tchar ) ) {
      perror( head.c_str() );
    } else {
      datasize += Tsizeof( Tchar );
    }
  }


  // send number of entries
  Tsize_t entry = size();
  if ( send( osocket -> GetServerDescriptor(), &entry, Tsizeof( Tsize_t ), 0 ) != Tsizeof( Tsize_t ) ) {
    perror( head.c_str() );
  } else {
    datasize += Tsizeof( Tsize_t );
  }


  return( (Tint)datasize );
}

Tint TDataRecord::record( TOutputObjectSharedMemory* omemory )
{
  Tsize_t datasize = 0;
  Tvoid* ptr = omemory -> GetAddress();


  // write record size
  *( (Tint*)ptr ) = GetRecordSize();
  datasize += Tsizeof( Tint );
  ( (Tint*)ptr ) ++;


  // write object type
  *( (Tobject_t*)ptr ) = theObjectType;
  datasize += Tsizeof( Tobject_t );
  ( (Tobject_t*)ptr ) ++;


  // write object ID
  Tchar charbuf;
  Tsize_t nchar = theID.size() + 1;
  *( (Tsize_t*)ptr ) = nchar;
  datasize += Tsizeof( Tsize_t );
  ( (Tsize_t*)ptr ) ++;
  for ( Tsize_t i = 0; i < nchar; i ++ ) {
    if ( i == ( nchar - 1 ) ) {
      charbuf = '\0';
    } else {
      charbuf = theID[ i ];
    }
    *( (Tchar*)ptr ) = charbuf;
    datasize += Tsizeof( Tchar );
    ( (Tchar*)ptr ) ++;
  }


  // write number of entries
  Tsize_t entry = size();
  *( (Tsize_t*)ptr ) = entry;
  datasize += Tsizeof( Tsize_t );
  ( (Tsize_t*)ptr ) ++;


  omemory -> SetAddress( ptr );

  return( (Tint)datasize );
}

const TDataRecord& TDataRecord::operator=( const TDataRecord& right )
{
  *( (TStreamableObject*)this ) = *( (TStreamableObject*)(&right) );
  *( (TDataSectionList*)this ) = *( (TDataSectionList*)(&right) );
  return( *this );
}

Tbool TDataRecord::operator==( const TDataRecord& right ) const
{
  Tbool retval = Ttrue;
  retval &= ( *((TStreamableObject*)this) == *((TStreamableObject*)(&right)) );
  retval &= ( *((TDataSectionList*)this ) == *((TDataSectionList*)(&right)) );
  return( retval );
}

Tbool TDataRecord::operator!=( const TDataRecord& right ) const
{
  Tbool retval = Tfalse;
  retval |= ( *((TStreamableObject*)this) != *((TStreamableObject*)(&right)) );
  retval |= ( *((TDataSectionList*)this) != *((TDataSectionList*)(&right)) );
  return( retval );
}

Tvoid TDataRecord::Clear()
{
  theID = TunknownID;
  for ( Tsize_t i = 0; i < size(); i ++ )
    ( (*this)[ i ] ).Clear();
  clear();
  return;
}

Tbool TDataRecord::FindDataSection( const Tstring& id, TDataSection& section ) const
{
  for ( Tsize_t i = 0; i < size(); i ++ ) {
    if ( ( (*this)[ i ] ).GetID() == id ) {
      section = (*this)[ i ];
      return( Ttrue );
    }
  }
  return( Tfalse );
}

Tint TDataRecord::FindDataSection( const Tstring& id ) const
{
  Tint retval = -EFAULT;
  for ( Tsize_t i = 0; i < size(); i ++ ) {
    if ( ( (*this)[ i ] ).GetID() == id ) {
      retval = i;
      break;
    }
  }
  return( retval );
}

Tbool TDataRecord::FindDataSegment( const Tstring& secid, const Tstring& segid, TDataSegment& segment ) const
{
  TDataSection section;
  if ( FindDataSection( secid, section ) ) {
    return( section.FindDataSegment( segid, segment ) );
  } else {
    return( Tfalse );
  }
}

Tint TDataRecord::FindDataSegment( const Tstring& secid, const Tstring& segid ) const
{
  Tint secpos = FindDataSection( secid );
  if ( secpos >= 0 ) {
    return( ( (*this)[ secpos ] ).FindDataSegment( segid ) );
  } else {
    return( secpos );
  }
}

Tbool TDataRecord::FindDataSegment( Tstring idset[ 2 ], TDataSegment& segment ) const
{
  return( FindDataSegment( idset[ 0 ], idset[ 1 ], segment ) );
}

Tint TDataRecord::FindDataSegment( Tstring idset[ 2 ] ) const
{
  return( FindDataSegment( idset[ 0 ], idset[ 1 ] ) );
}

Tbool TDataRecord::FindDataSegment( const TstringList& idset, TDataSegment& segment ) const
{
  return( FindDataSegment( idset[ 0 ], idset[ 1 ], segment ) );
}

Tint TDataRecord::FindDataSegment( const TstringList& idset ) const
{
  return( FindDataSegment( idset[ 0 ], idset[ 1 ] ) );
}

Tbool TDataRecord::FindDataElement( const Tstring& secid, const Tstring& segid, const Tstring& eleid, TDataElement& element ) const
{
  TDataSection section;
  TDataSegment segment;
  if ( FindDataSection( secid, section ) && section.FindDataSegment( segid, segment ) ) {
    return( segment.FindDataElement( eleid, element ) );
  } else {
    return( Tfalse );
  }
}

Tint TDataRecord::FindDataElement( const Tstring& secid, const Tstring& segid, const Tstring& eleid ) const
{
  Tint secpos = FindDataSection( secid );
  if ( secpos >= 0 ) {
    Tint segpos = ( (*this)[ secpos ] ).FindDataSegment( segid );
    if ( segpos >= 0 ) {
      return( ( ( (*this)[ secpos ] )[ segpos ] ).FindDataElement( eleid ) );
    } else {
      return( segpos );
    }
  } else {
    return( secpos );
  }
}

Tbool TDataRecord::FindDataElement( Tstring idset[ 3 ], TDataElement& element ) const
{
  return( FindDataElement( idset[ 0 ], idset[ 1 ], idset[ 2 ], element ) );
}

Tint TDataRecord::FindDataElement( Tstring idset[ 3 ] ) const
{
  return( FindDataElement( idset[ 0 ], idset[ 1 ], idset[ 2 ] ) );
}

Tbool TDataRecord::FindDataElement( const TstringList& idset, TDataElement& element ) const
{
  return( FindDataElement( idset[ 0 ], idset[ 1 ], idset[ 2 ], element ) );
}

Tint TDataRecord::FindDataElement( const TstringList& idset ) const
{
  return( FindDataElement( idset[ 0 ], idset[ 1 ], idset[ 2 ] ) );
}

Tint TDataRecord::Serialize( Tvoid* buffer )
{
  Tsize_t datasize = 0;

  // write record size
  *( (Tint*)buffer ) = GetRecordSize();
  ( (Tint*)buffer ) ++;
  datasize += Tsizeof( Tint );


  // write object type
  *( (Tobject_t*)buffer ) = theObjectType;
  ( (Tobject_t*)buffer ) ++;
  datasize += Tsizeof( Tobject_t );


  // write object ID
  Tchar charbuf;
  Tsize_t nchar = theID.size() + 1;
  *( (Tsize_t*)buffer ) = nchar;
  ( (Tsize_t*)buffer ) ++;
  datasize += Tsizeof( Tsize_t );
  for ( Tsize_t i = 0; i < nchar; i ++ ) {
    if ( i == ( nchar - 1 ) ) {
      charbuf = '\0';
    } else {
      charbuf = theID[ i ];
    }
    *( (Tchar*)buffer ) = charbuf;
    ( (Tchar*)buffer ) ++;
    datasize += Tsizeof( Tchar );
  }

   
  // write number of entries
  *( (Tsize_t*)buffer ) = size();
  ( (Tsize_t*)buffer ) ++;
  datasize += Tsizeof( Tsize_t );

  for ( Tsize_t i = 0; i < size(); i ++ ) {
    Tint d = ( (*this)[ i ] ).Serialize( buffer );
    datasize += d;
    (Tbyte*)buffer += d;
  }


  return( (Tint)datasize );
}
