// ============================================================================
//  $Id: TRegularExpression.cc,v 1.4 2004/03/07 10:30:34 goiwai Exp $
//  $Name: CLDAQ-1-14-03 $
//  $Log: TRegularExpression.cc,v $
//  Revision 1.4  2004/03/07 10:30:34  goiwai
//  ROOTに組みこむためのおまじないマクロを埋めこみました。
//  全てにおいて完全に動作するわけではありません。
//
//  Revision 1.3  2004/02/06 07:31:00  goiwai
//  SubMatchした文字列取得時に発生する不具合を修正しました.
//
//  Revision 1.2  2004/01/29 04:16:37  goiwai
//  GetSubMatches->GetSubMatch にして若干仕様変更
//  Splitをオーバーロード
//
//  Revision 1.1  2003/10/06 16:33:15  goiwai
//  UNIXの正規表現を扱うための部品です.速度よりも可読性を重視したので遅い
//  です.
//
// ============================================================================
#include "TRegularExpression.hh"

TRegularExpression::TRegularExpression( const Tstring& pattern, Tint option )
  : thePattern( pattern ),
    theOption( option ),
    theNumberOfSubMatches( -1 ),
    theCompiledPattern( 0 ),
    theMatch( 0 ),
    theSubMatch( 0 )
{
  Compile();
}

TRegularExpression::TRegularExpression( Tint option, const Tstring& pattern )
  : thePattern( pattern ),
    theOption( option ),
    theNumberOfSubMatches( -1 ),
    theCompiledPattern( 0 ),
    theMatch( 0 ),
    theSubMatch( 0 )
{
  Compile();
}

TRegularExpression::TRegularExpression( const TRegularExpression& right )
  : thePattern( right.thePattern ),
    theOption( right.theOption ),
    theNumberOfSubMatches( -1 ),
    theCompiledPattern( 0 ),
    theMatch( 0 ),
    theSubMatch( 0 )
{
  Compile();
}

TRegularExpression::~TRegularExpression()
{
  free();
}

const TRegularExpression& TRegularExpression::operator=( const TRegularExpression& right )
{
  thePattern = right.thePattern;
  theOption = right.theOption;
  free();
  theNumberOfSubMatches = -1;
  theCompiledPattern = 0;
  theMatch = 0;
  theSubMatch = 0;
  Compile();
  return *this;
}

const TRegularExpression& TRegularExpression::operator=( const Tstring& right )
{
  *this = TRegularExpression( right );
  return *this;
}

Tbool TRegularExpression::operator==( const Tstring& right )
{
  return IsMatch( right );
}

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

Tvoid TRegularExpression::Compile()
{
  free();

  theCompiledPattern = new Tregex_t();
  Tint retval = regcomp( theCompiledPattern, thePattern.c_str(), theOption );
  if ( retval != 0 ) {
    char m[ 128 ];
    regerror( retval, theCompiledPattern, m, Tsizeof(m) );
    regfree( theCompiledPattern );
    delete theCompiledPattern;
    theCompiledPattern = 0;
    theNumberOfSubMatches = -1;
    return;
  }



  theNumberOfSubMatches = theCompiledPattern -> re_nsub;
  Tregmatch_t* buf = new Tregmatch_t[ theNumberOfSubMatches + 1 ];
  theMatch = buf;

  if ( theNumberOfSubMatches > 0 ) {
    buf ++;
    theSubMatch = buf;
  }

  return;
}

Tint TRegularExpression::Index( const Tstring& source, Tint pos )
{
  Tint result = -1;
  if ( IsMatch( source, pos ) ) {
    Tint so = theMatch -> rm_so;
    result = so + pos;
  }
  return result;
}

TintList TRegularExpression::Indexes( const Tstring& source, Tint pos )
{
  TintList result;
  while ( IsMatch( source, pos ) ) {
    Tint so = theMatch -> rm_so;
    Tint eo = theMatch -> rm_eo;
    Tint pbuf = so + pos;
    result.push_back( pbuf );
    pos = pos + eo;
  }
  return result;
}

