// =====================================================================
//  $Id: TSoftwareDataFileModule.cc,v 1.5 2004/03/07 10:30:31 goiwai Exp $
//  $Name: CLDAQ-1-14-03 $
//  $Log: TSoftwareDataFileModule.cc,v $
//  Revision 1.5  2004/03/07 10:30:31  goiwai
//  ROOTに組みこむためのおまじないマクロを埋めこみました。
//  全てにおいて完全に動作するわけではありません。
//
//  Revision 1.4  2003/10/06 17:02:40  goiwai
//  *** empty log message ***
//
//  Revision 1.3  2003/07/30 16:19:11  goiwai
//  ファイルにコミットログをつけることにしました.
//
// =====================================================================
#include "TSoftwareDataFileModule.hh"
#include "TDataSegment.hh"
#include "TDataElement.hh"

TSoftwareDataFileModule::TSoftwareDataFileModule( const Tstring& filename, Tint nchannel )
  : TSoftwareModule( nchannel ),
    theFileName( filename ), theSeparater( Tspace ), 
    theChannel( nchannel, 0.0 ), theBufferLength( tDefaultBufferLength ),
    theNumberOfLine( 0 )
{
  theCommentStringList.clear();
  theCommentStringList.push_back( Tsharp );
  theCommentStringList.push_back( Twslash );

  theInputFileStream.open( theFileName.c_str() );
  if ( !( theInputFileStream.good() ) || !( theInputFileStream.is_open() ) ) {
    Tcerr << "TSoftwareDataFileModule::TSoftwareDataFileModule: ";
    Tcerr << "fail to open a file " << theFileName << Tendl;
    exit( theStatus = -EFAULT );
  }
  theNumberOfLine = getNumberOfLine();
  Update();
}

TSoftwareDataFileModule::TSoftwareDataFileModule( const Tstring& filename, const TstringList& comment, Tint nchannel )
  : TSoftwareModule( nchannel ), 
    theFileName( filename ), theSeparater( Tspace ),
    theChannel( nchannel, 0.0 ), theBufferLength( tDefaultBufferLength ),
    theNumberOfLine( 0 ), theCommentStringList( comment )
{
  theInputFileStream.open( theFileName.c_str() );
  if ( !( theInputFileStream.good() ) || !( theInputFileStream.is_open() ) ) {
    Tcerr << "TSoftwareDataFileModule::TSoftwareDataFileModule: ";
    Tcerr << "fail to open a file " << theFileName << Tendl;
    exit( theStatus = -EFAULT );
  }
  theNumberOfLine = getNumberOfLine();
  Update();
}

TSoftwareDataFileModule::TSoftwareDataFileModule( const Tstring& filename, const Tstring& separater, Tint nchannel )
  : TSoftwareModule( nchannel ),
    theFileName( filename ), theSeparater( separater ),
    theChannel( nchannel, 0.0 ), theBufferLength( tDefaultBufferLength ),
    theNumberOfLine( 0 )
{
  theCommentStringList.clear();
  theCommentStringList.push_back( Tsharp );
  theCommentStringList.push_back( Twslash );

  theInputFileStream.open( theFileName.c_str() );
  if ( !( theInputFileStream.good() ) || !( theInputFileStream.is_open() ) ) {
    Tcerr << "TSoftwareDataFileModule::TSoftwareDataFileModule: ";
    Tcerr << "fail to open a file " << theFileName << Tendl;
    exit( theStatus = -EFAULT );
  }
  theNumberOfLine = getNumberOfLine();
  Update();
}

TSoftwareDataFileModule::TSoftwareDataFileModule( const Tstring& filename, const TstringList& comment, const Tstring& separater, Tint nchannel )
  : TSoftwareModule( nchannel ), 
    theFileName( filename ), theSeparater( separater ),
    theChannel( nchannel, 0.0 ), theBufferLength( tDefaultBufferLength ),
    theNumberOfLine( 0 ), theCommentStringList( comment )
{
  theInputFileStream.open( theFileName.c_str() );
  if ( !( theInputFileStream.good() ) || !( theInputFileStream.is_open() ) ) {
    Tcerr << "TSoftwareDataFileModule::TSoftwareDataFileModule: ";
    Tcerr << "fail to open a file " << theFileName << Tendl;
    exit( theStatus = -EFAULT );
  }
  theNumberOfLine = getNumberOfLine();
  Update();
}

