/*
 * EveryBuddy 
 *
 * Copyright (C) 1999, Torrey Searle <tsearle@uci.edu>
 *
 * 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
 *
 */

/*
 * icq.c
 * ICQ implementation
 */

 
#include <gtk/gtk.h>
#if defined( _WIN32 )
#include "../libicq/libicq.h"
#else
#include "libicq/libicq.h"
#include "libicq/send.h"
#endif

#include "message_parse.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "status.h"
#include "service.h"
#include "icq.h"
#include "libicq/tcp.h"
#include "chat_window.h"
#include "info_window.h"
#include "util.h"
#include "value_pair.h"
#include "dialog.h"
#include "chat_room.h"
#include "globals.h"

#include "pixmaps/icq_online.xpm"
#include "pixmaps/icq_away.xpm"

/*this is an evil hack to deal with the single user nature of libICQ*/
static eb_local_account * icq_user_account = NULL;
static gchar* ICQ_STATUS_STRINGS[] = {"", "(Away)", "(N/A)", "(Occ)", "(DND)", "(Offline)", "(Invis)", "(Chat)"};

GList * icq_buddies = NULL;

/*
 * Now here is just some stuff to deal with ICQ group chat
 */

typedef struct _icq_buff
{
	unsigned long uin;
	char str[2048];
	int len;
	char r;
	char g;
	char b;
	char style;
	char active;
} icq_buff;

typedef struct _icq_info_data
{
    USER_EXT_INFO * ext_info;
    USER_INFO_PTR user_info;
    gchar *away;
} icq_info_data;


GList * icq_chat_messages = NULL;

icq_buff * find_icq_buff( unsigned long uin )
{
	GList * node;
	for( node = icq_chat_messages; node; node = node->next )
	{
		if( ((icq_buff *)(node->data))->uin == uin )
		{
			return node->data;
		}
	}

	return NULL;
}

/*this is to make things look pretty*/

char * icq_to_html( char  * r, char * g, char * b, char * style, char * input, int len )
{
	GString * string = g_string_sized_new( 2048 );
	int i;
	int font_stack = 0;
	char * result;
	assert( len >= 0 );

	if(*style&0x1)
	{
		g_string_append(string, "<B>");
	}
	if(*style&0x2)
	{
		g_string_append(string, "<I>");
	}
	if(*style&0x4)
	{
		g_string_append(string, "<U>");
	}
	g_string_append( string, "<font color=\"#");
	{
		char buff[3];
		g_snprintf(buff, 3, "%02x", *r );
		g_string_append( string, buff);
		g_snprintf(buff, 3, "%02x", *g );
		g_string_append( string, buff);
		g_snprintf(buff, 3, "%02x", *b );
		g_string_append( string, buff);
	}
	g_string_append(string, "\">");
	font_stack++;

	for( i = 0; i < len; i++ )
	{
		if( input[i] == 0x03 || input[i] == 0x04 || input[i] == '\n' )
		{
			continue;
		}
		if( input[i] == 0x11 )
		{
			*style = input[++i];
			i+=3;
			if(*style&0x1)
			{
				g_string_append(string, "<B>");
			}
			if(*style&0x2)
			{
				g_string_append(string, "<I>");
			}
			if(*style&0x4)
			{
				g_string_append(string, "<U>");
			}
		}
		if( input[i] == 0x00 )
		{
			char buff[3];
			g_string_append( string, "<font color=\"#");
			g_snprintf(buff, 3, "%02x", input[++i]);
			*r = input[i];
			g_string_append( string, buff);
			g_snprintf(buff, 3, "%02x", input[++i]);
			*g = input[i];
			g_string_append( string, buff);
			g_snprintf(buff, 3, "%02x", input[++i]);
			*b = input[i];
			g_string_append( string, buff);
			g_string_append(string, "\">");
			i++;
			font_stack++;
			continue;
		}
		if( input[i] == 0x10 )
		{
			short len = input[i] + (((short)input[i+1]) << 8 );
			i += 2 + len + 2;
			continue;
		}
		if( input[i] == 0x11 || input[i] == 0x12 )
		{
			i += 4;
			continue;
		}
		g_string_append_c( string, input[i] );
		

	}
	for( i =0; i < font_stack; i++ )
	{
		g_string_append( string, "</font>" );
	}
	
	result = string->str;
	g_string_free( string, FALSE );
#ifdef DEBUG
	fprintf(stderr, "%s\n", result);
#endif
	return result;
}


/*
 *  this is to help deypher the statuses that you send and recieve
 */
enum
{
	ICQ_ONLINE,
	ICQ_AWAY,
	ICQ_NA,
	ICQ_OCCUPIED,
	ICQ_DND,
	ICQ_OFFLINE,
	ICQ_INVISIBLE,
        ICQ_FREE_CHAT
};
struct icq_account_data
{
	int status;
};

typedef struct icq_local_account_data
{
    gchar password[1024];
    gint timeout_id;
    gint select_id;
} icq_local_account;

gint connection = STATUS_OFFLINE;


CONTACT_PTR getContact( glong uin )
{
	int i;
	for(i=0; i < Num_Contacts; i++)
	{
		if( Contacts[i].uin == uin )
			return &Contacts[i];
	}
	return 0;
}

gint icq_get_current_state(eb_local_account * account );
void icq_logout( eb_local_account * account );
void icq_login( eb_local_account * account );
eb_chat_room * icq_make_chat_room( gchar * name, eb_local_account * account );
void icq_info_update(info_window *iw); 
void icq_info_data_cleanup(info_window *iw);
void icq_get_info( eb_local_account * account_from, eb_account * account_to); 


static void authorize_callback( GtkWidget * widget, gpointer data )
{
	long uin = (long)data;
	int response = (int)gtk_object_get_user_data(GTK_OBJECT(widget));

	if(response)
	{
		Send_AuthMessage(uin);
	}
}


/* the callbacks that get used by libicq  and get 
   triggered on incomming events*/

