/* 
   elmo - ELectronic Mail Operator

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

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

   showing e-mail in scrolling window
   
*/
/****************************************************************************
 *    IMPLEMENTATION HEADERS
 ****************************************************************************/

#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>

#include "ecurses.h"
#include "mailreader.h"
#include "status.h"
#include "error.h"
#include "xmalloc.h"
#include "attach.h"
#include "cmd.h"
#include "wrapbox.h"
#include "gettext.h"
#include "folder.h"
#include "select.h"
#include "color.h"
#include "label.h"
#include "interface.h"
#include "ask.h"
#include "rarray.h"

/****************************************************************************
 *    IMPLEMENTATION PRIVATE DEFINITIONS / ENUMERATIONS / SIMPLE TYPEDEFS
 ****************************************************************************/
/****************************************************************************
 *    IMPLEMENTATION PRIVATE CLASS PROTOTYPES / EXTERNAL CLASS REFERENCES
 ****************************************************************************/
/****************************************************************************
 *    IMPLEMENTATION PRIVATE STRUCTURES / UTILITY CLASSES
 ****************************************************************************/

struct header {
        str_t  *str;
        chtype  color;
};

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

/* This tells us if user is watching a header or a message. */
static int header_view = 0;

/* Mailreader window consists of select_t object and an optional label. */
static elabel_t *label       = NULL;
static select_t *mail_select = NULL;

/* Message body and header split into single lines. */
static rstring_t *mail_lines   = NULL;

/* Message body as a string. */
static char *mail_body = NULL;

/* Colors used in mailreader window. */
static chtype text_color;
static chtype signature_color;
static chtype quote_color;
static chtype to_color;
static chtype from_color;
static chtype subject_color;
static chtype cc_color;
static chtype mua_color;
static chtype date_color;
static chtype replyto_color;
static chtype sigok_color;
static chtype sigwarn_color;
static chtype sigfail_color;
static chtype hilight_color;

/* This is the index of the first line of the signature. */
static unsigned signature_index = -1;

/* Header consists of struct header objects. */
static rarray_t *headers = NULL;

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

static void
header_destroy (void)
{
        int            i;
        struct header *header;

        if (headers == NULL)
                return;
        
        for (i = 0; i < headers->count; i++){
                header = (struct header *) headers->array[i];
                str_destroy (header->str);
                xfree (header);
        }
        rarray_destroy (headers);

        headers = NULL;
}


static void
header_create (void)
{
        header_destroy ();
        headers = rarray_create_size (10);
}


static void
header_add_str (str_t *str, chtype color)
{
        struct header *item = xmalloc (sizeof (struct header));

        item->str   = str;
        item->color = color;
        rarray_add (headers, item);
}


static void
header_add (chtype color, const char *fmt, ...)
{
        str_t   *str;
        va_list  ap;
        
        va_start (ap, fmt);
        str = str_create ();
        str_vsprintf (str, fmt, ap);
        va_end (ap);

        header_add_str (str, color);
}


static void
header_add_raddress (chtype color, raddress_t *addr, const char *s)
{
        int i;

        if (addr && addr->count > 0){
                if (addr->array[0] && addr->array[0]->full)
                        header_add (color, "%s: %s", s, addr->array[0]->full);
                for (i = 1; i < addr->count; i++)
                        header_add (color, "    %s", addr->array[i]->full);
        }
}


static void
header_add_signature (mime_info_t *mime)
{
        switch (mime->sig){

                case SIG_NOT_SIGNED:
                        break;

                case SIG_OK:
                        if (mime->sig_by)
                                header_add (sigok_color, "PGP: ok (%s)",
                                            mime->sig_by);
                        else
                                header_add_str (str_dup ("PGP: ok"),
                                                sigok_color);
                        break;

                case SIG_WARN:
                        if (mime->sig_by)
                                header_add (sigwarn_color, "PGP: %s (%s)",
                                            mime->sig_text, mime->sig_by);
                        else
                                header_add (sigwarn_color, "PGP: %s",
                                            mime->sig_text);
                        break;

                case SIG_FAIL:
                        if (mime->sig_by)
                                header_add (sigfail_color, "PGP: %s (%s)",
                                            mime->sig_text, mime->sig_by);
                        else
                                header_add (sigfail_color, "PGP: %s",
                                            mime->sig_text);
                        break;
        }
}


static int
header_draw_line (WINDOW *window, int maxlen, int index)
{
        struct header *header;
        
        if (headers == NULL || index >= headers->count)
                return 0;

        header = headers->array[index];
        wattrset (window, header->color);
        maxlen -= window_addnstr (window, header->str->str, maxlen);

        while (maxlen-- > 0)
                window_addch (window, ' ');
        return 1;
}


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


