// =====================================================================
//  $Id: TDecompressObjectFilter.cc,v 1.6 2004/06/24 15:15:25 goiwai Exp $
//  $Name: CLDAQ-1-14-06 $
//  $Log: TDecompressObjectFilter.cc,v $
//  Revision 1.6  2004/06/24 15:15:25  goiwai
//  たくさんの変更
//  Tcout,Tcerr,Tlogをバッファ経由にした。
//  何個かexternした。Tlog,Tcout,Tcerr,Tinfo...Tcritなど出力関係。
//
//  Revision 1.5  2004/03/07 16:52:34  goiwai
//  __CLDAQ_ZLIB_USEプロプロセッサの位置をちょっとずらしただけです。
//
//  Revision 1.4  2004/03/07 10:30:32  goiwai
//  ROOTに組みこむためのおまじないマクロを埋めこみました。
//  全てにおいて完全に動作するわけではありません。
//
//  Revision 1.3  2004/01/19 23:01:17  goiwai
//  Tsyslog.h => TSystemLoggin.hh
//
//  Revision 1.2  2003/12/07 08:17:24  goiwai
//  ファイルの終端間際でデコードに失敗するバグを修整しました.
//
//  Revision 1.1  2003/12/06 10:48:38  goiwai
//  可逆な圧縮フィルタです.
//  手抜きの実装なので,まだファイルにしか対応していませんが,2倍以上速度性
//  能が向上します.
//
// =====================================================================
#ifdef __CLDAQ_ZLIB_USE
#include "TDecompressObjectFilter.hh"
#include "TStreamableObject.hh"
#include "TInputObjectStream.hh"
#include "TOutputObjectStream.hh"
#include "TObjectStream.hh"
#include "TInputObjectFile.hh"
#include "TDataRecord.hh"

static const Tint _maxbuf = 0x80000;
static Tbyte _buf[_maxbuf];
static TInputObjectFile* _ifs = 0;

TDecompressObjectFilter::TDecompressObjectFilter()
  : TInputObjectFilter(), theDecoder()
{;}

TDecompressObjectFilter::~TDecompressObjectFilter()
{;}

Tint TDecompressObjectFilter::Filtering( TStreamableObject& object, TInputObjectStream* stream )
{
  _ifs = (TInputObjectFile*)stream;
  Tvoid* staticbuffer = _buf;

  Tint totaldatasize = _ifs -> GetTotalDataSize();
  Tint nread = 0;
  _ifs -> SetLastDataSize( _ifs -> GetDataSize() );
  _ifs -> SetDataSize( nread );


  static const Tint buflen = 0xff;
  static Tbyte buffer[buflen];
  static Tint excess = 0;
  static Tint lastrecord = 0;
  //Tint lastinflated = excess + lastrecord;

  if ( _ifs -> IsEnd() && excess <= (Tint)Tsizeof(Tint) ) {
    //Tinfo << "EXCESS: " << excess << Tendl;
    return 0;
  }


  // 高速化手法みたいな
  if ( excess > 0 ) {
    Tvoid* p = _buf;
    ((Tbyte*)p) += lastrecord;
    memcpy(staticbuffer,p,excess);
    ((Tbyte*)staticbuffer) += excess;
  }


  // 先頭の 4バイト分だけを inflate オブジェクトサイズを読みますです
  Tint inflated = excess;
  while ( inflated < (Tint)Tsizeof(Tint) ) {
    Tint gcount = fread(buffer,Tsizeof(Tbyte),buflen,_ifs->GetFileStream());
    if ( gcount == 0 ) {
      if ( _ifs -> IsEnd() ) {
        return 0;
      }
      
      //CLDAQ_DEBUG("");
      //Tcout << "DEBUG: " << _ifs->GetTotalDataSize() << Tendl;
      //Tcout << "DEBUG: " << excess << Tendl;
      //Tcout << "DEBUG: " << inflated << Tendl;
      //Tcout << "DEBUG: " << lastrecord << Tendl;
      Tcerr << "READ ERROR in " << buflen << " BYTE READ" << Tendl;

      break;
    }
    nread += gcount;
    theDecoder.Decompress(buffer,gcount);
    Tbyte* p = (Tbyte*)theDecoder.GetInflatedData();
    Tint s = theDecoder.GetSizeOfInflatedData();
    inflated += s;
    memcpy(staticbuffer,p,s);
    ((Tbyte*)staticbuffer) += s;
  }



  // オブジェクトサイズ分だけを inflate
  Tint recordsize = *( (Tint*)_buf );
  while ( inflated < recordsize ) {
    Tint gcount = fread(buffer,Tsizeof(Tbyte),buflen,_ifs->GetFileStream());
    if ( gcount == 0 ) {
      if ( _ifs -> IsEnd() ) {
        return 0;
      }
//       CLDAQ_DEBUG("");
//       Tcout << "DEBUG: " << _ifs->GetTotalDataSize() << Tendl;
//       Tcout << "DEBUG: " << excess << Tendl;
//       Tcout << "DEBUG: " << inflated << Tendl;
//       Tcout << "DEBUG: " << lastrecord << Tendl;
      Tcerr << "READ ERROR in " << buflen << " BYTE READ" << Tendl;
      break;
    }
    nread += gcount;
    theDecoder.Decompress(buffer,gcount);
    Tbyte* p = (Tbyte*)theDecoder.GetInflatedData();
    Tint s = theDecoder.GetSizeOfInflatedData();
    inflated += s;
    memcpy(staticbuffer,p,s);
    ((Tbyte*)staticbuffer) += s;
  }


  Tobject_t objecttype = object.GetObjectType();
  Tint datasize = 0;
  TDataRecord record;
  TDataSection section;
  TDataSegment segment;
  TDataElement element;
  switch ( objecttype ) {
    case tObjectDataRecord:
      datasize = record.Deserialize( _buf );
      *( (TDataRecord*)&object ) = record;
      break;
    case tObjectDataSection:
      datasize = section.Deserialize( _buf );
      *( (TDataSection*)&object ) = section;
      break;
    case tObjectDataSegment:
      datasize = segment.Deserialize( _buf );
      *( (TDataSegment*)&object ) = segment;
      break;
    case tObjectDataElement:
      datasize = element.Deserialize( _buf );
      *( (TDataElement*)&object ) = element;
      break;
    default:
      break;
  }

  excess = inflated - recordsize;
  lastrecord = recordsize;


  _ifs -> SetDataSize( nread );
  _ifs -> SetTotalDataSize( totaldatasize + nread );

  return datasize;
}

Tvoid TDecompressObjectFilter::Initialize( TInputObjectStream* stream )
{
  Tstream_t streamtype = stream -> GetStreamType();
  if ( streamtype != tFileStream ) {
    Tcout << "Not TFileStream" << Tendl;
    exit( EXIT_SUCCESS );
  }
  return;
}

Tvoid TDecompressObjectFilter::Finalize( TInputObjectStream* stream )
{
  return;
}

#ifdef __CLDAQ_ROOT_DLL
    ClassImp(TDecompressObjectFilter)
#endif

#endif
