
#include "protocol.h"

extern XMPP_CALL_BACK_F *handleAppNtf;
LIST 					gContacts;
LIST 					gVContacts;
LIST 					groups;

typedef struct
{
  char 		name[32];
  BOOL 		open;
}GR_ITEM;

void ContactListInit( void )
{
/*
	FILE_HANDLE_T    cl_file = FILE_INVALID;
	WCHAR   		 cl_uri[256];
	
	u_mkpath(cl_uri, jabber_elf->dir, L"contact.list", 0);
	cl_file = DL_FsOpenFile( cl_uri, FILE_READ_MODE, 0);
*/
	List_Init(&gContacts, CONTACT_T);
	List_Init(&gVContacts, CONTACT_T *);
	
	ContactListInitGroupsList();
/*
	List_ReadFile( &gContacts, cl_file );
	
	DL_FsCloseFile( cl_file );
*/
}

void ContactListUninit( void )
{ 
	/*FILE_HANDLE_T    	cl_file = FILE_INVALID;
	WCHAR   		 	cl_uri[256];*/
 	int				n, i;
	CONTACT_T *		c;
 
	/*if( xmpp_account.cl_save ) {
		 u_mkpath(cl_uri, jabber_elf->dir, L"contact.list", 0);
		 cl_file = DL_FsOpenFile( cl_uri, FILE_WRITE_MODE, 0);
		
		 List_WriteFile( &gContacts, cl_file );
		 DL_FsCloseFile( cl_file );
	}*/
	
	n = List_GetNum(&gContacts);
	for ( i=0; i<n; i++ ) {
		c = (CONTACT_T *)List_GetItem(&gContacts, i);
			mfree( c->jid );
			mfree( c->nick );
			mfree( c->vcard_nick );
			mfree( c->status_msg );
			mfree( c->xstatus_msg );
			mfree( c->resource );
			ContactListMsgFreeQueue( c );
			List_Clear(&c->history);
	}
	
	List_Clear( &gVContacts );
	List_Clear( &gContacts );

	ContactListKillGroupsList();
}

int ContactListFillRoster(XMLNode* items)
{
	XMLNode *rostEx = items;
	UINT8 	gr_id=0, i=0, j=0;
	char    *name, *jid;
	char    *subs;
	char    group_name[64] = "";
	UINT8	r_subscr;
	dbg("ContactListFillRoster: Enter");
	while(rostEx)
	{
		
		subs = XML_Get_Attr_Value("subscription", rostEx->attr);
		jid = XML_Get_Attr_Value("jid", rostEx->attr);
		
			if(!subs) r_subscr = SUB_NONE;
			else if(!strcmp(subs, "none")) r_subscr = SUB_NONE;
			else if(!strcmp(subs, "both")) r_subscr = SUB_BOTH;
			else if(!strcmp(subs, "to"))   r_subscr = SUB_TO;
			else if(!strcmp(subs, "from")) r_subscr = SUB_FROM;
			else if(!strcmp(subs, "remove")) return ContactListDeleteContact( jid );
			
	
		// Получаем группу контакта, заносим в список групп
		XMLNode *group = XML_Get_Child_Node_By_Name(rostEx, "group");
		
		if(group && strcmp(group->value, "General"))
		{
			UTF8toCP1251(group->value, group_name);
		
			if( !(gr_id = ContactListGetGroupID(group_name)) )
			{
				gr_id = ContactListAddGroup(group_name);
				dbgf("ContactListFillRoster::ContactListAddGroup: group_name = %s, gr_id = %d", group->value, gr_id);
			}
		}
		else gr_id = 0;
		
		name = XML_Get_Attr_Value("name", rostEx->attr);
		if(!name) name = jid;
		
		if( XMPPIsReady() && ContactListFindContact(jid) )
		{
			ContactListChangeParams(jid,
									name,
									r_subscr,
									gr_id);
		} else {
			ContactListAddContact(jid,
								  name,
								  r_subscr,
								  gr_id);
		}	  
		rostEx=rostEx->next;
	}
	
	dbg("ContactListFillRoster: End");
    return RESULT_OK;
}

