/* $Cambridge: hermes/src/prayer/cmd/cmd_spell.c,v 1.2 2008/05/19 15:55:54 dpc22 Exp $ */
/************************************************
 *    Prayer - a Webmail Interface              *
 ************************************************/

/* Copyright (c) University of Cambridge 2000 - 2008 */
/* See the file NOTICE for conditions of use and distribution. */

#include "prayer_session.h"

static char *
generate_text(struct speller *speller, struct pool *pool, char *word)
{
    struct buffer *b = buffer_create(pool, 128);
    char *t, c;
    unsigned long len = utf8_len(word);

    speller_print_lastline(speller, b);

    /* Print word (need to highlight) */
    bputs(b, "<b>");
    t = word;
    while ((c = *t++) && (c != '\015') && (c != '\012'))
        html_quote_char(b, c);
    bputs(b, "</b>");

    /* Print remainder of line */
    t = speller->line;
    utf8_find_offset(&t, speller->offset + len);

    while ((c = *t) && (c != '\015') && (c != '\012')) {
        html_quote_char(b, c);
        t++;
    }

    /* And next line if that exists */
    if (c) {
        t += ((t[0] == '\015') && (t[1] == '\012')) ? 2 : 1;
        bputs(b, "" CRLF);

        while ((c = *t++) && (c != '\015') && (c != '\012'))
            html_quote_char(b, c);
    }

    /* Plus third line if we are at start of text */
    if (c && (speller->last_line == 0)) {
        t += ((t[0] == '\015') && (t[1] == '\012')) ? 2 : 1;
        bputs(b, "" CRLF);

        while ((c = *t++) && (c != '\015') && (c != '\012'))
            html_quote_char(b, c);
    }

    return(buffer_fetch(b, 0, buffer_size(b), NIL));
}

/* ====================================================================== */

static BOOL
process_line(struct session *session,
             struct request *request, struct speller *speller,
             char *s, BOOL is_alt_line)
{
    struct template_vals *tvals = session->template_vals;
    struct buffer *b = request->write_buffer;
    char *t, *word, c;
    unsigned long count, offset, len, maxlen, i, perline;
    char **alts;
    char *text;

    /* Format: word count offset: word1 word2 word3 */

    word = string_get_token(&s);

    if (is_alt_line)
        count = atoi(string_get_token(&s));
    else
        count = 0;

    offset = atoi(string_get_token(&s));  /* Snarfs trailing ':' as well */

    /* Adjust for "^" sent at start of each line */
    if (offset > 0)
        offset--;

    /* Copy line up to designated offset */
    speller_copy_to_offset(speller, offset);
    speller_record_cursize(speller, utf8_len(word));

    /* Check word against permanent dictionary */
    if (dictionary_lookup(session->options->dictionary, word))
        return (NIL);

    /* Check word against current ignore list */
    if (speller_check_ignore_list(speller, word))
        return (NIL);

    text = generate_text(speller, tvals->pool, word);

    template_vals_string(tvals, "$text", text);
    template_vals_string(tvals, "$word", word);
    template_vals_ulong(tvals, "$close_count", count);

    if (speller->no_changes > 0)
        template_vals_ulong(tvals, "$have_changes", 1);

    alts = pool_alloc(request->pool, (count + 1) * sizeof(char *));
    maxlen = 0;
    for (i = 0; i < count; i++) {
        while (*s == ' ')       /* Skip leading whitespace */
            s++;

        if (*s == '\0')         /* Reached end of line? */
            break;

        alts[i] = t = s;        /* Record start of token */

        while ((c = *s) && (c != ','))  /* Scan for end of line or ',' separator */
            s++;

        len = s - t;            /* Calculate length of string */
        if (len > maxlen)
            maxlen = len;

        if (*s)                 /* Make sure that string is tied off */
            *s++ = '\0';
    }
    alts[i] = NIL;

    if ((maxlen > 0) && (maxlen < 38))
        perline = 80 / (maxlen + 5);
    else
        perline = 1;

    for (i = 0; alts[i]; i++) {
        template_vals_foreach_init(tvals, "@close", i);
        template_vals_foreach_string(tvals, "@close", i, "word", alts[i]);
        if ((i % perline) == (perline - 1))
            template_vals_foreach_ulong(tvals, "@close", i, "break", 1);
    }

    session_seed_template(session, tvals);
    template_expand("spell", tvals, b);
    return (T);
}

