/*
 * libirc
 *
 * Copyright (C) 2000, Rob M. Flynn <rflynn@blueridge.net>
 *
 * 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
 *
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <time.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <netdb.h>
#include <unistd.h>
#include <arpa/inet.h>
#include "libirc.h"

#define BUF_LEN 2048


void (*irc_im_in)( irc_conn * conn, gchar * nick, gchar * message );
void (*irc_chat_room_in)( irc_conn * conn, gchar * nick, gchar * channel, 
			gchar * message );
void (*irc_chat_room_join)( irc_conn * conn, gchar * channel, gchar * nick, gchar * host);
void (*irc_chat_room_part)( irc_conn * conn, gchar * channel, gchar * nick, gchar * host);
void (*irc_user_quit)( irc_conn *conn, gchar *);
void (*irc_change_user_nick)( irc_conn * conn, gchar * nick, gchar * newnick);
void (*irc_action_in)( irc_conn * conn, gchar * nick, gchar * channel, gchar * message ); 
void (*irc_ctcp_version_reply_in)( irc_conn * conn, gchar * nick, gchar * message );
void (*irc_ctcp_version_request_in) ( irc_conn * conn, gchar * nick );
void (*irc_chat_room_set_window_title) ( irc_conn * conn, gchar * channel, gchar * topic, gchar * nick ); 
void (*irc_send_message) ( irc_conn * conn, gchar * target, gchar * message);
void (*irc_user_join_channel) ( irc_conn * conn, gchar * target );
void irc_callback( irc_conn * conn )
{
	int i = 0;
	char c;
	gchar buff[BUF_LEN];
	gchar **buff2;
	int parsed = 0;

	do
	{
		int status;
		status = recv(conn->fd, &c, 1, 0);
		if( !status )
			exit(1);
		buff[i] = c;
		i++;
	} while( c != '\n' );

	buff[i] = '\0';

	/* Parse QUIT messages */
	if (((strstr(buff, "QUIT")) && (!strstr(buff, "PRIVMSG")) &&
		(!strstr(buff, "NOTICE"))))
	{
		gchar u_host[255];
		gchar u_nick[128];
		gchar u_command[32];
		gchar u_message[2048];
		int j;
	
		for (i = 1, j = 0; buff[i] != '!'; i++, j++)
		{
			u_nick[j] = buff[i];
		}
		u_nick[j] = '\0'; i++;

		for (j = 0; buff[i] != ' '; i++, j++)
		{
			u_host[j] = buff[i];
		}
		u_host[j] = '\0'; i++;

		for (j = 0; buff[i] != ':'; i++, j++)
		{
			u_command[j] = buff[i];
		}
		u_command[j-1] = '\0'; i++;

		strcpy(u_message, buff + i);
		g_strchomp(u_message);
		parsed = 1;
		printf("%s has quit IRC\n", u_nick);
	}	

	/* Parse NICK messages */
        if (((strstr(buff, "NICK")) && (!strstr(buff, "PRIVMSG")) &&
                (!strstr(buff, "NOTICE"))))
	{
		gchar u_host[255];
		gchar u_nick[128];
		gchar u_command[32];
		gchar u_newnick[128];
		int j;

		parsed = 1;
		for (i = 1, j = 0; buff[i] != '!'; j++, i++)
		{
			u_nick[j] = buff[i];
		}
		u_nick[j] = '\0'; i++;

		for (j = 0; buff[i] != ' '; i++, j++)
		{
			u_host[j] = buff[i];
		}
		u_host[j] = '\0'; i++;

		for (j = 0; buff[i] != ' '; i++, j++)
		{
			u_host[j] = buff[i];
		}

		u_host[j] = '\0'; i++;

		while ((buff[i] == ' ') || (buff[i] == ':')) /* safety */
		{
			i++;
		}
		strcpy(u_newnick, buff + i);
		g_strchomp(u_newnick);
		irc_change_user_nick(conn, u_nick, u_newnick);
	}		

	/* Parse TOPIC of channel */

	if (((strstr(buff, "TOPIC #")) && (!strstr(buff, "PRIVMSG")) &&
		(!strstr(buff, "NOTICE"))))
	{
		gchar u_nick[128];
		gchar u_channel[128];
		gchar u_topic[BUF_LEN];
		int j;

		parsed = 1;
		for (j = 0, i = 1; buff[i] != '!'; i++, j++)
		{
			u_nick[j] = buff[i];
		}
		u_nick[j] = '\0'; i++;

		for (j = 0; buff[i] != '#'; i++, j++)
		{
			/* Blah */
		}
		i++;

		for (j = 1; buff[i] != ':'; i++, j++)
		{
			u_channel[j] = buff[i];
		}
		u_channel[0] = '#';
		u_channel[j-1] = '\0'; i++;

		strcpy(u_topic, buff + i);
		g_strchomp(u_topic);

		irc_chat_room_set_window_title(conn, u_channel, u_topic, u_nick);
	}
	
	if (((strstr(buff, " 332 ")) && (!strstr(buff, "PRIVMSG")) &&
		(!strstr(buff, "NOTICE"))))
	{
		gchar u_host[255];
		gchar u_channel[128];
		gchar u_topic[BUF_LEN];
		int j;

		for (j = 0, i = 1; buff[i] != ' '; j++, i++)
		{
			u_host[j] = buff[i];
		}
		u_host[j] = '\0'; i++;

		for (j = 0; buff[i] != '#'; j++, i++)
		{
			/* Junk */
		}
		i++;

		for (j = 1; buff[i] != ':'; j++, i++)
		{
			u_channel[j] = buff[i];
		}			
		u_channel[0] = '#';
		u_channel[j-1] = '\0';
		i++;	

		strcpy(u_topic, buff + i);
		g_strchomp(u_topic);
#ifdef DEBUG
		printf("LIBIRC: The topic of %s is %s\n", u_channel, u_topic);
#endif
		irc_chat_room_set_window_title(conn, u_channel, u_topic, NULL);	
		parsed = 1;	
	}

	
	/* Parse NAME list */
	if (((strstr(buff, " 353 ")) && (!strstr(buff, "PRIVMSG")) &&
		(!strstr(buff, "NOTICE"))))
	{
		gchar u_host[255];
		gchar u_command[32];
		gchar u_channel[128];
		gchar u_names[BUF_LEN];
		int j;

		parsed = 1;
		for (j = 0, i = 0; buff[i] != ' '; j++, i++)
		{
			u_host[j] = buff[i];
		}
		u_host[j] = '\0'; i++;

		for (j = 0; buff[i] != ' '; j++, i++)
		{
			u_command[j] = buff[i];
		}
		u_command[j] = '\0'; i++;

		for (j = 0; buff[i] != '#'; j++, i++)
		{
			/* Just Junk */
		}
		i++;

		for (j = 1; buff[i] != ':'; j++, i++)
		{
			u_channel[j] = buff[i];
		}
		u_channel[0] = '#';
		u_channel[j-1] = '\0'; i++;
	
		while ((buff[i] == ' ') || (buff[i] == ':')) /* Safety */
		{
			i++;
		}

		strcpy(u_names, buff + i);
		g_strchomp(u_names);

		buff2 = g_strsplit(u_names, " ", 0);
		for (i = 0; buff2[i] != NULL; i++)
		{
			if (g_strcasecmp(buff2[i], conn->nick) != 0)
			{
				irc_chat_room_join(conn, u_channel, buff2[i], NULL);
			}
		}	
	}

	/* Parse JOIN messages */
	if (((strstr(buff, "JOIN :#")) || (strstr(buff, "PART #"))) && 
		(!strstr(buff, "PRIVMSG")) && (!strstr(buff, "NOTICE"))) 
	{
		gchar u_nick[128];
		gchar u_host[255];
		gchar u_command[32];
		gchar u_channel[128];
		int j;
		parsed = 1;
	
		for (j = 0, i = 1; buff[i] != '!'; j++, i++)
		{
			u_nick[j] = buff[i];
		}
		u_nick[j] = '\0'; i++;

		for (j = 0; buff[i] != ' '; j++, i++)
		{
			u_host[j] = buff[i];
		}
		u_host[j] = '\0'; i++;

		for (j = 0; (buff[i] != ':') && (buff[i] != '#'); j++, i++)
		{
			u_command[j] = buff[i];
		}
		u_command[j-1]='\0'; i++;
		strcpy(u_channel, buff + i);
		g_strchomp(u_channel);

		if (g_strcasecmp(u_command, "JOIN") == 0)
		{
			irc_chat_room_join(conn, u_channel, u_nick, u_host);
		}
		else
		if (g_strcasecmp(u_command, "PART") == 0)
		{
			irc_chat_room_part(conn, u_channel, u_nick, u_host);
		}
	}

	/* Response to Version Replies */
	if ( (strstr(buff, "NOTICE")) && (strstr(buff, ":\001VERSION")))
	{
		gchar u_nick[128];
		gchar u_host[255];
		gchar u_command[32];
		gchar u_channel[128];
		gchar u_message[BUF_LEN];
		int j;

		for (j = 0, i = 1; buff[i] != '!'; j++, i++)
		{
			u_nick[j] = buff[i];
		}
		u_nick[j] = '\0'; i++;
	
		for (j = 0; buff[i] != ' '; j++, i++)
		{
			u_host[j] = buff[i];
		}
		u_host[j] = '\0'; i++;

		for (j = 0; buff[i] != ' '; j++, i++)
		{
			u_command[j] = buff[i];
		}
		u_command[j] = '\0'; i++;

		for (j = 0; buff[i] != ' '; j++, i++)
		{
			/* This is just my nick */
		}
		i++;
		
		for (j = 0; buff[i] != ' '; j++, i++)
		{
			/* More Junk */
		}
		i++;

		strcpy(u_message, buff + i);
		irc_ctcp_version_reply_in(conn, u_nick, u_message);
	}

	/* Parse Channel and Private Messages and Actions */
	if ( (strstr(buff, "PRIVMSG ")) && (buff[0] == ':'))  
	{
		gchar u_nick[128];
		gchar u_host[255];
		gchar u_command[32];
		gchar u_channel[128];
		gchar u_message[BUF_LEN];
		int j;
		int msgcode = 0;

		parsed = 1;

		for (j = 0, i = 1; buff[i] != '!'; j++, i++)
		{
			u_nick[j] = buff[i];	
		} 	
		u_nick[j] = '\0'; i++;

		for (j = 0; buff[i] != ' '; j++, i++)
		{
			u_host[j] = buff[i];
		}
		u_host[j] = '\0'; i++;

		for (j = 0; buff[i] != ' '; j++, i++)
		{
			u_command[j] = buff[i];
		}
		u_command[j] = '\0'; i++;

		for (j = 0; buff[i] != ':'; j++, i++)
		{
			u_channel[j] = buff[i];
		}
		u_channel[j-1] = '\0'; i++;

		strncpy(u_message, buff + i, 2048);
		g_strchomp(u_message);

		if (strlen(u_message) > 7)
		{
			if (g_strncasecmp(u_message, "\001VERSION", 8) == 0)
			{
				msgcode = 2;	
			}
			if (g_strncasecmp(u_message, "\001ACTION ", 8) == 0)
			{
				strcpy(buff, u_message);
				for (j = 0, i = 8; buff[i] !='\001'; i++, j++)
				{
					u_message[j] = buff[i];
				}
				u_message[j]='\0';
				msgcode = 1;
			}
		}
		
		if (msgcode == 1)
		{
			irc_action_in(conn, u_nick, u_channel, u_message );
		}
		else
		if (msgcode == 2)
		{
			irc_ctcp_version_request_in(conn, u_nick);
		}
		else
		if (u_channel[0] == '#')  /* ChannelMessage */
		{
			irc_chat_room_in(conn, u_nick, u_channel, u_message );
		}
		else    /* Private Message */
		{
			irc_im_in( conn, u_nick, u_message );
		}
		
	}	

	/* Parse Server PING message */
	if (strncmp(buff, "PING :", 6) == 0)
	{
		parsed = 1;
#ifdef DEBUG
		printf("LIBIRC: Detected PING request!!\n");
#endif
		buff2 = g_strsplit(buff, ":", 1);
		g_strchomp(buff2[1]);
#ifdef DEBUG
		printf("LIBIRC: Response should be: PONG :%s\n", buff2[1]);
#endif
		g_snprintf(buff, BUF_LEN, "PONG :%s\n", buff2[1]);
#ifdef DEBUG
		printf("conn->fd = %d  strlen(buff) = %d\n", conn->fd, strlen(buff));	
#endif
		write(conn->fd, buff, strlen(buff)); 
		g_strfreev (buff2);
	}
