// =====================================================================
//  $Id: TInputObjectSocket.cc,v 1.5 2004/03/07 10:30:32 goiwai Exp $
//  $Name: CLDAQ-1-14-06 $
//  $Log: TInputObjectSocket.cc,v $
//  Revision 1.5  2004/03/07 10:30:32  goiwai
//  ROOTに組みこむためのおまじないマクロを埋めこみました。
//  全てにおいて完全に動作するわけではありません。
//
//  Revision 1.4  2003/12/06 10:56:19  goiwai
//  フィルタ関連の修整と Read(),Write()の戻り値の修整です.
//  Read()もWrite()も処理したレコードサイズを返します.
//  フィルタがある場合も同様です.
//  それぞれのストリームのDataSizeの扱いですが,これは扱ったレコードサイズ
//  でなく扱ったファイルやソケットなどのサイズになります.レコードサイズと
//  一致するとは限りません.
//
//  Revision 1.3  2003/10/06 17:02:42  goiwai
//  *** empty log message ***
//
//  Revision 1.2  2003/07/30 16:19:30  goiwai
//  ファイルにコミットログをつけることにしました.
//
// =====================================================================
#include "TInputObjectSocket.hh"
#include "TStreamableObject.hh"
#include "TDataRecord.hh"
#include "TDataSection.hh"
#include "TDataSegment.hh"
#include "TDataElement.hh"
#include "TInputObjectFilter.hh"

TInputObjectSocket::TInputObjectSocket( Tint port )
  : TObjectSocket( port ), TInputObjectStream( tSocketStream ),
    theClientDescriptor( -1 )
{
  initialize();
  OpenServer();
  OpenClient();
}

TInputObjectSocket::TInputObjectSocket( TInputObjectFilter* filter, Tint port )
  : TObjectSocket( port ), TInputObjectStream( filter, tSocketStream ),
    theClientDescriptor( -1 )
{
  initialize();
  OpenServer();
  OpenClient();
}

TInputObjectSocket::~TInputObjectSocket()
{
  CloseClient();
  CloseServer();
}

Tint TInputObjectSocket::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;
  }

  theTotalDataSize += theDataSize;
  return theDataSize;
}

