/* Text buffer code V1.10 16/4/04
   See textbuf.h for docs
   Copyright 2008 Jeffrey Lee
   This file is part of WOUM.
   WOUM 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 3 of the License, or
   (at your option) any later version.
   WOUM 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 WOUM.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef _TEXTBUF_C
#define _TEXTBUF_C

#include <stdlib.h>
#include "textbuf.h"

textbuf *textbuf_new(int w,int h,int minh,int maxh,int newline)
{
	textbuf *t;
	if (w < 1)
		return 0;
	t = malloc(sizeof(textbuf));
	if (t == 0)
		return 0;
	t->w = w; /* Width, exclusive */
	t->cx = t->cy = 0; /* Cursor in top left */
	if (minh < 1) /* Sanity checks on min/max sizes */
		minh = 1;
	if (maxh < minh)
		maxh = minh;
	if (h > maxh) /* Sanity checks on current size */
		h = maxh;
	if (h < minh)
		h = minh;
	t->minh = minh;
	t->maxh = maxh;
	t->nl = newline;
	t->h = h;
	t->b = malloc(sizeof(char *)*h);
	if (t->b == 0)
	{
		free(t);
		return 0;
	}
	while (h--) /* Fill the buffer */
	{
		t->b[h] = malloc(sizeof(char)*t->w);
		if (t->b[h] == 0)
		{
			while (++h < t->h)
				free(t->b[h]);
			free(t->b);
			free(t);
			return 0;
		}
		for (w=0;w<t->w;w++)
			t->b[h][w] = 32;
	}
	t->redraw = 0; /* No callback handlers */
	t->resize = 0;
	t->scroll = 0;
	return t;
}

void textbuf_kill(textbuf *t)
{
	while (t->h--)
		free(t->b[t->h]);
	free(t->b);
	free(t);
}

void textbuf_setcur(textbuf *t,int x,int y)
{
	if (t->redraw)
		(t->redraw)(t,t->cx,t->cy,t->cx+1,t->cy+1);
	if (x < 0)
		x = 0;
	if (x >= t->w)
		x = t->w-1;
	if (y < 0)
		y = 0;
	if (y >= t->h)
		y = t->h-1;
	t->cx = x;
	t->cy = y;
	if (t->redraw)
		(t->redraw)(t,t->cx,t->cy,t->cx+1,t->cy+1);
}

void textbuf_writec(textbuf *t,int c)
{
	char *cp;
	char **b;
	if (t->redraw)
		(t->redraw)(t,t->cx,t->cy,t->cx+1,t->cy+1);
	if (c == 127)
	{
		t->cx--;
		if (t->cx == -1)
		{
			t->cx = t->w-1;
			t->cy--;
			if (t->cy < 0)
				t->cy = 0;
		}
		if (t->redraw)
			(t->redraw)(t,t->cx,t->cy,t->cx+1,t->cy+1);
		return;
	}
	if ((c == t->nl) && (t->nl))
	{
		t->cx = 0;
		if (t->redraw)
			(t->redraw)(t,t->cx,t->cy,t->cx+1,t->cy+1);
		t->cy++;
		/* Fall through to end code */
	}
	else if (c == 13)
	{
		t->cx = 0;
		if (t->redraw)
			(t->redraw)(t,t->cx,t->cy,t->cx+1,t->cy+1);
		return;
	}
	else if (c == 10)
		t->cy++; /* Fall to end */
	else if (c >= 32)
		t->b[t->cy][t->cx++] = c; /* Fall to end */
	else
		return;
	if (t->cx == t->w)
	{
		t->cx = 0;
		t->cy++;
	}
	if (t->cy >= t->h)
	{
		if (t->cy >= t->maxh)
		{
			/* Scroll */
			cp = t->b[0];
			for (c=1;c<t->h;c++)
				t->b[c-1] = t->b[c];
			t->b[t->h-1] = cp;
			t->cy--;
		}
		else
		{
			/* Expand */
			t->h++;
			b = realloc(t->b,sizeof(char *)*t->h);
			if (b == 0)
			{
				b = malloc(sizeof(char *)*t->h);
				for (c=0;c<t->h-1;c++)
					b[c] = t->b[c];
				free(t->b);
			}
			t->b = b;
			t->b[t->h-1] = malloc(sizeof(char)*t->w);
			if (t->resize)
				(t->resize)(t);
		}
		for (c=0;c<t->w;c++)
			t->b[t->h-1][c] = 32;
		if (t->scroll)
			(t->scroll)(t);
		if (t->redraw)
			(t->redraw)(t,0,t->h-1,t->w,t->h);
	}
	else if (t->redraw)
		(t->redraw)(t,t->cx,t->cy,t->cx+1,t->cy+1);

}

void textbuf_write(textbuf *t,int nchars,char *chars)
{
	int i;
	for (i=0;i<nchars;i++)
		textbuf_writec(t,chars[i]);
}

int textbuf_clear(textbuf *t,int h)
{
	/* Reset to min size */
	int i;
	char **b;
	t->cx = t->cy = 0;
	if (h < t->minh)
		h = t->minh;
	else if (h > t->maxh)
		h = t->maxh;
	b = malloc(sizeof(char *)*h);
	if (b == 0)
		return 1;
	while (t->h-- > h)
		free(t->b[t->h]); /* Free the unused lines */
	t->h = h; /* Because the -- would have brought it below h */
	for (h=0;h<t->h;h++)
	{
		b[h] = t->b[h]; /* Copy ones we want to keep */
		for (i=0;i<t->w;i++)
			b[h][i] = 32; /* And fill it with spaces */
	}
	free(t->b); /* Free old array */
	t->b = b; /* Replace with new one */
	if (t->resize)
		(t->resize)(t);
	if (t->redraw)
		(t->redraw)(t,0,0,t->w-1,t->h-1);
	return 0;
}

int textbuf_resize(textbuf *t,int w,int h)
{
	int i,ii,l;
	char **b;
	if (h < t->minh)
		h = t->minh;
	else if (h > t->maxh)
		h = t->maxh;
	b = malloc(sizeof(char *)*h);
	if (b == 0)
		return 1;
	for (i=0;i<h;i++)
	{
		b[i] = malloc(sizeof(char)*w);
		if (b[i] == 0)
		{
			while (--i >= 0)
				free(b[i]);
			free(b);
			return 1;
		}
		/* Work out which line to copy */
		l = t->cy+(i-(h-1)); /* add current offset from new baseline to offset of desired baseline (cy) */
		for (ii=0;ii<w;ii++)
			if ((ii < t->w) && (l >= 0) && (l < t->h))
				b[i][ii] = t->b[l][ii];
			else
				b[i][ii] = 32;
	}
	/* Delete old ones */
	for (i=0;i<t->h;i++)
		free(t->b[i]);
	free(t->b);
	t->b = b;
	/* Calculate cursor pos and size info */
	t->w = w;
	t->h = h;
	if (t->cx >= w)
		t->cx = w-1;
	t->cy = h-1;
	t->b = b;
	if (t->resize)
		(t->resize)(t);
	if (t->redraw)
		(t->redraw)(t,0,0,t->w-1,t->h-1);
	return 0;
}

char *textbuf_getline(textbuf *t,int y)
{
	if (y < 0)
		return t->b[0];
	if (y >= t->h)
		return t->b[t->h-1];
	return t->b[y];
}

char textbuf_getchar(textbuf *t,int x,int y)
{
	if (x < 0)
		x = 0;
	if (x >= t->w)
		x = t->w-1;
	return textbuf_getline(t,y)[x];
}

#endif