#ifdef DEBUG
	if (parsed == 0)
	{
		printf("LIBIRC: %s", buff);
	}
#endif
}

irc_conn * irc_connect( gchar *server, gint port )
{
	irc_conn * test;
	struct hostent *host;
	struct sockaddr_in site;
	int i;

	host = gethostbyname(server);
	if (!host) { return NULL; }

	site.sin_family = AF_INET;
	site.sin_addr.s_addr = *(long *)(host->h_addr);
	site.sin_port = htons(port);
	
	i = socket(AF_INET, SOCK_STREAM, 0);
	if (i < 0) { return NULL; }
	
	if (connect(i, (struct sockaddr *)&site, sizeof(site)) < 0)
	{
		return NULL;
	}

	test = g_new0(irc_conn, 1);
	test->fd = i;
	test->port = port;
	test->buff = g_string_new(NULL);
	test->server = (gchar *)g_malloc(strlen(server)+1);
	strncpy(test->server, server, strlen(server));
	return test;
}
	
irc_conn * irc_signon( irc_conn *c, gchar *nick, gchar *passwd )
{
	gchar buff[BUF_LEN];
	int i;

	g_snprintf(buff, BUF_LEN, "NICK %s\nUSER %s localhost %s :libIrc User\n", nick, nick, c->server);
#ifdef DEUBG
	printf("LIBIRC: %s\n", buff);
#endif
	write(c->fd, buff, strlen(buff));

	c->nick = (gchar *)g_malloc(strlen(nick)+1);
	strcpy(c->nick, nick);
}