static void
generate_header (mail_t *mail)
{
        header_create ();
        
        if (mail->date_str)
                header_add (date_color, "Date: %s", mail->date_str);
        if (mail->from)
                header_add (from_color, "From: %s", mail->from->full);
        if (mail->subject)
                header_add (subject_color, "Subject: %s", mail->subject);

        header_add_raddress (to_color, mail->to, "To");
        header_add_raddress (cc_color, mail->cc, "Cc");
        header_add_raddress (cc_color, mail->bcc, "Bcc");
        
        if (mail->reply_to && mail->reply_to != mail->from)
                header_add (replyto_color, "Reply-To: %s",
                            mail->reply_to->full);
        if (mail->mua)
                header_add (mua_color, "MUA: %s", mail->mua);

        header_add_signature (mail->mime);
        header_add_str (str_dup (""), text_color);
}



static int
set_mail_lines (mail_t *mail, mime_t *mime)
{
        int     width, nothing;
        str_t  *str;
        
        if (mail_body){
                xfree (mail_body);
                mail_body = NULL;
        }

        str = wrapbox_mail_body (mail, mime, 0);
        if (str == NULL && errno != 0){
                error_ (errno, _("impossible to display mail contents"));
                return 1;
        }
        mail_body = (str) ? str_finished (str) : NULL;

        if (mail_lines){
                rstring_delete (mail_lines);
                mail_lines = NULL;
        }

        generate_header (mail);
        getmaxyx (mail_select->win, nothing, width);
        
        if (mail_body){
                mail_lines = rstring_split_lines (mail_body, width);
        }
        return 0;
}



static int
set_header_lines (void)
{
        mail_t *mail = folder_mail_selected ();

        if (mail == NULL)
                return 1;
        
        if (mail_body){
                xfree (mail_body);
                mail_body = NULL;
        }
        
        if (wrapbox_mail_header (mail, &mail_body)){
                error_ (errno, _("impossible to display mail header"));
                return 1;
        }

        if (mail_lines){
                rstring_delete (mail_lines);
                mail_lines = NULL;
        }

        header_destroy ();

        if (mail_body){
                mail_lines = rstring_split_lines (mail_body, COLS);
        }
        return 0;
}



static int
quote_delta (int state, char c)
{
        if (state == 0 && isspace (c))
                return 0;

        if (c == '>')
                return 111;

        if (isalnum (c))
                return state + 1;

        return 5;
}


static int
is_quote (char *str)
{
        int state = 0;

        while (*str && state < 4){
                state = quote_delta (state, *str);
                str++;
        }

        return state == 111;
}



static int
sig_delta (int state, char c)
{
        if (state < 2 && c == '-')
                return state + 1;

        if (state == 2 && c == ' ')
                return 3;
        
        return 4;
}


static int
is_signature (char *str, int index)
{
        int state = 0;
        
        if (index >= signature_index)
                return 1;

        while (*str && state < 4){
                state = sig_delta (state, *str);
                str++;
        }

        if (*str == '\0' && state == 3){
                signature_index = index;
                return 1;
        }

        return 0;
}



static void
change_color (WINDOW *window, char *str, int index)
{
        if (is_signature (str, index)){
                wattrset (window, signature_color);
        }
        else if (is_quote (str)){
                wattrset (window, quote_color);
        }
        else {
                wattrset (window, text_color);
        }
}


static int
add_string (WINDOW *win, char *str, int maxlen, search_t *search)
{
        int pos = -1;
        int len = 1;
  
        if (maxlen <= 0)
                return 0;

        if (search)
                pos = search_perform (search, str);

        if (pos != -1){
                len  = search->pattern->len;
                pos -= len - 1;
                if (pos < 0)
                        pos = 0;
                window_addnstr (win, str, pos);
                wattrset (win, hilight_color);
                window_addnstr (win, str + pos, len);
                wattrset (win, text_color);
                maxlen -= pos + len;
        }

        return pos + len + window_addnstr (win, str + pos + len, maxlen - 1);
}



static void
draw_line (WINDOW *window, int maxlen, int index, search_t *search)
{
        int i;
        
        if (headers == NULL && mail_lines == NULL)
                return;

        if (header_draw_line (window, maxlen, index))
                return;
        
        if (headers)
                index -= headers->count;
        
        if (mail_lines && index < mail_lines->count){
                change_color (window, mail_lines->array[index], index);
                i = add_string (window, mail_lines->array[index], maxlen,
                                search);
                while (i < maxlen){
                        window_addch (window, ' ');
                        i++;
                }
        }
        else {
                for (i = 0; i < maxlen; i++){
                        window_addch (window, ' ');
                }
        }
}


