/*
 * 4m v2.15 (editor.c)
 * (C) Copyright 1990 by Carrick Sean Casey
 * (C) Copyright 1993 by Scott Chasin and Michaeljon Miller
 *
 * 3-24-93 See file COPYING for copyright information.
 *
 * Input and line editing
 */


#ifdef ESIX
# ifdef __STDC__
#  undef __STDC__
# endif
#endif

#include "4m.h"
#include "externs.h"

#ifdef HAS_STRING
# include <string.h>
#endif

#ifdef HAS_STRINGS
# include <strings.h>
#endif

#ifndef NO_STDLIB_H
# include <stdlib.h>
#endif

#include <ctype.h>

#include "../h/term.h"

static int 
     paste(),
     do_mixup(),
     do_reverse(),
     erase(),
     line_erase(),
     new_line(),
     word_erase(),
     edit_help(),
     do_tab(),
     bol(),
     eol(),
     swap_chars(),
     delete(),
     up(),
     down(),
     left(),
     right(),
     do_quit(),
     do_suspend(),
     toggle_ins(),
     do_escape();

int clear_screen();

static char *mixup();
static char *reverse();

typedef struct _edit_cmds {
    char *key;
    char *description;
    int (*function) (/* int */);
}   edit_cmds;

static char yankbuffer[MAX_INPUTSTR] = {0};
extern char inputbuffer[];

static edit_cmds ec_tab[] = {
    /* { */  "^@", "Unassigned", 0  /* } */,
    /* { */  "^A", "Beginning of Line", bol  /* } */,
    /* { */  "^B", "Reverse String", do_reverse  /* } */,
    /* { */  "^C", "Unassigned", 0  /* } */,
    /* { */  "^D", "Unassigned", 0  /* } */,
    /* { */  "^E", "End of Line", eol  /* } */,
    /* { */  "^F", "Unassigned", 0  /* } */,
    /* { */  "^G", "Unassigned", 0  /* } */,
    /* { */  "^H", "Backspace", erase  /* } */,
    /* { */  "^I", "History", do_tab  /* } */,
    /* { */  "^J", "Commit Line", new_line  /* } */,
    /* { */  "^K", "Mess with Case", do_mixup  /* } */,
    /* { */  "^L", "Clear Screen", clear_screen  /* } */,
    /* { */  "^M", "Commit Line", new_line  /* } */,
    /* { */  "^N", "Unassigned", 0  /* } */,
    /* { */  "^O", "Unassigned", 0  /* } */,
    /* { */  "^P", "Unassigned", 0  /* } */,
    /* { */  "^Q", "Leave 4m", do_quit  /* } */,
    /* { */  "^R", "Unassigned", 0  /* } */,
    /* { */  "^T", "Editor Help", edit_help  /* } */,
    /* { */  "^S", "Unassigned", 0  /* } */,
    /* { */  "^U", "Erase Line", line_erase  /* } */,
    /* { */  "^V", "Paste Last Erase", paste  /* } */,
    /* { */  "^W", "Erase Word", word_erase  /* } */,
    /* { */  "^X", "Swap two letters", swap_chars  /* } */,
    /* { */  "^Y", "Yank One Word", word_erase  /* } */,
    /* { */  "^Z", "Suspend 4m", do_suspend  /* } */,
    /* { */  "Esc", NULL, do_escape  /* } */,
    /* { */  "Del", "Delete character", erase  /* } */  /* this one must be odd length */
};

static edit_cmds ansi_cmds[] = {
    /* { */  "Esc-A", "Up one line", up  /* } */,
    /* { */  "Esc-B", "Down one line", down  /* } */,
    /* { */  "Esc-C", "Right one letter", right  /* } */,
    /* { */  "Esc-D", "Left one letter", left  /* } */,
    /* { */  "Esc-E", "Unassigned", 0  /* } */,
    /* { */  "Esc-F", "Unassigned", 0  /* } */,
    /* { */  "Esc-G", "Unassigned", 0  /* } */,
    /* { */  "Esc-H", "Unassigned", 0  /* } */,
    /* { */  "Esc-I", "Toggle Insert/Overstrike", toggle_ins  /* } */,
    /* { */  "", "", 0  /* } */ /* keep this table even length */
};



