/* Copyright (C) 1997 Bjoern Beutel. */

/* Description. =============================================================*/

/* Options for malaga and functions to start and terminate malaga. */

/* Includes. ================================================================*/

#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <stdlib.h>
#include <setjmp.h>
#include <time.h>
#include <glib.h>
#include "basic.h"
#include "pools.h"
#include "values.h"
#include "input.h"
#include "commands.h"
#include "options.h"
#include "rule_type.h"
#include "rules.h"
#include "files.h"
#include "analysis.h"
#include "cache.h"
#include "symbols.h"
#include "lexicon.h"
#include "transmit.h"
#include "display.h"
#include "scanner.h"
#include "patterns.h"
#include "hangul.h"
#include "malaga_lib.h"

/* Global variables. ========================================================*/

bool_t auto_tree; /* TRUE if tree is shown automatically. */
bool_t auto_result; /* TRUE if result is shown automatically. */
bool_t result_as_list; /* TRUE if results will be combined into a list. */
text_t *grammar_info; /* Information about grammar. */

string_t result_format, unknown_format, error_format; 
/* Format strings for output. */

/* Variables. ===============================================================*/

static string_t morphology_file, syntax_file, lexicon_file;
static string_t symbol_file, extended_symbol_file;

static bool_t info_in_project_file;
/* Indicates whether we have read grammar info from the current project file.
 * Used to insert empty lines between grammar infos from different project
 * files. */

/* Functions. ===============================================================*/

static void 
do_mor_pruning_option( string_t arguments )
/* Set minimum number of states needed to call the morphology pruning rule. */
{ 
  if (*arguments == EOS) 
    printf( "mor-pruning: %d\n", mor_pruning_min );
  else
  {
    if (rule_system[MORPHOLOGY]->pruning_rule == -1)
      complain("No morphology pruning rule.");
    mor_pruning_min = parse_int( &arguments );
    parse_end( &arguments );
  }
}

static command_t mor_pruning_option = 
{ 
  "mor-pruning", do_mor_pruning_option,
  "Usage:\n"
  "  set mor-pruning 0 -- Turn off morphology pruning.\n"
  "  set mor-pruning N -- Call pruning rule for a minimum of N states.\n"
};

/*---------------------------------------------------------------------------*/

static void 
do_syn_pruning_option( string_t arguments )
/* Set minimum number of states needed to call the syntax pruning rule. */
{ 
  if (*arguments == EOS) 
    printf( "syn-pruning: %d\n", syn_pruning_min );
  else 
  {
    if (rule_system[SYNTAX] == NULL || rule_system[SYNTAX]->pruning_rule == -1)
      complain("No syntax pruning rule.");
    syn_pruning_min = parse_int( &arguments );
    parse_end( &arguments );
  }
}

static command_t syn_pruning_option = 
{ 
  "syn-pruning", do_syn_pruning_option,
  "Usage:\n"
  "  set syn-pruning 0 -- Turn off syntax pruning.\n"
  "  set syn-pruning N -- Call pruning rule for a minimum of N states.\n"
};

/*---------------------------------------------------------------------------*/

static void 
do_robust_rule_option( string_t arguments )
/* Enable/disable robust-rule. */
{ 
  if (*arguments == EOS) 
  { 
    printf( "robust-rule: %s\n", 
            get_analysis_option( ROBUST_RULE_OPTION ) ? "yes" : "no" );
  } 
  else 
    set_analysis_option( ROBUST_RULE_OPTION, parse_yes_no( &arguments ) );
  parse_end( &arguments );
}

static command_t robust_rule_option = 
{ 
  "robust-rule robust", do_robust_rule_option,
  "Usage:\n"
  "  set robust-rule yes -- Turn robust_rule on.\n"
  "  set robust-rule no -- Turn robust_rule off.\n"
};

/*---------------------------------------------------------------------------*/