int ContactListAddContact(char* jid, char* nick, UINT8 subscription, UINT8 group_id)
{
	CONTACT_T 		contact;
	
	dbgf("ContactListAddContact: jid = %s, group_id = %d", jid, group_id);
	
	memclr(&contact, sizeof(CONTACT_T));
	
	
		contact.jid = malloc(strlen(jid) + 1);	if(!contact.jid) return RESULT_FAIL;
		UTF8toCP1251(jid, contact.jid);
			
		contact.nick = malloc(strlen(nick) + 1);	if(!contact.nick) return RESULT_FAIL;
		UTF8toCP1251(nick, contact.nick);
		
		contact.group_id = group_id; 
		contact.status = XMPPS_OFFLINE;
		contact.subscription = subscription;
		List_Init(&contact.history, MSG_T);
	
		
		if( !strchr(jid, '@') ) {
			contact.type = CONTACT_TRANSPORT; // Транспортный агент
			if(!ContactListGetGroupID("Transports")) 
				contact.group_id = ContactListAddGroup("Transports");
			else
				contact.group_id = ContactListGetGroupID("Transports");			
		} else {
			contact.type = CONTACT_DEF;
		}
	
	if ( List_AppendItem(&gContacts, &contact) < 0 )
		return RESULT_FAIL;
		
    return RESULT_OK;
}

int ContactListDeleteContact(const char* jid)
{ 
	int				i, n;
	CONTACT_T *		c;
	if ( jid == NULL )
		return NULL;
	n = List_GetNum(&gContacts);
	for ( i = 0; i < n; i++ ) {
		c = (CONTACT_T *)List_GetItem(&gContacts, i);
		if ( !strcmp(c->jid, jid) ) {
			mfree( c->jid );
			mfree( c->nick );
			mfree( c->vcard_nick );
			mfree( c->status_msg );
			mfree( c->xstatus_msg );
			ContactListMsgFreeQueue( c );
			List_Clear(&c->history);
			return List_DeleteItem( &gContacts, i );
		}	
	}
		
    return -1;
}

int ContactListChangeParams(char* jid, char* nick, UINT8 subscription, UINT8 group_id)
{
	CONTACT_T *		contact = ContactListFindContact(jid);
	if ( contact == NULL )
		return -1;
		
	contact->nick = malloc(strlen(nick) + 1);
	UTF8toCP1251(nick, contact->nick);

	contact->subscription = subscription;
	contact->group_id = group_id;
	
    return RESULT_OK;
}

int ContactListChangeStatus(char *jid, UINT8 status)
{
    CONTACT_T *		contact = ContactListFindContact(jid);
	if ( contact == NULL )
		return -1;
		
	contact->status = status;
	if(status == XMPPS_OFFLINE) contact->xstatus = NULL;
	handleAppNtf( XMPP_NTF__USER_STATUS, jid, &status, status );

    return RESULT_OK;  
}
int ContactListChangeComposingStatus(char *jid, BOOL composing)
{
    CONTACT_T *		contact = ContactListFindContact(jid);
	
	if ( contact == NULL )
		return -1;
	
	contact->composing = composing;
	handleAppNtf( XMPP_NTF__USER_TYPING, jid, NULL, composing );
	
	return RESULT_OK;
}

// Инициализация групп
void ContactListInitGroupsList()
{
	GR_ITEM  g_ex;
	List_Init(&groups, GR_ITEM);

	g_ex.open = false;
	strcpy(g_ex.name, "General");
	
	List_AppendItem(&groups, &g_ex);
}

// Уничтожить список групп
void ContactListKillGroupsList()
{
	List_Clear(&groups); 
}

// Добавляем группу в список
int ContactListAddGroup(char *gr_name)
{
	if( !gr_name )return 0;
	// Создаем структуру
	GR_ITEM g_ex;
	g_ex.open = false;
	strcpy(g_ex.name, gr_name);
	
	// Добавляем структуру в список
	return List_AppendItem(&groups, &g_ex);
}

// Возвращает ID группы с именем gr_name или 0, если такой нет
int ContactListGetGroupID(char *gr_name)
{
	int				i, n;
	GR_ITEM *		group;
	
	if ( gr_name == NULL )
		return NULL;
		
	n = List_GetNum(&groups);
	for ( i=0; i < n; i++ ) {
		group = (GR_ITEM *)List_GetItem(&groups, i);
		if ( !strcmp(group->name, gr_name) )
			return i;
	}

  return 0; // не найдено
}

// Возвращает имя группы с ID = gr_id или NULL, если такой нет
char* ContactListGetGroupNameByID(int gr_id)
{
	GR_ITEM *		group;

	group = (GR_ITEM *)List_GetItem(&groups, gr_id);
	
	if( group )
		return group->name; 
	else
		return NULL; // не найдено
}

