// ============================================================================
//  $Id: TArgument.cc,v 1.4 2004/03/07 10:30:34 goiwai Exp $
//  $Name: CLDAQ-1-14-06 $
//  $Log: TArgument.cc,v $
//  Revision 1.4  2004/03/07 10:30:34  goiwai
//  ROOTに組みこむためのおまじないマクロを埋めこみました。
//  全てにおいて完全に動作するわけではありません。
//
//  Revision 1.3  2003/10/10 11:50:15  goiwai
//  なんかコンパイルにこけたりするんでバッファ長を短くしました.なんでだろ？
//
//  Revision 1.2  2003/10/10 09:37:56  goiwai
//  データメンバに theUsage を追加しました.
//
//  Revision 1.1  2003/10/06 16:32:25  goiwai
//  GNUスタイルの引数を簡単にかつ直感的に解釈するための部品です.
//
// ============================================================================
#include "TArgument.hh"
#include "TRegularExpression.hh"

static const Tsize_t _buflen = 127;
static Tchar _buf[ _buflen + 1 ];

TArgument::TArgument( Tint argc, Tchar** argv, const Tstring& usage )
  : theNumberOfOriginalArguments( argc ),
    theOriginalArguments( argv ),
    theOptionTable(),
    theOptionMap(),
    theProgramName( basename( Tstring( theOriginalArguments[ 0 ] ) ) ),
    theDirectoryName( dirname( Tstring( theOriginalArguments[ 0 ] ) ) ),
    theElements(),
    theNumberOfElements( 0 ),
    theUsage( usage )
{
  if ( usage.empty() || usage.size() == 0 ) {
    Tostrstream strbuf( _buf, _buflen );
    strbuf << "Usage: " + theProgramName << " [OPTION]... [FILE]..." << Tendl;
    strbuf << Tendl;
    strbuf << "Mandatory arguments to long options are mandatory for short options too.";
    theUsage = strbuf.str();
  }

  Analyse();

  if ( theOptionMap.HasOption( "help" ) || theOptionMap.HasOption( "h" ) ) {
    ShowHelp();
  }
  if ( theOptionMap.HasOption( "version" ) || theOptionMap.HasOption( "v" ) ) {
    ShowVersion();
  }
}

TArgument::TArgument( Tint argc, Tchar** argv, const TOptionTable& table, const Tstring& usage )
  : theNumberOfOriginalArguments( argc ),
    theOriginalArguments( argv ),
    theOptionTable( table ),
    theOptionMap(),
    theProgramName( basename( Tstring( theOriginalArguments[ 0 ] ) ) ),
    theDirectoryName( dirname( Tstring( theOriginalArguments[ 0 ] ) ) ),
    theElements(),
    theNumberOfElements( 0 ),
    theUsage( usage )
{
  if ( usage.empty() || usage.size() == 0 ) {
    Tostrstream strbuf( _buf, _buflen );
    strbuf << "Usage: " + theProgramName << " [OPTION]... [FILE]..." << Tendl;
    strbuf << Tendl;
    strbuf << "Mandatory arguments to long options are mandatory for short options too.";
    theUsage = strbuf.str();
  }

  Analyse();

  if ( theOptionMap.HasOption( "help" ) || theOptionMap.HasOption( "h" ) ) {
    ShowHelp();
  }
  if ( theOptionMap.HasOption( "version" ) || theOptionMap.HasOption( "v" ) ) {
    ShowVersion();
  }
}

TArgument::~TArgument()
{;}