static void 
do_mor_out_filter_option( string_t arguments )
/* Enable/disable morphology output filter. */
{ 
  if (*arguments == EOS) 
  { 
    printf( "mor-out-filter: %s\n", 
            get_analysis_option( MOR_OUT_FILTER_OPTION ) ? "yes" : "no" );
  } 
  else 
    set_analysis_option( MOR_OUT_FILTER_OPTION, parse_yes_no( &arguments ) );
  parse_end( &arguments );
}

static command_t mor_out_filter_option = 
{ 
  "mor-out-filter mofil", do_mor_out_filter_option,
  "Usage:\n"
  "  set mor-out-filter yes -- Turn morphology output filter on.\n"
  "  set mor-out-filter no -- Turn morphology output filter off.\n"
};

/*---------------------------------------------------------------------------*/

static void 
do_syn_out_filter_option( string_t arguments )
/* Enable/disable syntax output filter. */
{ 
  if (*arguments == EOS) 
  { 
    printf( "syn-out-filter: %s\n", 
            get_analysis_option( SYN_OUT_FILTER_OPTION ) ? "yes" : "no" );
  } 
  else 
    set_analysis_option( SYN_OUT_FILTER_OPTION, parse_yes_no( &arguments ) );
  parse_end( &arguments );
}

static command_t syn_out_filter_option = 
{ 
  "syn-out-filter sofil", do_syn_out_filter_option,
  "Usage:\n"
  "  set syn-out-filter yes -- Turn syntax output filter on.\n"
  "  set syn-out-filter no -- Turn syntax output filter off.\n"
};

/*---------------------------------------------------------------------------*/

static void 
do_syn_in_filter_option( string_t arguments )
/* Enable/disable syntax input filter. */
{ 
  if (*arguments == EOS) 
  { 
    printf( "syn-in-filter: %s\n", 
            get_analysis_option( SYN_IN_FILTER_OPTION ) ? "yes" : "no" );
  } 
  else 
    set_analysis_option( SYN_IN_FILTER_OPTION, parse_yes_no( &arguments ) );
  parse_end( &arguments );
}
static command_t syn_in_filter_option = 
{ 
  "syn-in-filter sifil", do_syn_in_filter_option,
  "Usage:\n"
  "  set syn-in-filter yes -- Turn syntax input filter on.\n"
  "  set syn-in-filter no -- Turn syntax input filter off.\n"
};

/*---------------------------------------------------------------------------*/

static void 
do_mor_incomplete_option( string_t arguments )
/* Enable/disable acceptance of incompletely parsed words. */
{ 
  if (*arguments == EOS) 
  { 
    printf( "mor-incomplete: %s\n", 
            get_analysis_option( MOR_INCOMPLETE_OPTION ) ? "yes" : "no" );
  } 
  else 
    set_analysis_option( MOR_INCOMPLETE_OPTION, parse_yes_no( &arguments ) );
  parse_end( &arguments );
}
static command_t mor_incomplete_option = 
{ 
  "mor-incomplete", do_mor_incomplete_option,
  "Usage:\n"
  "  set mor-incomplete yes -- "
  "Accept words that have been incompletely parsed.\n"
  "  set mor-incomplete no -- "
  "Only accept words that have been completely parsed.\n"
};

/*---------------------------------------------------------------------------*/

static void 
do_syn_incomplete_option( string_t arguments )
/* Enable/disable acceptance of incompletely parsed words. */
{ 
  if (*arguments == EOS) 
  { 
    printf( "syn-incomplete: %s\n", 
            get_analysis_option( SYN_INCOMPLETE_OPTION ) ? "yes" : "no" );
  } 
  else 
    set_analysis_option( SYN_INCOMPLETE_OPTION, parse_yes_no( &arguments ) );
  parse_end( &arguments );
}
static command_t syn_incomplete_option = 
{ 
  "syn-incomplete", do_syn_incomplete_option,
  "Usage:\n"
  "  set syn-incomplete yes -- "
  "Accept sentences that have been incompletely parsed.\n"
  "  set syn-incomplete no -- "
  "Only accept sentences that have been completely parsed.\n"
};

/*---------------------------------------------------------------------------*/

