/******************************************************************************
*   TinTin++                                                                  *
*   Copyright (C) 2004 (See CREDITS file)                                     *
*                                                                             *
*   This program is protected under the GNU GPL (See COPYING)                 *
*                                                                             *
*   This program 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 of the License, 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 *
******************************************************************************/

/******************************************************************************
*              (T)he K(I)cki(N) (T)ickin D(I)kumud Clie(N)t                   *
*                                                                             *
*                     coded by Igor van den Hoven 2004                        *
******************************************************************************/

#include "tintin.h"


void init_buffer(struct session *ses, int size)
{
	int cnt;

	push_call("init_buffer(%p,%p)",ses,size);

	if (ses->scroll_max == size)
	{
		pop_call();
		return;
	}

	if (ses->buffer)
	{
		cnt = ses->scroll_row;
		
		do
		{
			if (++cnt == ses->scroll_max)
			{
				cnt = 0;
			}

			if (ses->buffer[cnt] == NULL)
			{
				break;
			}

			str_unhash(ses->buffer[cnt]);
		}
		while (cnt != ses->scroll_row);
	}

	if (ses->buffer)
	{
		free(ses->buffer);
	}

	if (size)
	{
		ses->buffer = (char **) calloc(size, sizeof(char *));

		ses->scroll_max  = size;
		ses->scroll_row  = size - 1;
		ses->scroll_line = - 1;
	}
	pop_call();
	return;
}


void add_line_buffer(struct session *ses, char *line, int more_output)
{
	char linebuf[STRING_SIZE];
	char *pti, *pto;
	int lines;
	int sav_row, sav_col, cur_row, cur_col, top_row, bot_row;

	push_call("add_line_buffer(%p,%s,%d)",ses,line,more_output);

	if (ses->buffer == NULL || HAS_BIT(ses->flags, SES_FLAG_SCROLLSTOP))
	{
		pop_call();
		return;
	}

	sav_row = ses->sav_row;
	sav_col = ses->sav_col;
	cur_row = ses->cur_row;
	cur_col = ses->cur_col;
	top_row = ses->top_row;
	bot_row = ses->bot_row;

	if (more_output == TRUE)
	{
		if (strlen(ses->more_output) < BUFFER_SIZE / 2)
		{
			strcat(ses->more_output, line);

			pop_call();
			return;
		}
	}


	strcat(ses->more_output, line);

	pti = pto = ses->more_output;

	while (*pti != 0)
	{
		while (skip_vt102_codes_non_graph(pti))
		{
			interpret_vt102_codes(ses, pti, FALSE);

			pti += skip_vt102_codes_non_graph(pti);
		}

		if (*pti == 0)
		{
			break;
		}

		if (SCROLL(ses))
		{
			*pto++ = *pti++;
		}
		else
		{
			pti++;
		}
	}
	*pto = 0;

	lines = word_wrap(ses, ses->more_output, linebuf, FALSE);

	ses->more_output[0] = 0;

	ses->buffer[ses->scroll_row] = str_hash(linebuf, lines);

	if (more_output == -1)
	{
		str_hash_grep(ses->buffer[ses->scroll_row], TRUE);
	}

	if (!HAS_BIT(ses->flags, SES_FLAG_LOGLEVEL))
	{
		if (ses->logfile)
		{
			logit(ses, linebuf, ses->logfile, TRUE);
		}
	}

	if (gtd->chat)
	{
		chat_forward_session(ses, linebuf);
	}

	if (ses->logline)
	{
		logit(ses, linebuf, ses->logline, TRUE);

		fclose(ses->logline);
		ses->logline = NULL;
	}

	if (--ses->scroll_row < 0)
	{
		ses->scroll_row = ses->scroll_max -1;
	}

	if (ses->buffer[ses->scroll_row])
	{
		ses->buffer[ses->scroll_row] = str_unhash(ses->buffer[ses->scroll_row]);
	}

	ses->sav_row = sav_row;
	ses->sav_col = sav_col;
	ses->cur_row = cur_row;
	ses->cur_col = cur_col;
	ses->top_row = top_row;
	ses->bot_row = bot_row;

	pop_call();
	return;
}

