/*
 *      Code from:              Philipp Meinen
 *      Copyright (C):          2003-2004 Philipp Meinen
 *
 *      Email:                  lancelot@lancelot2k.dyndns.org
 *      Homepage:               http://lancelot2k.dyndns.org
 *      License:                GPL
 *                                                      */

#define _FUNCTIONS_CC_
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/select.h>
#include <fcntl.h>
#include "functions.h"

static char * readoneline(int filedesc);
static void parse_config_params(struct from_config_file * settings, char * oneline);
static char * get_config_file_name(void);

void usage(char * name)
{
	printf("Usage:\n");
	printf("%s [options]\n\n", name);
	printf("Valid options are:\n");
	printf("-i\t\tignore that user root starts the ircclient\n");
	printf("-c\t\twrite a new default configfile\n");
	printf("-t\t\ttest a configfile\n");
	printf("-h or --help\tWhat you are reading now\n");
	printf("-v\t\tprint a version info\n");
}

struct from_config_file * get_config_file_settings(void)
{
	char * directory = get_config_file_name();
	if(directory == NULL) return NULL;
	struct from_config_file * settings;
	settings = new struct from_config_file;
	memset(settings, '\0', sizeof(struct from_config_file));
	set_default_config(settings);
	/* now open the configfile */
	int configfile;
	configfile = open(directory, O_RDONLY|O_CREAT, S_IRUSR|S_IWUSR);
	delete[] directory;
	if(configfile == -1) return settings;;
	char * oneline;
	/* read one line from the config file and parse it */
	do {
		oneline = readoneline(configfile);
		if(oneline == NULL) {
			break;
		}
		parse_config_params(settings, oneline);
		delete[] oneline;
		oneline = NULL;
	} while(1);
	close(configfile);
	return settings;
}

void create_default_config(void)
{
	char * directory = get_config_file_name();
	printf("Writing a new config-file to %s\n", directory);
	int configfile;
	configfile = open(directory, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR);
	delete[] directory;
        if(configfile == -1) {
                printf("error while opening the config-file\n");
                return;
        }
	write(configfile, "nickname=\n", 10);
	write(configfile, "realname=", 9);
	write(configfile, DEFAULT_REALNAME, (sizeof DEFAULT_REALNAME)-1);
	write(configfile, "\nserver=", 8);
	write(configfile, DEFAULT_SERVERNAME, (sizeof DEFAULT_SERVERNAME)-1);
	write(configfile, "\nport=", 6);
	write(configfile, DEFAULT_PORT_STR, (sizeof DEFAULT_PORT_STR)-1);
	write(configfile, "\nmodes=", 7);
	write(configfile, DEFAULT_MODES, (sizeof DEFAULT_MODES)-1);
	write(configfile, "\nautoconnect=", 13);
	write(configfile, DEFAULT_AUTOCONNECT_STR, (sizeof DEFAULT_AUTOCONNECT_STR)-1);
	write(configfile, "\nautoreconnect=", 15);
	write(configfile, DEFAULT_AUTORECONNECT_STR, (sizeof DEFAULT_AUTORECONNECT_STR)-1);
	write(configfile, "\ncommand=/join #netwalker", 25);
	close(configfile);
	return;
}

char * readoneline(int filedesc)
{
	int num = 0;
	char * buf = new char[READ_BUFLEN];
	buf[0] = '\0';
	char onechar[2];
	onechar[1] = '\0';
	do {
		num = read(filedesc, onechar, 1);
		if(num < 1) {
			/* end of file or error */
			if(strlen(buf) > 0) {
				return buf;
			} else {
				/* when the end of the file was found and there is no buffer
				 * to return we will return NULL */
				delete[] buf;
				return NULL;
			}
		} else {
			/* not end of file */
			if(onechar[0] == '\n') {
				/* we have found the end of the line */
				return buf;
			}
			int len = strlen(buf);
			if(len < READ_BUFLEN-1) {
				/* there is enough space in the buffer */
				strncat(buf, onechar, (READ_BUFLEN-len-1));
			}
			/* if one line has more then READ_BUFLEN characters:
			 * they simply wont be added to this buffer */
		}
	} while(1);
	return NULL;
}

