/******************************************************************************
*   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 Bill Reiss 1993                           *
*                     recoded by Igor van den Hoven 2004                      *
******************************************************************************/

#include "tintin.h"

DO_COMMAND(do_variable)
{
	char arg1[BUFFER_SIZE], arg2[BUFFER_SIZE], *str;

	struct listroot *root = ses->list[LIST_VARIABLE];

	arg = sub_arg_in_braces(ses, arg, arg1, GET_NST, SUB_VAR|SUB_FUN);

	str = arg;

	arg = sub_arg_in_braces(ses, arg, arg2, GET_ALL, SUB_VAR|SUB_FUN);

	if (*arg1 == 0)
	{
		show_list(root, 0);
	}
	else if (*arg1 && *str == 0)
	{
		struct listnode *node = search_nest_node(root, arg1);

		if (node)
		{
			show_node(root, node, 0);
		}
		else if (show_node_with_wild(ses, arg1, LIST_VARIABLE) == FALSE)
		{
			show_message(ses, LIST_VARIABLE, "#VARIABLE: NO MATCH(ES) FOUND FOR {%s}.", arg1);
		}
	}
	else
	{
		set_nest_node(root, arg1, "%s", arg2);

		while (*arg)
		{
			arg = sub_arg_in_braces(ses, arg, arg2, GET_ALL, SUB_VAR|SUB_FUN);

			if (*arg2)
			{
				add_nest_node(root, arg1, "%s", arg2);
			}
		}
		show_message(ses, LIST_VARIABLE, "#OK. VARIABLE {%s} HAS BEEN SET TO {%s}.", arg1, str);
	}
	return ses;
}

DO_COMMAND(do_unvariable)
{
	char arg1[BUFFER_SIZE];

	get_arg_in_braces(arg, arg1, TRUE);

	if (delete_nest_node(ses->list[LIST_VARIABLE], arg1))
	{
		show_message(ses, LIST_VARIABLE, "#OK. {%s} IS NO LONGER A VARIABLE.", arg1);
	}
	else
	{
		delete_node_with_wild(ses, LIST_VARIABLE, arg1);
	}
	return ses;
}


DO_COMMAND(do_replace)
{
	char arg1[BUFFER_SIZE], arg2[BUFFER_SIZE], arg3[BUFFER_SIZE], buf[BUFFER_SIZE], tmp[BUFFER_SIZE], *pti, *ptm;
	struct listroot *root;
	struct listnode *node;

	root = ses->list[LIST_VARIABLE];

	arg = get_arg_in_braces(arg, arg1, GET_NST);
	arg = sub_arg_in_braces(ses, arg, arg2, GET_ONE, SUB_VAR|SUB_FUN);
	arg = sub_arg_in_braces(ses, arg, arg3, GET_ALL, SUB_VAR|SUB_FUN);

	if (*arg1 == 0 || *arg2 == 0)
	{
		show_message(ses, LIST_VARIABLE, "#Syntax: #replace <var> <old text> <new text>", ses);
	}
	else if ((node = search_nest_node(root, arg1)) == NULL)
	{
		show_message(ses, LIST_VARIABLE, "#REPLACE: VARIABLE {%s} NOT FOUND.", arg1);
	}
	else if (tintin_regexp(NULL, node->right, arg2, 0, SUB_CMD) == FALSE)
	{
		show_message(ses, LIST_VARIABLE, "#REPLACE: {%s} NOT FOUND IN {%s}.", arg2, node->right);
	}
	else
	{
		pti = node->right;
		*buf = 0;

		do
		{
			if (*gtd->cmds[0] == 0) // Set by tintin_regexp
			{
				break;
			}

			ptm = strstr(pti, gtd->cmds[0]);

			if (ptm == NULL)
			{
				break;
			}

			*ptm = 0;

			substitute(ses, arg3, tmp, SUB_CMD);

			cat_sprintf(buf, "%s%s", pti, tmp);

			pti = ptm + strlen(gtd->cmds[0]);
		}
		while (tintin_regexp(NULL, pti, arg2, 0, SUB_CMD));

		strcat(buf, pti);

		set_nest_node(ses->list[LIST_VARIABLE], arg1, "%s", buf);
	}
	return ses;
}

/*
	support routines for #format - Igor
*/

void colorstring(char *str)
{
	char result[BUFFER_SIZE], *pti;
	int cnt;

	result[0] = 0;

	for (pti = str ; *pti ; pti++)
	{
		for (cnt = 0 ; *color_table[cnt].name ; cnt++)
		{
			if (is_abbrev(color_table[cnt].name, pti))
			{
				strcat(result, color_table[cnt].code);
				pti += strlen(color_table[cnt].name) - 1;

				break;
			}
		}
	}
	strcpy(str, result);
}

void headerstring(char *str)
{
	char buf[BUFFER_SIZE];

	if ((int) strlen(str) > gtd->ses->cols - 2)
	{
		str[gtd->ses->cols - 2] = 0;
	}

	memset(buf, '#', gtd->ses->cols);

	memcpy(&buf[(gtd->ses->cols - strlen(str)) / 2], str, strlen(str));

	buf[gtd->ses->cols] = 0;

	strcpy(str, buf);
}