void EventLogin( void * ignore )
{
#ifdef DEBUG	
    printf("EventLogin\n");
#endif
	set_status=STATUS_ONLINE;
	connection = STATUS_ONLINE;
/*
	gtk_widget_destroy(icq_user_account->status_button);
	icq_user_account->status_button = MakeStatusButton(icq_user_account);
	gtk_widget_show(icq_user_account->status_button);
	gtk_container_add(GTK_CONTAINER(icq_user_account->status_frame),icq_user_account->status_button);
*/
	icq_user_account->connected = 1;
	if(icq_user_account->status_menu)
	{
		gtk_check_menu_item_set_active
		(
			GTK_CHECK_MENU_ITEM
			(
				g_slist_nth(icq_user_account->status_menu, ICQ_ONLINE)->data
			), TRUE
		);

	}
}

#define SRV_FORCE_DISCONNECT 0x0028
#define SRV_MULTI_PACKET   0x0212
#define SRV_BAD_PASS    0x6400
#define SRV_GO_AWAY        0x00F0
#define SRV_WHAT_THE_HELL 0x0064


void EventDisconnect(void*data)
{
#ifdef DEBUG
    printf("EventDisconnect\n");
#endif
	icq_logout(icq_user_account);
	connection = STATUS_OFFLINE;
	set_status=STATUS_OFFLINE;
/*
	gtk_widget_destroy(icq_user_account->status_button);
	icq_user_account->status_button = MakeStatusButton(icq_user_account);
	gtk_widget_show(icq_user_account->status_button);
	gtk_container_add(GTK_CONTAINER(icq_user_account->status_frame),icq_user_account->status_button);
*/
	icq_user_account->connected = 0;
	if(icq_user_account->status_menu)
	{
		gtk_check_menu_item_set_active
		(
			GTK_CHECK_MENU_ITEM
			(
				g_slist_nth(icq_user_account->status_menu, ICQ_OFFLINE)->data
			), TRUE
		);

	}
}

static void EventChatDisconnect(void * data )
{
	unsigned long uin = (unsigned long)data;
	icq_buff * ib =  find_icq_buff( uin );
	eb_account * ea;
	eb_chat_room * ecr = find_chat_room_by_id("ICQ");
	char buffer[20];

	if( ib )
	{
		icq_chat_messages = g_list_remove( icq_chat_messages, ib);
	}

	ea = find_account_by_handle(buffer, ICQ_SERVICE_ID);
	g_snprintf(buffer, 20, "%ld", uin );


	if( ecr )
	{
		eb_chat_room_buddy_leave( ecr, buffer);
	}
}


	


static void EventChatConnect(void * data )
{
	unsigned long uin = (unsigned long)data;
	icq_buff * ib = g_new0(icq_buff, 1);
	char buffer[20];
	eb_account * ea;
	eb_chat_room * ecr = find_chat_room_by_id("ICQ");

	if(!ecr)
	{
		fprintf(stderr, "Chat room not found!!!\n");
		return;
	}

	g_snprintf(buffer, 20, "%ld", uin );
	ea = find_account_by_handle(buffer, ICQ_SERVICE_ID);

#ifdef DEBUG
	fprintf(stderr, "EventChatConnect\n");
	fprintf(stderr, "EventChatConnect %ld\n", uin );
#endif

	ib->uin = uin;

	icq_chat_messages = g_list_append( icq_chat_messages, ib);
#ifdef DEBUG
	fprintf(stderr, "EventChatConnected done\n");
#endif

	if(ea)
	{
		eb_chat_room_buddy_arrive( ecr, ea->account_contact->nick, ea->handle );
	}
	else
	{
		eb_chat_room_buddy_arrive( ecr, buffer, buffer );
	}


}

static void EventChatRead(void*data)
{
	CHAT_DATA_PTR cd = data;
	eb_chat_room * ecr = find_chat_room_by_id("ICQ");
	char buff[20];

	icq_buff * ib =  find_icq_buff(cd->uin);
	eb_account * ea;
	
	
	if(!isgraph(cd->c))
	{
		fprintf(stderr, "Reading Value 0x%02x\n", cd->c);
	}
	else
	{
		fprintf(stderr, "Reading Value '%c'\n", cd->c);
	}
	
	if(!ecr)
	{
		fprintf(stderr, "ICQ: EventChatRead chat room not found!\n");
		return;
	}

	if(!ib)
	{
		fprintf( stderr, "ICQ: EventChatRead chat buffer not found!\n");
		return;
	}
	
	
	g_snprintf( buff, 20, "%d", cd->uin);
	ea = find_account_by_handle(buff, ICQ_SERVICE_ID);

	if( cd->c == '\r' && (ib->active || 
				(ib->len>1 && isprint(ib->str[ib->len-1])) ))
	{
		char * message; 
		
		if( ib->active || ib->len < 47 )
		{
ICQ_MESSAGE_OK:
			message = icq_to_html(&(ib->r), &(ib->g), &(ib->b), &(ib->style), 
					              ib->str, ib->len);

			ib->active = 1;
		}
		else
		{
			/*
			 * Occasionaly libicq messes up and we get the information from
			 * the first packet the client sends after the chat is initiated
			 * if that does end up being the case, we may as well parse
			 * some information out of it while we are filtering it out
			 * this is a hack, and as a result I have added some checks
			 * to make sure we don't overstep our buffer, and if we do
			 * assume that it is not have that beginning packet, and processes
			 * it like any other chat message
			 *
			 * -Torrey
			 */
			
			short name_len = ib->str[8] + (((short)ib->str[9]) << 8 );
			short font_len;
			short adjust_factor = 0;
			short adjust_factor2 = 0;
			int i;
			
			if( 10+name_len+35 > ib->len )
			{
#ifdef DEBUG
				fprintf(stderr, "Message length test 1 faild, skipping\n");
#endif
				goto ICQ_MESSAGE_OK;
			}

#ifdef DEBUG
			fprintf(stderr, "Checkpoints %d %d\n", ib->str[10+name_len+24],
					                            ib->str[10+name_len+26] );
#endif

			for( i = 24; i <= 32; i++ )
			{
				if( ib->str[10+name_len+i] == 4 )
					break;
			}
			adjust_factor = i-24;
					

					
			font_len = ib->str[10+name_len+35+adjust_factor] +
					    (((short)ib->str[10+name_len+36+adjust_factor]) << 8);

			if( 47+name_len+font_len > ib->len )
			{
#ifdef DEBUG
				fprintf(stderr, "Message length test 2 faild, skipping\n");
#endif
				goto ICQ_MESSAGE_OK;
			}


			/*
			ib->r = ib->str[10+name_len+adjust_factor];
			ib->g = ib->str[11+name_len+adjust_factor];
			ib->b = ib->str[12+name_len+adjust_factor];
			*/

			ib->active = 1;

			for(adjust_factor2 = 0; 
				!ib->str[10+name_len+36+font_len+3+adjust_factor+adjust_factor2]
				&& 10+name_len+36+font_len+3+adjust_factor+adjust_factor2 < ib->len;
				adjust_factor2++);
			
			message = icq_to_html(&(ib->r), &(ib->g), &(ib->b), &(ib->style),
					        ib->str+10+name_len+36+font_len+3+adjust_factor+adjust_factor2, 
							ib->len-10-name_len-36-font_len-3-adjust_factor-adjust_factor2);
		}

		if( !ea )
		{
			eb_chat_room_show_message( ecr, buff,  message);
		}
		else
		{
			eb_chat_room_show_message( ecr, ea->account_contact->nick,  
					                   message);
		}

		ib->len = 0;
		g_free( message );
	}
	else if( cd->c == '\b' && (ib->active || ib->len > 47 ) )
	{
			if( ib->len > 0 )
			{
				ib->len--;
			}
	}
	else
	{
		ib->str[ib->len++] = cd->c;
	}
#ifdef DEBUG
	fprintf(stderr, "EventChatRead end\n");
#endif

}