DO_COMMAND(do_grep)
{
	char left[BUFFER_SIZE], right[BUFFER_SIZE];
	int scroll_cnt, grep_cnt, grep_min, grep_max, grep_add, page;

	grep_cnt = grep_add = scroll_cnt = grep_min = 0;
	grep_max = ses->bot_row - ses->top_row - 2;

	get_arg_in_braces(arg, left, FALSE);
	substitute(ses, left, left, SUB_VAR|SUB_FUN);

	if (ses->buffer == NULL)
	{
		tintin_puts2(ses, "#GREP, NO SCROLL BUFFER AVAILABLE.");
	}
	else if (*left == 0)
	{
		tintin_puts2(ses, "#GREP WHAT?");
	}
	else
	{
		page = get_number(ses, left);

		if (page)
		{
			arg = get_arg_in_braces(arg, left, FALSE);
			arg = get_arg_in_braces(arg, right, TRUE);

			if (*right == 0)
			{
				tintin_printf2(ses, "#GREP PAGE {%s} FOR WHAT?", left);

				return ses;
			}
		}
		else
		{
			page = 1;

			arg = get_arg_in_braces(arg, right, TRUE);
		}

		if (page > 0)
		{
			grep_min = grep_max * page - grep_max;
			grep_max = grep_max * page;
		}
		else
		{
			grep_min = grep_max * (page * -1) - grep_max;
			grep_max = grep_max * (page * -1);
		}

		SET_BIT(ses->flags, SES_FLAG_SCROLLSTOP);

		tintin_header(ses, " GREPPING PAGE %d FOR %s ", page, right);

		if (page > 0)
		{
			scroll_cnt = ses->scroll_row;

			do
			{
				if (scroll_cnt == ses->scroll_max - 1)
				{
					scroll_cnt = 0;
				}
				else
				{
					scroll_cnt++;
				}

				if (ses->buffer[scroll_cnt] == NULL)
				{
					break;
				}

				if (str_hash_grep(ses->buffer[scroll_cnt], FALSE))
				{
					continue;
				}

				if (find(ses, ses->buffer[scroll_cnt], right, SUB_NONE))
				{
					grep_add = str_hash_lines(ses->buffer[scroll_cnt]);

					if (grep_cnt + grep_add > grep_max)
					{
						break;
					}

					grep_cnt += grep_add;
				}
			}
			while (scroll_cnt != ses->scroll_row);

			if (grep_cnt <= grep_min)
			{
				tintin_puts2(ses, "#NO MATCHES FOUND.");
			}
			else do
			{
				if (scroll_cnt == 0)
				{
					scroll_cnt = ses->scroll_max - 1;
				}
				else
				{
					scroll_cnt--;
				}

				if (ses->buffer[scroll_cnt] == NULL)
				{
					break;
				}

				if (str_hash_grep(ses->buffer[scroll_cnt], FALSE))
				{
					continue;
				}

				if (find(ses, ses->buffer[scroll_cnt], right, SUB_NONE))
				{
					grep_add = str_hash_lines(ses->buffer[scroll_cnt]);

					if (grep_cnt - grep_add < grep_min)
					{
						break;
					}

					grep_cnt -= grep_add;

					tintin_puts2(ses, ses->buffer[scroll_cnt]);
				}
			}
			while (scroll_cnt != ses->scroll_row);
		}
		else
		{
			if (ses->buffer[0])
			{
				scroll_cnt = ses->scroll_row - 1;
			}
			else
			{
				scroll_cnt = ses->scroll_max - 1;
			}

			do
			{
				if (scroll_cnt == -1)
				{
					scroll_cnt = 0;
				}
				else
				{
					scroll_cnt--;
				}

				if (ses->buffer[scroll_cnt] == NULL)
				{
					break;
				}

				if (str_hash_grep(ses->buffer[scroll_cnt], FALSE))
				{
					continue;
				}

				if (find(ses, ses->buffer[scroll_cnt], right, SUB_NONE))
				{
					grep_add = str_hash_lines(ses->buffer[scroll_cnt]);

					if (grep_cnt >= grep_min)
					{
						tintin_puts2(ses, ses->buffer[scroll_cnt]);
					}

					if (grep_cnt + grep_add >= grep_max)
					{
						break;
					}

					grep_cnt += grep_add;
				}
			}
			while (scroll_cnt != ses->scroll_row);

			if (grep_cnt <= grep_min)
			{
				tintin_puts2(ses, "#NO MATCHES FOUND.");
			}
		}
		tintin_header(ses, "");

		DEL_BIT(ses->flags, SES_FLAG_SCROLLSTOP);
	}
	return ses;
}

