/* This file was contributed by Suzanne Skinner and is copyrighted
   under the GNU General Public License. (C) 2002 Suzanne Skinner.

   The code contained in this file 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; either
   version 2, or (at your option) any later version.

   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.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ncurses.h>

#include "dynstr.h"
#include "getline.h"

static gl_keybinding gl_keybindings[] = {
    { KEY_LEFT,       glf_cursor_left         },
    { KEY_RIGHT,      glf_cursor_right        },
    { KEY_HOME,       glf_beginning_of_line   },
    { KEY_FIND,       glf_beginning_of_line   },     /* sometimes "home" key */
    { '\001',         glf_beginning_of_line   },     /* ctrl-a */
    { KEY_END,        glf_end_of_line         },
    { KEY_SELECT,     glf_end_of_line         },     /* sometimes "end" key */
    { '\005',         glf_end_of_line         },     /* ctrl-e */
    { KEY_BACKSPACE,  glf_delete_char_back    },
    { '\b',           glf_delete_char_back    },
    { '\177',         glf_delete_char_back    },     /* ctrl-? (DEL) */
    { 330,            glf_delete_char_forward },     /* "delete" key */
    { '\004',         glf_delete_char_forward },     /* ctrl-d */
    { '\025',         glf_delete_all          },     /* ctrl-u */
    { '\013',         glf_delete_to_end       },     /* ctrl-k */
    { KEY_UP,         glf_history_back        },
    { KEY_DOWN,       glf_history_forward     },
    { '\n',           glf_done                },
    { '\r',           glf_done                },
    { '\007',         glf_abort               },     /* ctrl-g */

    { 0, NULL }
};

static gl_history_entry *gl_history_top = NULL;
static gl_history_entry *gl_history_bot = NULL;
static gl_history_entry *gl_history_pos = NULL;
static int gl_history_len = 0;

int gl_handle_keystroke(dynstr *str, int prefix_len, int cursor,
                        chtype keystroke)
{
    gl_keybinding *p;

    for (p=gl_keybindings; p->handler; p++) {
        if (p->key == keystroke)
            return p->handler(str, prefix_len, cursor);
    }
    if ((32 <= keystroke && keystroke <= 126) ||
	(160 <= keystroke && keystroke <= 255)) {  /* printable */
        ds_insert_char(str, cursor, (char)keystroke);
        cursor++;
    }
    return cursor;
}

/*** Bindable Functions ***/

GL_FUNC(glf_cursor_left)
{
    if (cursor > prefix_len)
        cursor--;
    return cursor;
}

GL_FUNC(glf_cursor_right)
{
    if (cursor < ds_len(str))
        cursor++;
    return cursor;
}

GL_FUNC(glf_beginning_of_line)
{
    return prefix_len;
}

GL_FUNC(glf_end_of_line)
{
    return ds_len(str);
}

GL_FUNC(glf_delete_char_back)
{
    if (cursor > prefix_len) {
        ds_delete_char(str, cursor-1);
        cursor--;
    }
    return cursor;
}

GL_FUNC(glf_delete_char_forward)
{
    if (cursor < ds_len(str))
        ds_delete_char(str, cursor);
    return cursor;
}

GL_FUNC(glf_delete_all)
{
    ds_truncate(str, prefix_len);
    return prefix_len;
}

GL_FUNC(glf_delete_to_end)
{
    ds_truncate(str, cursor);
    return cursor;
}

GL_FUNC(glf_history_back)
{
    if (!gl_history_pos || !gl_history_pos->next)
        return cursor;
    if (!gl_history_pos->prev) {
        /* update only very last history line */
        if (gl_history_pos->line)
            free(gl_history_pos->line);
	gl_history_pos->line = strdup(ds_get(str));
    }
    gl_history_pos = gl_history_pos->next;
    ds_set(str, gl_history_pos->line);
    return ds_len(str);
}

GL_FUNC(glf_history_forward)
{
    if (!gl_history_pos || !gl_history_pos->prev)
        return cursor;
    gl_history_pos = gl_history_pos->prev;
    ds_set(str, gl_history_pos->line);
    return ds_len(str);
}

GL_FUNC(glf_abort)
{
    return GL_ABORT;
}

GL_FUNC(glf_done)
{
    gl_history_entry *new_entry;

    if (ds_len(str) > prefix_len) {
        if (!gl_history_top) {
            gl_history_top = gl_history_bot = gl_history_pos =
                malloc(sizeof(gl_history_entry));
            gl_history_top->prev = gl_history_top->next = NULL;
            gl_history_top->line = NULL;
            gl_history_len++;
        }
        if (gl_history_top->line)
            free(gl_history_top->line);
	if (gl_history_top->next 
	    && strcmp(gl_history_top->next->line, ds_get(str))==0) {
  	    /* do not enter duplicate into history */
  	    gl_history_top->line = NULL;
	} else {
	    gl_history_top->line = strdup(ds_get(str));
	    new_entry = malloc(sizeof(gl_history_entry));
	    new_entry->line = NULL;
	    new_entry->next = gl_history_top;
	    new_entry->prev = NULL;
	    gl_history_top->prev = new_entry;
	    gl_history_top = new_entry;
	    if (gl_history_len == GL_HISTORY_MAXLEN) {
	        gl_history_bot = gl_history_bot->prev;
		free(gl_history_bot->next);
		gl_history_bot->next = NULL;
	    }
	    else
	        gl_history_len++;
	}
    }
    gl_history_pos = gl_history_top;
    return GL_DONE;
}