TSoftwareDataFileModule::TSoftwareDataFileModule( const TSoftwareDataFileModule& right )
  : TSoftwareModule( right ),
    theFileName( right.theFileName ),
    theSeparater( right.theSeparater ),
    theChannel( right.theChannel ),
    theBufferLength( right.theBufferLength ),
    theNumberOfLine( 0 ),
    theCommentStringList( right.theCommentStringList )
{
  theInputFileStream.open( theFileName.c_str() );
  if ( !( theInputFileStream.good() ) || !( theInputFileStream.is_open() ) ) {
    Tcerr << "TSoftwareDataFileModule::TSoftwareDataFileModule: ";
    Tcerr << "fail to open a file " << theFileName << Tendl;
    exit( theStatus = -EFAULT );
  }
  theNumberOfLine = getNumberOfLine();
  Update();
}

TSoftwareDataFileModule::~TSoftwareDataFileModule()
{
  if ( theInputFileStream.is_open() )
    theInputFileStream.close();
}

Tint TSoftwareDataFileModule::Clear()
{
  for ( Tint i = 0; i < theNumberOfChannels; i ++ ) {
    theChannel[ i ] = 0.0;
  }
  return theStatus = tStatusSuccess;
}

Tint TSoftwareDataFileModule::Update()
{
  Tchar* linebuf = new Tchar[ theBufferLength ];
  Tstring strbuf;

  do {
    if ( theInputFileStream.eof() ) {
      // back to top of file, use a data again.
      Initialize();
    }
    theInputFileStream.getline( linebuf, theBufferLength );
    strbuf = linebuf;
    if ( !( strbuf.empty() ) )  // for empty line
      eraseComment( strbuf );
  } while ( strbuf.empty() );

  delete [] linebuf;

  theChannel = divideLine( strbuf );

  return theStatus = tStatusSuccess;
}

Tint TSoftwareDataFileModule::Initialize()
{
  Clear();
  if ( theInputFileStream.is_open() )
    theInputFileStream.close();
  theInputFileStream.open( theFileName.c_str() );
  if ( !( theInputFileStream.good() ) || !( theInputFileStream.is_open() ) ) {
    Tcerr << "TSoftwareDataFileModule::TSoftwareDataFileModule: ";
    Tcerr << "fail to open a file " << theFileName << Tendl;
  }
  theNumberOfLine = getNumberOfLine();
  return theStatus = tStatusSuccess;
}

Tvoid TSoftwareDataFileModule::FillData( TDataElement& element, Tint channel )
{
  if ( channel < 0 || channel >= theNumberOfChannels ) {
    Tcerr << "TSoftwareDataFileModule::FillData: invalid ID " << channel << Tendl;
    theStatus = -EFAULT;
    element.FillData( &theStatus, tTypeInt, 1 );
  } else {
    element.FillData( &theChannel[ channel ], tTypeDouble, 1 );
  }
  return;
}

const TSoftwareDataFileModule& TSoftwareDataFileModule::operator=( const TSoftwareDataFileModule& right )
{
  *( (TSoftwareModule*)this ) = *( (TSoftwareModule*)(&right) );
  theFileName = right.theFileName;
  theSeparater = right.theSeparater;
  theChannel = right.theChannel;
  theBufferLength = right.theBufferLength;
  theCommentStringList = right.theCommentStringList;
  theInputFileStream.open( theFileName.c_str() );
  if ( !( theInputFileStream.good() ) || !( theInputFileStream.is_open() ) ) {
    Tcerr << "TSoftwareDataFileModule::operator=: ";
    Tcerr << "fail to open a file " << theFileName << Tendl;
    exit( theStatus = -EFAULT );
  }
  theNumberOfLine = getNumberOfLine();
  Update();
  return *this;
}

Tbool TSoftwareDataFileModule::operator==( const TSoftwareDataFileModule& right ) const
{
  Tbool ret = Ttrue;
  ret &= ( *( (TSoftwareModule*)this ) == *( (TSoftwareModule*)(&right) ) );
  ret &= ( theFileName == right.theFileName );
  ret &= ( theSeparater == right.theSeparater );
  ret &= ( theChannel == right.theChannel );
  ret &= ( theBufferLength == right.theBufferLength );
  ret &= ( theNumberOfLine == right.theNumberOfLine );
  ret &= ( theCommentStringList == right.theCommentStringList );
  return ret;
}