int show_buffer(struct session *ses)
{
	char temp[STRING_SIZE];
	int scroll_size, scroll_cnt, scroll_tmp, scroll_add, scroll_cut;

	if (ses != gtd->ses)
	{
		return TRUE;
	}

	scroll_size = get_scroll_size(ses);
	scroll_add  = 0 - ses->scroll_base;
	scroll_tmp  = 0;
	scroll_cnt  = ses->scroll_line;
	scroll_cut  = 0;
	/*
		Find the upper limit of the buffer shown
	*/

	while (TRUE)
	{
		if (ses->buffer[scroll_cnt] == NULL)
		{
			break;
		}

		scroll_tmp = str_hash_lines(ses->buffer[scroll_cnt]);

		if (scroll_add + scroll_tmp > scroll_size)
		{
			if (scroll_add == scroll_size)
			{
				scroll_cut = 0;
			}
			else
			{
				scroll_cut = scroll_tmp - (scroll_size - scroll_add);
			}
			break;
		}

		scroll_add += scroll_tmp;

		if (scroll_cnt == ses->scroll_max - 1)
		{
			scroll_cnt = 0;
		}
		else
		{
			scroll_cnt++;
		}
	}


	if (ses->buffer[scroll_cnt] == NULL)
	{
		erase_screen(ses);
	}

	if (IS_SPLIT(ses))
	{
		save_pos(ses);
		goto_rowcol(ses, ses->bot_row, 1);
		SET_BIT(ses->flags, SES_FLAG_READMUD);
	}

	/*
		If the top line exists of multiple lines split it in the middle.
	*/

	if (ses->buffer[scroll_cnt] && scroll_cut)
	{
		if (scroll_add >= 0)
		{
			word_wrap_split(ses, ses->buffer[scroll_cnt], temp, scroll_tmp - scroll_cut, scroll_cut);

			printf("%s\n", temp);
		}
		else
		{
			word_wrap_split(ses, ses->buffer[scroll_cnt], temp, ses->scroll_base, scroll_size);

			goto eof;
		}
	}

	/*
		Print away
	*/

	while (TRUE)
	{
		if (scroll_cnt == 0)
		{
			scroll_cnt = ses->scroll_max - 1;
		}
		else
		{
			scroll_cnt--;
		}

		if (ses->buffer[scroll_cnt] == NULL)
		{
			break;
		}

		scroll_tmp = word_wrap(ses, ses->buffer[scroll_cnt], temp, FALSE);

		if (scroll_add - scroll_tmp < 0)
		{
			scroll_cut = scroll_add;
			break;
		}

		scroll_add -= scroll_tmp;

		printf("%s\n", temp);
	}

	/*
		If the bottom line exists of multiple lines split it in the middle
	*/

	if (scroll_tmp && ses->buffer[scroll_cnt])
	{
		word_wrap_split(ses, ses->buffer[scroll_cnt], temp, 0, scroll_cut);
	}

	eof:

	if (IS_SPLIT(ses))
	{
		restore_pos(ses);
		DEL_BIT(ses->flags, SES_FLAG_READMUD);
	}
	return TRUE;
}