void EventMessage(void*data)
{
	gchar buff[255];
	gchar message[1024];
	eb_account * sender=NULL;
	CLIENT_MESSAGE_PTR c_messg;
	
	c_messg = (CLIENT_MESSAGE_PTR)data;
	
	sprintf( buff, "%d", c_messg->uin);
	sender = find_account_by_handle(buff, ICQ_SERVICE_ID);
	
	if(!sender)
	{
		eb_account * ea = g_new0(eb_account, 1);
		struct icq_account_data * iad = g_new0(struct icq_account_data,1);
		strcpy(ea->handle, buff);
		ea->service_id = ICQ_SERVICE_ID;
		iad->status = STATUS_OFFLINE;
		ea->protocol_account_data = iad;
		
		if(!do_ignore_unknown)
		{
			add_unknown(ea);
                //switch these two lines to make it not automatically display on unknow user
			ICQ_Get_Info( c_messg->uin );
		}
		else
		{
			return;
		}
        //        icq_get_info(find_suitable_local_account(NULL,ICQ_SERVICE_ID),ea); 
		sender = ea;

		g_warning("Unknown user %s", buff);
	}
	if( (c_messg->type & 0x00FF) == URL_MESS )
	{
		g_snprintf(message,1024,"<a href=\"%s\">%s</a><BR>%s", c_messg->url, c_messg->url, c_messg->msg);
	}
	else if( c_messg->type == AWAY_MESS )
	{
	  if(sender->infowindow != NULL) {
             if(sender->infowindow->info_data == NULL)
               sender->infowindow->info_data = malloc(sizeof(icq_info_data));
             if(((icq_info_data* )sender->infowindow->info_data)->away != NULL)
                free(((icq_info_data* )sender->infowindow->info_data)->away); 
             ((icq_info_data* )sender->infowindow->info_data)->away = malloc(strlen(c_messg->msg)+1);
            strcpy(((icq_info_data* )sender->infowindow->info_data)->away,c_messg->msg);
	    icq_info_update(sender->infowindow);  
          }
          if(sender->account_contact->chatwindow != NULL) {
		g_snprintf(message,1024, "User is away: %s", c_messg->msg );
          }
	  else return;
	}
	else if(c_messg->type == USER_ADDED_MESS)
	{
		g_snprintf(message,1024, "I have just added you to my contact list.");
	}
	else if(c_messg->type == MSG_MESS || c_messg->type == MSG_MULTI_MESS )
	{
		g_snprintf(message,1024, "%s", c_messg->msg );
	}
	else if(c_messg->type == CHAT_MESS )
	{
#ifdef DEBUG
		fprintf(stderr, "accepting chat request\n");
#endif
		invite_dialog(icq_user_account, sender->account_contact->nick,
					  "ICQ", (gpointer)c_messg->uin );
		return;
	}
	else if(c_messg->type == FILE_MESS )
	{
		char message[1024];

		g_snprintf(message, 1024, "ICQ user %d would like to\nsend you the file\n%s\ndo you want to accept?", c_messg->uin, c_messg->filename);

		//do_dialog(message, "Incomming ICQ File Request", NULL, NULL);
		return;
	}
	else if(c_messg->type == AUTH_REQ_MESS)
	{
		char * c = strchr( c_messg->msg, '\xFE' );
		char dialog_message[1024];

		*c = '\0';

		c = strchr( c+1, '\xFE' );
		c = strchr( c+1, '\xFE' );
		c = strchr( c+1, '\xFE' );
		c = strchr( c+1, '\xFE' );

		g_snprintf(dialog_message, 1024, "ICQ user %s would like to add you to their contact list.\nReason: %s\nWould you like to authorize them?",
				c_messg->msg, c+1 );

		do_dialog( dialog_message, "Authorize ICQ User", authorize_callback,
					(gpointer)c_messg->uin );

		return;
	}
	else
	{
		g_snprintf(message, 1024, "Unknown packet type %x contents %s", 
				c_messg->type, c_messg->msg );
	}

	eb_parse_incomming_message( icq_user_account,
										   sender,
										   message );
#ifdef DEBUG
	printf("EventMessage\n");
#endif

}