//Возвращает количество групп
int ContactListGetGroupCount(void)
{
	return List_GetNum(&groups);
}

int ContactListUpdate( UINT32 params, UINT8 curren_group )
{
	int				n, i;
	CONTACT_T *		contact;
	List_Clear(&gVContacts);

	n = List_GetNum(&gContacts);
	for ( i=0; i<n; i++ ) {
		contact = (CONTACT_T *)List_GetItem(&gContacts, i);

			if ( !params ) {	// Показывать только онлайн контакты 
				if ( ( contact->status != XMPPS_OFFLINE || contact->new_msg ) && contact->group_id == curren_group )
					List_AppendItem(&gVContacts, &contact);
			} else {			// Показывать все контакты 
				if ( contact->group_id == curren_group )
						List_AppendItem(&gVContacts, &contact);
			}
		}
		
	ContactListShort();
	
	return List_GetNum(&gVContacts);
}

int ContactListShort( )
{
	int				n, i, j = 0;
	CONTACT_T *		contact;
	CONTACT_T *		temp;
	
	n = List_GetNum(&gContacts);
	for ( i=0; i<n; i++ ) {
		contact = (CONTACT_T *)List_GetItem(&gContacts, i);
		if ( contact->new_msg ) {
			List_DeleteItem( &gContacts, List_FindItem(&gContacts, contact) );
			List_InsertItem( &gVContacts, contact, j++ );
		
		}
			
	}
	
	return RESULT_OK;
}

int ContactListGetNum()
{
	int				count = List_GetNum(&gContacts);
	
	dbgf("ContactListGetNum: count = %d", count);
	
     return count;
}

CONTACT_T * ContactListGetContact( int index )
{
	CONTACT_T ** contact;
	contact = (CONTACT_T **)List_GetItem(&gVContacts, index);
	if ( contact == NULL )
		return NULL;
	return *contact;
}

// поиск jid'a в контакт листе
CONTACT_T * ContactListFindContact( const char *jid )
{
	int				i, n;
	CONTACT_T *		contact;
	if ( jid == NULL )
		return NULL;
	n = List_GetNum(&gContacts);
	for ( i=0; i<n; i++ ) {
		contact = (CONTACT_T *)List_GetItem(&gContacts, i);
		if ( !strcmp(contact->jid, jid) )
			return contact;
	}
	return NULL;
}

int ContactListGetContactCount(int group_id)
{
	int				i, n, count = 0;
	CONTACT_T *		contact;

	n = List_GetNum(&gContacts);
	for ( i=0; i<n; i++ ) {
		contact = (CONTACT_T *) List_GetItem(&gContacts, i);
		if ( contact->group_id == group_id )
			count++;
	}
	return count;
}
int ContactListGetOnlineContactCount(int group_id)
{
	int				i, n, count = 0;
	CONTACT_T *		contact;

	n = List_GetNum(&gContacts);
	for ( i=0; i<n; i++ ) {
		contact = (CONTACT_T *) List_GetItem(&gContacts, i);
		if ( contact->group_id == group_id && contact->status != XMPPS_OFFLINE )
			count++;
	}
	return count;
	
}

int ContactListGetIncomingMsgCount()
{
	int				i, n, count = 0;
	CONTACT_T *		contact;
	
	n = List_GetNum(&gContacts);
	for ( i=0; i<n; i++ ) {
		contact = (CONTACT_T *) List_GetItem(&gContacts, i);
		if ( contact->new_msg )
			count++;
	}
	return count;
}

int ContactListGetChatCount()
{
	int				i, n, count = 0;
	CONTACT_T *		contact;

	n = List_GetNum(&gContacts);
	for ( i=0; i<n; i++ ) {
		contact = (CONTACT_T *) List_GetItem(&gContacts, i);
		if ( contact->chat )
			count++;
	}
	return count;
}

int ContactListGetNumberOfOnlineUsers()
{
	int				i, n, count = 0;
	CONTACT_T *		contact;

	n = List_GetNum(&gContacts);
	for ( i=0; i<n; i++ ) {
		contact = (CONTACT_T *) List_GetItem(&gContacts, i);
		if ( contact->status != XMPPS_OFFLINE )
			count++;
	}
	return count;
}

