/*
 * dml - Dialog Markup Language
 *
 * $Id: textarea.c,v 1.3 2002/07/29 13:07:05 malekith Exp $
 * Author: Michal Moskal <malekith@pld-linux.org>
 * include COPYING-GNU
 */

#include "pi.h"
#include "ml.h"
#include <string.h>
#include <ctype.h>

void pi_textarea_draw(struct pi_textarea *self)
{
	int c[4];
	int x, y, n, k, l;
	char *p;
	
	pi_locate((struct pi_object*)self, &x, &y);
	
	c[1] = pi_color_textarea_c1;
	c[2] = pi_color_textarea_c2;
	c[3] = pi_color_textarea_c3;

	n = self->pos.h;
	k = self->top;
	
	while (n--) {
		pi_goto(x, y++);
		l = self->pos.w - 1;
		for (p = k >= self->nlines ? "\003~" : self->lines[k++]; 
		     *p && l--; p++) {
			pi_color(c[(int)*p++]);
			pi_putch(*p);
		}
		pi_color(c[1]);
		while (l-- > 0)
			pi_putch(' ');

		pi_scrollbar(self->pos.h - n - 1 + self->top, 
		             self->top, self->pos.h, 
			     self->nlines, 
			pi_is_active((struct pi_object*)self) ?
					pi_color_atextarea_scrollbar :
					pi_color_textarea_scrollbar,
			pi_is_active((struct pi_object*)self) ?
					pi_color_atextarea_scrollbar_marked :
					pi_color_textarea_scrollbar_marked);
	}
}

void pi_textarea_kill(struct pi_textarea *self)
{
	int i;
	
	for (i = 0; i < self->nlines; i++)
		xfree(self->lines[i]);
	xfree(self->lines);
}

/*
 * tags reconized:
 * <br>
 * <b></b>
 * <i></i>
 */

struct parser_state {
	struct dbuf *lines, *line;
	const char *input;
	int pos;
	int mode;
	int br;
	int w;
};

static char *get_token(struct parser_state *ctx)
{
	struct dbuf *db;
	const char *p;
	
	p = ctx->input;
	while (isspace(*p))
		p++;
	ctx->input = p;
	if (*p == 0)
		return 0;
	if (*p == pi_tag_start) {
		switch (*++p) {
		case pi_tag_b:
			ctx->mode = 2;
			break;
		case pi_tag_e_b:
		case pi_tag_e_i:
			ctx->mode = 1;
			break;
		case pi_tag_i:
			ctx->mode = 3;
			break;
		case pi_tag_br:
			ctx->br = 1;
			break;
		}
		p = strchr(p, pi_tag_end);
		if (p == 0) 
			return 0;		/* ??? */
		p++;
	}

	db = db_new();
	while (*p) {
		if (isspace(*p) || *p == pi_tag_start)
			break;
		db_addc(db, *p++);
	}
	while (isspace(*p))
		p++;
	ctx->input = p;
	db_addc(db, 0);
	return db_finish(db, 0);
}

static void break_into_lines(struct parser_state *ctx)
{
	char *p, *l;

	do {
		p = get_token(ctx);
		if (p == 0 || ctx->br || 
		    (p && (int)strlen(p) > ctx->w - ctx->pos)) {
			ctx->br = 0;
			db_addc(ctx->line, 0);
			db_addc(ctx->line, 0);
			l = db_finish(ctx->line, 0);
			ctx->line = db_new();
			db_addp(ctx->lines, l);
			ctx->pos = 0;
		}
		if (p == 0 || *p == 0)
			continue;
		l = p;
		while (*p) {
			db_addc(ctx->line, ctx->mode);
			db_addc(ctx->line, *p++);
			ctx->pos++;
		}
		db_addc(ctx->line, ctx->mode);
		db_addc(ctx->line, ' ');
		ctx->pos++;
		xfree(l);
	} while (p);
}

void pi_textarea_set_caption(struct pi_textarea *self, char *parsed)
{
	struct parser_state ctx;
	int s;

	ctx.lines = db_new();
	ctx.line = db_new();
	ctx.pos = 0;
	ctx.w = self->pos.w - 1;
	ctx.mode = 1;
	ctx.br = 0;
	ctx.input = parsed;

	break_into_lines(&ctx);
	db_kill(ctx.line);
	pi_textarea_kill(self);
	self->lines = db_finish(ctx.lines, &s);
	self->nlines = s / sizeof(char*);
	self->top = 0;
}

void pi_textarea_key(struct pi_textarea *self, int key)
{
	switch (key) {
	case pi_key_up:
		if (self->top) { 
			self->top--;
			key = 0;
		}
		break;
		
	case pi_key_down:
		if (self->top + self->pos.h < self->nlines) {
			self->top++;
			key = 0;
		}
		break;

	case pi_key_pgup:
		self->top -= self->pos.h;
		if (self->top < 0)
			self->top = 0;
		key = 0;
		break;
		
	case ' ':
	case pi_key_pgdn:
		if (self->top + self->pos.h < self->nlines) 
			self->top += self->pos.h;
		key = 0;
		break;
	}
	
	if (key)
		pi_object_key((struct pi_object*)self, key);
	else
		pi_draw((struct pi_object*)self);
}

static struct pi_funcs textarea_funcs = {
	1,
	(pi_handler_void)pi_textarea_draw,
	(pi_handler_int)pi_object_cc,
	(pi_handler_int)pi_textarea_key,
	(pi_handler_void)pi_textarea_kill,
	(pi_handler_rstring)pi_object_fetch,
};

struct pi_textarea *pi_new_textarea(int size)
{
	struct pi_textarea *self;

	if (size == 0)
		size = sizeof(struct pi_textarea);
	self = (struct pi_textarea*)pi_new_object(size);

	self->f = &textarea_funcs;

	return self;
}