void EventChangeStatus(void * data)
{
	USER_UPDATE_PTR user_update;
	eb_account * ea;
	gchar buff[255];
	user_update = (USER_UPDATE_PTR)data;

	if(!user_update)
	{
		return;
	}
	if((gint)data == SRV_GO_AWAY || (gint)data == SRV_FORCE_DISCONNECT
	    || (gint)data == SRV_MULTI_PACKET )
	{
		icq_logout(icq_user_account);
		icq_login(icq_user_account);
		return;
	}
	if((gint)data == SRV_BAD_PASS || (gint)data == SRV_WHAT_THE_HELL)
	{
		icq_logout(icq_user_account);
		return;
	}
	sprintf(buff, "%d", user_update->uin);
	ea = find_account_by_handle(buff,ICQ_SERVICE_ID);
	
	if(ea)
	{
		int newstat;
		struct icq_account_data * iad = ea->protocol_account_data;
		newstat = (user_update->status+1)%65536-1;
		if (iad->status == STATUS_OFFLINE && newstat != STATUS_OFFLINE)
			buddy_login(ea);
		else if (iad->status != STATUS_OFFLINE && newstat == STATUS_OFFLINE)
			buddy_logoff(ea);
		iad->status = newstat;
		buddy_update_status(ea);
	}
		
	

#ifdef DEBUG
    printf("EventChangeStatus %d %d\n", user_update->uin, user_update->status);
#endif
}

void EventSearchResults( void * ignore )
{
#ifdef DEBUG
    printf("EventSearchResults\n");
#endif
}

void EventInfo(void*data)
{
	gchar buff[255];
        USER_INFO_PTR ui = data;
	eb_local_account * ela;
	eb_account * ea;
	g_snprintf(buff,255, "%d", ui->uin);

        /*Now this is what I call an ugly solution!! */
        ICQ_Get_Away_Message(ui->uin);

	ela = find_local_account_by_handle(buff);
	if(ela && strlen(ui->nick) > 0 )
	{
		strcpy(ela->alias, ui->nick );
	}
	else
	{
		ea = find_account_by_handle(buff,ICQ_SERVICE_ID);
		if( ea && strlen(ui->nick) > 0 )
		{
			if(!strcmp(ea->handle, ea->account_contact->nick))
			{
			 	strcpy(ea->account_contact->nick, ui->nick );
				contact_update_status(ea->account_contact);
				MakeEditContactList();
			}
			/* Since this lookup was just to get the unknown person's
			 * screen name, we just stop here
			 */

			return;
		}
                if( ea )
                {
                  ela = find_suitable_local_account( NULL, ICQ_SERVICE_ID );
 
                  if(ea->infowindow != NULL) {
                    if(ea->infowindow->info_type != ICQ_SERVICE_ID) {
                    /*hmm, I wonder what should really be done here*/
                       return;
                    }
        
                    if(((icq_info_data *)ea->infowindow->info_data)->user_info != NULL)
                        g_free(((icq_info_data *)ea->infowindow->info_data)->user_info);

                   ((icq_info_data *)ea->infowindow->info_data)->user_info = malloc(sizeof(USER_INFO));
                   memcpy( ((icq_info_data *)ea->infowindow->info_data)->user_info, ui, sizeof(USER_INFO));
                   icq_info_update(ea->infowindow);
		}
            }
        }
#ifdef DEBUG
    printf("EventInfo\n");
    printf("%s\n", ui->nick);
#endif
   
	
}

void EventExtInfo( void * data )
{

        USER_EXT_INFO_PTR ui = data;
        gchar buff[255];
        eb_local_account * ela;
        eb_account * ea;
        g_snprintf(buff,255, "%d", ui->uin);
        ela = find_local_account_by_handle(buff);
        if(ela)
        {
          // This is me, I don't think I need to do anything.
        }
        else
        {
                ea = find_account_by_handle(buff,ICQ_SERVICE_ID);
                if( ea )
                {
                  ela = find_suitable_local_account( NULL, ICQ_SERVICE_ID );
 
                  if(ea->infowindow != NULL) 
                  {
                    if(ea->infowindow->info_type != ICQ_SERVICE_ID) {
                    /*hmm, I wonder what should really be done here*/
                       return;
                    }
        
                    if(((icq_info_data *)ea->infowindow->info_data)->ext_info != NULL)
                        g_free(((icq_info_data *)ea->infowindow->info_data)->ext_info);

                    ((icq_info_data *)ea->infowindow->info_data)->ext_info = malloc(sizeof(USER_EXT_INFO));
                    memcpy( ((icq_info_data *)ea->infowindow->info_data)->ext_info, ui, sizeof(USER_EXT_INFO));
                    icq_info_update(ea->infowindow);
                  }
                }
        }

#ifdef DEBUG
    printf("EventExtInfo\n");
#endif
}

void RegisterCallbacks()
{
  ICQ_Register_Callback(EVENT_LOGIN, EventLogin);
  ICQ_Register_Callback(EVENT_DISCONNECT, EventDisconnect);
  ICQ_Register_Callback(EVENT_MESSAGE, EventMessage);
  ICQ_Register_Callback(EVENT_ONLINE, EventChangeStatus);
  ICQ_Register_Callback(EVENT_OFFLINE, EventChangeStatus);
  ICQ_Register_Callback(EVENT_STATUS_UPDATE, EventChangeStatus);
  ICQ_Register_Callback(EVENT_SEARCH_RESULTS, EventSearchResults);
  ICQ_Register_Callback(EVENT_INFO, EventInfo);
  ICQ_Register_Callback(EVENT_EXT_INFO, EventExtInfo);
  ICQ_Register_Callback(EVENT_CHAT_READ, EventChatRead);
  ICQ_Register_Callback(EVENT_CHAT_CONNECT, EventChatConnect);
  ICQ_Register_Callback(EVENT_CHAT_DISCONNECT, EventChatDisconnect);
}


/* here are the timers that will maintain the ICQ connection and poll for events*/

static gint SelectServer(gpointer data)
{
  ICQ_Check_Response(10000);
  return TRUE;
}


