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

TDataSegment::TDataSegment( const Tstring& id )
  : TStreamableObject( tObjectDataSegment, id ), TDataElementList()
{;}

TDataSegment::TDataSegment( const TDataSegment& right )
  : TStreamableObject( right ), TDataElementList( right )
{;}

TDataSegment::~TDataSegment()
{;}

Tint TDataSegment::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 TDataSegment::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 << "TDataSegment::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 TDataSegment& right )
{
  tos << "Data Segment(" << 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 << Ttab << Ttab << "[" << i << "] " << right[ i ];
    if ( i != right.size() - 1 ) {
      tos << Tendl;
    }
  }

  return tos;
}

Tint TDataSegment::record( TOutputObjectFile* ofile )
{
  Tsize_t datasize = 0;
  static const Tsize_t nmemb = 1;
  Tsize_t ss = 0;
  static const Tstring head = "TDataSegment::record";

  // write record size
  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 TDataSegment::record( TOutputObjectSocket* osocket )
{
  Tsize_t datasize = 0;
  static const Tstring head = "TDataSegment::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 TDataSegment::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 TDataSegment& TDataSegment::operator=( const TDataSegment& right )
{
  *( (TStreamableObject*)this ) = *( (TStreamableObject*)(&right) );
  *( (TDataElementList*)this ) = *( (TDataElementList*)(&right) );
  return *this;
}

Tbool TDataSegment::operator==( const TDataSegment& right ) const
{
  Tbool ret = Ttrue;
  ret &= ( *((TStreamableObject*)this) == *((TStreamableObject*)(&right)) );
  ret &= ( *((TDataElementList*)this ) == *( (TDataElementList*)(&right)) );
  return ret;
}

Tbool TDataSegment::operator!=( const TDataSegment& right ) const
{
  Tbool ret = Tfalse;
  ret |= ( *((TStreamableObject*)this) != *((TStreamableObject*)(&right)) );
  ret |= ( *((TDataElementList*)this) != *((TDataElementList*)(&right)) );
  return ret;
}

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

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

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

Tbool TDataSegment::FindDataElement( const Tstring& id, TDataElement& element ) const
{
  for ( Tsize_t i = 0; i < size(); i ++ ) {
    if ( ( (*this)[ i ] ).GetID() == id ) {
      element = (*this)[ i ];
      return Ttrue;
    }
  }
  return Tfalse;
}

Tint TDataSegment::FindDataElement( 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;
}

Tint TDataSegment::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 TDataSegment::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 dataelement
  for ( Tsize_t i = 0; i < nentry; i ++ ) {
    TDataElement element;
    Tint d = element.Deserialize( p );
    this -> push_back( element );
    datasize += d;
    (Tbyte*)p += d;
  }

  return (Tint)datasize;
}

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

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

const TDataElement& TDataSegment::operator[]( const Tstring& id ) const
{
  return (*this)[ FindDataElement( id ) ];
}

TDataElement& TDataSegment::operator[]( const Tstring& id )
{
  return (*this)[ FindDataElement( id ) ];
}

#ifdef __CLDAQ_ROOT_DLL
    ClassImp(TDataSegment)
#endif
