/******************************************************************************
*   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 Peter Unold 1992                           *
******************************************************************************/

#include "tintin.h"

#include <signal.h>
#include <sys/socket.h>

/*************** globals ******************/

struct session *gts;
struct tintin_data *gtd;

void pipe_handler(int signal)
{
	restore_terminal();

	clean_screen(gtd->ses);

	tintin_printf(NULL, "broken_pipe: dumping stack");

	dump_stack();
}

/*
	when the screen size changes, take note of it
*/

void winch_handler(int signal)
{
	struct session *ses;

	init_screen_size(gts);

	for (ses = gts->next ; ses ; ses = ses->next)
	{
		init_screen_size(ses);

		if (HAS_BIT(ses->telopts, TELOPT_FLAG_NAWS))
		{
			send_sb_naws(ses, 0, NULL);
		}
	}

	/*
		we have to reinitialize the signals for sysv machines

	if (signal(SIGWINCH, winch_handler) == BADSIG)
	{
		syserr("signal SIGWINCH");
	}
	*/
}


void abort_handler(int signal)
{
	static char crashed = FALSE;

	if (crashed)
	{
		exit(-1);
	}
	crashed = TRUE;

	restore_terminal();

	clean_screen(gtd->ses);

	dump_stack();

	fflush(NULL);

	exit(-1);

	if (gtd->ses->connect_retry > utime())
	{
		gtd->ses->connect_retry = 0;
	}
	else if (HAS_BIT(gtd->ses->telopts, TELOPT_FLAG_SGA) && !HAS_BIT(gtd->ses->telopts, TELOPT_FLAG_ECHO))
	{
		socket_printf(gtd->ses, 1, "%c", 3);
	}
	else
	{
		do_zap(gtd->ses, "");
	}
}

void suspend_handler(int signal)
{
	printf("\033[r\033[%d;%dH", gtd->ses->rows, 1);

	fflush(stdout);

	restore_terminal();

	kill(0, SIGSTOP);

	dirty_screen(gtd->ses);

	init_terminal();

	tintin_puts(NULL, "#RETURNING BACK TO TINTIN++.");
}

void trap_handler(int signal)
{
	static char crashed = FALSE;

	if (crashed)
	{
		exit(-1);
	}
	crashed = TRUE;

	restore_terminal();

	clean_screen(gtd->ses);

	dump_stack();

	fflush(NULL);

	exit(-1);
}


/****************************************************************************/
/* main() - show title - setup signals - init lists - readcoms - mainloop() */
/****************************************************************************/


int main(int argc, char **argv)
{
	int greeting = TRUE;

	#ifdef SOCKS
		SOCKSinit(argv[0]);
	#endif

	if (signal(SIGTERM, trap_handler) == BADSIG)
	{
		syserr("signal SIGTERM");
	}

	if (signal(SIGSEGV, trap_handler) == BADSIG)
	{
		syserr("signal SIGSEGV");
	}

	if (signal(SIGHUP, trap_handler) == BADSIG)
	{
		syserr("signal SIGHUP");
	}

	if (signal(SIGABRT, abort_handler) == BADSIG)
	{
		syserr("signal SIGTERM");
	}

	if (signal(SIGINT, abort_handler) == BADSIG)
	{
		syserr("signal SIGINT");
	}

	if (signal(SIGTSTP, suspend_handler) == BADSIG)
	{
		syserr("signal SIGSTOP");
	}

	if (signal(SIGPIPE, pipe_handler) == BADSIG)
	{
		syserr("signal SIGPIPE");
	}

	if (signal(SIGWINCH, winch_handler) == BADSIG)
	{
		syserr("signal SIGWINCH");
	}

/*
	if (getenv("HOME") != NULL)
	{
		char filename[256];

		sprintf(filename, "%s/%s", getenv("HOME"), HISTORY_FILE);

		read_history(gts, filename);
	}
*/

	srand48(time(NULL));

	if (argc > 1)
	{
		int c;

		while ((c = getopt(argc, argv, "e: G h r: t: v")) != EOF)
		{
			if (c == 'G')
			{
				greeting = FALSE;
			}
		}

		optind = 1;
	}

	init_tintin(greeting);

	if (argc > 1)
	{
		int c;

		optind = 1;

		while ((c = getopt(argc, argv, "e: G h r: t: v")) != EOF)
		{
			switch (c)
			{
				case 'e':
					gtd->ses = script_driver(gtd->ses, -1, optarg);
					break;

				case 'G':
					break;

				case 'h':
					tintin_printf(NULL, "Usage: %s [OPTION]... [FILE]...", argv[0]);
					tintin_printf(NULL, "");
					tintin_printf(NULL, "  -e  Execute given command.");
					tintin_printf(NULL, "  -G  Don't show the greeting screen.");
					tintin_printf(NULL, "  -h  This help section.");
					tintin_printf(NULL, "  -r  Read given file.");
					tintin_printf(NULL, "  -t  Set given title.");
					tintin_printf(NULL, "  -v  Enable verbose mode.");

					restore_terminal();
					exit(1);
					break;

				case 'r':
					gtd->ses = do_read(gtd->ses, optarg);
					break;

				case 't':
					printf("\033]0;%s\007", optarg);
					break;

				case 'v':
					do_configure(gtd->ses, "{VERBOSE} {ON}");
					break;

				default:
					tintin_printf(NULL, "Unknown option '%c'.", c);
					break;
			}
		}

		if (argv[optind] != NULL)
		{
			gtd->ses = do_read(gtd->ses, argv[optind]);
		}
	}
	check_all_events(gts, 0, 2, "PROGRAM START", CLIENT_NAME, CLIENT_VERSION);
	check_all_events(gts, 0, 2, "SCREEN RESIZE", ntos(gts->cols), ntos(gts->rows));

	mainloop();

	return 0;
}


