/*
 **********************************************************************
 *
 * G_fatal_error (msg)
 *      char *msg          One line error message
 *
 * G_warning (msg)
 *      char *msg
 *
 *   Gives the message, msg, to the user.  Also print a message to 
 *   $GISBASE/GIS_ERROR_LOG if the file exists and is writeable; 
 *   and to the file $HOME/GIS_ERROR_LOG in the user's home directory 
 *   if $HOME is set and the file exists.
 *   G_warning() returns, which G_fatal_error() exits.
 *
 *   note:  By default, the message is handled by an internal routine
 *      which prints the message to the screen.   Using G_set_error_routine()
 *      the programmer can have the message handled by another routine.
 *      This is especially useful if the message should go to a particular
 *      location on the screen when using curses or to a location on
 *      a graphics device (monitor).
 *          
 **********************************************************************
 * G_set_error_routine (error_routine)
 *      int (*error_routine)()
 *
 *   Establishes error_routine as the routine that will handle 
 *   the printing of subsequent error messages. error_routine
 *   will be called like this:
 *      error_routine(msg, fatal)  
 *         char *msg ;
 **********************************************************************
 * G_unset_error_routine ()
 *
 *   After this call subsequent error messages will be handled in the
 *   default method.  Error messages are printed directly to the
 *   screen:
 *           ERROR: message
 *   -or-    WARNING: message
 *
 **********************************************************************/
/*
     Throughout the code the references to these routines attempt to
	send format strings and arguments.  It seems that we expect
	these to handle varargs, so now they do.    7-Mar-1999
		Bill Hughes
*/

#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <stdarg.h>
#include <sys/types.h>
#include <grass/glocale.h>
#include <grass/gis.h>

#define MSG  0
#define WARN 1
#define ERR  2

/* static int (*error)() = 0; */
static int (*ext_error)() = 0; /* Roger Bivand 17 June 2000 */
static int no_warn = 0;
static int no_sleep = 1;
static int message_id = 1;

extern char *getenv();
static int print_word (FILE *, char **, int *, const int);
static void print_sentence (FILE *, const int, const char *);
static int print_error (const char *, const int);
static int mail_msg (const char *, const int);
static int write_error (const char *, const int, const char *, 
                        const time_t, const char *);
static int log_error (const char *, const int);

/*!
 * \brief Print a message to stderr
 *
 * The output format depends on enviroment variable GRASS_MESSAGE_FORMAT
*/
void G_message (char *msg,...)
{
    char buffer[2000];  /* G_asprintf does not work */
    va_list ap;

    va_start(ap, msg);
    vsprintf(buffer,msg,ap);
    va_end(ap);

    print_error (buffer,MSG);
}

int G_fatal_error ( char *msg,...)
{
    char buffer[2000];  /* No novels to the error logs, OK? */
    va_list ap;

    va_start(ap,msg);
    vsprintf(buffer,msg,ap);
    va_end(ap);

    print_error (buffer,ERR);

    if ( ext_error ) return 0; /* do not exit error routine is specified */
    
    exit (EXIT_FAILURE);
}

int G_warning ( char *msg, ...)
{
    char buffer[2000];
    va_list ap;

    if (no_warn) return 0;

    va_start(ap,msg);
    vsprintf(buffer,msg,ap);
    va_end(ap);
    print_error (buffer,WARN);

    return 0;
}

int G_suppress_warnings (int flag)
{
    int prev;

    prev = no_warn;
    no_warn = flag;
    return prev;
}

int G_sleep_on_error (int flag)
{
    int prev;

    prev = !no_sleep;
    no_sleep = !flag;
    return prev;
}

int G_set_error_routine ( int (*error_routine)())
{
    ext_error = error_routine; /* Roger Bivand 17 June 2000 */
    return 0;
}

int G_unset_error_routine ()
{
    ext_error = 0; /* Roger Bivand 17 June 2000 */

    return 0;
}

/* Print info to stderr and optionaly to log file and optionaly send mail */
static int print_error(const char *msg, const int type)
{
    static char *prefix_std[3];
    int fatal, format;
    
    if ( !prefix_std[0] ) { /* First time: set prefixes  */
        prefix_std[0] = "";
	prefix_std[1] = _("WARNING: ");
	prefix_std[2] = _("ERROR: ");
    }
    
    if ( type == ERR )
	fatal = 1;
    else /* WARN */
	fatal = 0;

    if ( (type == WARN || type == ERR) && ext_error) { /* Function defined by application */
	ext_error (msg, fatal);
    } else {
	char *w;
	int len, lead;

        format = G_info_format();

	if ( format == G_INFO_FORMAT_STANDARD ) {
	    if ( type == WARN || type == ERR ) { 
		log_error (msg, fatal);
	    }

	    fprintf(stderr,"%s", prefix_std[type] );
	    len = lead = strlen ( prefix_std[type] );
	    w = (char *)msg;

	    while (print_word(stderr,&w,&len,lead))
		    ;

	    if ( (type != MSG) && isatty(fileno(stderr))) { /* Bell */
		fprintf(stderr,"\7");
		fflush (stderr);
		if (!no_sleep)
		    G_sleep (5);
	    } else if ( (type == WARN || type == ERR) && getenv("GRASS_ERROR_MAIL")) { /* Mail */
		mail_msg (msg, fatal);
	    }
	} else { /* GUI */
	    print_sentence ( stderr, type, msg );
	}
    }

    return 0;
}

