// error.cc -- debugging and error macros
// Copyright (C) 1999-2000 Tommi Syrjänen <Tommi.Syrjanen@hut.fi>
//  
// 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 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.
//  

#include "../config.h"
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "error.h"
#include "global.h"
#include "debug.h"
#include "list.h"
#include "symbol.h"
#include "term.h"

void error(int level, char *fmt, ...)  
{
  int pos = 0;
  static char buf[BUFSIZ] = { 0 };
  
  va_list(ap);
  va_start(ap, fmt);
  if (level >= SYS_ERR) {
    pos += vsprintf(buf, fmt, ap);
    if (pos >= BUFSIZ) {
      fprintf(stderr, "buffer owerflow -- core dumped\n");
      abort(); // buffer owerflow, don't do anything
    }
    buf[pos] = '\0';
    perror(buf);
  } else {
    vfprintf(stderr, fmt, ap);
    fprintf(stderr, "\n");
  }
  va_end(ap);
  if (level > USR_ERR) {
    print_warnings();
    exit(E_ERROR);
  }
  else
    sys_data.num_errors++;
}

struct warn_label {
  char *label;
  int value;
};
  
static warn_label labels[] =
{
  { "a", WARN_ALL },
  { "all", WARN_ALL },
  { "ar", WARN_ARITY },
  { "arity", WARN_ARITY },
  { "error", WARN_ERROR },
  { "er", WARN_ERROR },
  { "extended", WARN_EXTENDED },
  { "ex", WARN_EXTENDED } ,
  { "l" ,WARN_LIBRARY },
  { "library", WARN_LIBRARY },
  { "si", WARN_SIMILAR },
  { "similar", WARN_SIMILAR },
  { "sy", WARN_SYNTAX },
  { "syntax", WARN_SYNTAX },
  { "typo", WARN_TYPO },
  { "t", WARN_TYPO },
  { "u", WARN_UNSAT },
  { "unsat", WARN_UNSAT },
  { "w", WARN_WEIGHT },
  { "weight", WARN_WEIGHT },
  { "*", WARN_NONE }
};
  
  
int get_warn_from_string(char *st)
{
  int i = 0;

  while (strcmp(labels[i].label,st) && (*labels[i].label != '*')) { 
    i++;
  }
  return labels[i].value;
}

void warn(int cause, long lineno, char *fmt, ...)
{
  static char warn_buf[BUFSIZ] = { 0 };
  int pos = 0;
  if (!(cause & sys_data.warnings))
    return;

  if (lineno)
    pos += sprintf(warn_buf, "%s: %s: ", error_file_and_line(lineno),
		   (sys_data.abort_when_warn) ? "Error" : "Warning");
  else
    pos += sprintf(warn_buf, "%s: ", (sys_data.abort_when_warn) ? 
		   "Error" : "Warning");

  if (pos >= BUFSIZ) {
    fprintf(stderr, "buffer overflow -- aborted");
    abort();
  }
  
  va_list(ap);
  va_start(ap, fmt);
  pos += vsprintf(&warn_buf[pos], fmt, ap);
  va_end(ap);

  if (pos >= BUFSIZ) {
    fprintf(stderr, "buffer overflow -- aborted");
    abort();
  }
  warn_buf[pos] = '\0';

  warning_list->Insert(strdup(warn_buf), lineno);
  
  if (sys_data.abort_when_warn)
    sys_data.num_errors++;
};

// for debugging, prints out all enabled warnings 
void decode_warnings(int w)
{
  fprintf(stderr, "enabled warnings: ");
  if ( w & WARN_WEIGHT) {
    fprintf(stderr, "weight, ");
  }

  if( w & WARN_ARITY ) {
    fprintf(stderr, "arity, " );
  }

  if( w & WARN_UNSAT ) {
    fprintf(stderr, "unsat, " );
  }

  if( w & WARN_SIMILAR ) {
    fprintf(stderr, "similar, " );
  }

  //  if( w & WARN_TYPO ) {
  //    fprintf(stderr, "typo, " );
  //  }

  if (w & WARN_LIBRARY) {
    fprintf(stderr, "library, " );
  }

  fprintf(stderr, "\n");
}

void print_warnings()
{
  char *st = NULL;

  while ((st = warning_list->Iterate())) {
    fprintf(stderr, "%s\n", st);
  }
  warning_list->Clear();
}

  
RuntimeError::RuntimeError(ErrorType t, long a1, long a2)
{
  type = t;
  arg1 = a1;
  arg2 = a2;
}

static const char *error_messages[] = {
  "invalid argument type",
  "comparison of incompatible types:",
  "divide by zero"
};

const char *RuntimeError::GetErrorMessage()
{
  return error_messages[type];
}

void print_argument(long x)
{
  if (IS_NUMBER(x)) {
    fprintf(stderr, "%ld", GET_VALUE(x));
  } else {
    char *st = constant_table->LookupByValue(x);

    if (!st)
      int_error("missing constant", "");

    fprintf(stderr, "%s", st);
  }
}

void RuntimeError::Print()
{
  fprintf(stderr, "%s ", GetErrorMessage());

  switch (type) {
  case ERR_ARGUMENT:
    fprintf(stderr, "for argument '");
    print_argument(arg1);
    fprintf(stderr, "'");
    break;
  case ERR_INVALID_COMPARE:
    fprintf(stderr, "'");
    print_argument(arg1);
    fprintf(stderr, "' and '");
    print_argument(arg2);
    fprintf(stderr, "' can't be compared");
    break;
  case ERR_DIVIDE_BY_ZERO:
    break;
  default:
    int_error("invalid error type", "");
  }
}

void runtime_error(InternalFunction fun, ErrorType t, long a1 = 0,
		   long a2 = 0) 
{
  RuntimeError rt(t, a1, a2);

#ifdef CXX_HAS_NO_EXCEPTIONS
  rt.Print();
  fprintf(stderr, " in function '%s'\n", function_strings[fun]);
  
  exit(1);
#else
  throw rt;
#endif
}

char *error_file_and_line(long lineno)
{
  static char buf[BUFSIZ];
  assert (lineno > 0);
  
  int file_number = sys_data.num_input_files -1;

  if (sys_data.num_input_files <= 1) {
    sprintf(buf, "%ld", lineno);
  } else {
    
    while (sys_data.file_start_lines[file_number] > lineno)
      file_number--;
    
    if (strlen(sys_data.input_files[file_number]) > (BUFSIZ - 10))
      error(SYS_ERR, "buffer overflow");
    
    sprintf(buf, "%s:%ld", sys_data.input_files[file_number],
	    lineno-sys_data.file_start_lines[file_number]+1);
  }
  return buf;
}
