/*
   File: rtsopt.c
   Specifies option and argument treatment

   Copyright 2005 Radboud University of Nijmegen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
 
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Library General Public License for more details.
 
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

   CVS ID: "$Id: rtsopt.c,v 1.50 2006/09/27 16:01:15 marcs Exp $"
*/

/* standard includes */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /* HAVE_CONFIG_H */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <time.h>

/* This will have to change: no getopt.h under WIN32 */
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#else /* HAVE_GETOPT_H */
#ifdef HAVE_GETOPT
#include <unistd.h>
#endif /* HAVE_GETOPT */
#endif /* HAVE_GETOPT_H */

/* libabase includes */
#include <abase_error.h>

/* local includes */
#include "rtscode.h"
#include "rtsio.h"
#include "rtsopt.h"

/*-----------------------------------------------------------------------------
// Private data. Oh well, not so private as you think.
//---------------------------------------------------------------------------*/
static char *parser_name = NULL;
static char *input_file_name = NULL;
static char *output_file_name = NULL;
static char *alphabet_file_name = NULL;
static char *input_pos_fname = NULL;
int parsing_stats_option;
int lexer_stats_option;
int total_stats_option;
int show_options_option;
int show_timebreak_option;
int text_tracing_option;
int best_parsings;
int io_sync_option;
int hybrid_parsing_option;
int count_option;
int lex_out_format;
/* max_edit_distance;   // defined in lexicon_search.h */

/* exported bounds */
int max_parses;
int max_parsetime;

static void init_options ()
{ parsing_stats_option = 0;
  lexer_stats_option = 0;
  total_stats_option = 0;
  show_options_option = 0;
  show_timebreak_option = 0;
  text_tracing_option = 0;
  best_parsings = 0;
  io_sync_option = 0;
  hybrid_parsing_option = 0;
  count_option = 0;
  lex_out_format = G_OUT_FORMAT_STANDARD;
  max_parses = INT_MAX;
  max_parsetime = INT_MAX;
}

static void set_max_number_of_parses (int v)
{ if (v < 0) max_parses = 0;
  else max_parses = v;
}

#if USE_EDIT_DISTANCE
static void set_max_edit_distance (int v)
{ if (v < 0) max_edit_distance = 0;
  else max_edit_distance = v;
}
#endif

static void set_max_parse_time (int v)
{ if (v < 0) max_parsetime = 0;
  else max_parsetime = v;
}

/*------------------------------------------------------------------------------
// Public
//----------------------------------------------------------------------------*/
void store_parser_name (char* name)
{ // Until we are reading Acode
#ifdef WIN32
    int len = strlen(name);
    if (len > 4 && strcasecmp (name + len - 4, ".exe") == 0)	/* WIN32: stricmp */
       name[len - 4] = '\0';
#endif
    parser_name = name;
}

char* get_parser_name ()
{ return (parser_name);
}

void store_input_file_name (char* name)
{ input_file_name = name;
}

char* get_input_file_name ()
{ return (input_file_name);
}

static void store_output_file_name (char* name)
{ output_file_name = name;
}

char* get_output_file_name ()
{ return (output_file_name);
}

static void store_alphabet_file_name (char* name)
{ alphabet_file_name = name;
}

char* get_alphabet_file_name ()
{ return (alphabet_file_name);
}

static void store_input_pos_fname (char* name)
{ input_pos_fname = name;
}

char* get_input_pos_fname ()
{ return (input_pos_fname);
}

#define	NL	"<NL>"
#define	CR	"<CR>"
#define	TAB	"<TAB>"

static void print_quoted (char *buf, unsigned char *s)
{ char c;
  char *p = buf;

  while ((c = *s++))
    switch (c)
       { case '\n':
           sprintf (p, NL);
           p += strlen (NL);
           break;
         case '\r':
           sprintf (p, CR);
           p += strlen (CR);
           break;
         case '\t':
           sprintf (p, TAB);
           p += strlen (TAB);
           break;
         default:
           *p++ = c;
           break;
        };
  *p = '\0';
}

static char* convert_time_to_text(long time)
{ time_t clock = (time_t) time;
  char *txt_time = ctime (&clock);
  txt_time[24] = '\0';
  return (txt_time);
}

void show_options()
{   abs_message ("Grammar last changed on %s\n"
                 "Parser generated using AGFL coder version %s",
                 convert_time_to_text(gra_version), agfl_version);

    if (client_option)
       abs_message ("Operating in client mode");

    if (word_terminator_chars != NULL) {
        char buf[256];

        print_quoted(buf, word_terminator_chars);
        abs_message ("Word terminators are '%s'", buf);
    }

    if (invisible_chars != NULL) {
        char buf[256];

        print_quoted (buf, invisible_chars);
        abs_message ("Invisible characters are '%s'", buf);
    }

#if USE_EDIT_DISTANCE
    if (max_edit_distance > 0)
	abs_message ("Maximum edit distance is %d", max_edit_distance);
#endif

    if (graph_option)
       abs_message ("Displaying lexical graph");

    if (directors_option)
       abs_message ("Directors available");

    if (pos_memo_option)
       abs_message ("Positive memoization available");

    if (neg_memo_option)
       abs_message ("Negative memoization available");

    if (max_parses < INT_MAX)
       abs_message ("Maximum number of parses is %d", max_parses);

    if (max_parsetime < INT_MAX)
       abs_message ("Maximum parse time is %d seconds", max_parsetime);

    if (label_bracket && !no_output)
       abs_message ("Labelled bracket output");

    if (no_output)
       abs_message ("Suppressing output");
    else if (transduce_option && !no_output)
       abs_message ("Transducing");

    if (counters_option)
       abs_message ("Showing counters\n");

    if (generate_option)
       abs_message ("This is a sentence generator");

    if (best_parsings)
       abs_message ("Best parsing enabled");

    if (hybrid_parsing_option)
       abs_message ("Hybrid parsing enabled");
}

