/* 
   elmo - ELectronic Mail Operator 

   Copyright (C) 2003 rzyjontko

   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; version 2.

   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.  

   ----------------------------------------------------------------------

   error display system

   An error message must be visible.  It has to remain on the screen
   sufficiently long, so that a user is able to read it.  The message
   should then disappear.  Sometimes one error may generate next errors.
   A configreader may be a perfect example.  This is why messages are
   queued for display.

   Other MUAs display error messages enough long to read it, but they don't
   clear them, what has always been annoying me.
*/

#define _GNU_SOURCE 1

/****************************************************************************
 *    IMPLEMENTATION HEADERS
 ****************************************************************************/

#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <unistd.h>
#include <sys/types.h>
#include <time.h>

#include "ecurses.h"
#include "error.h"
#include "xmalloc.h"
#include "str.h"
#include "cmd.h"
#include "elmo.h"
#include "interface.h"

/****************************************************************************
 *    IMPLEMENTATION PRIVATE DEFINITIONS / ENUMERATIONS / SIMPLE TYPEDEFS
 ****************************************************************************/

/* DELAY is a minimal number of seconds, that the message MUST remain
   on the screen.  REMAIN is a maximal number of seconds, that the message
   MAY remain on the screen. */
#define DELAY  1
#define REMAIN 4

/****************************************************************************
 *    IMPLEMENTATION PRIVATE CLASS PROTOTYPES / EXTERNAL CLASS REFERENCES
 ****************************************************************************/
/****************************************************************************
 *    IMPLEMENTATION PRIVATE STRUCTURES / UTILITY CLASSES
 ****************************************************************************/

struct list {
        struct list *next;
        char        *msg;
};


struct queue {
        struct list *list;     /* first element of the queue */
        struct list *last;     /* last element of the queue */
        time_t       time;     /* the time, when last message was displayed */
        int          visible;  /* if anything is being displayed now */
};

/****************************************************************************
 *    IMPLEMENTATION REQUIRED EXTERNAL REFERENCES (AVOID)
 ****************************************************************************/
/****************************************************************************
 *    IMPLEMENTATION PRIVATE DATA
 ****************************************************************************/

/* The message queue. */
static struct queue msg_queue;

/* 0 before error_init, and after error_free_resources
   when 1 - errors are displayed in cmd_win
   when 0 - errors are written to stderr */
static int initialized = 0;

/****************************************************************************
 *    INTERFACE DATA
 ****************************************************************************/
/****************************************************************************
 *    IMPLEMENTATION PRIVATE FUNCTION PROTOTYPES
 ****************************************************************************/
/****************************************************************************
 *    IMPLEMENTATION PRIVATE FUNCTIONS
 ****************************************************************************/

static void
queue_insert (char *msg)
{
        struct list *elem = xmalloc (sizeof (struct list));
  
        elem->next = NULL;
        elem->msg  = msg;
  
        if (msg_queue.last == NULL){
                msg_queue.list = msg_queue.last = elem;
        }
        else {
                msg_queue.last->next = elem;
                msg_queue.last       = elem;
        }
}


static char *
queue_get (void)
{
        char        *result;
        struct list *tmp;
  
        if (msg_queue.list == NULL)
                return NULL;

        tmp    = msg_queue.list;
        result = tmp->msg;

        msg_queue.list = tmp->next;
        if (msg_queue.list == NULL)
                msg_queue.last = NULL;

        xfree (tmp);
        return result;
}



static char *
prepare_error_msg (int errnum, const char *fmt, va_list ap)
{
        str_t *str = str_create ();

        str_vsprintf (str, fmt, ap);
  
        if (errnum){
                str_sprintf (str, ": %s", strerror (errnum));
        }

        return str_finished (str);
}


static void
display_msg (const char *msg)
{
	if (initialized){
                cmd_error_color ();
                window_mvaddstr (cmd_win, 0, 0, msg);
                wclrtoeol (cmd_win);
                wnoutrefresh (cmd_win);
        }
        else {
                fprintf (stderr, "%s\n", msg);
        }
}


/*
   There are 2 things to do each time this function is called.

   1 - Clear the messages that are displayed too long.
   2 - Display new messages if the last one was being displayed long enough.
*/
static void
error_hook (int nothing)
{
        char   *msg;
        time_t  now;

        if (msg_queue.visible || msg_queue.list){
                now = time (NULL);
                if (now - msg_queue.time > DELAY){
                        msg = queue_get ();
                        if (msg){
                                display_msg (msg);
                                msg_queue.time    = now;
                                msg_queue.visible = 1;
                                xfree (msg);
                        }
                }

                if (now - msg_queue.time > REMAIN){
                        display_msg ("");
                        msg_queue.visible = 0;
                }
        }
}


/****************************************************************************
 *    INTERFACE FUNCTIONS
 ****************************************************************************/

void
error_init (void)
{
        cmd_add_timeout_handler (error_hook);
        
        memset (&msg_queue, '\0', sizeof (msg_queue));
        initialized = 1;
}




void
error_critical (int status, int errnum, const char *msg)
{
        elmo_finish (0);

        if (errnum)
                fprintf (stderr, "%s: %s\n", msg, strerror (errnum));
        else
                fprintf (stderr, "%s\n", msg);

        exit (status);
}



void
error_ (int errnum, const char *fmt, ...)
{
        va_list  ap;
        char    *msg;

        va_start (ap, fmt);
        msg = prepare_error_msg (errnum, fmt, ap);
        va_end (ap);

        queue_insert (msg);
}



void
error_regex (int errcode, regex_t *compiled, const char *re)
{
        int   length = regerror (errcode, compiled, NULL, 0);
        char *buffer = xmalloc (length + 1);

        regerror (errcode, compiled, buffer, length);

        if (re == NULL){
                queue_insert (buffer);
        }
        else {
                error_ (0, "%s: %s", buffer, re);
                xfree (buffer);
        }
}



void
error_free_resources (void)
{
        struct list *list;
        struct list *next;

        for (list = msg_queue.list; list; list = next){
                next = list->next;
                if (list->msg)
                        xfree (list->msg);
                xfree (list);
        }

        initialized = 0;
}


/****************************************************************************
 *    INTERFACE CLASS BODIES
 ****************************************************************************/
/****************************************************************************
 *
 *    END MODULE error.c
 *
 ****************************************************************************/


syntax highlighted by Code2HTML, v. 0.9.1