Tint TRegularExpression::Size( const Tstring& source, Tint pos )
{
  Tint result = 0;
  if ( IsMatch( source, pos ) ) {
    Tint so = theMatch -> rm_so;
    Tint eo = theMatch -> rm_eo;
    result = eo - so;
  }
  return result;
}

TintList TRegularExpression::Sizes( const Tstring& source, Tint pos )
{
  TintList result;
  while ( IsMatch( source, pos ) ) {
    Tint so = theMatch -> rm_so;
    Tint eo = theMatch -> rm_eo;
    Tint sbuf = eo - so;
    result.push_back( sbuf );
    pos = pos + eo;
  }
  return result;
}

Tstring TRegularExpression::MatchString( const Tstring& source, Tint pos )
{
  Tstring result = "";
  if ( IsMatch( source, pos ) ) {
    Tint so = theMatch -> rm_so;
    Tint eo = theMatch -> rm_eo;
    Tint index = so + pos;
    Tint size = eo - so;
    result = source.substr( index, size );
  }
  return result;
}

TstringList TRegularExpression::MatchStrings( const Tstring& source, Tint pos )
{
  TstringList result;
  while ( IsMatch( source, pos ) ) {
    Tint so = theMatch -> rm_so;
    Tint eo = theMatch -> rm_eo;
    Tint index = so + pos;
    Tint size = eo - so;
    Tstring strbuf = source.substr( index, size );
    result.push_back( strbuf );
    pos = pos + eo;
  }
  return result;
}

Tbool TRegularExpression::IsMatch( const Tstring& source, Tint pos )
{
  if ( execute( source, pos ) == 0 ) {
    return Ttrue;
  } else {
    return Tfalse;
  }
}

Tint TRegularExpression::GetNumberOfMatches( const Tstring& source, Tint pos )
{
  Tint nmatch = 0;
  while ( IsMatch( source, pos ) ) {
    Tint eo = theMatch -> rm_eo;
    pos = pos + eo;
    nmatch ++;
  }
  return nmatch;
}

Tstring TRegularExpression::Substitute( const Tstring& source, const Tstring& substr, Tint pos )
{
  Tstring result = source;
  if ( IsMatch( source, pos ) ) {
    Tint so = theMatch -> rm_so;
    Tint eo = theMatch -> rm_eo;
    Tint index = so + pos;
    Tint size = eo - so;
    result.replace( index, size, substr );
  }
  return result;
}

Tstring TRegularExpression::SubstituteAll( const Tstring& source, const Tstring& substr, Tint pos )
{
  Tstring result = source;
  while ( IsMatch( source, pos ) ) {
    Tint so = theMatch -> rm_so;
    Tint eo = theMatch -> rm_eo;
    Tint index = so + pos;
    Tint size = eo - so;
    result.replace( index, size, substr );
    pos = pos + eo;
  }
  return result;
}

TstringList TRegularExpression::Split( const Tstring& source, Tint pos )
{
  TstringList result;

  Tint lastend = -1;
  while ( IsMatch( source, pos ) ) {
    Tint so = theMatch -> rm_so;
    Tint eo = theMatch -> rm_eo;
    Tsize_t gbegin = so + pos;
    Tsize_t gend = eo + pos - 1;
    if ( gbegin != 0 ) {
      Tint cpos = lastend + 1;
      Tint csize = gbegin - cpos;
      Tstring strbuf = source.substr( cpos, csize );
      if ( ! strbuf.empty() ) {
        result.push_back( strbuf );
      }
    }
    lastend = gend;
    pos = pos + eo;
  }

  Tint srcsize = (Tint)source.size();
  if ( lastend + 1 == srcsize ) {
#ifdef __CLDAQ_DEBUG
    CLDAQ_DEBUG("last string is matched separater");
#endif
  } else if ( lastend < srcsize ) {
#ifdef __CLDAQ_DEBUG
    CLDAQ_DEBUG("some string is exist after last separator");
#endif
    Tint cpos = lastend + 1;
    Tint csize = srcsize - cpos;
    Tstring strbuf = source.substr( cpos, csize );
    result.push_back( strbuf );
  } else if ( lastend > srcsize ) {
    CLDAQ_WARN("last separator is exist on out of source");
  } else {
    CLDAQ_WARN("unexpected operation");
  }

  return result;
}