Tvoid TArgument::Analyse()
{
  TOptionList oplist = theOptionTable.GetOptionList();
  Tsize_t listlen = oplist.size();
  struct option* longop = new option[ listlen + 1 ];
  Tstring shortop;


  for ( Tsize_t i = 0; i < listlen; i ++ ) {
    Tstring longbuf = oplist[ i ].GetLongOption();
    Tstring shortbuf = oplist[ i ].GetShortOption();
    Tint argstyle = oplist[ i ].GetArgumentStyle();
    struct option buf = { longbuf.c_str(), argstyle, 0, 0 };
    longop[ i ] = buf;

    shortop += shortbuf;
    if ( argstyle == TOption::tNeed ) {
      shortop += ":";
    } else if ( argstyle == TOption::tEither ) {
      shortop += "::";
    }
  }
  struct option delim = { 0, 0, 0, 0 };
  longop[ listlen ] = delim;


  Tint retval = 0;
  Tint optionindex = 0;
  while ( ( retval = getopt_long( theNumberOfOriginalArguments, theOriginalArguments, shortop.c_str(), longop, &optionindex ) ) != -1 ) {

    // long option
    if ( retval == 0 ) {
      Tstring optionname = longop[ optionindex ].name;
      Tstring parameter = "";
      if ( optarg ) {
        parameter = optarg;
      }
      for ( Tsize_t i = 0; i < listlen; i ++ ) {
        if ( oplist[ i ] == optionname ) {
          theOptionMap.AddOption( oplist[ i ], parameter );
          break;
        }
      }
    } else if ( retval == '?' ) {
      Tcout << Tendl;
      ShowUsage();
      break;
    } else if ( isalnum( retval ) ) {
      Tstring optionname( 1, (Tchar)retval );
      Tstring parameter = "";
      if ( optarg ) {
        parameter = optarg;
      }
      for ( Tsize_t i = 0; i < listlen; i ++ ) {
        if ( oplist[ i ] == optionname ) {
          theOptionMap.AddOption( oplist[ i ], parameter );
          break;
        }
      }
    } else {
      Tcout << Tendl;
      ShowUsage();
      break;
    }
  }

  if ( optind < theNumberOfOriginalArguments ) {
    while ( optind < theNumberOfOriginalArguments ) {
      theElements.push_back( theOriginalArguments[ optind ++ ] );
    }
  }
  theNumberOfElements = (Tint)theElements.size();

  delete [] longop;

  return;
}

Tostream& operator<<( Tostream& tos, const TArgument& right )
{
  tos << "Orginal:" << Tendl;
  for ( Tint i = 0; i < right.theNumberOfOriginalArguments; i ++ ) {
    tos << Twquote << right.theOriginalArguments[ i ] << Twquote;
    if ( i != right.theNumberOfOriginalArguments - 1 ) {
      tos << ", ";
    }
  }
  tos << Tendl;


  tos << "Directory and Program:" << Tendl;
  tos << Twquote << right.theDirectoryName << Twquote << ", ";
  tos << Twquote << right.theProgramName << Twquote << Tendl;

  if ( right.theNumberOfElements > 0 ) {
    tos << "Element:" << Tendl;
    for ( Tint i = 0; i < right.theNumberOfElements; i ++ ) {
      tos << Twquote << right.theElements[ i ] << Twquote;
      if ( i != right.theNumberOfElements - 1 ) {
        tos << ", ";
      }
    }
    tos << Tendl;
  }

  if ( !right.theOptionTable.GetOptionList().empty() ) {
    tos << "Option Table:" << Tendl;
    tos << right.theOptionTable << Tendl;
  }

  if ( !right.theOptionMap.GetOptionList().empty() ) {
    tos << "Given Options:" << Tendl;
    tos << right.theOptionMap << Tendl;
  }

  return tos;
}

Tvoid TArgument::ShowUsage() const
{
  Tcout << theUsage << Tendl;
  Tcout << theOptionTable << Tendl;
  exit( 0 );
}

Tvoid TArgument::ShowHelp() const
{
  Tcout << theUsage << Tendl;
  Tcout << theOptionTable << Tendl;
  exit( 0 );
}

Tvoid TArgument::ShowVersion() const
{
  Tcout << theProgramName << " (" << Tproject << ") " << Tversion << Tendl;
  Tcout << "$Id: TArgument.cc,v 1.4 2004/03/07 10:30:34 goiwai Exp $" << Tendl;
  exit( 0 );
}

#ifdef __CLDAQ_ROOT_DLL
    ClassImp(TArgument)
#endif