extern int write ();
extern int _flsbuf ();
extern int _filbuf ();
extern time_t time ();
extern char lower ();
extern char upper ();
extern char *strrchr ();
extern int histcount ();
extern char *histget ();


extern void askquit ();
extern void suspend ();


extern void title ();
extern void status ();

static void 
clear_input_line(from)
     int from;
{
    extern int lines;

    from = from >= 0 ? from : 0;

    gotoxy(from, lines - 2);
    clear_eol();
    gotoxy(from, lines - 2);
    return;
}

static void 
add_char(c)
     register int c;
{
    char tmp[MAX_INPUTSTR];

    if (strlen(inputbuffer) > MAX_INPUTSTR - 3) {
        if (gv.editorbeep)
            write(1, "\007", 1);
        return;
    }
    hidecursor();
    if ((insert_mode) && (inputbuf - inputbuffer < strlen(inputbuffer))) {
        memset(tmp, 0, sizeof(MAX_INPUTSTR));
        strcpy(tmp, inputbuf);

    /* clear_input_line(ipos); */

        *inputbuf = c;
        gotoxy(ipos, lines - 2);
        putchar(c);

        memset(inputbuf + 1, 0, strlen(tmp) + 1);
        strcpy(inputbuf + 1, tmp);

        gotoxy(ipos + 1, lines - 2);
        write(1, tmp, strlen(inputbuf) > (TC_co - 2) - ipos ?
              (TC_co - 2) - ipos : strlen(inputbuf));

        inputbuf++;
        ipos++;

        gotoxy(ipos, lines - 2);
    } else {
        if (ipos == TC_co - 2) {
            clear_input_line(0);
            ipos = 0;
        }
        *inputbuf++ = (char) c;
        ipos++;
        putchar(c);
    }

    showcursor();

    return;
}


int 
do_edit()
{
    register int c;

    c = getchar();

    if (c > '\037' && c < '\177') {
        add_char(c);
    } else {
        if (c == '\177') {
            return (erase(c));
        } else if ((c >= 0 && c < (sizeof(ec_tab) / sizeof(*ec_tab)))
                   && ec_tab[c].function != 0) {
            return ((*ec_tab[c].function) (c));
        } else {
            write(1, "\007", 1);
        }
    }

    return (0);
}

/* ARGSUSED */
static int 
paste(c)
     int c;
{
    register char *cp;

    if (strlen(yankbuffer)) {
        for (cp = yankbuffer; *cp; cp++) {
            add_char(*cp);
        }
    }
    yankbuffer[0] = 0;  /* only allow it to be pasted once */

    return (0);
}

static char *
mixup(str)
     register char *str;
{
    register char *ptr;
    static char area[MAX_INPUTSTR];

    srand(time(NULL));

    ptr = area;
    while (*str) {
        *(ptr++) = (*str + rand()) & 1 ? lower(*str) : upper(*str);
        str++;
    }
    *ptr = '\0';

    return (area);
}

static char *
reverse(str)
     char *str;
{
    register int i;
    char s[MAX_INPUTSTR], *st;

    if ((str) && (str[0])) {
        st = str;
        for (; (*st); st++);
        for (i = 0, st--; (st != str); i++)
            s[i] = (*st--);
        s[i] = (*st);
        s[i + 1] = 0;
    } else
        s[0] = 0;
    return (s);
}