/* ====================================================================== */

void cmd_spell(struct session *session)
{
    struct request *request = session->request;
    struct options *options = session->options;
    struct prefs *prefs = options->prefs;
    struct draft *draft = session->draft;
    struct speller *speller = session->speller;
    char *line, *s;
    struct assoc *h;

    if (!draft->body) {
        session_message(session, "No draft message currently active");
        session_redirect(session, request, "list");
        return;
    }

    if (!session->sequence_okay) {
        if (speller_active(speller))
            speller_stop(speller);
        session_message(session,
                        "Aborting Spell Check: Browser history in use");
        session_redirect(session, request, "compose");
        return;
    }

    request_decode_form(request);
    if (!(h = request->form)) {
        /* No arguments to URL: Start or restart speller */
        if (speller_active(speller))
            speller_stop(speller);

        if (prefs->line_wrap_on_spell)
            draft_line_wrap_body(draft);

        if (!speller_start(speller, session, draft->body,
                           prefs->ispell_language, prefs->spell_skip_quoted)) {
            session_message(session, "Couldn't start spell checker!");
            session_redirect(session, request, "compose");
            return;
        }
        speller_feedline(speller);
    } else {
        if (!speller_active(speller)) {
            session_message(session, "Spell checker not currently active");
            session_redirect(session, request, "compose");
            return;
        }

        /* Process GET form arguments */
        if (assoc_lookup(h, "cancel")) {
            speller_stop(speller);
            session_message(session, "Spell Check cancelled");
            session_redirect(session, request, "compose");
            return;
        }

        if (assoc_lookup(h, "apply")) {
            speller_copy_remainder(speller);
            draft->body =
                pool_strdup(draft->pool, speller_output(speller));
            speller_stop(speller);
            if (speller->no_changes != 1)
                session_message(session,
                                "Spell Check finished (%lu changes made)",
                                speller->no_changes);
            else
                session_message(session,
                                "Spell Check finished (1 change made)");
            session_redirect(session, request, "compose");
            return;
        }

        if ((s = assoc_lookup(h, "add"))) {
            dictionary_add(options->dictionary, s);
            options->save = T;
        } else if ((s = assoc_lookup(h, "accept"))) {
            speller_add_ignore_list(speller, s);
        } else if ((s = assoc_lookup(h, "replace"))) {
            speller->no_changes++;
        } else if (!(s = assoc_lookup(h, "leave"))) {
            speller_stop(speller);
            session_message(session, "Unexpected input from user");
            session_log(session, "[cmd_spell] Unexpected input from user");
            session_redirect(session, request, "compose");
            return;
        }

        /* Replace word with substitute version */
        speller_copy_string(speller, s);
        speller_skip_input(speller, speller_fetch_cursize(speller));
    }

    while (1) {
        if (!(line = speller_getline(speller, request->pool))) {
            speller_stop(speller);
            session_message(session,
                            "Spell check process shut down unexpectedly");
            session_log(session,
                        "[cmd_spell] Spell check process shut down unexpectedly");
            session_redirect(session, request, "compose");
            return;
        }

        if (line[0] == '\0') {
            if (!speller_copy_from_offset(speller))     /* Rest of line okay */
                break;

            speller_feedline(speller);
            continue;
        }

        switch (line[0]) {
        case '*':              /* Word correct */
            break;
        case '-':              /* Compound word (should not happen w/o -C option on ispell) */
            break;
        case '+':              /* Derivative word (insufficient context?) */
            break;
        case '&':              /* Word incorrect: have options */
        case '?':              /* Word incorrect: no options   */
            if (!process_line(session, request, speller, line + 2, T))
                break;
            response_html(request, 200);        /* OK */
            return;
        case '#':              /* Word incorrect: no options   */
            if (!process_line(session, request, speller, line + 2, NIL))
                break;
            response_html(request, 200);        /* OK */
            return;
        default:
            /* Ignore all other responses for the time being */
            break;
        }
    }

    draft->body = pool_strdup(draft->pool, speller_output(speller));
    speller_stop(speller);
    if (speller->no_changes != 1)
        session_message(session,
                        "Spell Check finished (%lu changes made)",
                        speller->no_changes);
    else
        session_message(session, "Spell Check finished (1 change made)");
    session_redirect(session, request, "compose");
}