static gint KeepAlive(gpointer data)
{
	if(connection==STATUS_ONLINE)
  		ICQ_Keep_Alive();
  return TRUE;
}

void RemoveTimers(icq_local_account * ila )
{
	gtk_timeout_remove(ila->select_id);
	gtk_timeout_remove(ila->timeout_id);
}

void AddTimers(icq_local_account * ila)
{
  		ila->select_id = gtk_timeout_add(100, SelectServer, NULL);
  		ila->timeout_id = gtk_timeout_add((guint32)60000, KeepAlive, NULL);
}

/* the callbacks that get used by EveryBuddy */

void icq_send_chat_room_message( eb_chat_room * room, char * message )
{
	GList * node;
	icq_buff * ib;
	char * buffer = g_new0(char, strlen(message)+3);

	strcpy(buffer, message);
	strcat(buffer, "\r\n");

	for( node = icq_chat_messages; node; node = node->next )
	{
		ib = node->data;
#ifdef DEBUG
		fprintf(stderr, "sending \"%s\" to %ld\n", message, ib->uin );
#endif
		ICQ_Send_Chat(ib->uin, buffer);
	}
	eb_chat_room_show_message( room, room->chat_room_account->alias,  message);
	g_free(buffer);
}
	
	
gboolean icq_query_connected (eb_account * account)
{
	CONTACT_PTR contact = getContact(atol(account->handle));
	struct icq_account_data * iad = account->protocol_account_data;
	assert( eb_services[account->service_id].protocol_id  == ICQ_PROTOCOL_ID);
	
	
	/*if the user is not in the local contact, add it */
	if(!contact && icq_user_account && icq_user_account->connected )
	{
		ICQ_Add_User(atol(account->handle), account->handle);
		contact = getContact(atol(account->handle));
	}
#if 0
	if( connection == STATUS_OFFLINE )
		g_warning("connection == STATUS_OFFLINE\n");
	if( contact->status == STATUS_OFFLINE )
		g_warning("%s status == offline\n", contact->nick );
#endif
	return iad->status != STATUS_OFFLINE 
		   && connection != STATUS_OFFLINE;

}

void icq_add_user( eb_account * account )
{
	assert(eb_services[account->service_id].protocol_id == ICQ_PROTOCOL_ID);
	icq_buddies = g_list_append(icq_buddies, account->handle );
	if(icq_user_account && icq_user_account->connected)
	{
		ICQ_Add_User( atol(account->handle), account->handle );
	}
}

eb_account * icq_new_account( gchar * account )
{
	eb_account * a = g_new0(eb_account, 1);
	struct icq_account_data * iad = g_new(struct icq_account_data, 1);
	
	a->protocol_account_data = iad;
	a->service_id = ICQ_SERVICE_ID;
	strcpy(a->handle, account);
	iad->status = STATUS_OFFLINE;
	
	return a;
}
	
void icq_del_user( eb_account * account )
{
	assert(eb_services[account->service_id].protocol_id == ICQ_PROTOCOL_ID);
	ICQ_Delete_User( atol(account->handle));
}

void icq_login( eb_local_account * account)
{
	GList * node;
	icq_local_account * ila;
	int count = 0;
	ICQ_SetDebug(ICQ_VERB_ERR|ICQ_VERB_WARN);
	RegisterCallbacks();
	ila = (icq_local_account *)account->protocol_local_account_data;
	assert( eb_services[account->service_id].protocol_id == ICQ_PROTOCOL_ID);
	UIN = atol(account->handle);
	strcpy(passwd , ila->password);
	set_status = STATUS_OFFLINE;
	connection = STATUS_ONLINE;
	remote_port = eb_services[account->service_id].port;
	strcpy(server, eb_services[account->service_id].server);
	
	AddTimers(ila);
#ifdef DEBUG
	printf("/n/n %d %s /n/n", UIN, passwd);
#endif
	ICQ_Change_Status(STATUS_ONLINE);
	//account->connected=1;

	for( node = icq_buddies, count = 0; node && count < 100; node = node->next, count++ )
	{
		char * handle = node->data;
		ICQ_Add_User( atol(handle), handle );
	}
}

eb_account * icq_read_config(GList *config, struct contact *contact)
{
	eb_account * ea = g_new0(eb_account, 1 );
	struct icq_account_data * iad =  g_new0(struct icq_account_data,1);
	iad->status = STATUS_OFFLINE;
	

	/*you know, eventually error handling should be put in here*/
	strcpy(ea->handle, value_pair_get_value(config, "NAME"));
	

	ea->service_id = ICQ_SERVICE_ID;
	ea->protocol_account_data = iad;
    ea->account_contact = contact;
	ea->list_item = NULL;
	ea->online = 0;
	ea->status = NULL;
	ea->pix = NULL;
	ea->icon_handler = -1;
	ea->status_handler = -1;
	
	icq_add_user(ea);
	
	return ea;
}
	
	
eb_local_account * icq_read_local_config(GList * pairs)
{
	
	eb_local_account * ela = g_new0(eb_local_account, 1);
	icq_local_account * ila = g_new0(icq_local_account, 1);
	

	/*you know, eventually error handling should be put in here*/
	ela->handle=strdup(value_pair_get_value(pairs, "SCREEN_NAME"));
	strcpy(ela->alias, ela->handle);
	strcpy(ila->password, value_pair_get_value(pairs, "PASSWORD"));
	
	ela->service_id = ICQ_SERVICE_ID;
	ela->protocol_local_account_data = ila;
	
	icq_user_account = ela;
	return ela;
}

	
GList * icq_write_local_config( eb_local_account * account )
{
	icq_local_account * ila = account->protocol_local_account_data;
	GList * list = NULL;
	value_pair * vp;

	vp = g_new0( value_pair, 1 );

	strcpy( vp->key, "SCREEN_NAME");
	strcpy( vp->value, account->handle );

	list = g_list_append( list, vp );

	vp = g_new0( value_pair, 1 );

	strcpy( vp->key, "PASSWORD" );
	strcpy( vp->value, ila->password );

	list = g_list_append( list, vp );
	
	return list;
}