/* ARGSUSED */
static int 
erase(c)
     int c;
{
    register char *cp;
    char tmp[MAX_INPUTSTR];

    if (ipos > 0) {
        memset(tmp, 0, MAX_INPUTSTR);
        strcpy(tmp, inputbuf);

        inputbuf--;

        memset(inputbuf, 0, strlen(tmp) + 1);
        strcpy(inputbuf, tmp);

        ipos--;
        clear_input_line(ipos);

        gotoxy(ipos, lines - 2);
        write(1, tmp, strlen(inputbuf) > (TC_co - 2) - ipos ?
              (TC_co - 2) - ipos : strlen(inputbuf));

        gotoxy(ipos, lines - 2);
    } else if (inputbuf > inputbuffer && ipos == 0) {
        cp = inputbuf - inputbuffer >= (TC_co - 2) ?
            inputbuf - (TC_co - 2) : inputbuffer;
        hidecursor();
        clear_input_line(0);
    /* *inputbuf-- = '\0'; */
        ipos = strlen(cp);
        write(1, cp, ipos);
        gotoxy(ipos, lines - 2);
        showcursor();
    } else {
        if (gv.editorbeep)
            write(1, "\007", 1);
    }

    return (0);
}

/* ARGSUSED */
static int 
line_erase(c)
     int c;
{
    if (strlen(inputbuffer)) {
        strcpy(yankbuffer, inputbuffer);
        ipos = 0;
        inputbuf = inputbuffer;
        memset(inputbuffer, 0, MAX_INPUTSTR);
        clear_input_line(0);
    } else {
        if (gv.editorbeep)
            write(1, "\007", 1);
    }

    return (0);
}

/* ARGSUSED */
static int 
word_erase(c)
     int c;
{
    register char *cp;

    hidecursor();
    if ((cp = (char *) strrchr(inputbuffer, ' ')) != 0) {
        char *t = inputbuf;

        while (*cp == ' ' && cp > inputbuffer)
            --cp;

        if (c == '\031') {
            *(++inputbuf) = 0;
            strcpy(yankbuffer, cp + 1);
        }
        *(++cp) = 0;
        inputbuf = cp;
        if (ipos > t - cp) {
            ipos -= t - cp;
            clear_input_line(ipos);
            return (0);
        }
        cp = (t - inputbuffer > (TC_co - 2)) ? t - (TC_co - 2) : inputbuffer;
        ipos = strlen(cp);
        clear_input_line(0);
        write(1, cp, strlen(cp));
        gotoxy(ipos, lines - 2);
    } else if (ipos > 0 && strlen(inputbuffer)) {
        ipos = 0;
        inputbuf = inputbuffer;
        *inputbuf = 0;
        clear_input_line(0);
    } else {
        if (gv.editorbeep)
            write(1, "\007", 1);
    }

    showcursor();
    return (0);
}

/* ARGSUSED */
static int 
new_line(c)
     int c;
{
    if (!strlen(inputbuffer)) {
        return (0);
    }
/*  *inputbuf = '\0';  */
    return (1);
}

/* ARGSUSED */
int 
clear_screen(c)
     int c;
{
    if (strlen(inputbuffer) == 0) {
        c_clear(0, 0);
        title();
        status(1);
    }
    return (0);
}

/* ARGSUSED */
static int 
do_tab(c)
     int c;
{
    register int i;
    char tmp[20];

    if (histcount() &&
        (inputbuffer[0] == gv.cmdchar || inputbuf == inputbuffer)) {
        sprintf(tmp, "%cm %s ", gv.cmdchar, histget());
        clear_input_line(0);
        ipos = 0;
        inputbuf = inputbuffer;
        memset(inputbuffer, 0, MAX_INPUTSTR - 1);
        for (i = 0; i < strlen(tmp); i++)
            add_char(tmp[i]);
    } else {
    /* put out a tab char */
        add_char('\t');
    }
    return (0);
}

/* ARGSUSED */
static int 
do_reverse(c)
     int c;
{
    register char *cp;

    if (strlen(inputbuffer) > 2) {
        clear_input_line(0);
        *inputbuf = 0;
        cp = reverse(inputbuffer);
        memset(inputbuffer, 0, strlen(inputbuffer));
        strcpy(inputbuffer, cp);
        inputbuf = cp;
        *inputbuf++ = 0;
        write(1, cp, ipos);
        gotoxy(ipos, lines - 2);
    } else if (gv.editorbeep)
        write(1, "\007", 1);

    return (0);
}