DO_COMMAND(do_buffer)
{
	char left[BUFFER_SIZE], right[BUFFER_SIZE];
	int cnt;

	arg = get_arg_in_braces(arg, left, FALSE);
	arg = get_arg_in_braces(arg, right, TRUE);
	substitute(ses, right, right, SUB_VAR|SUB_FUN);

	if (HAS_BIT(gtd->flags, TINTIN_FLAG_RESETBUFFER))
	{
		DEL_BIT(gtd->flags, TINTIN_FLAG_RESETBUFFER);

		reset_hash_table();
	}

	if (*left == 0)
	{
		tintin_header(ses, " BUFFER COMMANDS ");

		for (cnt = 0 ; *buffer_table[cnt].name != 0 ; cnt++)
		{
			tintin_printf2(ses, "  [%-13s] %s", buffer_table[cnt].name, buffer_table[cnt].desc);
		}
		tintin_header(ses, "");

		return ses;
	}

	for (cnt = 0 ; *buffer_table[cnt].name ; cnt++)
	{
		if (!is_abbrev(left, buffer_table[cnt].name))
		{
			continue;
		}

		buffer_table[cnt].fun(ses, right);

		return ses;
	}

	do_buffer(ses, "");

	return ses;
}

DO_BUFFER(buffer_clear)
{
	int cnt;

	cnt = ses->scroll_row;
		
	do
	{
		if (++cnt == ses->scroll_max)
		{
			cnt = 0;
		}

		if (ses->buffer[cnt] == NULL)
		{
			break;
		}
		ses->buffer[cnt] = str_unhash(ses->buffer[cnt]);
	}
	while (cnt != ses->scroll_row);

	ses->scroll_row  = ses->scroll_max - 1;
	ses->scroll_line = - 1;

	return;
}

DO_BUFFER(buffer_up)
{
	int scroll_size, scroll_cnt, buffer_add, buffer_tmp;

	if (ses->buffer == NULL)
	{
		return;
	}

	if (ses->scroll_line == -1)
	{
		ses->scroll_line = ses->scroll_row + 1;
	}

	scroll_size = get_scroll_size(ses);
	scroll_cnt  = ses->scroll_line;
	buffer_add  = 0 - ses->scroll_base;

	while (TRUE)
	{
		if (ses->buffer[scroll_cnt] == NULL)
		{
			return;
		}

		buffer_tmp = str_hash_lines(ses->buffer[scroll_cnt]);

		if (scroll_size < buffer_add + buffer_tmp)
		{
			ses->scroll_line = scroll_cnt;
			ses->scroll_base = scroll_size - buffer_add;

			break;
		}

		buffer_add += buffer_tmp;

		if (scroll_cnt == ses->scroll_max - 1)
		{
			scroll_cnt = 0;
		}
		else
		{
			scroll_cnt++;
		}
	}

	show_buffer(ses);
}

DO_BUFFER(buffer_down)
{
	int scroll_size, scroll_cnt, buffer_add, buffer_tmp;

	if (ses->buffer == NULL)
	{
		return;
	}

	if (ses->scroll_line == -1)
	{
		return;
	}

	scroll_size = get_scroll_size(ses);
	scroll_cnt  = ses->scroll_line;
	buffer_add  = ses->scroll_base;

	if (scroll_size <= buffer_add)
	{
		ses->scroll_base = buffer_add - scroll_size;

		show_buffer(ses);
		return;
	}

	if (scroll_cnt == 0)
	{
		scroll_cnt = ses->scroll_max - 1;
	}
	else
	{
		scroll_cnt--;
	}

	while (TRUE)
	{
		if (ses->buffer[scroll_cnt] == NULL)
		{
			buffer_end(ses, "");
			return;
		}

		buffer_tmp = str_hash_lines(ses->buffer[scroll_cnt]);

		if (scroll_size <= buffer_add + buffer_tmp)
		{
			ses->scroll_line = scroll_cnt;
			ses->scroll_base = buffer_tmp - (scroll_size - buffer_add);

			break;
		}

		buffer_add += buffer_tmp;

		if (scroll_cnt == 0)
		{
			scroll_cnt = ses->scroll_max - 1;
		}
		else
		{
			scroll_cnt--;
		}
	}

	show_buffer(ses);
}