void irc_signoff ( irc_conn *c, gchar *message )
{
	gchar buff[BUF_LEN];

	if (message != NULL)
	{
		g_snprintf(buff, BUF_LEN, "QUIT %s\n", message);
	}
	else
	{
		g_snprintf(buff, BUF_LEN, "QUIT\n");
	}

#ifdef DEBUG
	printf("LIBIRC: %s\n", buff);
#endif
	write(c->fd, buff, strlen(buff));
	
}

void irc_send_privmsg ( irc_conn *c, gchar *target, gchar *message )
{
	gchar buff[BUF_LEN];

	g_snprintf(buff, BUF_LEN, "PRIVMSG %s :%s\n", target, message );
#ifdef DEBUG
	printf("LIBIRC: %s", buff);
#endif
	write(c->fd, buff, strlen(buff));
}

void irc_chat_room_set_topic ( irc_conn *c, gchar * channel, gchar * topic)
{
	gchar buff[BUF_LEN];

	if (topic == NULL)
	{
		printf("TOPIC IS NULL\n");
		g_snprintf(buff, BUF_LEN, "TOPIC %s\n", channel);
	}
	else
	{
		g_snprintf(buff, BUF_LEN, "TOPIC %s :%s\n", channel, topic);
	}
#ifdef DEBUG
	printf("LIBIRC: %s", buff);
#endif

	write(c->fd, buff, strlen(buff));
}