void parse_config_params(struct from_config_file * settings, char * oneline)
{
	int len = strlen(oneline);
	if(len >= 6) {
		if(strncmp(oneline, "port=", 5) == 0) {
			settings->port = atoi(oneline+5);
			if((settings->port < 1) || (settings->port > 65535)) settings->port = 6667;
				else settings->port = DEFAULT_PORT_NUM;
			return;
		}
	}
	if(len >= 7) {
		if(strncmp(oneline, "modes=", 6) == 0) {
			strncpy(settings->modes, oneline+6, MODE_TO_SET_LEN-1);
			return;
		}
	}
	if(len >= 8) {
		if(strncmp(oneline, "server=", 7) == 0) {
			strncpy(settings->servername, oneline+7, SERVER_NAME_LEN-1);
			return;
		}
	}
	if(len >= 9) {
		if(strncmp(oneline, "command=", 8) == 0) {
			if(settings->command_on_startup_num < COMMAND_MAX) {
				strncpy(settings->command_on_startup[settings->command_on_startup_num], oneline+8, COMMAND_LEN-1);
				settings->command_on_startup_num++;
			}
			return;
		}
	}
	if(len >= 10) {
		if(strncmp(oneline, "nickname=", 9) == 0) {
			strncpy(settings->nickname, oneline+9, NICKNAME_LEN-1);
			return;
		} else if(strncmp(oneline, "realname=", 9) == 0) {
			oneline += 9;
			strncpy(settings->realname, oneline, REALNAME_LEN-1);
			oneline -= 9;
			return;
		}
	}
	if(len >= 13) {
		if(strncmp(oneline, "autoconnect=", 12) == 0) {
			string_toupper(oneline+12);
			if(strncmp(oneline+12, "YES", 3) == 0 || strncmp(oneline+12, "1", 1) == 0) {
				settings->autoconnect = 1;
			} else settings->autoconnect = 0;
		}
	}
	if(len >= 15) {
		if(strncmp(oneline, "leave_message=", 14) == 0) {
			strncpy(settings->part_quit_msg, oneline+14, PART_QUIT_MSG_LEN-1);
		} else if(strncmp(oneline, "autoreconnect=", 14) == 0) {
			string_toupper(oneline+14);
			if(strncmp(oneline+14, "YES", 3) == 0 || strncmp(oneline+14, "1", 1) == 0) {
				settings->autoreconnect = 1;
			} else settings->autoreconnect = 0;
		}
	}
	return;
}

char * get_config_file_name()
{
	/* find the config-file */
	char * homedir = getenv("HOME");
	if(homedir == NULL){
		printf("error while reading your home-directory");
		return NULL;
	}
	int dirlen = strlen(homedir) + strlen(FILES_CONFIG_FILE) + 1;
	char * directory = new char[dirlen];
	snprintf(directory, dirlen, "%s%s", homedir, FILES_CONFIG_FILE);
	return directory;
}

//int write_config_file(struct from_config_file * settings)
//{
//	char * directory = get_config_file_name();
//        int configfile, i;
//        configfile = open(directory, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR);
//        delete[] directory;
//        if(configfile == -1) {
//                return 0;
//        }
//	char writebuf[READ_BUFLEN];
//	
//	snprintf(writebuf, READ_BUFLEN-1, "nickname=%s\n", settings->nickname);
//	if(write(configfile, writebuf, strlen(writebuf)) == -1) {
//        	close(configfile);
//		return 0;
//        }
//	/* do i have to check the write() function every time? :/ */
//	snprintf(writebuf, READ_BUFLEN-1, "realname=%s\n", settings->realname);
//	write(configfile, writebuf, strlen(writebuf));
//	
//	snprintf(writebuf, READ_BUFLEN-1, "server=%s\n", settings->servername);
//	write(configfile, writebuf, strlen(writebuf));
//	
//	snprintf(writebuf, READ_BUFLEN-1, "port=%d\n", settings->port);
//	write(configfile, writebuf, strlen(writebuf));
//
//	for(i = 0; i < settings->command_on_startup_num; i++)
//	{
//		snprintf(writebuf, READ_BUFLEN-1, "command=%s\n", settings->command_on_startup[i]);
//		write(configfile, writebuf, strlen(writebuf));
//	}
//	
//	close(configfile);	
//	return 1;
//}