void icq_logout( eb_local_account * account )
{
	GList * l;
	icq_local_account * ila;
	ila = (icq_local_account *)account->protocol_local_account_data;
	assert( eb_services[account->service_id].protocol_id  == ICQ_PROTOCOL_ID);
	ICQ_Change_Status(STATUS_OFFLINE);
	RemoveTimers(ila);
	account->connected = 0;
	
	for (l = icq_buddies; l; l = l->next) {
		eb_account * ea = find_account_by_handle(l->data,ICQ_SERVICE_ID);
		struct icq_account_data * iad = ea->protocol_account_data;
		buddy_logoff(ea);
		iad->status = STATUS_OFFLINE;
	}
}

void icq_send_im( eb_local_account * account_from,
				  eb_account * account_to,
				  gchar *message )
{
	CONTACT_PTR contact = getContact(atol(account_to->handle));
	struct icq_account_data * iad = account_to->protocol_account_data;
	assert( eb_services[account_from->service_id].protocol_id  == ICQ_PROTOCOL_ID );
	assert( eb_services[account_to->service_id].protocol_id  == ICQ_PROTOCOL_ID );
	
	/*if the user is not in the local contact, add it */
	if(!contact)
	{
		ICQ_Add_User(atol(account_to->handle), account_to->handle);
		contact = getContact(atol(account_to->handle));
	}

	/* if the person is away get the away message */

	if( iad->status != ICQ_ONLINE && iad->status != ICQ_OFFLINE 
			&& iad->status != ICQ_INVISIBLE )
	{
		ICQ_Get_Away_Message( contact->uin );
	}

	ICQ_Send_Message(contact->uin, message);
}

GList * icq_get_states()
{
	GList  * states = NULL;
	states = g_list_append(states, "Online");
	states = g_list_append(states, "Away");
	states = g_list_append(states, "Not Available");
	states = g_list_append(states, "Occupied");
	states = g_list_append(states, "Do Not Disturb");
	states = g_list_append(states, "Offline");
	states = g_list_append(states, "Free for Chat");
	return states;
}

gint icq_get_current_state(eb_local_account * account )
{
	assert( eb_services[account->service_id].protocol_id  == ICQ_PROTOCOL_ID );
	switch(set_status)
	{
		case STATUS_OFFLINE:
			return ICQ_OFFLINE;
		case STATUS_NA:
			return ICQ_NA;
		case STATUS_ONLINE:
			return ICQ_ONLINE;
		case STATUS_OCCUPIED:
			return ICQ_OCCUPIED;
		case STATUS_AWAY:
			return ICQ_AWAY;
		case STATUS_DND:
			return ICQ_DND;
		case STATUS_INVISIBLE:
			return ICQ_INVISIBLE;
                case STATUS_FREE_CHAT:
                        return ICQ_FREE_CHAT;
		default:
			printf("error unknown state %d\n", set_status);
			exit(0);
			return 0;
	}
}

void icq_set_current_state(eb_local_account * account, gint state )
{
	assert( eb_services[account->service_id].protocol_id  == ICQ_PROTOCOL_ID );
	switch(state)
	{
		case ICQ_OFFLINE:
			ICQ_Change_Status( STATUS_OFFLINE );
			set_status = STATUS_OFFLINE;
			if(connection != STATUS_OFFLINE)
			{
				icq_logout(account);
			}
			connection = STATUS_OFFLINE;
			account->connected = 0;
			break;
		case ICQ_NA:
			ICQ_Change_Status( STATUS_NA );
			set_status = STATUS_NA;
			connection = STATUS_ONLINE;
			account->connected = 1;
			break;
		case ICQ_ONLINE:
			if(connection == STATUS_OFFLINE )
			{
				icq_login(account);
			}
			ICQ_Change_Status( STATUS_ONLINE );
			set_status = STATUS_ONLINE;
			connection = STATUS_ONLINE;
			account->connected = 1;
			break;
		case ICQ_OCCUPIED:
			ICQ_Change_Status( STATUS_OCCUPIED );
			connection = STATUS_ONLINE;
			account->connected = 1;
			break;
		case ICQ_AWAY:
			ICQ_Change_Status( STATUS_AWAY );
			set_status = STATUS_AWAY;
			connection = STATUS_ONLINE;
			account->connected = 1;
			break;
		case ICQ_DND:
			ICQ_Change_Status( STATUS_DND );
			set_status = STATUS_DND;
			connection = STATUS_ONLINE;
			account->connected = 1;
			break;
		case ICQ_FREE_CHAT:
			ICQ_Change_Status( STATUS_FREE_CHAT );
			set_status = STATUS_FREE_CHAT;
			connection = STATUS_ONLINE;
			account->connected = 1;
			break;
		default:
			ICQ_Change_Status( STATUS_OFFLINE );
			set_status = STATUS_OFFLINE;
			connection = STATUS_OFFLINE;
			account->connected = 0;
			break;
			
	}
}

static gint pixmaps = 0;
static GdkPixmap * icq_pixmap[ICQ_FREE_CHAT+1];
static GdkBitmap * icq_bitmap[ICQ_FREE_CHAT+1];

void icq_init_pixmaps()
{
	gint i;
	gchar ** xpm;
	
	for (i=ICQ_ONLINE; i<=ICQ_FREE_CHAT; i++) {
		switch(i) {
		case ICQ_AWAY:
			xpm = icq_away_xpm;
			break;
		case ICQ_NA:
			xpm = icq_away_xpm;
			break;
		case ICQ_OCCUPIED:
			xpm = icq_away_xpm;
			break;
		case ICQ_DND:
			xpm = icq_away_xpm;
			break;
                case ICQ_FREE_CHAT:
                        xpm = icq_online_xpm;
                        break;
		default:
			xpm = icq_online_xpm;
			break;
		}
		icq_pixmap[i] = gdk_pixmap_create_from_xpm_d(statuswindow->window,
			&icq_bitmap[i], NULL, xpm);
	}
	pixmaps = 1;
}