int ContactListMsgAdd( CONTACT_T * c, MSG_T * msg )
{
	int 		msg_num;
	
	if ( c == NULL || msg == NULL )
		return -1;

	if ( ContactListMsgGetNum( c ) > MSG_COUNT - 1 )
		List_DeleteItem( &c->history, 0 );		// Удаляем первое сообщение с памяти
	
	msg_num = List_AppendItem( &c->history, msg );
	
	return msg_num;
}

int ContactListMsgFreeQueue( CONTACT_T * c )
{
	int 	i, msg_num;
	MSG_T 	*msgdb;
	
	msg_num = ContactListMsgGetNum( c );
	
	if ( msg_num == 0 )
		return -1;
		
	for( i = 0; i < msg_num; i++ ) {
	
		
		msgdb = ContactListMsgGet( c, i );
		if ( msgdb ) {
			mfree( msgdb->text );
		};
			
	}
	
	List_Clear( &c->history );
	
	return RESULT_OK;
}

int ContactListMsgGetNum( CONTACT_T * c )
{
	int 		msg_num;
	
	if ( c == NULL )
		return -1;
	
	msg_num = List_GetNum(&c->history);
	
	dbgf("ContactListMsgGetNum:  msg_num = %d", msg_num);
	
	return msg_num;
}

MSG_T * ContactListMsgGet( CONTACT_T * c, UINT16 i )
{
	if ( c == NULL )
		return NULL;
		
	return List_GetItem(&c->history, i);
}

// Добавляет в список контактов системное сообщение
// Полезно, когда происходят действия с подпиской
int ContactListAddSystemMessage(char* jid, char status, char* status_msg)
{
	char msg[128];
	
	switch (status)
	{
		case XMPPS_SUBSCRIBE:	strcpy(msg, "Пришел запрос на авторизацию"); break;
		case XMPPS_SUBSCRIBED:	strcpy(msg, "Авторизация дана!"); break;
		case XMPPS_UNSUBSCRIBE:	strcpy(msg, "Авторизация удалена!"); break;
		case XMPPS_UNSUBSCRIBED:strcpy(msg, "В авторизации отказано"); break;
		default: return 0;
	}
	handleAppNtf( XMPP_NTF__INCOMING_MESSAGE_SYSTEM, jid, msg, strlen(msg) );
	
	return 0;
}

// Получить jid из jid/resourse
char*  UtilGetJIDByFullJID(char * full_jid, char jid[] )
{
	int		 i, j;
	
		for (i=strlen(full_jid); i > 0; i--)
			if (full_jid[i] == '/') break;
			
		if( i == 0 ) {
			strcpy(jid, full_jid); 
			return jid;
		}
		
		for (j=0; j < i; j++)
			jid[j] = full_jid[j];
		jid[j] = 0;
    return jid;
}

char* UtilGetResourceByFullJID(char* full_jid)
{
  char* pre_res = strstr(full_jid, "/");
  char* res_name = pre_res==NULL ? NULL : pre_res +1;
  return res_name;
}

// Получить внутренний номер данного типа присутствия по строке с присутсвием
int UtilGetPresenceIndex(char* presence_str)
{
	int		 i;
	
	if( !presence_str ) return NULL;
	
	for( i=0; i < 12; i++ )
		if(!strcmp(presence_str, presences[i]) ) return i;

  return NULL;
}


// Получить внутренний номер данного типа роли/полномочий по строке с присутсвием
int UtilGetAffRoleIndex(char* str)
{
	#define AFFS_CNT 5
	#define ROLS_CNT 4
	const char* jabber_affs[] = {"none", "outcast", "member", "admin", "owner"};
	const char* jabber_rols[] = {"none", "visitor", "participant", "moderator"};

	if( !str ) return NULL;
	
	int i;
	for ( i = 0; i < AFFS_CNT; i++ )
		if(!strcmp(str, jabber_affs[i]))return i;

	
	for ( i = 0; i < ROLS_CNT; i++ )
		if(!strcmp(str, jabber_rols[i]))return i;


  return NULL;
}

char*  UtilGetIcqTransport( void )
{
	int				i, n;
	CONTACT_T *		contact;

	n = List_GetNum(&gContacts);
	for ( i = 0; i < n; i++ ) {
		contact = (CONTACT_T *) List_GetItem(&gContacts, i);
		if ( contact->type == CONTACT_TRANSPORT ) {
			if(strstr( contact->jid, "icq" )){
				return contact->jid;
			}
		}
	}
	return NULL;
}