void lowerstring(char *str)
{
	char *pts;

	for (pts = str ; *pts ; pts++)
	{
		*pts = tolower((int) *pts);
	}
}

void upperstring(char *str)
{
	char *pts;

	for (pts = str ; *pts ; pts++)
	{
		*pts = toupper((int) *pts);
	}
}

void reversestring(char *str)
{
	char t;
	int a = 0, z = strlen(str) - 1;

	while (z > a)
	{
		t = str[z];
		str[z--] = str[a];
		str[a++] = t;
	}
}

void mathstring(struct session *ses, char *str)
{
	get_number_string(ses, str, str);
}

void thousandgroupingstring(struct session *ses, char *str)
{
	char result[BUFFER_SIZE], strold[BUFFER_SIZE];
	int cnt1, cnt2, cnt3, cnt4;

	get_number_string(ses, str, strold);

	cnt1 = strlen(strold);
	cnt2 = BUFFER_SIZE / 2;
	cnt4 = strchr(strold, '.') ? 0 : 1;

	result[cnt2+1] = 0;

	for (cnt3 = 0 ; cnt1 >= 0 ; cnt1--, cnt2--)
	{
		if (cnt3++ % 3 == 0 && cnt3 != 1 && isdigit((int) strold[cnt1]))
		{
			result[cnt2--] = ',';
		}

		result[cnt2] = strold[cnt1];

		if (!isdigit((int) result[cnt2]))
		{
			cnt4 = 1;
			cnt3 = 0;
			continue;
		}
	}

	strcpy(str, result + cnt2 + 1);
}


void stripspaces(char *str)
{
	int cnt;

	for (cnt = strlen(str) - 1 ; cnt >= 0 ; cnt--)
	{
		if (!isspace((int) str[cnt]))
		{
			break;
		}
		str[cnt] = 0;
	}

	for (cnt = 0 ; str[cnt] != 0 ; cnt++)
	{
		if (!isspace((int) str[cnt]))
		{
			break;
		}
	}
	strcpy(str, &str[cnt]);
}

void wrapstring(struct session *ses, char *str)
{
	char *pti, *lis, *sos, *soc, buf[BUFFER_SIZE], color[BUFFER_SIZE], tmp[BUFFER_SIZE];
	int col = 1, cnt = 1;

	push_call("wrapstring(%p,%p)",ses,str);

	pti = lis = sos = soc = str;

	buf[0] = color[0] = 0;

	while (*pti != 0)
	{
		if (skip_vt102_codes(pti))
		{
			pti += skip_vt102_codes(pti);

			continue;
		}

		if (*pti == ' ')
		{
			lis = pti;
		}

		if (col > ses->cols)
		{
			col = 1;

			if (pti - lis > 15)
			{
				sprintf(tmp, "%.*s", (int) (sos - soc), soc);

				get_color_codes(color, tmp, color);

				cat_sprintf(buf, "{%d}{%s%.*s}", cnt++, color, pti - sos, sos);

				soc = sos;
				lis = sos = pti;
			}
			else
			{
				sprintf(tmp, "%.*s", (int) (sos - soc), soc);

				get_color_codes(color, tmp, color);

				cat_sprintf(buf, "{%d}{%s%.*s}", cnt++, color, lis - sos, sos);

				lis++;

				soc = sos;
				pti = sos = lis;
			}
		}
		else
		{
			pti++;
			col++;
		}
	}
	sprintf(tmp, "%.*s", (int) (sos - soc), soc);

	get_color_codes(color, tmp, color);

	cat_sprintf(buf, "{%d}{%s%s}", cnt, color, sos);

	strcpy(str, buf);

	pop_call();
	return;
}

int stringlength(struct session *ses, char *str)
{
	char temp[BUFFER_SIZE];

	substitute(ses, str, temp, SUB_COL|SUB_ESC);

	return strip_vt102_strlen(temp);
}

void timestring(struct session *ses, char *str)
{
	char left[BUFFER_SIZE], right[BUFFER_SIZE], *arg;

	struct tm timeval_tm;
	time_t    timeval_t;

	arg = get_arg_in_braces(str, left, TRUE);

	if (*arg == COMMAND_SEPARATOR)
	{
		arg++;
	}
	arg = get_arg_in_braces(arg, right, TRUE);

	if (*right)
	{
		timeval_t = (time_t) get_number(ses, right);
	}
	else
	{
		timeval_t = (time_t) time(NULL);
	}

	timeval_tm = *localtime(&timeval_t);

	strftime(str, BUFFER_SIZE, left, &timeval_tm);
}