TDataRecord TInputObjectSocket::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 TInputObjectSocket::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 TInputObjectSocket::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 TInputObjectSocket::getDataElement()
{
  static const Tstring head = "TInputObjectSocket::getDataElement";
  Tint recsize;
  Tobject_t otype;
  Tstring id;
  Telement_t etype;
  Tint nprvs;
  TDataElement element;


  // receive record size
  if ( recv( theClientDescriptor, &recsize, Tsizeof( Tint ), 0 ) != (Tint)Tsizeof( Tint ) ) {
    theDataSize = 0;
    perror( head.c_str() );
    return element;
  } else {
    theDataSize += Tsizeof( Tint );
  }


  // receive object type
  if ( recv( theClientDescriptor, &otype, Tsizeof( Tobject_t ), 0 ) != (Tint)Tsizeof( Tobject_t ) ) {
    theDataSize = 0;
    perror( head.c_str() );
    return element;
  } else {
    theDataSize += Tsizeof( Tobject_t );
    element.SetObjectType( otype );
  }

  // receive object id
  Tsize_t nchar;
  Tchar* charbuf = 0;
  if ( recv( theClientDescriptor, &nchar, Tsizeof( Tsize_t ), 0 ) != (Tint)Tsizeof( Tsize_t ) ) {
    theDataSize = 0;
    perror( head.c_str() );
    return element;
  } else {
    theDataSize += Tsizeof( Tsize_t );
    charbuf = new Tchar[ nchar ];
  }
  for ( Tsize_t i = 0; i < nchar; i ++ ) {
    if ( recv( theClientDescriptor, &charbuf[ i ], Tsizeof( Tchar ), 0 ) != (Tint)Tsizeof( Tchar ) ) {
      theDataSize = 0;
      perror( head.c_str() );
      delete [] charbuf;
      return element;
    } else {
      theDataSize += Tsizeof( Tchar );
    }
  }
  id = charbuf;
  delete [] charbuf;
  element.SetID( id );



  // receive element type
  if ( recv( theClientDescriptor, &etype, Tsizeof( Telement_t ), 0 ) != Tsizeof( Telement_t ) ) {
    theDataSize = 0;
    perror( head.c_str() );
    return element;
  } else {
    theDataSize += Tsizeof( Telement_t );
    element.SetElementType( etype );
  }



  // receive number of primitives
  if ( recv( theClientDescriptor, &nprvs, Tsizeof( Tint ), 0 ) != Tsizeof( Tint ) ) {
    theDataSize = 0;
    perror( head.c_str() );
    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;
  Tsize_t rsize;

  switch ( etype ) {

    case tTypeInt:
      ii = new Tint[ nprvs ];
      rsize = Tsizeof( Tint );
      for ( Tint i = 0; i < nprvs; i ++ ) {
	if ( recv( theClientDescriptor, &ii[ i ], rsize, 0 ) != (Tint)rsize ) {
	  theDataSize = 0;
	  delete [] ii;
	  return element;
	} else {
	  theDataSize += (Tint)rsize;
	}
      }
      element.FillData( ii, nprvs );
      delete [] ii;
      break;


    case tTypeUnsignedInt:
      ui = new TUint[ nprvs ];
      rsize = Tsizeof( TUint );
      for ( Tint i = 0; i < nprvs; i ++ ) {
	if ( recv( theClientDescriptor, &ui[ i ], rsize, 0 ) != (Tint)rsize ) {
	  theDataSize = 0;
	  delete [] ui;
	  return element;
	} else {
	  theDataSize += (Tint)rsize;
	}
      }
      element.FillData( ui, nprvs );
      delete [] ui;
      break;


    case tTypeWord:
    case tTypeUnsignedShort:
      us = new TUshort[ nprvs ];
      rsize = Tsizeof( TUshort );
      for ( Tint i = 0; i < nprvs; i ++ ) {
	if ( recv( theClientDescriptor, &us[ i ], rsize, 0 ) != (Tint)rsize ) {
	  theDataSize = 0;
	  delete [] us;
	  return element;
	} else {
	  theDataSize += (Tint)rsize;
	}
      }
      element.FillData( us, nprvs );
      delete [] us;
      break;

    case tTypeDouble:
      dd = new Tdouble[ nprvs ];
      rsize = Tsizeof( Tdouble );
      for ( Tint i = 0; i < nprvs; i ++ ) {
	if ( recv( theClientDescriptor, &dd[ i ], rsize, 0 ) != (Tint)rsize ) {
	  theDataSize = 0;
	  delete [] dd;
	  return element;
	} else {
	  theDataSize += (Tint)rsize;
	}
      }
      element.FillData( dd, nprvs );
      delete [] dd;
      break;

    case tTypeFloat:
      ff = new Tfloat[ nprvs ];
      rsize = Tsizeof( Tfloat );
      for ( Tint i = 0; i < nprvs; i ++ ) {
	if ( recv( theClientDescriptor, &ff[ i ], rsize, 0 ) != (Tint)rsize ) {
	  theDataSize = 0;
	  delete [] ff;
	  return element;
	} else {
	  theDataSize += (Tint)rsize;
	}
      }
      element.FillData( ff, nprvs );
      delete [] ff;
      break;

    case tTypeShort:
      ss = new Tshort[ nprvs ];
      rsize = Tsizeof( Tshort );
      for ( Tint i = 0; i < nprvs; i ++ ) {
	if ( recv( theClientDescriptor, &ss[ i ], rsize, 0 ) != (Tint)rsize ) {
	  theDataSize = 0;
	  delete [] ss;
	  return element;
	} else {
	  theDataSize += (Tint)rsize;
	}
      }
      element.FillData( ss, nprvs );
      delete [] ss;
      break;

    case tTypeLong:
      ll = new Tlong[ nprvs ];
      rsize = Tsizeof( Tlong );
      for ( Tint i = 0; i < nprvs; i ++ ) {
	if ( recv( theClientDescriptor, &ll[ i ], rsize, 0 ) != (Tint)rsize ) {
	  theDataSize = 0;
	  delete [] ll;
	  return element;
	} else {
	  theDataSize += (Tint)rsize;
	}
      }
      element.FillData( ll, nprvs );
      delete [] ll;
      break;

    case tTypeUnsignedLong:
      ul = new TUlong[ nprvs ];
      rsize = Tsizeof( TUlong );
      for ( Tint i = 0; i < nprvs; i ++ ) {
	if ( recv( theClientDescriptor, &ul[ i ], rsize, 0 ) != (Tint)rsize ) {
	  theDataSize = 0;
	  delete [] ul;
	  return element;
	} else {
	  theDataSize += (Tint)rsize;
	}
      }
      element.FillData( ul, nprvs );
      delete [] ul;
      break;

    case tTypeString:
      strbuf = new Tstring[ nprvs ];
      for ( Tint i = 0; i < nprvs; i ++ ) {
	Tsize_t nchar;
	rsize = Tsizeof( Tsize_t );
	if ( recv( theClientDescriptor, &nchar, rsize, 0 ) != (Tint)rsize ) {
	  theDataSize = 0;
	  delete [] strbuf;
	  return element;
	} else {
	  theDataSize += (Tint)rsize;
	  cc = new Tchar[ nchar ];
	}
	rsize = Tsizeof( Tchar );
	for ( Tsize_t j = 0; j < nchar; j ++ ) {
	  if ( recv( theClientDescriptor, &cc[ j ], rsize, 0 ) != (Tint)rsize ) {
	    theDataSize = 0;
	    delete [] cc;
	    delete [] strbuf;
	    return element;
	  } else {
	    theDataSize += (Tint)rsize;
	  }
	}
	strbuf[ i ] = cc;
	delete [] cc;
      }
      element.FillData( strbuf, nprvs );
      delete [] strbuf;
      break;

    case tTypeUnknown:
    case tTypeObject:
    default:
      break;
  }

  return element;
}

Tint TInputObjectSocket::OpenClient()
{
  Tsocklen_t server_len = (Tsocklen_t)Tsizeof( theAddress );
  if ( bind( theServerDescriptor, (struct sockaddr *)&theAddress, server_len ) == -1 ) {
    perror( "TInputObjectSocket::OpenClient" );
    exit( -errno );
  }

  if ( listen( theServerDescriptor, tDefaultBackLog ) == -1 ) {
    perror( "TInputObjectSocket::OpenClient" );
    exit( -errno );
  }

  struct sockaddr_in client_address;
  Tsocklen_t client_len;
  theClientDescriptor = accept( theServerDescriptor, (struct sockaddr *)&client_address, &client_len );
  if ( theClientDescriptor == -1 )
    perror( "TInputObjectSocket::OpenClient" );
  return theClientDescriptor;
}

Tint TInputObjectSocket::CloseClient()
{
  if ( shutdown( theClientDescriptor, 2 ) == -1 ) {
    perror( "TInputObjectSocket::CloseClient" );
    return -1;
  }

  if ( close( theClientDescriptor ) == -1 ) {
    perror( "TInputObjectSocket::CloseClient" );
    return -1;
  }
  theClientDescriptor = -1;
  return 0;
}

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

  // receive record size
  if ( recv( theClientDescriptor, &recsize, Tsizeof( Tint ), 0 ) != (Tint)Tsizeof( Tint ) ) {
    //perror( head.c_str() );
    theDataSize = 0;
    return;
  } else {
    theDataSize += Tsizeof( Tint );
  }


  // receive object type
  if ( recv( theClientDescriptor, &type, Tsizeof( Tobject_t ), 0 ) != (Tint)Tsizeof( Tobject_t ) ) {
    //perror( head.c_str() );
    theDataSize = 0;
    return;
  } else {
    theDataSize += Tsizeof( Tobject_t );
  }

  // receive object id
  Tsize_t nchar;
  Tchar* charbuf = 0;
  if ( recv( theClientDescriptor, &nchar, Tsizeof( Tsize_t ), 0 ) != (Tint)Tsizeof( Tsize_t ) ) {
    //perror( head.c_str() );
    theDataSize = 0;
    return;
  } else {
    theDataSize += Tsizeof( Tsize_t );
    charbuf = new Tchar[ nchar ];
  }
  for ( Tsize_t i = 0; i < nchar; i ++ ) {
    if ( recv( theClientDescriptor, &charbuf[ i ], Tsizeof( Tchar ), 0 ) != (Tint)Tsizeof( Tchar ) ) {
      //perror( head.c_str() );
      theDataSize = 0;
      delete [] charbuf;
      return;
    } else {
      theDataSize += Tsizeof( Tchar );
    }
  }
  id = charbuf;
  delete [] charbuf;


  // receive entries
  if ( recv( theClientDescriptor, &entries, Tsizeof( Tsize_t ), 0 ) != Tsizeof( Tsize_t ) ) {
    //perror( head.c_str() );
    theDataSize = 0;
    return;
  } else {
    theDataSize += Tsizeof( Tsize_t );
  }

  return;
}

Tvoid TInputObjectSocket::initialize()
{
  theAddress.sin_family = PF_INET;
  theAddress.sin_port = htons( (TUshort)thePortNumber );
  theAddress.sin_addr.s_addr = htonl( INADDR_ANY );
  //signal( SIGCHLD, SIG_IGN );
  //signal( SIGPIPE, SIG_IGN );
  return;
}

#ifdef __CLDAQ_ROOT_DLL
    ClassImp(TInputObjectSocket)
#endif