void irc_send_ctcp_version ( irc_conn *c, gchar * nick )
{
	gchar buff[BUF_LEN];
	
	g_snprintf(buff, BUF_LEN, "PRIVMSG %s :%cVERSION%c\n", nick, '\001', '\001');
 
#ifdef DEBUG
	printf("LIBIRC: %s", buff);
#endif

	write(c->fd, buff, strlen(buff));
}

void irc_send_ctcp_version_reply( irc_conn * c, gchar * nick, gchar * name, gchar * version, gchar * environment )
{
	gchar buff[BUF_LEN];

	g_snprintf(buff, BUF_LEN, "NOTICE %s :%cVERSION %s:%s:%s%c\n", nick, '\001',
			name, version, environment, '\001');
#ifdef DEBUG
	printf("LIBIRC: %s", buff);
#endif
	write(c->fd, buff, strlen(buff));	
}

void irc_send_action ( irc_conn *c, gchar *target, gchar *message )
{
	gchar buff[BUF_LEN];

	g_snprintf(buff, BUF_LEN, "PRIVMSG %s :%cACTION %s%c\n", target,
			'\001', message, '\001');
#ifdef DEBUG
	printf("LIBIRC: %s", buff);
#endif
	write(c->fd, buff, strlen(buff));
}

void irc_part_channel ( irc_conn *c, gchar *channel )
{
	gchar buff[BUF_LEN];
	gchar tempchan[255];

	strcpy(tempchan, channel);
	if (channel[0] != '#')
	{
		g_snprintf(tempchan, 255, "#%s", channel);
	}
	
	g_snprintf(buff, BUF_LEN, "PART %s\n",tempchan);
#ifdef DEBUG
	printf("LIBIRC: %s", buff);
#endif
	write(c->fd, buff, strlen(buff));
}