static void 
do_result_list_option( string_t arguments )
/* Enable/disable combination of all results into a single list. */
{ 
  if (*arguments == EOS) 
    printf( "result-list: %s\n", result_as_list ? "yes" : "no" );
  else 
    result_as_list = parse_yes_no( &arguments );
  parse_end( &arguments );
}
static command_t result_list_option = 
{ 
  "result-list", do_result_list_option,
  "Usage:\n"
  "  set result-list yes -- Combine results into a single list.\n"
  "  set result-list no -- Leave results unchanged.\n"
};

/*---------------------------------------------------------------------------*/

static void 
do_cache_size_option( string_t arguments )
/* Change the cache size. */
{ 
  int_t size;

  if (*arguments == EOS) 
  { 
    printf( "cache-size: %d (%d used)\n", 
	    get_cache_maximum(), get_cache_size() );
  } 
  else 
  { 
    size = parse_int( &arguments );
    set_cache_size( size );
    set_analysis_option( CACHE_OPTION, (size > 0) );
  }
  parse_end( &arguments );
}

static command_t cache_size_option = 
{ 
  "cache-size", do_cache_size_option,
  "Usage: set cache-size SIZE -- Set cache size to SIZE.\n"
  "If SIZE == 0, cache is switched off.\n"
};

/*---------------------------------------------------------------------------*/

static void 
do_auto_tree_option( string_t arguments )
/* Determine if tree is shown automatically. */
{ 
  if (*arguments == EOS) 
    printf( "auto-tree: %s\n", auto_tree ? "yes" : "no" );
  else 
    auto_tree = parse_yes_no( &arguments );
  parse_end( &arguments );
}

static command_t auto_tree_option = 
{ 
  "auto-tree tree", do_auto_tree_option,
  "Usage:\n"
  "  set auto-tree yes -- Show tree after analysis.\n"
  "  set auto-tree no -- Don't show tree after analysis.\n"
};

/*---------------------------------------------------------------------------*/

static void 
do_auto_result_option( string_t arguments )
/* Determine if result is shown automatically. */
{ 
  if (*arguments == EOS) 
    printf( "auto-result: %s\n", auto_result ? "yes" : "no" );
  else 
    auto_result = parse_yes_no( &arguments );
  parse_end( &arguments );
}

static command_t auto_result_option = 
{ 
  "auto-result result output", do_auto_result_option,
  "Usage:\n"
  "  set auto-result yes -- Show result after analysis.\n"
  "  set auto-result no -- Don't show result after analysis.\n"
};

/*---------------------------------------------------------------------------*/

static void 
do_error_format_option( string_t arguments )
/* Change analysis-error print format to ARGUMENTS. */
{ 
  string_t format;

  if (*arguments == EOS) 
  { 
    format = new_string_readable( error_format, NULL );
    printf( "error-format: %s\n", format );
    free_mem( &format );
  } 
  else 
  { 
    format = parse_word( &arguments );
    free_mem( &error_format );
    error_format = format;
  }
  parse_end( &arguments );
}

static command_t error_format_option = 
{ 
  "error-format", do_error_format_option,
  "Set the format in which to print analysis input that produced an error.\n"
  "Usage: set error-format ERROR_FORMAT_STRING\n"
  "The ERROR_FORMAT_STRING may contain the following special sequences:\n"
  "  %e -- Error message.\n"
  "  %l -- Input line number.\n"
  "  %n -- Number of analysis states.\n"
  "  %s -- Surface.\n"
};

/*---------------------------------------------------------------------------*/

static void 
do_result_format_option( string_t arguments )
/* Change analysis result format to ARGUMENTS. */
{ 
  string_t format;

  if (*arguments == EOS) 
  { 
    format = new_string_readable( result_format, NULL );
    printf( "result-format: %s\n", format );
    free_mem( &format );
  } 
  else 
  { 
    format = parse_word( &arguments );
    free_mem( &result_format );
    result_format = format;
  }
  parse_end( &arguments );
}

