/* buffer.c - functions using the kill/yank/block buffers
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "mstring.h"
#include "terminal.h"
#include "editor.h"
#include "hook.h"

char *ed__killbuf = 0;			 /* kill buffer */
int ed__newkill = 1;			 /* new kill */
long ed__block_line = -1;		 /* block start line */
char *ed__block_data = 0;		 /* block data */

void ed__line_del(long);


/* Cut from the current line, starting at character "c", and append to the
 * kill buffer, adding a newline at the end of the previous kill buffer
 * contents if it wasn't empty before.
 */
void ed__op__cut_from(long c)
{
	int add_newline = 1;
	char *a;
	char *b;
	int n;

	if (ed__curline >= ed__lines)
		return;

	a = ed__array[ed__curline];
	if (!a)
		return;

	if (c > strlen(a))
		c = strlen(a);

	if (ed__newkill) {
		free(ed__killbuf);
		ed__killbuf = 0;
		ed__newkill = 0;
	}

	n = (ed__killbuf) ? strlen(ed__killbuf) : 0;

	if (!ed__killbuf)
		add_newline = 0;

	b = realloc(ed__killbuf, n + strlen(a) + 3);
	if (!b)
		return;

	ed__killbuf = b;

	b[n] = 0;
	if (add_newline)
		strcat(b, "\n");
	strcat(b, a + c);
	a[c] = 0;
}


/* Cut the current line and add it to the kill buffer. Does nothing if we are
 * currently on the last line and the last line is blank.
 */
void ed__op_cutline(void)
{
	char *a;
	long i;

	if (ed__curline >= ed__lines)
		return;

	a = ed__array[ed__curline];
	if (!a)
		return;

	if ((ed__curline == (ed__lines - 1)) && (a[0] == 0))
		return;

	ed__op__cut_from(0);
	if (ed__curline < (ed__lines - 1))
		ed__line_del(ed__curline);

	t_goto(0, ed__start_line + (ed__curline - ed__topline));

	for (i = ed__curline;
	     i < (ed__topline + t_rows - 1 - ed__start_line); i++) {
		ed_redraw_line(i);
	}

	a = ed__array[ed__curline];
	if (!a)
		a = "";
	if (ed__curchar > strlen(a)) {
		ed__curchar = strlen(a);
		ed__curcol = mstrlen(a);
	}

	t_goto(ed__curchar - ed__leftchar,
	       ed__start_line + (ed__curline - ed__topline));
}


/* Cut the current line from the cursor onwards and add it to the kill buffer.
 */
void ed__op_cutforw(void)
{
	char *a;
	int c;

	a = ed__array[ed__curline];
	if (!a)
		a = "";

	c = ed__curchar;
	if (c > strlen(a))
		c = strlen(a);
	if (strlen(a + c) < 1) {	    /* delete if cut area is blank */
		ed__op_del_right();
		return;
	}

	ed__op__cut_from(ed__curchar);

	t_goto(ed__curchar - ed__leftchar,
	       ed__start_line + (ed__curline - ed__topline));

	t_clrtoeol();			    /* blank stuff that was cut */
}


/* Paste (yank) the contents of the kill buffer.
 */
void ed__op_paste(void)
{
	int i;

	if (!ed__killbuf)
		return;

	for (i = 0; i < strlen(ed__killbuf); i++)
		ed__op_key(ed__killbuf[i]);
}


/* Mark a block. If "ed__block_line" is -1, mark the start; if it's anything
 * else, mark the end and store it.
 */