void irc_join_channel ( irc_conn *c, gchar *channel )
{
	int i;
	gchar buff[BUF_LEN];
	gchar tempchan[128];

	/* Set Up Channel Name Properly */
	strcpy(tempchan, channel);
	if ( tempchan[0] != '#' )
	{
		for (i = strlen(channel) + 1; i--; i > 1)
		{
			tempchan[i] = channel[i-1];
		}	
		tempchan[0] = '#';
		tempchan[strlen(channel)+1]='\0';
	}

	g_snprintf(buff, BUF_LEN, "JOIN %s\n", tempchan);
#ifdef DEBUG
	printf("LIBIRC: %s", buff);
#endif
	write(c->fd, buff, strlen(buff)); 
}

int irc_parse_command( irc_conn * c, gchar * target, gchar * command )
{
	printf("Parsing Command Line: %s\n", command);

	if ((g_strncasecmp(command, "/msg ", 5) == 0) && (strlen(command) > 7 ))
	{
		gchar **buff;

		buff = g_strsplit(command, " ", 2);
		irc_send_message(c, buff[1], buff[2]);
		return 0;
	
	}

	if ((g_strncasecmp(command, "/join #", 7) == 0) && (strlen(command) > 7))
	{
		gchar **buff;

		buff = g_strsplit(command, " ", 1);

		irc_user_join_channel(c, buff[1]);	
		return -1;	
	}

	if ((g_strncasecmp(command, "/me ", 4) == 0) && (strlen(command) > 4))
	{
		gchar **buff;

		buff = g_strsplit(command, " ", 1);
		printf("Target is: %s (%d)\n", target, strlen(target));
		irc_send_action(c, target, buff[1]);
		return 2;		
	}

	if ((g_strncasecmp(command, "/version ", 10) == 0) && (strlen(command) > 10))
	{
	}

	if ((g_strncasecmp(command, "/topic", 6) == 0))
	{
	}

	return 1;
}
