/******************************************************************************
*   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 save_pos(struct session *ses)
{
	printf("\0337"); 

	ses->sav_row = ses->cur_row;
	ses->sav_col = ses->cur_col;
}


void restore_pos(struct session *ses)
{
	printf("\0338\0338"); 

	ses->cur_row = ses->sav_row;
	ses->cur_col = ses->sav_col;
}

void restore_cursor(struct session *ses)
{
	printf("\033[%d;%dH", ses->rows, gtd->input_pos+1);
}

void goto_rowcol(struct session *ses, int row, int col)
{
	printf("\033[%d;%dH", row, col);

	ses->cur_row = row;
}


void erase_toeol(void)
{
	printf("\033[K");
}

/*
	save cursor, goto top row, delete (bot - top) rows, restore cursor
*/

void erase_screen(struct session *ses)
{
	printf("\0337\033[%d;1H\033[%dM\0338", ses->top_row, ses->bot_row - ses->top_row);

	ses->sav_row = ses->cur_row;
	ses->sav_col = ses->cur_col;
}

/*
	doesn't do much
*/

void reset(void)
{
	printf("%cc", ESCAPE);
}


void scroll_region(struct session *ses, int top, int bot)
{
	printf("%c[%d;%dr", ESCAPE, top, bot);

	ses->top_row = top;
	ses->bot_row = bot;
}


void reset_scroll_region(struct session *ses)
{
	if (ses == gtd->ses)
	{
		printf("%c[r", ESCAPE);
	}
	ses->top_row = 1;
	ses->bot_row = ses->rows;
}


int skip_vt102_codes(char *str)
{
	int skip;

	push_call("skip_vt102_codes(%p)",str);

	switch (str[0])
	{
		case   5:   /* ENQ */
		case   7:   /* BEL */
		case   8:   /* BS  */
	/*	case   9: *//* HT  */
	/*	case  10: *//* LF  */
		case  11:   /* VT  */
		case  12:   /* FF  */
		case  13:   /* CR  */
		case  14:   /* SO  */
		case  15:   /* SI  */
		case  17:   /* DC1 */
		case  19:   /* DC3 */
		case  24:   /* CAN */
		case  26:   /* SUB */
		case 127:   /* DEL */
			pop_call();
			return 1;

		case  27:   /* ESC */
			break;

		default:
			pop_call();
			return 0;
	}

	switch (str[1])
	{
		case '\0':
			pop_call();
			return 1;

		case '%':
		case '#':
		case '(':
		case ')':
			pop_call();
			return str[2] ? 3 : 2;

		case ']':
			switch (str[2])
			{
				case 'P':
					for (skip = 3 ; skip < 10 ; skip++)
					{
						if (str[skip] == 0)
						{
							break;
						}
					}
					pop_call();
					return skip;

				case 'R':
					pop_call();
					return 3;
			}
			pop_call();
			return 2;

		case '[':
			break;

		default:
			pop_call();
			return 2;
	}

	for (skip = 2 ; str[skip] != 0 ; skip++)
	{
		if (isalpha((int) str[skip]))
		{
			pop_call();
			return skip + 1;
		}

		switch (str[skip])
		{
			case '@':
			case '`':
			case ']':
				pop_call();
				return skip + 1;
		}
	}
	pop_call();
	return skip;
}

int find_non_color_codes(char *str)
{
	int skip;

	switch (str[0])
	{
		case  27:   /* ESC */
			break;

		default:
			return 0;
	}

	switch (str[1])
	{
		case '[':
			break;

		default:
			return 0;
	}

	for (skip = 2 ; str[skip] != 0 ; skip++)
	{
		switch (str[skip])
		{
			case 'm':
				return skip + 1;
			case '@':
			case '`':
			case ']':
				return 0;
		}

		if (isalpha((int) str[skip]))
		{
			return 0;
		}
	}
	return 0;
}