void set_default_config(struct from_config_file * settings)
{
	/* set some default parameter */
	char * username = getenv("USER");
	if(username != NULL) {
		if(username[0] != '\0') {
			strncpy(settings->nickname, username, NICKNAME_LEN-1);
		}
	}
	if(settings->nickname[0] == '\0') {
		strncpy(settings->nickname, DEFAULT_NICKNAME, NICKNAME_LEN-1);
 	}
        strncpy(settings->realname, DEFAULT_REALNAME, REALNAME_LEN-1);
	strncpy(settings->servername, DEFAULT_SERVERNAME, SERVER_NAME_LEN-1);
	strncpy(settings->part_quit_msg, DEFAULT_PART_QUIT_MSG, PART_QUIT_MSG_LEN-1);
	strncpy(settings->modes, DEFAULT_MODES, MODE_TO_SET_LEN-1);
	settings->command_on_startup_num = 0;
	settings->port                   = DEFAULT_PORT_NUM;
	settings->autoconnect            = DEFAULT_AUTOCONNECT;
	settings->autoreconnect          = DEFAULT_AUTORECONNECT;
	return;
}

int num_lines(int text_len, int screen_len)
{
	if(text_len > screen_len) {
		/* text is longer than only 1 line */
		int len = 1;
		do {
			text_len -= screen_len;
			len++;
		} while(text_len > screen_len);
		return len;
	} else { 
		/* text is only 1 line long */
		return 1;
	}
}

int position_of_num_char(char * string, char search_char, int count)
{
	int char_count = 0;
	if(count < 1) return -1;
	char *x = string;
	int position = 0;
	while(*x) {
		if(*x == search_char) {
			char_count++;
			if(count == char_count) return position;
		}
		x++;
		position++;
	}
	return -1;
}

void test_config_file(void)
{
	char * directory = get_config_file_name();
	printf("Reading settings from config-file %s\n\n", directory);
	int i;
	struct from_config_file * settings = NULL;
	settings = get_config_file_settings();
	set_default_config(settings);
	if(settings == NULL) return;
	printf("The following options will be taken if netwalker irc-client is getting started:\n\n");
	printf("Nickname:\t\t\t\t%s\n", settings->nickname);
	printf("Realname:\t\t\t\t%s\n", settings->realname);
	printf("Server:\t\t\t\t\t%s:%i\n", settings->servername, settings->port);
	printf("Modes:\t\t\t\t\t%s\n", settings->modes);
	printf("Leave Message:\t\t\t\t%s\n", settings->part_quit_msg);
	for(i = 0; i < settings->command_on_startup_num; i++)
		printf("%i. Command to send after connect:\t%s\n", (i+1), settings->command_on_startup[i]);
	return;
}

void print_version(void)
{
	printf("This is the Netwalker irc-client version: %s\n", CLIENT_VERSION);
	return;
}

int search_next_num_char(char * string, char search_char, int count, int startposition)
{
	int position = position_of_num_char(string+startposition, search_char, count);
	if(position == -1) return -1;
	return (position + startposition);
}

int count_chars_from_pos_to_pos(char * string, char search_char, int startposition, int endposition)
{
	int len = (signed)strlen(string);
	if((endposition < 0) || (startposition < 0) || (startposition > len-2) || (endposition > len-2)) return -1;
	char *x = string+startposition;
	int count = 0;
	if(endposition == 0) {
		/* search the hole string */
		while(*x) {
			if(*x == search_char) count++;
			x++;
		}
	} else {
		/* search only a part of the string */
		int to_scan = (endposition - startposition) + 1;
		while(to_scan > 0) {
			if(*x == search_char) count++;
			to_scan--;
			x++;
		}
	}
	return count;
}

void string_tolower(char * string)
{
	char * x = string;
	while(*x) {
		*x = tolower(*x);
		x++;
	}
	return;
}

void string_toupper(char * string)
{
	char * x = string;
	while(*x) {
		*x = toupper(*x);
		x++;
	}
	return;
}

