// =====================================================================
//  $Id: TDataRecord.cc,v 1.10 2004/03/07 10:30:30 goiwai Exp $
//  $Name: CLDAQ-1-14-03 $
//  $Log: TDataRecord.cc,v $
//  Revision 1.10  2004/03/07 10:30:30  goiwai
//  ROOTに組みこむためのおまじないマクロを埋めこみました。
//  全てにおいて完全に動作するわけではありません。
//
//  Revision 1.9  2003/12/07 05:07:34  goiwai
//  Test()が作るデータを4kBちょうどにしました.
//
//  Revision 1.8  2003/12/06 10:59:11  goiwai
//  Serialize(Tvoid*) -> Serialize(const Tvoid*)に変更しました.
//  Deserialize(const Tvoid*)を追加しました.
//  手抜きの実装なので,バッファオーバーフローの可能性があります.
//  Reallocate()とかするような仕組みが必要です.
//
//  Revision 1.7  2003/11/04 12:03:05  goiwai
//  ==演算子と!=演算子を実装しました.具体的には
//  if ( record == "String" ) {
//    hoeghoge;
//  }
//  のような使い方が出来ます.単純に theID メンバとを比較して結果を返します.
//
//  Revision 1.6  2003/10/12 13:06:34  goiwai
//  Update()を追加しました.
//
//  Revision 1.5  2003/10/06 16:42:08  goiwai
//  テスト用の関数としてTest()を加えました.適当なデータレコードを発生しま
//  す.
//
//  Revision 1.4  2003/08/25 09:19:37  goiwai
//  operator[]( const Tstring& id ) を加えました.
//  record[0] とか record["tag"] とかすれば,TDataSection を得ることが出来
//  ます.
//  size()を越えた範囲にも,チェックしないでアクセスします.
//  マッチしなかった場合は落ちます.
//  同じ名前でタグされた DataSection がある場合,最初にマッチしたものが返っ
//  てきます.
//
//  Revision 1.3  2003/07/30 16:17:51  goiwai
//  ファイルにコミットログをつけることにしました.
//
// =====================================================================
#include "TDataRecord.hh"
#include "TOutputObjectStream.hh"
#include "TOutputObjectFile.hh"
#include "TOutputObjectSocket.hh"
#include "TOutputObjectSharedMemory.hh"
#include "TSystemClock.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 << "), "
      << "ID: " << right.theID
      << ", Capacity: " << right.capacity()
      << ", Entry: " << right.size();

  if ( right.empty() ) {
    return tos;
  }

  tos << Tendl;

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

  return tos;
}

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;
}

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;
}

Tbool TDataRecord::operator==( const Tstring& right ) const
{
  if ( theID == right ) {
    return Ttrue;
  } else {
    return Tfalse;
  }
}

Tbool TDataRecord::operator!=( const Tstring& right ) const
{
  if ( theID != right ) {
    return Ttrue;
  } else {
    return Tfalse;
  }
}

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( const Tvoid* buffer )
{
  Tvoid* p = const_cast<Tvoid*>(buffer);
  Tsize_t datasize = 0;

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


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


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

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

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

  return (Tint)datasize;
}

Tint TDataRecord::Deserialize( const Tvoid* buffer )
{
  Tvoid* p = const_cast<Tvoid*>(buffer);  
  Tsize_t datasize = 0;

  // read record size
  //SetRecordSize( *( (Tint*)p ) );
  ( (Tint*)p ) ++;
  datasize += Tsizeof( Tint );


  // read object type
  theObjectType = *( (Tobject_t*)p );
  ( (Tobject_t*)p ) ++;
  datasize += Tsizeof( Tobject_t );


  // read object ID
  Tsize_t nchar = *( (Tsize_t*)p );
  ( (Tsize_t*)p ) ++;
  datasize += Tsizeof( Tsize_t );

  Tchar* cc = new Tchar[ nchar ];
  for ( Tsize_t i = 0; i < nchar; i ++ ) {
    cc[i] = *( (Tchar*)p );
    datasize += Tsizeof( Tchar );
    ( (Tchar*)p ) ++;
  }
  theID = cc;
  delete [] cc;

  // read number of entries
  Tsize_t nentry = *( (Tsize_t*)p );
  ( (Tsize_t*)p ) ++;
  datasize += Tsizeof( Tsize_t );


  // push datasection
  for ( Tsize_t i = 0; i < nentry; i ++ ) {
    TDataSection section;
    Tint d = section.Deserialize( p );
    push_back( section );
    datasize += d;
    (Tbyte*)p += d;
  }

  return (Tint)datasize;
}

const TDataSection& TDataRecord::operator[]( Tint n ) const
{
  return *( begin() + n );
}

TDataSection& TDataRecord::operator[]( Tint n )
{
  return *( begin() + n );
}

const TDataSection& TDataRecord::operator[]( const Tstring& id ) const
{
  return (*this)[ FindDataSection( id ) ];
}

TDataSection& TDataRecord::operator[]( const Tstring& id )
{
  return (*this)[ FindDataSection( id ) ];
}

TDataRecord TDataRecord::Test( Tint somevalue )
{
  TDataRecord record( "This is a Data Record." );
  TDataSection section( "This is Data Section." );
  TDataSegment segment0( "This is a Data Segement identified 0." );
  TDataSegment segment1( "This is a Data Segement identified 1." );
  TDataSegment segment2( "This is a Data Segement identified 2." );


  TSystemClock clock;
  Tint pasttime = clock.GetTime();
  Tstring tstr = clock.WhatTimeIsItNow();

  const Tint nstrdata = 8;
  Tstring strdata[ nstrdata ] = { 
    "ABCDEF GHIJK LMN OPQR STU VWXYZ END", 
    "abcefghijklmnopqrstuvwxyz abcefghijklmnopqrstuvwxyz end",
    "ABCDEF GHIJK LMN OPQR STU VWXYZ END", 
    "abcefghijklmnopqrstuvwxyz abcefghijklmnopqrstuvwxyz end",
    "ABCDEFABCDEFABCDEFABCDEFABCDEFABCDEF",
    "GHIJKGHIJKGHIJK GHIJKGHIJKGHIJK",
    "LMNOPQRLMNOPQRLMNOPQR",
    "STU VWXYZ STU VWXYZ STU VWXYZ"
  };
  const Tint ndata = 0x500;
  TUshort array[ ndata ];
  for ( TUshort i = 0; i < ndata; i ++ ) {
    array[ i ] = i + i;
  }
  segment0.push_back( TDataElement( &pasttime, tTypeInt, "This is a integer data(TIME).", 1 ) );
  segment0.push_back( TDataElement( &tstr, tTypeString, "This is a string data(TIME).", 1 ) );
  segment0.push_back( TDataElement( strdata, tTypeString, "This is a string array.", nstrdata ) );
  segment0.push_back( TDataElement( array, tTypeUnsignedShort, "This is unsigned short data array.", ndata ) );
  for ( Tint i = 0; i < 8; i ++ ) {
    Tint data = i + i * i + 0x1234 * somevalue;
    segment1.push_back( TDataElement( &data, tTypeInt, itostr( i ) ) );
  }
  for ( Tdouble i = 0.0; i < 16.0; i ++ ) {
    Tdouble data = sin( i ) / exp( i );
    segment2.push_back( TDataElement( &data, tTypeDouble, dtostr( i ) ) );
  }
  section.push_back( segment0 );
  section.push_back( segment1 );
  section.push_back( segment2 );
  record.push_back( section );

  *this = record;

  return *this;
}

#ifdef __CLDAQ_ROOT_DLL
    ClassImp(TDataRecord)
#endif