/* ARGSUSED */
static int 
do_mixup(c)
     int c;
{
    register char *cp;

    if (strlen(inputbuffer) > 2) {
        clear_input_line(0);
        *inputbuf = 0;
        cp = mixup(inputbuffer);
        memset(inputbuffer, 0, strlen(inputbuffer));
        strcpy(inputbuffer, cp);
        inputbuf = cp;
        *inputbuf++ = 0;
        write(1, cp, ipos);
        gotoxy(ipos, lines - 2);
    } else if (gv.editorbeep)
        write(1, "\007", 1);

    return (0);
}

/* ARGSUSED */
static int 
edit_help(c)
     int c;
{
    register int i;
    register int y;
    char buffer[30];

    c_clear(0, 0);

    hidecursor();

    sprintf(buffer, "%-8s%-18s", "[Key]", "[Effect]");
    gotoxy(4, 1);
    write(1, buffer, strlen(buffer));
    gotoxy(35, 1);
    write(1, buffer, strlen(buffer));

    for (i = 1, y = 3; i < sizeof(ec_tab) / sizeof(*ec_tab); i += 2, y++) {
        if (ec_tab[i].function != 0 && ec_tab[i].description != NULL) {
            sprintf(buffer, "%-8s%-18s", ec_tab[i].key, ec_tab[i].description);
            gotoxy(4, y);
            write(1, buffer, strlen(buffer));
        }
        if (ec_tab[i + 1].function != 0 && ec_tab[i + 1].description != NULL) {
            sprintf(buffer, "%-8s%-18s", ec_tab[i + 1].key, ec_tab[i + 1].description);
            gotoxy(35, y);
            write(1, buffer, strlen(buffer));
        }
    }

    gotoxy(1, lines - 5);
    write(1, "[Press any key for next page]: ", 30);
    getchar();
    c_clear(0, 0);

    sprintf(buffer, "%-8s%-18s", "[Key]", "[Effect]");
    gotoxy(4, 1);
    write(1, buffer, strlen(buffer));
    gotoxy(35, 1);
    write(1, buffer, strlen(buffer));

    for (i = 0, y = 3; i < sizeof(ansi_cmds) / sizeof(*ansi_cmds); i += 2, y++) {
        if (ansi_cmds[i].function != 0) {
            sprintf(buffer, "%-8s%-18s", ansi_cmds[i].key, ansi_cmds[i].description);
            gotoxy(4, y);
            write(1, buffer, strlen(buffer));
        }
        if (ansi_cmds[i + 1].function != 0) {
            sprintf(buffer, "%-8s%-18s", ansi_cmds[i + 1].key, ansi_cmds[i + 1].description);
            gotoxy(35, y);
            write(1, buffer, strlen(buffer));
        }
    }

    gotoxy(1, lines - 5);
    write(1, "[Press any key to return]: ", 27);
    getchar();

    showcursor();
    c_clear(0, 0);

    return (0);
}

/* ARGSUSED */
static int 
do_escape(c)
     int c;
{
 /* this thing has the tendency to eat the */
 /* next few characters in the input stream */

    register int cmd;
    extern int ungetc();

    cmd = getchar();

    if (cmd == 'i' || cmd == 'I')
        return (toggle_ins(c));
    else if (cmd == 'h' || cmd == 'H' || cmd == '?')
        return (edit_help(0));
    else if (cmd == '[') {
        cmd = getchar();
        cmd -= 'A';

        if ((cmd >= 0 && cmd < sizeof(ansi_cmds) / sizeof(*ansi_cmds)) &&
            ansi_cmds[cmd].function != 0) {
            return ((*ansi_cmds[cmd].function) (cmd));
        } else {
            if (gv.editorbeep)
                write(1, "\007", 1);
        }
    } else {
        ungetc(cmd, stdin);
    }

    return (0);
}

/* ARGSUSED */
static int 
up(c)
     int c;
{
    return (0);
}

/* ARGSUSED */
static int 
down(c)
     int c;
{
    return (0);
}