#include <ctime>
int CheckForInputData(void)
{
	fd_set read_set;
	fd_set write_set;
	struct timeval timeout;
	FD_ZERO(&read_set);
	FD_ZERO(&write_set);
	FD_SET(0, &read_set); /* add stdin to the input sets */
	if(IO_INPUT_WAITLEN < 1) {
		/* default timeout: 1/2 second */
		timeout.tv_sec = (long)0;
		timeout.tv_usec = (long)500000;
	} else {
		timeout.tv_sec = (long)IO_INPUT_WAITLEN / 1000000;
		timeout.tv_usec = (long)IO_INPUT_WAITLEN % 1000000;
	}
	/* it is not very dangerous if we get an error in the select function
	 * reaseon: every time when we get a SIGWINCH there is an "error"
	 * and every time we switch our programm from back to foreground there is an error */
	int fd_network = Classes.Connection->GetNetworkSocket();
	if(fd_network == -1) {
		/* we only have to look for stdin data */
		int ret = select(1, &read_set, (fd_set *)NULL, (fd_set *)NULL, &timeout);
		if(ret == -1) return -2;
		if(ret != 0) {
			if(FD_ISSET(0, &read_set)) {
				/* stdin data ready*/
				return 1;
			} else return -1; /* error */
		}
	} else {
		int status = Classes.Connection->GetConnectStatus();
		int ret;
		if(status == STATUS_SYN_SENT) {
			/* only watch for stdin and write on the network */
			FD_SET(fd_network, &write_set); /* add the fd for the network data to the input set to check for write */
			ret = select((fd_network+1), &read_set, &write_set, (fd_set *)NULL, &timeout);
		} else {
			FD_SET(fd_network, &read_set); /* add the fd for the network data to the input sets */
			ret = select((fd_network+1), &read_set, (fd_set *)NULL, (fd_set *)NULL, &timeout);
		}
		if(ret == -1) return -2;
		if(ret > 0) {
			if(status == STATUS_SYN_SENT) {
				if(FD_ISSET(fd_network, &write_set)) {
					Classes.Connection->SetConnectStatus(STATUS_CONNECTED);
					/* connection SEEMS to be established!                                      *
					 * it is also possible, that the connection is refused, aborted or reseted! *
					 * but i check this in Server::WriteMessageToServer() !                     */
				}
				return 0;
			} else {
				if(FD_ISSET(fd_network, &read_set)) {
					/* network data ready */
					return 2;
				}
			}
			if(FD_ISSET(0, &read_set)) {
				/* stdin data ready*/
				return 1;
			}
			return -1; /* error */
		}
	}
	return 0; /* no data */
}

void make_first_char_away(char * string)
{
	if(string[0] == '\0') return;
	char *x = string;
	x++;
	while(*x) {
		*(x-1) = *x;
		x++;
	}
	*(x-1) = '\0';
	return;
}

void Bring_away_special_chars(char * string)
{
	/* there are a lot of chars < 32 which have to be cut away
	 * we dont bring away this chars because:
	 * 0   --->   we cant make it disapper :p
	 * 1   --->   action (/me or ctcp)
	 * 2   --->   bold text
	 * 3   --->   colored text
	 * 15  --->   format reset control char
	 * 31  --->   underlined text*/
#define num 25
	if(string == NULL) return;
	if(string[0] == '\0') return;
	unsigned int chars_to_test_for[num] = { 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 };
	char *x = string;
	for(int i = 0; i < num; i++) {
		while(*x) {
			if((unsigned int)*x == chars_to_test_for[i]) make_first_char_away(x);
			x++;
		}
	}
#undef num
}

int does_match(char * string, char * pattern)
{
        if(string == NULL || pattern == NULL) return 0;
        if(string[0] == '\0' || pattern[0] == '\0') return 0;
        int len = strlen(pattern);
        int match_to_char = 0;
        int startpos = 0;
        int pos = 0;
        while(*(string+pos)) {
                if(*(string+pos) == pattern[match_to_char]) {
                        if(!match_to_char)
                                startpos = pos;
                        match_to_char++;
                } else {
                        if(match_to_char) {
                                pos = startpos;
                        }
                        match_to_char = 0;
                }
                if(match_to_char == len) return 1;
                pos++;
        }
        return 0;
}

int does_match_case_insensitive(char * string, char * pattern)
{
        if(string == NULL || pattern == NULL) return 0;
        if(string[0] == '\0' || pattern[0] == '\0') return 0;
	int string_len = strlen(string);
	int pattern_len = strlen(pattern);
	char tmp_string[string_len+1];
	char tmp_pattern[pattern_len+1];
	memcpy(tmp_string, string, string_len+1);
	memcpy(tmp_pattern, pattern, pattern_len+1);
	string_tolower(tmp_string);
	string_tolower(tmp_pattern);
	return does_match(tmp_string, tmp_pattern);
}