void init_tintin(int greeting)
{
	int ref, index;

	gts = (struct session *) calloc(1, sizeof(struct session));

	for (index = 0 ; index < LIST_MAX ; index++)
	{
		gts->list[index] = init_list(gts, index, 32);
	}

	gts->name           = strdup("gts");
	gts->group          = strdup("");
	gts->host           = strdup("");
	gts->port           = strdup("");
	gts->cmd_color      = strdup("");
	gts->telopts        = TELOPT_FLAG_ECHO;
	gts->flags          = SES_FLAG_MCCP;
	gts->socket         = 1;
	gts->read_max       = 16384;

	gtd                 = (struct tintin_data *) calloc(1, sizeof(struct tintin_data));

	gtd->ses            = gts;

	gtd->str_hash_size  = sizeof(struct str_hash_data);

	gtd->mccp_len       = 4096;
	gtd->mccp_buf       = (unsigned char *) calloc(1, gtd->mccp_len);

	gtd->mud_output_max = 16384;
	gtd->mud_output_buf = (char *) calloc(1, gtd->mud_output_max);

	gtd->input_off      = 1;

	for (index = 0 ; index < 100 ; index++)
	{
		gtd->vars[index] = strdup("");
		gtd->cmds[index] = strdup("");
	}

	for (ref = 0 ; ref < 26 ; ref++)
	{
		for (index = 0 ; *command_table[index].name != 0 ; index++)
		{
			if (*command_table[index].name == 'a' + ref)
			{
				gtd->command_ref[ref] = index;
				break;
			}
		}
	}

	init_screen_size(gts);

	printf("\033="); // set application keypad mode

	gts->input_level++;

	do_configure(gts, "{AUTO TAB}         {5000}");
	do_configure(gts, "{BUFFER SIZE}     {20000}");
	do_configure(gts, "{COMMAND COLOR}   {<078>}");
	do_configure(gts, "{COMMAND ECHO}       {ON}");
	do_configure(gts, "{CONNECT RETRY}      {15}");
	do_configure(gts, "{HISTORY SIZE}     {1000}");
	do_configure(gts, "{LOG}               {RAW}");
	do_configure(gts, "{PACKET PATCH}     {0.00}");
	do_configure(gts, "{REPEAT CHAR}         {!}");
	do_configure(gts, "{REPEAT ENTER}      {OFF}");
	do_configure(gts, "{SCROLL LOCK}        {ON}");
	do_configure(gts, "{SPEEDWALK}         {OFF}");
	do_configure(gts, "{TINTIN CHAR}         {#}");
	do_configure(gts, "{VERBATIM}          {OFF}");
	do_configure(gts, "{VERBATIM CHAR}      {\\}");
	do_configure(gts, "{VERBOSE}           {OFF}");
	do_configure(gts, "{WORDWRAP}           {ON}");

	gts->input_level--;

	insert_node_list(gts->list[LIST_PATHDIR],  "n",  "s",  "1");
	insert_node_list(gts->list[LIST_PATHDIR],  "e",  "w",  "2");
	insert_node_list(gts->list[LIST_PATHDIR],  "s",  "n",  "4");
	insert_node_list(gts->list[LIST_PATHDIR],  "w",  "e",  "8");
	insert_node_list(gts->list[LIST_PATHDIR],  "u",  "d", "16");
	insert_node_list(gts->list[LIST_PATHDIR],  "d",  "u", "32");

	insert_node_list(gts->list[LIST_PATHDIR], "ne", "sw",  "3");
	insert_node_list(gts->list[LIST_PATHDIR], "nw", "se",  "9");
	insert_node_list(gts->list[LIST_PATHDIR], "se", "nw",  "6");
	insert_node_list(gts->list[LIST_PATHDIR], "sw", "ne", "12");

	init_terminal();

	if (greeting)
	{
		do_advertise(gts, "");

		do_help(gts, "GREETING");
	}
}


void quitmsg(char *message)
{
	struct session *ses;

	SET_BIT(gtd->flags, TINTIN_FLAG_TERMINATE);

	while ((ses = gts->next) != NULL)
	{
		cleanup_session(ses);
	}

	if (gtd->chat)
	{
		close(gtd->chat->fd);
	}

	check_all_events(gts, 0, 0, "PROGRAM TERMINATION");

/*
	if (gtd->history_size)
	{
		char filename[BUFFER_SIZE];

		sprintf(filename, "%s/%s", getenv("HOME"), HISTORY_FILE);

		history_write(gts, filename);
	}
*/
	restore_terminal();

	clean_screen(gts);

	if (message)
	{
		printf("\n%s\n", message);
	}

	printf("\nGoodbye from TinTin++\n\n");

	fflush(NULL);

	exit(0);
}