static int log_error (const char *msg, const int fatal)
{
    FILE *pwd;
    char cwd[1024];
    time_t clock;
    char *home;
    char *gisbase;

    /* get time */
    clock = time(NULL);

    /* get current working directory */
    sprintf(cwd,"?");
    if ( (pwd = G_popen ("pwd","r")) )
    {
	if (fgets(cwd, sizeof(cwd), pwd))
	{
	    char *c;

	    for (c = cwd; *c; c++)
		if (*c == '\n')
		    *c = 0;
	}
	G_pclose (pwd);
    }

    /* write the 2 possible error log files */
    if ((gisbase = G_gisbase ()))
	write_error (msg, fatal, gisbase, clock, cwd);

    home = G__home();
    if (home && gisbase && strcmp (home, gisbase))
	write_error (msg, fatal, home, clock, cwd);

    return 0;
}

static int write_error (const char *msg, const int fatal,
                        const char *dir, const time_t clock,
                        const char *cwd)
{
    char logfile[GNAME_MAX];
    FILE *log;

    if (dir == 0 || *dir == 0)
	return 1;
    sprintf (logfile, "%s/GIS_ERROR_LOG", dir) ;

    log = fopen (logfile,"r");
    if (!log)
    	/* GIS_ERROR_LOG file is not readable or does not exist */
	return 1;

    log = freopen(logfile, "a", log);
    if (!log)
    	/* the user doesn't have write permission */
	return 1;

    fprintf(log,"-------------------------------------\n");
    fprintf(log,"%-10s %s\n", "program:", G_program_name());
    fprintf(log,"%-10s %s\n", "user:", G_whoami());
    fprintf(log,"%-10s %s\n", "cwd:", cwd);
    fprintf(log,"%-10s %s\n", "date:", ctime(&clock));
    fprintf(log,"%-10s %s\n", fatal?"error:":"warning:", msg);
    fprintf(log,"-------------------------------------\n");

    fclose (log);

    return 0;
}

static int mail_msg (const char *msg, const int fatal)
{
    FILE *mail;
    char command[64];
    char *user;

    user = G_whoami();
    if (user == 0 || *user == 0)
	return 1;

    sprintf (command, "mail '%s'", G_whoami());
    if ( (mail = G_popen (command, "w")) )
    {
	fprintf(mail,"GIS %s: %s\n",fatal?"ERROR":"WARNING",msg);
	G_pclose (mail);
    }

    return 0;
}

/* Print one word, new line if necessary */
static int print_word (FILE *fd, char **word, int *len, const int lead)
{
    int  wlen, start, totlen;
    int  nl;
    char *w,*b;

    start = *len;
    w = *word;

    nl = 0;
    while (*w == ' ' || *w == '\t' || *w == '\n')
	if(*w++ == '\n')
	    nl++;

    wlen = 0;
    for (b = w; *b != 0 && *b != ' ' && *b != '\t' && *b != '\n'; b++)
	wlen++;

    if (wlen == 0)
    {
	fprintf (fd, "\n");
	return 0;
    }

    if ( start > lead ) { /* add space */
	totlen = start + wlen + 1;
    } else {
	totlen = start + wlen; 
    }
    
    if ( nl != 0 || totlen > 75)
    {
	while (--nl > 0)
	    fprintf (fd, "\n");
	fprintf (fd, "\n%*s",lead,"");
	start = lead;
    }

    if ( start > lead ) {
      fprintf (fd, " ");
      start++;
    }

    *len = start + wlen;

    while (wlen-- > 0)
	fprintf (fd, "%c", *w++);

    *word = w;

    return 1;
}

/* Print one message, prefix inserted before each new line */
static void print_sentence (FILE *fd, const int type, const char *msg)
{
    char *start;
    static char prefix[100];

    switch ( type ) {
	case MSG: 
    	    sprintf (prefix, "GRASS_INFO_MESSAGE(%d,%d): ", getpid(), message_id);
	    break;
	case WARN:
    	    sprintf (prefix, "GRASS_INFO_WARNING(%d,%d): ", getpid(), message_id);
	    break;
	case ERR:
    	    sprintf (prefix, "GRASS_INFO_ERROR(%d,%d): ", getpid(), message_id);
	    break;
    }

    start = (char *)msg;

    fprintf(stderr, "\n" );
    while ( *start != '\0' ) {
	fprintf ( fd, "%s", prefix);

	while ( *start != '\0' ) {
	    fprintf (fd, "%c", *start++);
		
	    if ( *start == '\n' ) {
	        *start++;
		break;
	    }
	}
	
	fprintf (fd, "\n" );
    }
    fprintf(stderr, "GRASS_INFO_END(%d,%d)\n", getpid(), message_id );
    message_id++;
}
    
int G_info_format ( void ) 
{
    static int grass_info_format = -1;
    char    *fstr;
    
    if ( grass_info_format < 0) {
        fstr = getenv( "GRASS_MESSAGE_FORMAT" );

        if ( fstr && G_strcasecmp(fstr,"gui") == 0 )
	    grass_info_format = G_INFO_FORMAT_GUI;
        else
	    grass_info_format = G_INFO_FORMAT_STANDARD;
    }

    return grass_info_format;
}