int have_to_call_connect(void)
{
	int status = Classes.Connection->GetConnectStatus();
	if(Classes.Connection->GetDoConnect()) return 2; /* we were forced to connect */
	if((status >= STATUS_RESOLV_IN_PROGRESS) && (status < STATUS_SYN_SENT)) {
		/* we are in the connect process but this is not finished, so call this function */
		return 2;
	}
	int m_reconnect = Classes.Connection->GetMaybeReconnect();
	if(Classes.IrcUser->GetAutoConnect() && status == STATUS_NOTHING && !m_reconnect) return 1;
	if(Classes.IrcUser->GetAutoReconnect() && status == STATUS_NOTHING && m_reconnect) return 1;
	/* we dont have to call the connect() function */
	return 0;
}

void help_me(char * user_input)
{
	/* each line of the help-index file has the followin format:
	 * command:helpfile
	 * */
	int had_helpfile = 0;
	/* it is not allowed to add a doublepoint in the searchstring! */
	int display_help_help = 0;
	char help_help[] = "help";
	if(!user_input) {
		user_input = help_help;
		display_help_help = 1;
	} else if(user_input[0] == '\0') {
		user_input = help_help;
		display_help_help = 1;
	}
	int active_channel = Classes.IrcScreen->GetActiveChannel();
	if(position_of_num_char(user_input, ':', 1) != -1) {
		Classes.MsgDB->SetIrcChatMessage(active_channel, DISPLAY_ERROR, "your searchstring contains a doublepoint!");
		return;
	}
	int help_index_file =  open(FILES_HELP_DIRECTORY FILES_HELP_INDEX_FILE, O_RDONLY);
	if(help_index_file == -1) {
		Classes.MsgDB->SetIrcChatMessage(active_channel, DISPLAY_ERROR, "Error while opening the index file for the help!");
		return;
	}
	char * oneline = NULL;
	int dblp_pos = 0;
	int len = 0;
	char mr_helper[HELP_BUF_LEN];
	if(display_help_help)
			Classes.MsgDB->SetIrcChatMessage(active_channel, DISPLAY_HELP, "Help is available for this commands:");
	do {
		if(oneline) {
			delete[] oneline;
			oneline = NULL;
		}
		oneline = readoneline(help_index_file);
		if(!oneline) break;
		len = strlen(oneline);
		dblp_pos = position_of_num_char(oneline, ':', 1);
		if(len < 3 || dblp_pos == -1 || dblp_pos == 0 || dblp_pos == len-1) continue;
		oneline[dblp_pos] = '\0';
		if(display_help_help) {
			snprintf(mr_helper, HELP_BUF_LEN, "    %s", oneline);
			Classes.MsgDB->SetIrcChatMessage(active_channel, DISPLAY_HELP, mr_helper);
			continue;
		}
		if(!does_match_case_insensitive(oneline, user_input)) continue;
		/* the user input does match with this command
		 * now we have to echo the output of the specified file */
		had_helpfile = 1;
		char * help_file = oneline+dblp_pos+1;
		snprintf(mr_helper, HELP_BUF_LEN, "%s%s", FILES_HELP_DIRECTORY, help_file);
		int help_file_fd = open(mr_helper, O_RDONLY);
		if(help_file_fd == -1) {
			Classes.MsgDB->SetIrcChatMessage(active_channel, DISPLAY_ERROR, "Error while opening a help-file!");
			return;
		}
		char * help_string = NULL;
		do {
			if(help_string) {
				delete[] help_string;
				help_string = NULL;
			}
			help_string = readoneline(help_file_fd);
			if(!help_string) break; /* end of file reached */
			Classes.MsgDB->SetIrcChatMessage(active_channel, DISPLAY_HELP, help_string);
		} while(1);
		if(help_string) {
			delete[] help_string;
			help_string = NULL;
		}
	} while(1);
	if(oneline) delete[] oneline;
	if(!had_helpfile && !display_help_help) {
		Classes.MsgDB->SetIrcChatMessage(active_channel, DISPLAY_HELP, "No helpfile for your search found -> maybe you should use \"/help help\"");
	}
	close(help_index_file);
	return;
}

void * nameresolv_wrapper(void * function)
{
	class Server * to_wrap = (class Server *)function;
	to_wrap->GetHostname();
	return NULL;
}

int text_strlen(char * string)
{
	if(string == NULL) return 0;
	char * x = string;
	int num = 0;
	while(*x) {
		switch(*x) {
			case FORMAT_BOLD_NUM:
				break;
			case FORMAT_COLOR_NUM:
				break;
			case FORMAT_UNDERLINE_NUM:
				break;
			case FORMAT_RESET_NUM:
				break;
			default:
				num++;
				break;
		}
		x++;
	}
	return num;
}

#undef _FUNCTIONS_CC_