void icq_get_status_pixmap( eb_account * account, GdkPixmap ** pm, GdkBitmap ** bm )
{
	struct icq_account_data * iad;
	gint icq_status;
	
	if (!pixmaps)
		icq_init_pixmaps();
	
	iad = account->protocol_account_data;
	
	switch(iad->status) {
	case STATUS_OFFLINE:
		icq_status = ICQ_OFFLINE;
		break;
	case STATUS_NA:
		icq_status = ICQ_NA;
		break;
	case STATUS_ONLINE:
		icq_status = ICQ_ONLINE;
		break;
	case STATUS_OCCUPIED:
		icq_status = ICQ_OCCUPIED;
		break;
	case STATUS_AWAY:
		icq_status = ICQ_AWAY;
		break;
	case STATUS_DND:
		icq_status = ICQ_DND;
		break;
	case STATUS_INVISIBLE:
		icq_status = ICQ_INVISIBLE;
		break;
        case STATUS_FREE_CHAT:
                icq_status = ICQ_FREE_CHAT;
                break;
	default:
		icq_status = ICQ_OFFLINE;
	}
	
	*pm = icq_pixmap[icq_status];
	*bm = icq_bitmap[icq_status];
}

gchar * icq_get_status_string( eb_account * account )
{

	struct icq_account_data * iad = account->protocol_account_data;
//	iad->status = iad->status% 65536;
	switch(iad->status)
	{
		/*status offline*/
		case STATUS_OFFLINE:
		//case -1:
			return ICQ_STATUS_STRINGS[ICQ_OFFLINE];
		/*status NA*/
		case STATUS_NA:
		//case 65541:
			return ICQ_STATUS_STRINGS[ICQ_NA];
		/*status Online*/
		case STATUS_ONLINE:
		//case 65536:
			return ICQ_STATUS_STRINGS[ICQ_ONLINE];
		/*status Occupied*/
		case STATUS_OCCUPIED:
		//case 65553:
			return ICQ_STATUS_STRINGS[ICQ_OCCUPIED];
		/*status Away*/
		case STATUS_AWAY:
		//case 65537:
			return ICQ_STATUS_STRINGS[ICQ_AWAY];
		/*status DND*/
		case STATUS_DND:
		//case 65555:
			return ICQ_STATUS_STRINGS[ICQ_DND];
		case STATUS_FREE_CHAT:
			return ICQ_STATUS_STRINGS[ICQ_FREE_CHAT];
		case STATUS_INVISIBLE:
			return ICQ_STATUS_STRINGS[ICQ_INVISIBLE];
		default:
			return ICQ_STATUS_STRINGS[ICQ_OFFLINE];
	}
}

void icq_set_idle(eb_local_account * account, gint idle )
{
	if((idle == 0) && icq_get_current_state(account) == ICQ_AWAY )
	{
		if(account->status_menu)
		{
			gtk_check_menu_item_set_active
			(
				GTK_CHECK_MENU_ITEM
				(
					g_slist_nth(account->status_menu, ICQ_ONLINE)->data
				), TRUE
			);

		}
	}
	else if ((idle >= 600) && (icq_get_current_state(account) == ICQ_ONLINE))
	{
		if(account->status_menu)
		{
			gtk_check_menu_item_set_active
			(
				GTK_CHECK_MENU_ITEM
				(
					g_slist_nth(account->status_menu, ICQ_AWAY)->data
				), TRUE
			);

		}

	}
}

void icq_send_file( eb_local_account * from, eb_account * to, char * file )
{
	int i;
	long remote_uin = atol(to->handle);
	for( i = 0; i < ICQ_MAX_CONTACTS; i++ )
	{
		if( Contacts[i].uin == remote_uin )
		{
			struct in_addr ip;
			char port[8];
			ip.s_addr =htonl(Contacts[i].current_ip);
			g_snprintf( port, 8, "%d", Contacts[i].tcp_port );
			ICQSendFile(inet_ntoa(ip), 
					port, from->handle, 
					file, "Everybuddy file x-fer" );
		}
			
	}
}

void icq_send_invite( eb_local_account * account, eb_chat_room * room,
					   char * user, char * message )
{
	long uin = atol(user);
	ICQ_Request_Chat(uin, message);
}

eb_chat_room * icq_make_chat_room( gchar * name, eb_local_account * account )
{
	eb_chat_room * ecr = find_chat_room_by_id("ICQ");
	
	if( ecr )
	{
		return NULL;
	}

	ecr = g_new0(eb_chat_room, 1);

	strcpy(ecr->room_name, "ICQ");
	strcpy(ecr->id, "ICQ");
	ecr->connected = 0;
	ecr->chat_room_account = account;

	eb_join_chat_room(ecr);
	eb_chat_room_buddy_arrive( ecr, account->alias, account->handle );

	return ecr;
}

void icq_leave_chat_room( eb_chat_room * room )
{
	GList * node;


	for( node = icq_chat_messages; node; node = node->next )
	{
		icq_buff * ib = node->data;
		TCP_TerminateChat(ib->uin);
		g_free( ib );
	}
	g_list_free(icq_chat_messages);
	icq_chat_messages = NULL;
}

	
void icq_accept_invite( eb_local_account * account, void * invitation )
{
	long uin = (long)invitation;
	eb_chat_room * ecr = icq_make_chat_room("", icq_user_account ); 
	if( ecr )
	{
		chat_rooms = g_list_append(chat_rooms, ecr);
	}
	else
	{
		ecr = find_chat_room_by_id("ICQ");
	}
	eb_join_chat_room(ecr);
	ICQ_Accept_Chat(uin);
}

		
void icq_decline_invite( eb_local_account * account, void * invitation )
{
	long uin = (long)invitation;
	ICQ_Refuse_Chat(uin);
}

void icq_set_away( eb_local_account * account, gchar * message)
{
	if (message)
	{
		if(account->status_menu)
		{
			gtk_check_menu_item_set_active
			(
				GTK_CHECK_MENU_ITEM
				(
					g_slist_nth(account->status_menu, ICQ_AWAY)->data
				), TRUE
			);

		}
	} else {
		if(account->status_menu)
		{
			gtk_check_menu_item_set_active
			(
				GTK_CHECK_MENU_ITEM
				(
					g_slist_nth(account->status_menu, ICQ_ONLINE)->data
				), TRUE
			);

		}

	}
}