int skip_vt102_codes_non_graph(char *str)
{
	int skip = 0;

	switch (str[skip])
	{
		case   5:   /* ENQ */
		case   7:   /* BEL */
	/*	case   8: *//* BS  */
	/*	case   9: *//* HT  */
	/*	case  10: *//* LF  */
		case  11:   /* VT  */
		case  12:   /* FF  */
		case  13:   /* CR  */
		case  14:   /* SO  */
		case  15:   /* SI  */
		case  17:   /* DC1 */
		case  19:   /* DC3 */
		case  24:   /* CAN */
		case  26:   /* SUB */
		case 127:   /* DEL */
			return 1;

		case  27:   /* ESC */
			break;

		default:
			return 0;
	}

	switch (str[1])
	{
		case '\0':
			return 0;

		case 'c':
		case 'D':
		case 'E':
		case 'H':
		case 'M':
		case 'Z':
		case '7':
		case '8':
		case '>':
		case '=':
			return 2;

		case '%':
		case '#':
		case '(':
		case ')':
			return 3;

		case ']':
			switch (str[2])
			{
				case 'P':
					return 10;
				case 'R':
					return 3;
			}
			return 2;

		case '[':
			break;

		default:
			return 2;
	}

	for (skip = 2 ; str[skip] != 0 ; skip++)
	{
		switch (str[skip])
		{
			case 'm':
				return 0;
			case '@':
			case '`':
			case ']':
				return skip + 1;
		}

		if (isalpha((int) str[skip]))
		{
			return skip + 1;
		}
	}
	return 0;
}