DO_BUFFER(buffer_home)
{
	if (ses->buffer == NULL)
	{
		return;
	}

	if (ses->buffer[0])
	{
		ses->scroll_line = ses->scroll_row - 1;
	}
	else
	{
		ses->scroll_line = ses->scroll_max - 1;
	}
	ses->scroll_base = str_hash_lines(ses->buffer[ses->scroll_line]);

	buffer_down(ses, "");
}

DO_BUFFER(buffer_end)
{
	if (ses->buffer == NULL)
	{
		return;
	}

	if (ses->scroll_row == ses->scroll_max - 1)
	{
		ses->scroll_line = 0;
	}
	else
	{
		ses->scroll_line = ses->scroll_row + 1;
	}

	ses->scroll_base = 0;

	show_buffer(ses);

	ses->scroll_line = -1;
	ses->scroll_base =  0;
}

DO_BUFFER(buffer_lock)
{
	if (ses->buffer == NULL)
	{
		return;
	}

	if (ses->scroll_line == -1)
	{
		ses->scroll_line = ses->scroll_row + 1;
	}
	else
	{
		buffer_end(ses, "");
	}
}

DO_BUFFER(buffer_find)
{
	char left[BUFFER_SIZE], right[BUFFER_SIZE];
	int scroll_cnt, grep_cnt, grep_max, page;

	grep_cnt = grep_max = scroll_cnt = 0;

	get_arg_in_braces(arg, left, FALSE);

	if (ses->buffer == NULL)
	{
		tintin_puts2(NULL, "#BUFFER, NO SCROLL BUFFER AVAILABLE.");
	}
	else if (*left == 0)
	{
		tintin_puts2(NULL, "#BUFFER, FIND WHAT?");
	}
	else
	{
		page = get_number(ses, left);

		if (page)
		{
			arg = get_arg_in_braces(arg, left,  FALSE);
			arg = get_arg_in_braces(arg, right, TRUE);

			if (*right == 0)
			{
				tintin_printf2(NULL, "#BUFFER, FIND OCCURANCE %d OF WHAT?", page);

				return;
			}
		}
		else
		{
			page = 1;

			arg = get_arg_in_braces(arg, right, TRUE);
		}

		if (page > 0)
		{
			scroll_cnt = ses->scroll_row;

			do
			{
				if (scroll_cnt == ses->scroll_max -1)
				{
					scroll_cnt = 0;
				}
				else
				{
					scroll_cnt++;
				}

				if (ses->buffer[scroll_cnt] == NULL)
				{
					break;
				}

				if (str_hash_grep(ses->buffer[scroll_cnt], FALSE))
				{
					continue;
				}

				if (find(ses, ses->buffer[scroll_cnt], right, SUB_NONE))
				{
					grep_cnt++;

					if (grep_cnt == page)
					{
						break;
					}
				}
			}
			while (scroll_cnt != ses->scroll_row);
		}
		else
		{
			if (ses->buffer[0])
			{
				scroll_cnt = ses->scroll_row - 1;
			}
			else
			{
				scroll_cnt = ses->scroll_max - 1;
			}

			do
			{
				if (scroll_cnt == 0)
				{
					scroll_cnt = ses->scroll_max -1;
				}
				else
				{
					scroll_cnt--;
				}

				if (ses->buffer[scroll_cnt] == NULL)
				{
					break;
				}

				if (str_hash_grep(ses->buffer[scroll_cnt], FALSE))
				{
					continue;
				}

				if (find(ses, ses->buffer[scroll_cnt], right, SUB_NONE))
				{
					grep_cnt--;

					if (grep_cnt == page)
					{
						break;
					}

				}
			}
			while (scroll_cnt != ses->scroll_row);
		}

		if (ses->buffer[scroll_cnt] == NULL || scroll_cnt == ses->scroll_row)
		{
			tintin_puts(NULL, "#BUFFER FIND, NO MATCHES FOUND.");
		}
		else
		{
			ses->scroll_line = scroll_cnt;

			show_buffer(ses);
		}
	}
	return;
}