void icq_get_info( eb_local_account * account_from, eb_account * account_to) {
  long remote_uin = atol(account_to->handle);
  if(!account_to->infowindow){
     account_to->infowindow = eb_info_window_new(account_from, account_to);
     account_to->infowindow->cleanup = icq_info_data_cleanup;
     gtk_widget_show(account_to->infowindow->window);
  }
  if(account_to->infowindow->info_type == -1 || account_to->infowindow->info_data == NULL){
    if(account_to->infowindow->info_data == NULL) {
      account_to->infowindow->info_data = g_new0(icq_info_data,1);
      account_to->infowindow->cleanup = icq_info_data_cleanup;
    }
    account_to->infowindow->info_type = ICQ_SERVICE_ID;
  }

  if(account_to->infowindow->info_type != ICQ_SERVICE_ID) {
  /*hmm, I wonder what should really be done here*/
     return;
  }
  Send_InfoRequest(remote_uin);
  Send_ExtInfoRequest(remote_uin);
  //ICQ_Get_Away_Message(remote_uin);
}

void icq_info_update(info_window *iw) {
     gchar buff[255];
     icq_info_data * iid = (icq_info_data *)iw->info_data;
     clear_info_window(iw);

     gtk_html_append_text(GTK_HTML(iw->info),"ICQ Info:<BR>",0);
     if(iid->away != NULL) {
        gtk_html_append_text(GTK_HTML(iw->info),iid->away,0);
        gtk_html_append_text(GTK_HTML(iw->info),"<BR><HR>",0);
     }
     if(iid->user_info != NULL) {
        USER_INFO_PTR ui = iid->user_info;
        g_snprintf(buff,255, "UIN: %d<br>",ui->uin);
       	gtk_html_append_text(GTK_HTML(iw->info),buff,0);
        g_snprintf(buff,255, "Nickname: %s<br>",ui->nick);
      	gtk_html_append_text(GTK_HTML(iw->info),buff,0);
        g_snprintf(buff,255, "First Name: %s<br>",ui->first);
        gtk_html_append_text(GTK_HTML(iw->info),buff,0);
        g_snprintf(buff,255, "Last Name: %s<br>",ui->last);
        gtk_html_append_text(GTK_HTML(iw->info),buff,0);
        g_snprintf(buff,255, "Email: %s<br>",ui->email);
        gtk_html_append_text(GTK_HTML(iw->info),buff,0);
        if(ui->auth_required) 
        gtk_html_append_text(GTK_HTML(iw->info),"Authorization Required<BR>",0);

     }
     if(iid->ext_info != NULL) {
        USER_EXT_INFO_PTR ui = iid->ext_info;
        if(iid->user_info == NULL) {
           g_snprintf(buff,255, "UIN: %d<br>",ui->uin);
           gtk_html_append_text(GTK_HTML(iw->info),buff,0);
        }
        g_snprintf(buff,255, "City: %s<br>",ui->city);
        gtk_html_append_text(GTK_HTML(iw->info),buff,0);
//        g_snprintf(buff,255, "Country: %s<br>",ui->country);
//        gtk_html_append_text(GTK_HTML(iw->info),buff,0);
        g_snprintf(buff,255, "State: %s<br>",ui->state);
        gtk_html_append_text(GTK_HTML(iw->info),buff,0);
        g_snprintf(buff,255, "Age: %s<br>",ui->age);
        gtk_html_append_text(GTK_HTML(iw->info),buff,0);
        g_snprintf(buff,255, "Sex: %s<br>",ui->sex);
        gtk_html_append_text(GTK_HTML(iw->info),buff,0);
        g_snprintf(buff,255, "Phone: %s<br>",ui->phone);
        gtk_html_append_text(GTK_HTML(iw->info),buff,0);
        g_snprintf(buff,255, "Url: %s<br>",ui->url);
        gtk_html_append_text(GTK_HTML(iw->info),buff,0);
        g_snprintf(buff,255, "About: %s<br>",ui->about);
        gtk_html_append_text(GTK_HTML(iw->info),buff,0);
     }
     gtk_adjustment_set_value(gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(iw->scrollwindow)),0);
}

void icq_info_data_cleanup(info_window *iw){
  icq_info_data *iid = (icq_info_data *)iw->info_data;
  if(iid->ext_info != NULL) free (iid->ext_info);
  if(iid->user_info != NULL) free (iid->user_info);
  if(iid->away != NULL) free (iid->away);
}

struct service_callbacks * icq_query_callbacks()
{
	struct service_callbacks * sc;

	sc = g_new0( struct service_callbacks, 1 );
	sc->query_connected = icq_query_connected;
	sc->login = icq_login;
	sc->logout = icq_logout;
	sc->send_im = icq_send_im;
	sc->read_local_account_config = icq_read_local_config;
	sc->write_local_config = icq_write_local_config;
	sc->read_account_config = icq_read_config;
	sc->get_states = icq_get_states;
	sc->get_current_state = icq_get_current_state;
	sc->set_current_state = icq_set_current_state;
	sc->add_user = icq_add_user;
	sc->del_user = icq_del_user;
	sc->new_account = icq_new_account;
	sc->get_status_string = icq_get_status_string;
	sc->get_status_pixmap = icq_get_status_pixmap;
	sc->set_idle = icq_set_idle;
	sc->set_away = icq_set_away;
	sc->send_file = icq_send_file;
	sc->send_invite = icq_send_invite;
	sc->make_chat_room = icq_make_chat_room;
	sc->send_chat_room_message = icq_send_chat_room_message;
	sc->leave_chat_room = icq_leave_chat_room;
	sc->decline_invite = icq_decline_invite;
	sc->accept_invite = icq_accept_invite;
	sc->get_info = icq_get_info;

	
	return sc;
}