void ed__op_mark(void)
{
	char buf[128];
	long a, b, c, i;
	int key, n;
	char *x;

	if (ed__block_line == -1) {	    /* mark block start */
		ed__block_line = ed__curline;
		t_goto(0, t_rows - 1);
		sprintf(buf, "\035R Block start at line %ld. "
			"Move to end and do [\035B^O\035b][\035BM\035b] again. "
			"\035r", ed__curline + 1);
		t_clrtoeol();
		t_write(buf);
		t_goto(ed__curchar - ed__leftchar,
		       ed__start_line + (ed__curline - ed__topline));
		return;
	}

	a = ed__block_line;
	b = ed__curline;
	if (a > b) {
		a = ed__curline;
		b = ed__block_line;
	}

	ed__block_line = ed__curline;
	t_goto(0, t_rows - 1);
	sprintf(buf, "\035R %ld line%s marked (end line %ld). "
		"[\035BC\035b]opy [\035BD\035b]elete [\035BQ\035b]:Cancel "
		"\035r",
		1 + (b - a), ((b - a) == 0) ? "" : "s", ed__curline + 1);
	t_clrtoeol();
	t_write(buf);

	for (n = 0; n < 20; n++) {
		key = t_getchar(1);	    /* read keypress */
		bbs_hook(HOOK_KEY_PRESSED, 0, &key);
		switch (key) {
		case 'c':
		case 'C':
			n = 40;
			break;
		case 'd':
		case 'D':
			n = 30;
			break;
		case 'q':
		case 'Q':
			n = 20;
			break;
		default:
			break;
		}
	}

	if ((n >= 20) && (n < 30)) {	    /* cancelled */
		t_goto(0, t_rows - 1);
		t_clrtoeol();
		t_write("\035R Cancelled \035r");
		t_goto(ed__curchar - ed__leftchar,
		       ed__start_line + (ed__curline - ed__topline));
		ed__block_line = -1;
		return;
	} else if ((n >= 30) && (n < 40)) { /* delete block */
		for (i = a; i <= b; i++) {  /* delete all marked lines */
			ed__line_del(a);    /* (always delete top line) */
		}
		ed__curline = a;	    /* move to top of block */
		if (ed__topline > ed__curline) {	/* ...scrolling as needed */
			ed__topline = ed__curline - 1;
			if (ed__topline < 0)
				ed__topline = 0;
		}
		ed__redraw = 1;		    /* redraw the screen please */
		t_goto(0, t_rows - 1);
		t_clrtoeol();
		t_goto(ed__curchar - ed__leftchar,
		       ed__start_line + (ed__curline - ed__topline));
		ed__block_line = -1;
		return;
	}

	t_goto(0, t_rows - 1);		    /* copy block */
	t_clrtoeol();

	t_goto(ed__curchar - ed__leftchar,
	       ed__start_line + (ed__curline - ed__topline));

	ed__block_line = -1;

	free(ed__block_data);
	ed__block_data = 0;
	c = 0;

	for (i = a; (i <= b) && (i < ed__lines); i++) {	/* find block size */
		x = ed__array[i];
		if (!x)
			x = "";
		c += strlen(x);
		c++;			    /* plus one for the \n */
	}

	ed__block_data = malloc(c + 2);
	if (!ed__block_data)
		return;

	ed__block_data[0] = 0;
	for (i = a; (i <= b) && (i < ed__lines); i++) {	/* store lines */
		x = ed__array[i];
		if (!x)
			x = "";
		strcat(ed__block_data, x);
		strcat(ed__block_data, "\n");
	}
}


/* Use the block currently stored in the block buffer.
 *
 * TODO: paste by memory dump instead of simulating keypresses
 */
void ed__op_block(void)
{
	int old_insert;
	int old_indent;
	int old_wrap;
	long i;

	if (!ed__block_data)
		return;

	old_insert = ed_opt__insert;
	old_indent = ed_opt__indent;
	old_wrap = ed_opt__wrap;

	ed_opt__insert = 1;
	ed_opt__indent = 0;
	ed_opt__wrap = 0;

	for (i = 0; i < strlen(ed__block_data); i++)
		ed__op_key(ed__block_data[i]);

	ed_opt__insert = old_insert;
	ed_opt__indent = old_indent;
	ed_opt__wrap = old_wrap;
}

/* EOF */