DO_BUFFER(buffer_get)
{
	char arg1[BUFFER_SIZE], arg2[BUFFER_SIZE], arg3[BUFFER_SIZE];
	int min, max, cur, cnt, add;

	arg = sub_arg_in_braces(ses, arg, arg1, GET_NST, FALSE);
	arg = get_arg_in_braces(arg, arg2, FALSE);
	arg = get_arg_in_braces(arg, arg3, FALSE);

	min = get_number(ses, arg2);
	max = get_number(ses, arg3);

	if (*arg1 == 0 || *arg2 == 0)
	{
		tintin_printf(ses, "#SYNTAX: #BUFFER GET <VARIABLE> <LOWER BOUND> [UPPER BOUND]");
		return;
	}

	if (*arg3 == 0)
	{
		cur = UMAX(0, (ses->scroll_row + min) % ses->scroll_max);

		if (ses->buffer[cur] == NULL)
		{
			set_nest_node(ses->list[LIST_VARIABLE], arg1, "");
		}
		else
		{
			set_nest_node(ses->list[LIST_VARIABLE], arg1, "%s", ses->buffer[cur]);
		}
		return;
	}

	cnt = 0;
	add = (min < max) ? 1 : -1;

	set_nest_node(ses->list[LIST_VARIABLE], arg1, "");

	while ((add == 1 && max >= min) || (add == -1 && max <= min))
	{
		sprintf(arg2, "%s[%d]", arg1, ++cnt);

		cur = (ses->scroll_row + min) % ses->scroll_max;

		if (ses->buffer[cur] == NULL)
		{
			set_nest_node(ses->list[LIST_VARIABLE], arg2, "");
		}
		else
		{
			set_nest_node(ses->list[LIST_VARIABLE], arg2, "%s", ses->buffer[cur]);
		}

		min = min + add;
	}
	return;
}

DO_BUFFER(buffer_write)
{
	FILE *fp;
	char left[BUFFER_SIZE], out[STRING_SIZE];
	int cnt;

	arg = get_arg_in_braces(arg, left, FALSE);

	if (*left == 0)
	{
		tintin_printf(ses, "#SYNTAX: #BUFFER WRITE <FILENAME>]");
	}
	else
	{
		if ((fp = fopen(left, "w")))
		{
			show_message(ses, LIST_MESSAGE, "#OK: WRITING BUFFER TO '%s'.", left);

			if (HAS_BIT(ses->flags, SES_FLAG_LOGHTML))
			{
				write_html_header(fp);
			}

			cnt = ses->scroll_row;

			do
			{
				if (cnt == 0)
				{
					cnt = ses->scroll_max - 1;
				}
				else
				{
					cnt--;
				}

				if (ses->buffer[cnt] == NULL)
				{
					continue;
				}

				if (HAS_BIT(ses->flags, SES_FLAG_LOGPLAIN))
				{
					strip_vt102_codes(ses->buffer[cnt], out);
				}
				else if (HAS_BIT(ses->flags, SES_FLAG_LOGHTML))
				{
					vt102_to_html(ses, ses->buffer[cnt], out);
				}
				else
				{
					strcpy(out, ses->buffer[cnt]);
				}
				strcat(out, "\n");

				fputs(out, fp);
			}
			while (cnt != ses->scroll_row);

			fclose(fp);
		}
		else
		{
			tintin_printf(ses, "#ERROR: #BUFFER WRITE {%s} - FAILED TO OPEN FILE.", left);
		}
	}
	return;
}