DO_COMMAND(do_format)
{
	char temp[BUFFER_SIZE], destvar[BUFFER_SIZE], format[BUFFER_SIZE], newformat[BUFFER_SIZE], arglist[20][BUFFER_SIZE], *ptf, *ptt, *pts, *ptn;
	struct tm timeval_tm;
	time_t    timeval_t;
	int i;

	arg = sub_arg_in_braces(ses, arg, destvar,  GET_NST, SUB_VAR|SUB_FUN);
	arg = sub_arg_in_braces(ses, arg, format,   GET_ONE, SUB_VAR|SUB_FUN);

	if (*destvar == 0 || *format == 0)
	{
		tintin_printf2(ses, "#SYNTAX: #format {variable} {format} {arg1} {arg2}");

		return ses;
	}

	for (i = 0 ; i < 20 ; i++)
	{
		arg = sub_arg_in_braces(ses, arg, arglist[i], GET_ONE, SUB_VAR|SUB_FUN);
	}

	i = 0;

	ptf = format;
	ptn = newformat;

	while (*ptf)
	{
		if (i == 20)
		{
			break;
		}

		if (*ptf == '%')
		{
			pts = ptn;

			*ptn++ = *ptf++;

			if (*ptf == 0)
			{
				break;
			}
			else if (*ptf == '%')
			{
				*ptn++ = *ptf++;
			}
			else
			{
				while (!isalpha((int) *ptf) && !isdigit((int) *ptf))
				{
					*ptn++ = *ptf++;
				}

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

				ptt = temp;

				while (!isalpha((int) *ptf))
				{
					*ptt++ = *ptf++;
				}

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

				*ptt = 0;

				ptt = temp;

				if (*ptt == '0')
				{
					*ptn++ = *ptt++;
				}

				if (is_number(ptt))
				{
					sprintf(ptt, "%d", atoi(ptt) + (int) strlen(arglist[i]) - stringlength(ses, arglist[i]));
				}
				
				while (*ptt)
				{
					*ptn++ = *ptt++;
				}

				*ptn = 0;

				switch (*ptf)
				{
					case 'a':
						sprintf(arglist[i], "%c", (char) get_number(ses, arglist[i]));
						break;

					case 'c':
						colorstring(arglist[i]);
						break;

					case 'd':
						sprintf(temp, "%slld", pts);
						sprintf(arglist[i], temp, (long long) get_number(ses, arglist[i]));
						break;

					case 'g':
						thousandgroupingstring(ses, arglist[i]);
						break;

					case 'h':
						headerstring(arglist[i]);
						break;

					case 'l':
						lowerstring(arglist[i]);
						break;

					case 'm':
						mathstring(ses, arglist[i]);
						break;

					case 'n':
						arglist[i][0] = toupper((int) arglist[i][0]);
						break;

					case 'p':
						stripspaces(arglist[i]);
						break;

					case 'r':
						reversestring(arglist[i]);
						break;

					case 's':
						break;

					case 't':
						timestring(ses, arglist[i]);
						break;

					case 'u':
						upperstring(arglist[i]);
						break;

					case 'w':
						substitute(ses, arglist[i], arglist[i], SUB_VAR|SUB_FUN|SUB_COL|SUB_ESC);
						wrapstring(ses, arglist[i]);
						break;

					case 'C':
						sprintf(arglist[i], "%d", ses->cols);
						break;

					case 'D':
						timeval_t  = (time_t) *arglist[i] ? atoll(arglist[i]) : time(NULL);
						timeval_tm = *localtime(&timeval_t);
						strftime(arglist[i], BUFFER_SIZE, "%d", &timeval_tm);
						break;

					case 'G':
						thousandgroupingstring(ses, arglist[i]);
						break;

					case 'L':
						sprintf(arglist[i], "%d", stringlength(ses, arglist[i]));
						break;

					case 'M':
						timeval_t  = (time_t) *arglist[i] ? atoll(arglist[i]) : time(NULL);
						timeval_tm = *localtime(&timeval_t);
						strftime(arglist[i], BUFFER_SIZE, "%m", &timeval_tm);
						break;

					case 'R':
						sprintf(arglist[i], "%d", ses->rows);
						break;

					case 'T':
						sprintf(arglist[i], "%d", (int) time(NULL));
						break;

					case 'U':
						sprintf(arglist[i], "%lld", utime());
						break;

					case 'Y':
						timeval_t  = (time_t) *arglist[i] ? atoll(arglist[i]) : time(NULL);
						timeval_tm = *localtime(&timeval_t);
						strftime(arglist[i], BUFFER_SIZE, "%Y", &timeval_tm);
						break;

					default:
						show_message(ses, LIST_VARIABLE, "#FORMAT: UNKNOWN ARGUMENT {%%%c}.", *ptf);
						break;
				}
				*ptn++ = 's';
				i++;
				ptf++;
			}
		}
		else
		{
			*ptn++ = *ptf++;
		}
	}
	*ptn = 0;

	sprintf(temp, newformat, arglist[0], arglist[1], arglist[2], arglist[3], arglist[4], arglist[5], arglist[6], arglist[7], arglist[8], arglist[9], arglist[10], arglist[11], arglist[12], arglist[13], arglist[14], arglist[15], arglist[16], arglist[17], arglist[18], arglist[19]);

	set_nest_node(ses->list[LIST_VARIABLE], destvar, "%s", temp);

	return ses;
}