void strip_vt102_codes(char *str, char *buf)
{
	char *pti, *pto;

	pti = (char *) str;
	pto = (char *) buf;

	while (*pti)
	{
		while (skip_vt102_codes(pti))
		{
			pti += skip_vt102_codes(pti);
		}

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


void strip_vt102_codes_non_graph(char *str, char *buf)
{
	char *pti, *pto;

	pti = str;
	pto = buf;

	while (*pti)
	{
		while (skip_vt102_codes_non_graph(pti))
		{
			pti += skip_vt102_codes_non_graph(pti);
		}

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

void strip_non_vt102_codes(char *str, char *buf)
{
	char *pti, *pto;
	int len;

	pti = str;
	pto = buf;

	while (*pti)
	{
		while ((len = skip_vt102_codes(pti)) != 0)
		{
			memcpy(pto, pti, len);
			pti += len;
			pto += len;
		}

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

// mix old and str, then copy compressed color string to buf which can point to old.

void get_color_codes(char *old, char *str, char *buf)
{
	char *pti, *pto, col[100], tmp[BUFFER_SIZE];
	int len, vtc, fgc, bgc, cnt;

	pto = tmp;

	pti = old;

	while (*pti)
	{
		while ((len = find_non_color_codes(pti)) != 0)
		{
			memcpy(pto, pti, len);
			pti += len;
			pto += len;
		}

		if (*pti)
		{
			pti++;
		}
	}

	pti = str;

	while (*pti)
	{
		while ((len = find_non_color_codes(pti)) != 0)
		{
			memcpy(pto, pti, len);
			pti += len;
			pto += len;
		}

		if (*pti)
		{
			pti++;
		}
	}

	*pto = 0;

	if (strlen(tmp) == 0)
	{
		buf[0] = 0;
		return;
	}

	vtc =  0;
	fgc = -1;
	bgc = -1;

	pti = tmp;

	while (*pti)
	{
		switch (*pti)
		{
			case 27:
				pti += 2;

				if (pti[-1] == 'm')
				{
					vtc =  0;
					fgc = -1;
					bgc = -1;
					break;
				}

				for (cnt = 0 ; pti[cnt] ; cnt++)
				{
					col[cnt] = pti[cnt];

					if (pti[cnt] == ';' || pti[cnt] == 'm')
					{
						col[cnt] = 0;

						cnt = -1;
						pti += 1 + strlen(col);

						if (HAS_BIT(vtc, COL_256) && (HAS_BIT(vtc, COL_XTF) || HAS_BIT(vtc, COL_XTB)))
						{
							if (HAS_BIT(vtc, COL_XTF))
							{
								fgc = URANGE(0, atoi(col), 255);
							}

							if (HAS_BIT(vtc, COL_XTB))
							{
								bgc = URANGE(0, atoi(col), 255);
							}
							DEL_BIT(vtc, COL_XTF|COL_XTB);
						}
						else
						{
							switch (atoi(col))
							{
								case 0:
									vtc = 0;
									fgc = -1;
									bgc = -1;
									break;
								case 1:
									SET_BIT(vtc, COL_BLD);
									break;
								case 4:
									SET_BIT(vtc, COL_UND);
									break;
								case 5:
									if (HAS_BIT(vtc, COL_XTF) || HAS_BIT(vtc, COL_XTB))
									{
										SET_BIT(vtc, COL_256);
									}
									else
									{
										SET_BIT(vtc, COL_BLK);
									}
									break;
								case 7:
									SET_BIT(vtc, COL_REV);
									break;
								case  2:
								case 21:
								case 22:
									DEL_BIT(vtc, COL_BLD);
									break;
								case 24:
									DEL_BIT(vtc, COL_UND);
									break;
								case 25:
									DEL_BIT(vtc, COL_UND);
									break;
								case 27:
									DEL_BIT(vtc, COL_BLK);
									break;
								case 38:
									DEL_BIT(vtc, COL_XTB);
									DEL_BIT(vtc, COL_256);
									SET_BIT(vtc, COL_XTF);
									fgc = -1;
									break;
								case 39:
									DEL_BIT(vtc, COL_UND);
									fgc = -1;
									break;
								case 48:
									DEL_BIT(vtc, COL_XTF);
									DEL_BIT(vtc, COL_256);
									SET_BIT(vtc, COL_XTB);
									bgc = -1;
									break;
								default:
									DEL_BIT(vtc, COL_256);

									/*
										Use 256 color's 16 color notation
									*/

									if (atoi(col) / 10 == 4)
									{
										bgc = atoi(col) % 10;
									}
									if (atoi(col) / 10 == 9)
									{
										bgc = atoi(col) % 10 + 8;
									}

									if (atoi(col) / 10 == 3)
									{
										fgc = atoi(col) % 10;
									}
									if (atoi(col) / 10 == 10)
									{
										fgc = atoi(col) % 10 + 8;
									}
									break;
							}
						}
					}

					if (pti[-1] == 'm')
					{
						break;
					}
				}
				break;

			default:
				pti++;
				break;
		}
	}

	strcpy(buf, "\033[0");

	if (HAS_BIT(vtc, COL_BLD))
	{
		strcat(buf, ";1");
	}
	if (HAS_BIT(vtc, COL_UND))
	{
		strcat(buf, ";4");
	}
	if (HAS_BIT(vtc, COL_BLK))
	{
		strcat(buf, ";5");
	}
	if (HAS_BIT(vtc, COL_REV))
	{
		strcat(buf, ";7");
	}

	if (fgc >= 16)
	{
		cat_sprintf(buf, ";38;5;%d", fgc);
	}
	else if (fgc >= 8)
	{
		cat_sprintf(buf, ";%d", fgc + 100);
	}
	else if (fgc >= 0)
	{
		cat_sprintf(buf, ";%d", fgc + 30);
	}

	if (bgc >= 16)
	{
		cat_sprintf(buf, ";48;5;%d", bgc);
	}
	else if (bgc >= 8)
	{
		cat_sprintf(buf, ";%d", fgc + 90);
	}
	else if (bgc >= 0)
	{
		cat_sprintf(buf, ";%d", bgc + 40);
	}

	strcat(buf, "m");
}

int strip_vt102_strlen(char *str)
{
	char *pti;
	int i = 0;

	pti = str;

	while (*pti)
	{
		while (skip_vt102_codes(pti))
		{
			pti += skip_vt102_codes(pti);
		}

		if (*pti)
		{
			pti++;
			i++;
		}
	}
	return i;
}

int strip_color_strlen(char *str)
{
	char *pti;
	int i = 0;

	pti = str;

	while (*pti)
	{
		if (pti[0] == '<' && isalnum((int) pti[1]) && isalnum((int) pti[2]) && isalnum((int) pti[3]) && pti[4] == '>')
		{
			pti += 5;
		}
		else
		{   
			pti++;
			i++;  
		}	   
	}
	return i;
}

int interpret_vt102_codes(struct session *ses, char *str, int real)
{
	char data[BUFFER_SIZE] = { 0 };
	int skip = 0;

	switch (str[skip])
	{
		case   5:
			return FALSE; /* ^E fucks with the terminal */

		case   8:
			ses->cur_col = UMAX(1, ses->cur_col - 1);
			return TRUE;

		case  27:   /* ESC */
			break;

		case  13:   /* CR  */
			ses->cur_col = 1;

		default:
			return TRUE;
	}

	switch (str[1])
	{
		case '7':
			ses->sav_row = ses->cur_row;
			ses->sav_col = ses->cur_col;
			return TRUE;

		case '8':
			ses->cur_row = ses->sav_row;
			ses->cur_col = ses->sav_col;
			return TRUE;

		case 'D':
			ses->cur_row = URANGE(1, ses->cur_row + 1, ses->rows);
			return TRUE;

		case 'E':
			ses->cur_row = URANGE(1, ses->cur_row + 1, ses->rows);
			ses->cur_col = 1;
			return TRUE;

		case 'M':
			ses->cur_row = URANGE(1, ses->cur_row - 1, ses->rows);
			return TRUE;

		case 'Z':
			if (real)
			{
				socket_printf(ses, 5, "%c%c%c%c%c", ESCAPE, '[', '?', '6', 'c');
			}
			return FALSE;

		case '[':
			break;

		default:
			return TRUE;
	}

	for (skip = 2 ; str[skip] != 0 ; skip++)
	{
		switch (str[skip])
		{
			case '@':
			case '`':
			case ']':
				return TRUE;

			case 'c':
				if (real)
				{
					socket_printf(ses, 5, "%c%c%c%c%c", ESCAPE, '[', '?', '6', 'c');
				}
				ses->cur_row = 1;
				ses->cur_col = 1;
				ses->sav_row = ses->cur_row;
				ses->sav_col = ses->cur_col;
				return FALSE;					

			case 'A':
				ses->cur_row -= UMAX(1, atoi(data));
				break;

			case 'B':
			case 'e':
				ses->cur_row += UMAX(1, atoi(data));
				break;

			case 'C':
			case 'a':
				ses->cur_col += UMAX(1, atoi(data));
				break;

			case 'D':
				ses->cur_col -= UMAX(1, atoi(data));
				break;

			case 'E':
				ses->cur_row -= UMAX(1, atoi(data));
				ses->cur_col = 1;
				break;

			case 'F':
				ses->cur_row -= UMAX(1, atoi(data));
				ses->cur_col = 1;
				break;

			case 'G':
				ses->cur_col = UMAX(1, atoi(data));
				break;

			case 'H':
			case 'f':
				if (sscanf(data, "%d;%d", &ses->cur_row, &ses->cur_col) != 2)
				{
					if (sscanf(data, "%d", &ses->cur_row) == 1)
					{
						ses->cur_col = 1;
					}
					else
					{
						ses->cur_row = 1;
						ses->cur_col = 1;
					}
				}
				break;

			case 'd':
				ses->cur_row = atoi(data);
				break;

			case 'r':
				if (sscanf(data, "%d;%d", &ses->top_row, &ses->bot_row) != 2)
				{
					if (sscanf(data, "%d", &ses->top_row) != 1)
					{
						ses->top_row = 1;
						ses->bot_row = ses->rows;
					}
					else
					{
						ses->bot_row = ses->rows;
					}
				}
				ses->cur_row = 1;
				ses->cur_col = 1;
				break;

			case 's':
				ses->sav_row = ses->cur_row;
				ses->sav_col = ses->cur_col;
				break;

			case 'u':
				ses->cur_row = ses->sav_row;
				ses->cur_col = ses->sav_col;
				break;

			default:
				data[skip - 2] = str[skip];
				data[skip - 1] = 0;
				break;
		}

		if (isalpha((int) str[skip]))
		{
			ses->cur_row = URANGE(1, ses->cur_row, ses->rows);

			ses->cur_col = URANGE(1, ses->cur_col, ses->cols + 1);


			ses->top_row = URANGE(1, ses->top_row, ses->rows);

			ses->bot_row = ses->bot_row ? URANGE(1, ses->bot_row, ses->rows) : ses->rows;
			
			return TRUE;
		}
	}
	return TRUE;
}