/*-----------------------------------
// -h shows options
//---------------------------------*/
static void show_version ()
{ abs_message ("This parser was generated with %s version %s at %s\n", PACKAGE,
               VERSION, convert_time_to_text (gra_version));
}

static void show_help ()
{   show_version ();
#ifdef WIN32
    abs_message ("Usage: %s.exe [options...] [input_fname]", get_parser_name ());
#else
    abs_message ("Usage: %s [options...] [input_fname]", get_parser_name ());
#endif
    abs_message (
        "OPTION      DEFAULT     PURPOSE\n"
        "-P number   infinite    maximum number of parses\n"
        "-T number   infinite    maximum total parse time (in seconds)\n"
#if USE_EDIT_DISTANCE
	"-E number   0           maximum edit distance\n"
#endif
        "-B          off         search parsings for best penalty level\n"
        "-b          off         labelled bracket output\n"
        "-H          off         enables hybrid parsing\n"
        "-O          off         suppress output\n"
	"-i name     input_fname use name to format output of $POS\n"
        "-o file     stdout      write output to file\n"
        "-t          off         transduce (or tree)\n"
        "-A file     off         use alphabet file\n"
        "-G          off         show lexical graph\n"
        "-V                      show version\n"
        "-S          off         enable I/O synchronisation\n"
        "-v          off         set verbosity. possible values:\n"
        "            options     show parser options\n"
        "            lexer       show all lexer output\n"
        "            parser      show parser stats\n"
        "            totals      show totals (ie. time)\n"
        "            lpt         lexer+parser+totals\n"
        "            timebreak   show when the timeout kicks in\n"
        "-c          off         count opcodes and rule calls\n"
#if 0
	/* some undocumented options */
        "-m          [wpt]       set lexing method\n"
        "            [G]         set lexing method with graph option -G\n"
        "-k          off         set text tracing mode\n"
#endif
        "-h          off         show this help");
    abs_exit (0);
}

void check_for_verbose_options (char *opt_arg, int *errflg)
{
    if (opt_arg == NULL)
      { abs_message ("Not specified what to print with -v, so assuming "
		     "parsing, lexer, options, and totals.");
        parsing_stats_option = 1;
        lexer_stats_option = 1;
        total_stats_option = 1;
        show_options_option = 1;
        return;
      }

    if (!strcasecmp (opt_arg, "lpt")) {
        lexer_stats_option = 1;
        parsing_stats_option = 1;
        total_stats_option = 1;
        return;
    }
    
    if (!strcasecmp (opt_arg, "parser")) {
        parsing_stats_option = 1;
        return;
    }

    if (!strcasecmp (opt_arg, "lexer")) {
        lexer_stats_option = 1;
        return;
    }

    if (!strcasecmp (opt_arg, "totals")) {
        total_stats_option = 1;
        return;
    }

    if (!strcasecmp(opt_arg, "options")) {
        show_options_option = 1;
        return;
    }
    
    if (!strcasecmp(opt_arg, "timebreak")) {
        show_timebreak_option = 1;
        return;
    }

    (*errflg)++;
}

void process_options(int argc, char* argv[])
{
    char c;
    int errflg = 0;
#ifdef PROFILING
    #error "enter opt_str"
    char* opt_str = "";
#else
    char* opt_str = "Bbs:ci:GP:T:OA:o:m:thVv:SHkE:";
#endif

    init_options ();
    store_parser_name ((char*) argv[0]);

    while ((c = getopt (argc, argv, opt_str)) != EOF)
      { switch (c)
          { case 'v': check_for_verbose_options (optarg, &errflg); break;
            case 'b': label_bracket = 1; break;
            case 'c': count_option = 1; break;
            case 'i': store_input_pos_fname (optarg); break;
            case 'G': graph_option = 1; break;
            case 'P': set_max_number_of_parses (atoi (optarg)); break;
            case 'T': set_max_parse_time (atoi (optarg)); break;
            case 'E':
#if USE_EDIT_DISTANCE
	      set_max_edit_distance (atoi(optarg));
#else
              abs_message ("option: -E not active\n");
#endif
	      break;
            case 'B': best_parsings = 1; break;
            case 'H': hybrid_parsing_option = 1; break;
            case 'O': no_output = 1; break;
	    case 'A': store_alphabet_file_name (optarg); break;
            case 'o': store_output_file_name (optarg); break;
            case 'm':
              if (!strchr ("wptG", optarg[0]) || optarg[1])
		{ /* see rtscode.h for values */
                  abs_message ("invalid lexing method -m: %s", optarg);
                  errflg++;
                }
	      else if (optarg[0] == 'G')
		{ graph_option = 1;
                  lex_out_format = G_OUT_FORMAT_TRELLIS_INPUT;
                };
              break;
            case 'S': io_sync_option = 1; break;
            case 't': transduce_option = 1; break;
            case 'V':
	      show_version ();
              abs_exit (0);
	      /* never returns */
            case 'h': errflg++; break;
            /* testing option: */
            case 'k':
              text_tracing_option = 1;
              break;
            case '?':
            default:
                abs_error ("unknown option '-%c', use '%s -h' for help", c, get_parser_name());
        	show_help ();
	  };
      };

    if (errflg) show_help ();
    if (optind == argc - 1)
      { /* Still one argument left on line */
	store_input_file_name (argv[optind]);
	if (input_pos_fname != NULL)
	  abs_message ("Warning: -i option overridden by argument");
	store_input_pos_fname (argv[optind]);
      }
    else if (optind < argc - 1)
      { abs_error ("Too many arguments");
        show_help ();
      };
}