Tbool TSoftwareDataFileModule::operator!=( const TSoftwareDataFileModule& right ) const
{
  Tbool ret = Tfalse;
  ret |= ( *( (TSoftwareModule*)this ) != *( (TSoftwareModule*)(&right) ) );
  ret |= ( theFileName != right.theFileName );
  ret |= ( theSeparater != right.theSeparater );
  ret |= ( theChannel != right.theChannel );
  ret |= ( theBufferLength != right.theBufferLength );
  ret |= ( theNumberOfLine != right.theNumberOfLine );
  ret |= ( theCommentStringList != right.theCommentStringList );
  return ret;
}

const Tstring& TSoftwareDataFileModule::eraseComment( Tstring& readline ) const
{
  for ( Tsize_t i = 0; i < theCommentStringList.size(); i ++ ) {
    Tsize_t pos = readline.find( theCommentStringList[ i ] );
    Tsize_t bufsize = readline.size();
    if ( pos >= 0 && pos <= bufsize - theCommentStringList[ i ].size() ) {
      readline.erase( pos, bufsize - pos );
    }
  }

  while ( !( readline.empty() ) && readline.rfind( Tspace ) == readline.size() - 1 ) {
    readline.erase( readline.end() - 1 );
  }

  while ( !( readline.empty() ) && readline.find( Tspace ) == 0 ) {
    readline.erase( readline.begin() );
  }

  return readline;
}

TdoubleList TSoftwareDataFileModule::divideLine( Tstring readline ) const
{
  Tsize_t begin = 0;
  Tsize_t end = 0;
  Tstring strbuf;
  TdoubleList dbllist;
  static const Tstring head = "TSoftwareDataFileModule::divideLine(): ";

  if ( !( readline.empty() ) ) {
    Tsize_t lastpos = readline.rfind( theSeparater ) + 1;
    do {
      begin = readline.find_first_not_of( theSeparater, end );
      end = readline.find( theSeparater, begin );
      if ( end > readline.size() ) {
	end = readline.size();
      }
      strbuf = readline.substr( begin, end - begin );
      dbllist.push_back( atof( strbuf.c_str() ) );
    } while ( begin != lastpos );
  }

  if ( dbllist.size() > (Tsize_t)theNumberOfChannels ) {
    Tcerr << head << "too much channels." << Tendl;
    Tcerr << head << "rows and channels are inconsistency." << Tendl;
    dbllist.erase( dbllist.begin() + theNumberOfChannels, dbllist.end() );
  } else if ( dbllist.size() < (Tsize_t)theNumberOfChannels ) {
    Tcerr << head << "less than required channels." << Tendl;
    Tcerr << head << "filled 0.0." << Tendl;
    for ( Tsize_t i = 0; i < theNumberOfChannels - dbllist.size(); i ++ ) {
      dbllist.push_back( 0.0 );
    }
  }

  return dbllist;
}

Tint TSoftwareDataFileModule::getNumberOfLine() const
{
  static const Tstring head = "TSoftwareDataFileModule::getNumberOfLine() ";

  Tint nline = 0;
  Tifstream tifs;
  tifs.open( theFileName.c_str() );
  if ( !( tifs.good() ) || !( tifs.is_open() ) ) {
    Tcerr << head << "fail to open a file " << theFileName << Tendl;
  }


  Tchar* linebuf = new Tchar[ theBufferLength ];
  Tstring strbuf;

  while ( !( tifs.eof() ) ) {
    do {
      if ( tifs.eof() ) {
	break;
      }
      tifs.getline( linebuf, theBufferLength );
      strbuf = linebuf;
      if ( !( strbuf.empty() ) ) { // for empty line
        eraseComment( strbuf );
      }
    } while ( strbuf.empty() );
    if ( !( tifs.eof() ) ) {
      nline ++;
    }
  }
  delete [] linebuf;
  tifs.close();

  return nline;
}

#ifdef __CLDAQ_ROOT_DLL
    ClassImp(TSoftwareDataFileModule)
#endif