static command_t result_format_option = 
{ 
  "result-format output-format", do_result_format_option,
  "Describe the format in which analysis results will be printed.\n"
  "Usage: set result-format RESULT_FORMAT_STRING\n"
  "The RESULT_FORMAT_STRING may contain the following special sequences:\n"
  "  %f -- Result feature structure.\n"
  "  %l -- Input line number.\n"
  "  %n -- Number of analysis states.\n"
  "  %r -- Ambiguity index.\n"
  "  %s -- Surface.\n"
};

/*---------------------------------------------------------------------------*/

static void 
do_unknown_format_option( string_t arguments )
/* Change unknown analysis format to ARGUMENTS. */
{ 
  string_t format;
  if (*arguments == EOS) 
  { 
    format = new_string_readable( unknown_format, NULL );
    printf( "unknown-format: %s\n", format );
    free_mem( &format );
  } 
  else 
  { 
    format = parse_word( &arguments );
    free_mem( &unknown_format );
    unknown_format = format;
  }
  parse_end( &arguments );
}

static command_t unknown_format_option = 
{ 
  "unknown-format", do_unknown_format_option,
  "Describe the format in which unknown forms will be printed.\n"
  "Usage: set unknown-format UNKNOWN_FORMAT_STRING\n"
  "The UNKNOWN_FORMAT_STRING may contain the following special sequences:\n"
  "  %l -- Input line number.\n"
  "  %n -- Number of analysis states.\n"
  "  %s -- Surface.\n"
};

/*---------------------------------------------------------------------------*/

static void 
read_project_file( string_t project_file )
/* Read the project file. */
{ 
  FILE *project_stream;
  char_t *project_line;
  string_t project_line_p, argument, include_file, extension;
  string_t *name_p;
  volatile int_t line_count;
  static bool_t err_pos_printed;

  info_in_project_file = err_pos_printed = FALSE;
  project_stream = open_stream( project_file, "r" );
  line_count = 0;
  while (TRUE) 
  { 
    TRY
      project_line = read_line( project_stream );
    IF_ERROR
    {
      print_text( error_text, " (\"%s\", line %d)",
		  name_in_path( project_file ), line_count + 1 );
      err_pos_printed = TRUE;
    }
    END_TRY;
    if (project_line == NULL) 
      break;
    line_count++;
    cut_comment( project_line );
    project_line_p = project_line;
    
    if (*project_line_p != EOS) 
    {
      argument = NULL;
      TRY
      {
	argument = parse_word( &project_line_p );
	extension = NULL; 
	name_p = NULL;
	if (strcmp_no_case( argument, "sym:" ) == 0) 
	{
	  extension = "sym";
	  name_p = &symbol_file; 
	}
	else if (strcmp_no_case( argument, "esym:" ) == 0) 
	{
	  extension = "esym";
	  name_p = &extended_symbol_file; 
	}
	else if (strcmp_no_case( argument, "lex:" ) == 0) 
	{
	  extension = "lex";
	  name_p = &lexicon_file; 
	}
	else if (strcmp_no_case( argument, "mor:" ) == 0) 
	{
	  extension = "mor";
	  name_p = &morphology_file; 
	}
	else if (strcmp_no_case( argument, "syn:" ) == 0) 
	{
	  extension = "syn";
	  name_p = &syntax_file; 
	}
	else if (strcmp_no_case( argument, "include:" ) == 0) 
	{ 
	  include_file = parse_absolute_path( &project_line_p, project_file );
	  parse_end( &project_line_p );
	  read_project_file( include_file );
	  free_mem( &include_file );
	} 
	else if (strcmp_no_case( argument, "info:" ) == 0) 
        { 
	  /* Insert an empty line if we already have info that stems from a
	   * different project file. */
	  if (grammar_info->string_size > 0 && ! info_in_project_file)
	    add_char_to_text( grammar_info, '\n' );
	  add_to_text( grammar_info, project_line_p );
	  add_char_to_text( grammar_info, '\n' );
	  info_in_project_file = TRUE;
	}
	free_mem( &argument );
      
	if (name_p != NULL && *name_p == NULL && *project_line_p != EOS) 
        { 
	  argument = parse_absolute_path( &project_line_p, project_file );
	  if (! has_extension( argument, extension ))
	  {
	    complain( "\"%s\" should have extension \"%s\".", 
		      name_in_path( argument ), extension );
	  }
	  set_binary_file_name( name_p, argument );
	  free_mem( &argument );
	}
      }
      IF_ERROR
      {
	if (! err_pos_printed)
	{
	  print_text( error_text, " (\"%s\", line %d)",
		      name_in_path( project_file ), line_count );
	  err_pos_printed = TRUE;
	}
      }
      END_TRY;
    }
    free_mem( &project_line );
  }
  close_stream( &project_stream, project_file );
  info_in_project_file = FALSE;
}