TstringList TRegularExpression::Split( const TRegularExpression& regex, const Tstring& source, Tint pos )
{
  TRegularExpression r( regex );
  return r.Split( source, pos );
}

TstringList TRegularExpression::Split( const Tstring& pattern, const Tstring& source, Tint pos )
{
  TRegularExpression regex( pattern );
  return Split( regex, source, pos );
}

Tstring TRegularExpression::GetSubMatch( Tint index, const Tstring& source, Tint pos )
{
  Tstring result;
  if ( index < theNumberOfSubMatches && IsMatch( source, pos ) ) {
    Tint so = theSubMatch[ index ].rm_so;
    Tint eo = theSubMatch[ index ].rm_eo;
    Tint subindex = so + pos;
    Tint subsize = eo - so;

    Tint sz = (Tint)source.size();
    if ( subindex < 0 || subindex >= sz || subsize <= 0 || subsize > sz-subindex ) {
      return result;
    }

    result = source.substr( subindex, subsize );
  }
  return result;
}

TstringList TRegularExpression::GetSubMatch( const Tstring& source, Tint pos )
{
  TstringList result;
  if ( theNumberOfSubMatches > 0 && IsMatch( source, pos ) ) {
    for ( Tint i = 0; i < theNumberOfSubMatches; i ++ ) {
      Tint so = theSubMatch[ i ].rm_so;
      Tint eo = theSubMatch[ i ].rm_eo;
      Tint index = so + pos;
      Tint size = eo - so;

      Tint sz = (Tint)source.size();
      if ( index < 0 || index >= sz || size <= 0 || size > sz-index  ) {
        continue;
      }

      Tstring strbuf = source.substr( index, size );
      result.push_back( strbuf );
    }
  }
  return result;  
}

Tvoid TRegularExpression::free()
{
  if ( theCompiledPattern ) {
    regfree( theCompiledPattern );
    delete theCompiledPattern;
    theCompiledPattern = 0;
    theNumberOfSubMatches = -1;
  }

  if ( theMatch ) {
    delete [] theMatch;
    theMatch = 0;
    theSubMatch = 0;
  }

  // if ( theSubMatch ) {
  //   ここを解放しようとすると落ちるなあ
  //   delete theSubMatch;
  //   theSubMatch = 0;
  //   theNumberOfSubMatches = -1;
  // }

  return;
}

Tint TRegularExpression::execute( const Tstring& source, Tint pos )
{
  if ( source.empty() || (Tsize_t)pos >= source.size() || pos < 0 ) {
#ifdef __CLDAQ_DEBUG
    CLDAQ_DEBUG("source string is empty");
    CLDAQ_DEBUG(source.c_str());
#endif
    return -1;
  } else if ( !theCompiledPattern || !theMatch ) {
    Tstring m = "not compiled pattern \"" + thePattern + "\"";
    CLDAQ_WARN( m.c_str() );
    return -1;
  } else {
    Tstring strbuf = source.substr( (Tsize_t)pos, (Tsize_t)(source.size()-pos) );
    const Tchar* cc = strbuf.c_str();
    Tint retval = 
      regexec( theCompiledPattern, cc, theNumberOfSubMatches + 1, theMatch, 0 );
    return retval;
  }
}

#ifdef __CLDAQ_ROOT_DLL
    ClassImp(TRegularExpression)
#endif