static int
reader_count (select_t *nothing)
{
        int ret = 1;
        
        if (headers)
                ret += headers->count;
        
        if (mail_lines)
                ret += mail_lines->count;

        return ret;
}


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


static int
load_mail (void)
{
        mail_t *mail = folder_mail_selected ();

        if (mail == NULL)
                return 1;
        
        select_first (mail_select);
        set_mail_lines (mail, NULL);
        header_view = 0;

        folder_after_read ();
        
        signature_index = -1;

        mailreader_redraw ();
        return 0;
}


static int
match_line (search_t *search, int index)
{
        if (headers == NULL && mail_lines == NULL)
                return 0;

        if (headers && index < headers->count)
                return 0;

        if (headers)
                index -= headers->count;

        if (mail_lines == NULL || index >= mail_lines->count)
                return 0;

        if (search_perform (search, mail_lines->array[index]) != -1)
                return 1;

        return 0;
}


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


void
mailreader_init (void)
{
        WINDOW *window;

        window      = interface_init ();
        mail_select = select_open (window, 1, draw_line, reader_count);
        header_view = 0;

        window_set_functions (window, mailreader_refresh, mailreader_redraw,
                              mailreader_set_focus, mailreader_unset_focus);
}



void
mailreader_free_resources (void)
{
        if (mail_select)
                select_close (mail_select);
        mail_select = NULL;

        if (mail_lines)
                rstring_delete (mail_lines);
        mail_lines = NULL;

        header_destroy ();

        if (mail_body)
                xfree (mail_body);
        mail_body = NULL;

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



void
mailreader_line_up (void)
{
        select_prev (mail_select);
}



void
mailreader_line_down (void)
{
        select_next (mail_select);
}



void
mailreader_page_up (void)
{
        select_prev_page (mail_select);
}



void
mailreader_page_down (void)
{
        select_next_page (mail_select);
}



void
mailreader_top (void)
{
        select_first (mail_select);
}



void
mailreader_bottom (void)
{
        select_last (mail_select);
}



void
mailreader_header_switch (void)
{
        mail_t *mail = folder_mail_selected ();

        if (mail == NULL)
                return;
        
        select_first (mail_select);
        
        if (! header_view){
                signature_index = -1;
                set_header_lines ();
                mailreader_redraw ();
                header_view = 1;
        }
        else {
                set_mail_lines (mail, NULL);
                mailreader_redraw ();
                header_view = 0;
        }
}



void
mailreader_load (void)
{
        load_mail ();
}


void
mailreader_show (void)
{
        if (load_mail ())
                return;
        
        window_show (mail_select->win);
}


void
mailreader_close (void)
{
        signature_index = -1;
        
        if (mail_lines)
                rstring_delete (mail_lines);
        mail_lines = 0;
        
        window_hide (mail_select->win);

        interface_focus (1);
}



void
mailreader_redraw (void)
{
        mail_t *mail;
        
        mail = folder_mail_selected ();

        if (mail == NULL)
                return;
        
        select_redraw (mail_select);

        if (label)
                label_redraw (label);

        status_put_mailinfo (address_name (mail->from), mail->subject);
}



void
mailreader_refresh (void)
{
        select_show (mail_select);

        if (label)
                label_show (label);

        status_refresh ();
}


void
mailreader_set_focus (void)
{
        mail_t *mail;
        
        mail = folder_mail_selected ();

        if (mail == NULL)
                return;
        
        if (label){
                label_set_focus (label);
        }

        status_put_mailinfo (address_name (mail->from), mail->subject);
        cmd_state_push (CMD_READ_MAIL);

        mailreader_redraw ();
}


void
mailreader_unset_focus (void)
{
        if (label){
                label_unset_focus (label);
                label_redraw (label);
        }

        cmd_state_pop ();
}


void
mailreader_change_mime (mail_t *mail, mime_t *mime)
{
        set_mail_lines (mail, mime);
        signature_index = -1;
        mailreader_redraw ();
}


void
mailreader_search_forward (void)
{
        select_search_setup_forward (mail_select, match_line);
}


void
mailreader_search_backward (void)
{
        select_search_setup_backward (mail_select, match_line);
}

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


syntax highlighted by Code2HTML, v. 0.9.1