/*---------------------------------------------------------------------------*/

/* The commands that can be called on startup, in alphabetical order. */
static command_t *malaga_options[] = 
{ 
  &alias_option, &auto_result_option, &auto_tree_option, 
  &auto_variables_option, &cache_size_option, &display_cmd_option, 
  &error_format_option, &hidden_option, &mor_incomplete_option, 
  &mor_out_filter_option, &mor_pruning_option, &result_format_option,
  &result_list_option, &robust_rule_option, &roman_hangul_option, 
  &sort_records_option, &switch_option, &syn_incomplete_option, 
  &syn_in_filter_option, &syn_out_filter_option, &syn_pruning_option,
  &transmit_cmd_option, &unknown_format_option, &use_display_option, 
  NULL
};

/*---------------------------------------------------------------------------*/

void 
init_malaga( string_t project_file )
/* Initialise this module. */
{ 
  string_t malagarc_path;

  init_input();
  grammar_info = new_text();

  /* Read project file. */
  if (! has_extension( project_file, "pro" )) 
  {
    complain( "Project file \"%s\" must have extension \".pro\".", 
	      project_file );
  }
  read_project_file( project_file );
  if (morphology_file == NULL) 
    complain( "Missing morphology rules." );
  if (lexicon_file == NULL) 
    complain( "Missing lexicon." );
  if (symbol_file == NULL) 
    complain( "Missing symbol file." );

  /* Init modules. */
  init_values();
  if (extended_symbol_file != NULL) 
    init_symbols( extended_symbol_file );
  else 
    init_symbols( symbol_file );
  init_hangul();
  init_lexicon( lexicon_file );
  init_scanner();
  init_transmit();
  init_analysis( morphology_file, syntax_file );

  /* Set options to default values. */
  error_format = new_string( "%l: %s: error: %e", NULL );
  result_format = new_string( "%l: %s: %f", NULL );
  unknown_format = new_string( "%l: %s: unknown", NULL );
  use_display = auto_tree = FALSE;
  auto_result = TRUE;

  /* Execute startup files. */
  options = malaga_options;
  execute_set_commands( project_file, "malaga:" );
  malagarc_path = NULL;
#ifdef POSIX
  TRY 
    malagarc_path = absolute_path( "~/.malagarc", NULL );
  IF_ERROR 
    RESUME;
  END_TRY;
#endif
#ifdef WIN32
  TRY 
    malagarc_path = absolute_path( "~\\malaga.ini", NULL );
  IF_ERROR 
    RESUME;
  END_TRY;
#endif
  if (malagarc_path != NULL && file_exists( malagarc_path ))
    execute_set_commands( malagarc_path, "malaga:" );
  free_mem( &malagarc_path );
}

/*---------------------------------------------------------------------------*/

void 
terminate_malaga( void )
/* Terminate this module. */
{
  free_aliases();
  free_mem( &error_format );
  free_mem( &result_format );
  free_mem( &unknown_format );
  terminate_analysis();
  free_mem( &syntax_file );
  free_mem( &morphology_file );
  terminate_patterns();
  terminate_transmit();
  terminate_scanner();
  terminate_lexicon();
  free_mem( &lexicon_file );
  terminate_hangul();
  terminate_symbols();
  free_mem( &extended_symbol_file );
  free_mem( &symbol_file );
  terminate_values();
  free_text( &grammar_info );
  terminate_input();
}

/* End of file. =============================================================*/