/* ARGSUSED */
static int 
left(c)
     int c;
{
    register char *cp;

    if (ipos > 0) {
        inputbuf--;
        ipos--;
        gotoxy(ipos, lines - 2);
    } else if (inputbuf > inputbuffer && ipos == 0) {
        cp = inputbuf - (TC_co - 2);
        if (cp < inputbuffer)
            cp = inputbuffer;

        inputbuf--;

        hidecursor();
        clear_input_line(0);
        ipos = strlen(cp) < TC_co - 2 ? strlen(cp) : TC_co - 2;
        write(1, cp, ipos);
    /* gotoxy(ipos, lines - 2); */
        showcursor();
    } else {
        inputbuf = inputbuffer;
        if (gv.editorbeep)
            write(1, "\007", 1);
    }

    return (0);
}

/* ARGSUSED */
static int 
right(c)
     int c;
{
    register int i, j, k;

    if (*inputbuf == 0) {
        if (gv.editorbeep)
            write(1, "\007", 1);
    } else if (ipos < TC_co - 2) {
        ipos++;
        inputbuf++;
        gotoxy(ipos, lines - 2);
    } else {
        i = inputbuf - inputbuffer;
        j = strlen(inputbuffer);

        k = (j - i <= TC_co - 2) ? strlen(inputbuf) : TC_co - 2;
        hidecursor();
        clear_input_line(0);
        inputbuf++;
        ipos = 0;
        write(1, inputbuf, k);
        gotoxy(ipos, lines - 2);
        showcursor();
    }

    return (0);
}

/* ARGSUSED */
static int 
toggle_ins(c)
     int c;
{

    insert_mode = !insert_mode;
    status(1);
    return (0);
}

/* ARGSUSED */
static int 
bol(c)
     int c;
{
    register int k;

    ipos = 0;
    inputbuf = inputbuffer;

    k = strlen(inputbuffer) > TC_co - 2 ? TC_co - 2 : strlen(inputbuffer);

    hidecursor();
    clear_input_line(0);
    write(1, inputbuffer, k);
    gotoxy(ipos, lines - 2);
    showcursor();

    return (0);
}

/* ARGSUSED */
static int 
eol(c)
     int c;
{
    register int k;
    register char *cp;

    k = strlen(inputbuffer) % (TC_co - 2);

    inputbuf = inputbuffer + strlen(inputbuffer);
    ipos = k;
    cp = inputbuf - k;

    hidecursor();
    clear_input_line(0);
    write(1, cp, k);
    gotoxy(ipos, lines - 2);
    showcursor();

    return (0);
}

/* ARGSUSED */
static int 
swap_chars(c)
     int c;
{
    char this, that;

    this = *inputbuf;
    that = *(inputbuf + 1);

    hidecursor();

    gotoxy(ipos + 1, lines - 2);
    putchar(this);

    gotoxy(ipos, lines - 2);
    putchar(that);

    gotoxy(ipos, lines - 2);

    *(inputbuf + 1) = this;
    *inputbuf = that;

    showcursor();

    return (0);
}

/* ARGSUSED */
static int 
delete(c)
     int c;
{
    char tmp[MAX_INPUTSTR];
    register char *cp;

    if (*inputbuf == 0) {
        if (gv.editorbeep)
            write(1, "\007", 1);
        return (0);
    }
    cp = inputbuf + 1;

    memset(tmp, 0, MAX_INPUTSTR);
    strcpy(tmp, cp);

    memset(inputbuf, 0, strlen(tmp) + 2);
    strcpy(inputbuf, tmp);

    clear_input_line(ipos);

    gotoxy(ipos, lines - 2);
    write(1, tmp, strlen(inputbuf) > (TC_co - 2) - ipos ?
          (TC_co - 2) - ipos : strlen(inputbuf));

    gotoxy(ipos, lines - 2);

    return (0);
}

static int 
do_quit(c)
     int c;
{
    (void) askquit();

    return(0);
}

static int
do_suspend(c)
     int c;
{
    (void) suspend();

    return(0);
}

fill_inputline (s)
char *s;
{

   clear_input_line(0);
   ipos = 0;
   inputbuf = inputbuffer;
   memset(inputbuffer, 0, MAX_INPUTSTR - 1);

   for (/* yip */; *s; s++)
       add_char (*s); 
}
