/* 
   elmo - ELectronic Mail Operator

   Copyright (C) 2002, 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.  

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

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

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include <stdio.h>
#include <stdarg.h>
#include <string.h>

#include "debug.h"
#include "error.h"
#include "select.h"
#include "color.h"
#include "ask.h"
#include "gettext.h"
#include "label.h"
#include "interface.h"
#include "str.h"
#include "xmalloc.h"
#include "cmd.h"

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

#ifndef DEBUG
# define DEBUG 0
#endif

#define DEBUG_FILE "debug.log"
#define DEFAULT_BACKLOG_SIZE 50

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

struct debug {
        str_t      *str;
        enum level  level;
};

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

#if DEBUG

/* Every debug information is written to this file. */
static FILE *debug_file = NULL;


/* These are colors used to diplay different kinds of messages. */
static chtype error_color;
static chtype warn_color;
static chtype info_color;
static chtype text_color;

/* Debug window consists (like most of windows) of select_t object, and
   optional label. */
static elabel_t *label        = NULL;
static select_t *debug_select = NULL;


/* The backlog is a kind of queue.  We store only backlog_size last
   messages. */
static struct debug *backlog       = NULL;
static int           backlog_size  = DEFAULT_BACKLOG_SIZE;
static int           backlog_first = 0;
static int           backlog_count = 0;

#endif

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

#if DEBUG
static void initialize_backlog (void);
#endif

/****************************************************************************
 *    IMPLEMENTATION PRIVATE FUNCTIONS
 ****************************************************************************/

#if DEBUG

static void
debug_refresh (void)
{
        select_show (debug_select);
        if (label)
                label_show (label);
}



static void
debug_redraw (void)
{
        select_redraw (debug_select);
        if (label)
                label_redraw (label);
}



static void
debug_set_focus (void)
{
        if (label)
                label_set_focus (label);
        cmd_state_push (CMD_DEBUG);
}



static void
debug_unset_focus (void)
{
        if (label)
                label_unset_focus (label);
        cmd_state_pop ();
}



static void
print_line (WINDOW *win, int maxlen, int index, search_t *search)
{
        str_t *str;

        if (index < backlog_count)
                str = backlog[index].str;
        else
                str = NULL;
        
        if (win == NULL)
                return;

        if (str){
                switch (backlog[index].level){

                        case DEBUG_ERROR:
                                wattrset (win, error_color);
                                break;

                        case DEBUG_WARN:
                                wattrset (win, warn_color);
                                break;

                        case DEBUG_INFO:
                                wattrset (win, info_color);
                                break;

                        default:
                                wattrset (win, text_color);
                                break;
                }

                maxlen -= window_addnstr (win, str->str, maxlen);
        }

        while (maxlen-- > 0)
                window_addch (win, ' ');
}



static int
debug_count (select_t *nothing)
{
        return backlog_count;
}


/* This file is generated by interface.pl script from interface.desc,
   and inc.in. */
static WINDOW *interface_init (void);
#include "debug.inc"

#endif


/****************************************************************************
 *    BACKLOG FUNCTIONS
 ****************************************************************************/

#if DEBUG

static void
initialize_backlog (void)
{
        if (backlog_size <= 0)
                backlog_size = DEFAULT_BACKLOG_SIZE;
        
        backlog       = xcalloc (backlog_size, sizeof (struct debug));
        backlog_count = 0;
        backlog_first = 0;
}


static void
backlog_destroy (void)
{
        int i;
        
        if (backlog == NULL)
                return;
        
        for (i = 0; i < backlog_size; i++){
                if (backlog[i].str)
                        str_destroy (backlog[i].str);
        }

        xfree (backlog);
        backlog = NULL;
}


static void
backlog_add (enum level level, const char *fmt, va_list ap)
{
        int    index;
        str_t *str;

        index = (backlog_count + backlog_first) % backlog_size;
        str   = backlog[index].str;

        if (str){
                str_clear (str);
        }
        else {
                str                = str_create ();
                backlog[index].str = str;
        }
        backlog[index].level = level;

        str_vsprintf (str, fmt, ap);

        if (backlog_count == backlog_size){
                backlog_first++;
        }
        else {
                backlog_count++;
        }
}

#endif

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

void
debug_start (void)
{
#if DEBUG
        WINDOW *window;

        window       = interface_init ();
        initialize_backlog ();
        debug_select = select_open (window, 1, print_line, debug_count);
        debug_file   = fopen (DEBUG_FILE, "a");

        window_set_functions (window, debug_refresh, debug_redraw,
                              debug_set_focus, debug_unset_focus);
        
        if (! debug_file)
                return;

        fprintf (debug_file, "start\n");
        fflush (debug_file);
#endif
}


void
debug_end (void)
{
#if DEBUG
        if (debug_select)
                select_close (debug_select);
        debug_select = NULL;

        if (label)
                label_destroy (label);
        label = NULL;

        backlog_destroy ();
        
        if (! debug_file)
                return;

        fprintf (debug_file, "\n");
        fflush (debug_file);
        fclose (debug_file);
#endif
}



void
debug_msg (enum level level, const char *fmt, ...)
{
#if DEBUG
        va_list arg;

        if (!debug_file)
                return;

        va_start (arg, fmt);
        backlog_add (level, fmt, arg);
        va_end (arg);
        
        switch (level){
                case DEBUG_ERROR:
                        fprintf (debug_file, "ERROR: ");
                        break;

                case DEBUG_WARN:
                        fprintf (debug_file, "WARN: ");
                        break;

                case DEBUG_INFO:
                        fprintf (debug_file, "INFO: ");
                        break;
        }

        va_start (arg, fmt);
        vfprintf (debug_file, fmt, arg);
        fprintf (debug_file, "\n");
        fflush (debug_file);
        va_end (arg);
#endif
}



void
debug_next (void)
{
#if DEBUG
        select_next (debug_select);
#endif
}



void
debug_prev (void)
{
#if DEBUG
        select_prev (debug_select);
#endif
}



void
debug_next_page (void)
{
#if DEBUG
        select_next_page (debug_select);
#endif
}



void
debug_prev_page (void)
{
#if DEBUG
        select_prev_page (debug_select);
#endif
}



void
debug_first (void)
{
#if DEBUG
        select_first (debug_select);
#endif
}



void
debug_last (void)
{
#if DEBUG
        select_last (debug_select);
#endif
}



void
debug_show (void)
{
#if DEBUG
        select_redraw (debug_select);
#endif
}


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


syntax highlighted by Code2HTML, v. 0.9.1