/**
 * @file user.c User functions
 *
 * purple
 *
 * Purple is the legal property of its developers, whose names are too numerous
 * to list here.  Please refer to the COPYRIGHT file distributed with this
 * source distribution.
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 */

#include "internal.h"
#include "debug.h"
#include "util.h"

#include "user.h"
#include "slp.h"

static void free_user_endpoint(MsnUserEndpoint *data)
{
	g_free(data->id);
	g_free(data->name);
	g_free(data);
}

/*new a user object*/
MsnUser *
msn_user_new(MsnUserList *userlist, const char *passport,
			 const char *friendly_name)
{
	MsnUser *user;

	user = g_new0(MsnUser, 1);

	user->userlist = userlist;

	msn_user_set_passport(user, passport);
	msn_user_set_friendly_name(user, friendly_name);

	return msn_user_ref(user);
}

/*destroy a user object*/
static void
msn_user_destroy(MsnUser *user)
{
	while (user->endpoints != NULL) {
		free_user_endpoint(user->endpoints->data);
		user->endpoints = g_slist_delete_link(user->endpoints, user->endpoints);
	}

	if (user->clientcaps != NULL)
		g_hash_table_destroy(user->clientcaps);

	if (user->group_ids != NULL)
	{
		GList *l;
		for (l = user->group_ids; l != NULL; l = l->next)
		{
			g_free(l->data);
		}
		g_list_free(user->group_ids);
	}

	if (user->msnobj != NULL)
		msn_object_destroy(user->msnobj, FALSE);

	g_free(user->passport);
	g_free(user->friendly_name);
	g_free(user->uid);
	if (user->extinfo) {
		g_free(user->extinfo->media_album);
		g_free(user->extinfo->media_artist);
		g_free(user->extinfo->media_title);
		g_free(user->extinfo->phone_home);
		g_free(user->extinfo->phone_mobile);
		g_free(user->extinfo->phone_work);
		g_free(user->extinfo);
	}
	g_free(user->statusline);
	g_free(user->invite_message);

	g_free(user);
}

MsnUser *
msn_user_ref(MsnUser *user)
{
	g_return_val_if_fail(user != NULL, NULL);

	user->refcount++;

	return user;
}

void
msn_user_unref(MsnUser *user)
{
	g_return_if_fail(user != NULL);

	user->refcount--;

	if(user->refcount == 0)
		msn_user_destroy(user);
}

void
msn_user_update(MsnUser *user)
{
	PurpleAccount *account;
	gboolean offline;

	g_return_if_fail(user != NULL);

	account = user->userlist->session->account;

	offline = (user->status == NULL);

	if (!offline) {
		purple_prpl_got_user_status(account, user->passport, user->status,
				"message", user->statusline, NULL);
	} else {
		if (user->mobile) {
			purple_prpl_got_user_status(account, user->passport, "mobile", NULL);
			purple_prpl_got_user_status(account, user->passport, "available", NULL);
		} else {
			purple_prpl_got_user_status(account, user->passport, "offline", NULL);
		}
	}

	if (!offline || !user->mobile) {
		purple_prpl_got_user_status_deactive(account, user->passport, "mobile");
	}

	if (!offline && user->extinfo && user->extinfo->media_type != CURRENT_MEDIA_UNKNOWN) {
		if (user->extinfo->media_type == CURRENT_MEDIA_MUSIC) {
			purple_prpl_got_user_status(account, user->passport, "tune",
			                            PURPLE_TUNE_ARTIST, user->extinfo->media_artist,
			                            PURPLE_TUNE_ALBUM, user->extinfo->media_album,
			                            PURPLE_TUNE_TITLE, user->extinfo->media_title,
			                            NULL);
		} else if (user->extinfo->media_type == CURRENT_MEDIA_GAMES) {
			purple_prpl_got_user_status(account, user->passport, "tune",
			                            "game", user->extinfo->media_title,
			                            NULL);
		} else if (user->extinfo->media_type == CURRENT_MEDIA_OFFICE) {
			purple_prpl_got_user_status(account, user->passport, "tune",
			                            "office", user->extinfo->media_title,
			                            NULL);
		} else {
			purple_debug_warning("msn", "Got CurrentMedia with unknown type %d.\n",
			                     user->extinfo->media_type);
		}
	} else {
		purple_prpl_got_user_status_deactive(account, user->passport, "tune");
	}

	if (user->idle)
		purple_prpl_got_user_idle(account, user->passport, TRUE, -1);
	else
		purple_prpl_got_user_idle(account, user->passport, FALSE, 0);
}

void
msn_user_set_state(MsnUser *user, const char *state)
{
	const char *status;

	g_return_if_fail(user != NULL);

	if (state == NULL) {
		user->status = NULL;
		return;
	}

	if (!g_ascii_strcasecmp(state, "BSY"))
		status = "busy";
	else if (!g_ascii_strcasecmp(state, "BRB"))
		status = "brb";
	else if (!g_ascii_strcasecmp(state, "AWY"))
		status = "away";
	else if (!g_ascii_strcasecmp(state, "PHN"))
		status = "phone";
	else if (!g_ascii_strcasecmp(state, "LUN"))
		status = "lunch";
	else if (!g_ascii_strcasecmp(state, "HDN"))
		status = NULL;
	else
		status = "available";

	if (!g_ascii_strcasecmp(state, "IDL"))
		user->idle = TRUE;
	else
		user->idle = FALSE;

	user->status = status;
}

void
msn_user_set_passport(MsnUser *user, const char *passport)
{
	g_return_if_fail(user != NULL);

	g_free(user->passport);
	user->passport = g_strdup(passport);
}

gboolean
msn_user_set_friendly_name(MsnUser *user, const char *name)
{
	g_return_val_if_fail(user != NULL, FALSE);

	if (!name)
		return FALSE;

	if (user->friendly_name && (!strcmp(user->friendly_name, name) ||
				!strcmp(user->passport, name)))
		return FALSE;

	g_free(user->friendly_name);
	user->friendly_name = g_strdup(name);

	serv_got_alias(purple_account_get_connection(user->userlist->session->account),
			user->passport, name);
	return TRUE;
}

void
msn_user_set_statusline(MsnUser *user, const char *statusline)
{
	g_return_if_fail(user != NULL);

	g_free(user->statusline);
	user->statusline = g_strdup(statusline);
}

void
msn_user_set_uid(MsnUser *user, const char *uid)
{
	g_return_if_fail(user != NULL);

	g_free(user->uid);
	user->uid = g_strdup(uid);
}

void
msn_user_set_endpoint_data(MsnUser *user, const char *input, MsnUserEndpoint *newep)
{
	MsnUserEndpoint *ep;
	char *endpoint;
	GSList *l;

	g_return_if_fail(user != NULL);
	g_return_if_fail(input != NULL);

	endpoint = g_ascii_strdown(input, -1);

	for (l = user->endpoints; l; l = l->next) {
		ep = l->data;
		if (g_str_equal(ep->id, endpoint)) {
			/* We have info about this endpoint! */

			g_free(endpoint);

			if (newep == NULL) {
				/* Delete it and exit */
				user->endpoints = g_slist_delete_link(user->endpoints, l);
				free_user_endpoint(ep);
				return;
			}

			/* Break out of our loop and update it */
			break;
		}
	}
	if (l == NULL) {
		/* Need to add a new endpoint */
		ep = g_new0(MsnUserEndpoint, 1);
		ep->id = endpoint;
		user->endpoints = g_slist_prepend(user->endpoints, ep);
	}

	ep->clientid = newep->clientid;
	ep->extcaps = newep->extcaps;
}

void
msn_user_clear_endpoints(MsnUser *user)
{
	MsnUserEndpoint *ep;
	GSList *l;

	g_return_if_fail(user != NULL);

	for (l = user->endpoints; l; l = g_slist_delete_link(l, l)) {
		ep = l->data;
		free_user_endpoint(ep);
	}

	user->endpoints = NULL;
}

void
msn_user_set_op(MsnUser *user, MsnListOp list_op)
{
	g_return_if_fail(user != NULL);

	user->list_op |= list_op;
}

void
msn_user_unset_op(MsnUser *user, MsnListOp list_op)
{
	g_return_if_fail(user != NULL);

	user->list_op &= ~list_op;
}

void
msn_user_set_buddy_icon(MsnUser *user, PurpleStoredImage *img)
{
	MsnObject *msnobj;

	g_return_if_fail(user != NULL);

	msnobj = msn_object_new_from_image(img, "TFR2C2.tmp",
			user->passport, MSN_OBJECT_USERTILE);

	if (!msnobj)
		purple_debug_error("msn", "Unable to open buddy icon from %s!\n", user->passport);

	msn_user_set_object(user, msnobj);
}

/*add group id to User object*/
void
msn_user_add_group_id(MsnUser *user, const char* group_id)
{
	MsnUserList *userlist;
	PurpleAccount *account;
	PurpleBuddy *b;
	PurpleGroup *g;
	const char *passport;
	const char *group_name;

	g_return_if_fail(user != NULL);
	g_return_if_fail(group_id != NULL);

	user->group_ids = g_list_append(user->group_ids, g_strdup(group_id));

	userlist = user->userlist;
	account = userlist->session->account;
	passport = msn_user_get_passport(user);

	group_name = msn_userlist_find_group_name(userlist, group_id);

	purple_debug_info("msn", "User: group id:%s,name:%s,user:%s\n", group_id, group_name, passport);

	g = purple_find_group(group_name);

	if ((group_id == NULL) && (g == NULL))
	{
		g = purple_group_new(group_name);
		purple_blist_add_group(g, NULL);
	}

	b = purple_find_buddy_in_group(account, passport, g);
	if (b == NULL)
	{
		b = purple_buddy_new(account, passport, NULL);
		purple_blist_add_buddy(b, NULL, g, NULL);
	}
	purple_buddy_set_protocol_data(b, user);
	/*Update the blist Node info*/
}

/*check if the msn user is online*/
gboolean
msn_user_is_online(PurpleAccount *account, const char *name)
{
	PurpleBuddy *buddy;

	buddy = purple_find_buddy(account, name);
	return PURPLE_BUDDY_IS_ONLINE(buddy);
}

gboolean
msn_user_is_yahoo(PurpleAccount *account, const char *name)
{
	MsnSession *session = NULL;
	MsnUser *user;
	PurpleConnection *gc;

	gc = purple_account_get_connection(account);
	if (gc != NULL)
		session = purple_connection_get_protocol_data(gc);

	if ((session != NULL) && (user = msn_userlist_find_user(session->userlist, name)) != NULL)
	{
		return (user->networkid == MSN_NETWORK_YAHOO);
	}
	return (strstr(name,"@yahoo.") != NULL);
}

void
msn_user_remove_group_id(MsnUser *user, const char *id)
{
	GList *l;

	g_return_if_fail(user != NULL);
	g_return_if_fail(id != NULL);

	l = g_list_find_custom(user->group_ids, id, (GCompareFunc)strcmp);

	if (l == NULL)
		return;

	g_free(l->data);
	user->group_ids = g_list_delete_link(user->group_ids, l);
}

void
msn_user_set_pending_group(MsnUser *user, const char *group)
{
	user->pending_group = g_strdup(group);
}

char *
msn_user_remove_pending_group(MsnUser *user)
{
	char *group = user->pending_group;
	user->pending_group = NULL;
	return group;
}

void
msn_user_set_home_phone(MsnUser *user, const char *number)
{
	g_return_if_fail(user != NULL);

	if (!number && !user->extinfo)
		return;

	if (user->extinfo)
		g_free(user->extinfo->phone_home);
	else
		user->extinfo = g_new0(MsnUserExtendedInfo, 1);

	user->extinfo->phone_home = g_strdup(number);
}

void
msn_user_set_work_phone(MsnUser *user, const char *number)
{
	g_return_if_fail(user != NULL);

	if (!number && !user->extinfo)
		return;

	if (user->extinfo)
		g_free(user->extinfo->phone_work);
	else
		user->extinfo = g_new0(MsnUserExtendedInfo, 1);

	user->extinfo->phone_work = g_strdup(number);
}

void
msn_user_set_mobile_phone(MsnUser *user, const char *number)
{
	g_return_if_fail(user != NULL);

	if (!number && !user->extinfo)
		return;

	if (user->extinfo)
		g_free(user->extinfo->phone_mobile);
	else
		user->extinfo = g_new0(MsnUserExtendedInfo, 1);

	user->extinfo->phone_mobile = g_strdup(number);
}

void
msn_user_set_clientid(MsnUser *user, guint clientid)
{
	g_return_if_fail(user != NULL);

	user->clientid = clientid;
}

void
msn_user_set_extcaps(MsnUser *user, guint extcaps)
{
	g_return_if_fail(user != NULL);

	user->extcaps = extcaps;
}

void
msn_user_set_network(MsnUser *user, MsnNetwork network)
{
	g_return_if_fail(user != NULL);

	user->networkid = network;
}

static gboolean
buddy_icon_cached(PurpleConnection *gc, MsnObject *obj)
{
	PurpleAccount *account;
	PurpleBuddy *buddy;
	const char *old;
	const char *new;

	g_return_val_if_fail(obj != NULL, FALSE);

	account = purple_connection_get_account(gc);

	buddy = purple_find_buddy(account, msn_object_get_creator(obj));
	if (buddy == NULL)
		return FALSE;

	old = purple_buddy_icons_get_checksum_for_user(buddy);
	new = msn_object_get_sha1(obj);

	if (new == NULL)
		return FALSE;

	/* If the old and new checksums are the same, and the file actually exists,
	 * then return TRUE */
	if (old != NULL && !strcmp(old, new))
		return TRUE;

	return FALSE;
}

static void
queue_buddy_icon_request(MsnUser *user)
{
	PurpleAccount *account;
	MsnObject *obj;
	GQueue *queue;

	g_return_if_fail(user != NULL);

	account = user->userlist->session->account;

	obj = msn_user_get_object(user);

	if (obj == NULL) {
		purple_buddy_icons_set_for_user(account, user->passport, NULL, 0, NULL);
		return;
	}

	if (!buddy_icon_cached(purple_account_get_connection(account), obj)) {
		MsnUserList *userlist;

		userlist = user->userlist;
		queue = userlist->buddy_icon_requests;

		if (purple_debug_is_verbose())
			purple_debug_info("msn", "Queueing buddy icon request for %s (buddy_icon_window = %i)\n",
			                  user->passport, userlist->buddy_icon_window);

		g_queue_push_tail(queue, user);

		if (userlist->buddy_icon_window > 0)
			msn_release_buddy_icon_request(userlist);
	}
}

void
msn_user_set_object(MsnUser *user, MsnObject *obj)
{
	g_return_if_fail(user != NULL);

	if (user->msnobj != NULL)
		msn_object_destroy(user->msnobj, TRUE);

	user->msnobj = obj;

	if (user->list_op & MSN_LIST_FL_OP)
		queue_buddy_icon_request(user);
}

void
msn_user_set_client_caps(MsnUser *user, GHashTable *info)
{
	g_return_if_fail(user != NULL);
	g_return_if_fail(info != NULL);

	if (user->clientcaps != NULL)
		g_hash_table_destroy(user->clientcaps);

	user->clientcaps = info;
}

void
msn_user_set_invite_message(MsnUser *user, const char *message)
{
	g_return_if_fail(user != NULL);

	g_free(user->invite_message);
	user->invite_message = g_strdup(message);
}

const char *
msn_user_get_passport(const MsnUser *user)
{
	g_return_val_if_fail(user != NULL, NULL);

	return user->passport;
}

const char *
msn_user_get_friendly_name(const MsnUser *user)
{
	g_return_val_if_fail(user != NULL, NULL);

	return user->friendly_name;
}

const char *
msn_user_get_home_phone(const MsnUser *user)
{
	g_return_val_if_fail(user != NULL, NULL);

	return user->extinfo ? user->extinfo->phone_home : NULL;
}

const char *
msn_user_get_work_phone(const MsnUser *user)
{
	g_return_val_if_fail(user != NULL, NULL);

	return user->extinfo ? user->extinfo->phone_work : NULL;
}

const char *
msn_user_get_mobile_phone(const MsnUser *user)
{
	g_return_val_if_fail(user != NULL, NULL);

	return user->extinfo ? user->extinfo->phone_mobile : NULL;
}

guint
msn_user_get_clientid(const MsnUser *user)
{
	g_return_val_if_fail(user != NULL, 0);

	return user->clientid;
}

guint
msn_user_get_extcaps(const MsnUser *user)
{
	g_return_val_if_fail(user != NULL, 0);

	return user->extcaps;
}

MsnUserEndpoint *
msn_user_get_endpoint_data(MsnUser *user, const char *input)
{
	char *endpoint;
	GSList *l;
	MsnUserEndpoint *ep;

	g_return_val_if_fail(user != NULL, NULL);
	g_return_val_if_fail(input != NULL, NULL);

	endpoint = g_ascii_strdown(input, -1);

	for (l = user->endpoints; l; l = l->next) {
		ep = l->data;
		if (g_str_equal(ep->id, endpoint)) {
			g_free(endpoint);
			return ep;
		}
	}

	g_free(endpoint);

	return NULL;
}

MsnNetwork
msn_user_get_network(const MsnUser *user)
{
	g_return_val_if_fail(user != NULL, MSN_NETWORK_UNKNOWN);

	return user->networkid;
}

MsnObject *
msn_user_get_object(const MsnUser *user)
{
	g_return_val_if_fail(user != NULL, NULL);

	return user->msnobj;
}

GHashTable *
msn_user_get_client_caps(const MsnUser *user)
{
	g_return_val_if_fail(user != NULL, NULL);

	return user->clientcaps;
}

const char *
msn_user_get_invite_message(const MsnUser *user)
{
	g_return_val_if_fail(user != NULL, NULL);

	return user->invite_message;
}

gboolean
msn_user_is_capable(MsnUser *user, char *endpoint, guint capability, guint extcap)
{
	g_return_val_if_fail(user != NULL, FALSE);

	if (endpoint != NULL) {
		MsnUserEndpoint *ep = msn_user_get_endpoint_data(user, endpoint);
		if (ep != NULL)
			return (ep->clientid & capability) && (ep->extcaps & extcap);
		else
			return FALSE;
	}

	return (user->clientid & capability) && (user->extcaps & extcap);
}

/**************************************************************************
 * Utility functions
 **************************************************************************/

int
msn_user_passport_cmp(MsnUser *user, const char *passport)
{
	const char *str;
	char *pass;
	int result;

	str = purple_normalize_nocase(NULL, msn_user_get_passport(user));
	pass = g_strdup(str);

#if GLIB_CHECK_VERSION(2,16,0)
	result = g_strcmp0(pass, purple_normalize_nocase(NULL, passport));
#else
	str = purple_normalize_nocase(NULL, passport);
	if (!pass)
		result = -(pass != str);
	else if (!str)
		result = pass != str;
	else
		result = strcmp(pass, str);
#endif /* GLIB < 2.16.0 */

	g_free(pass);

	return result;
}

gboolean
msn_user_is_in_group(MsnUser *user, const char * group_id)
{
	if (user == NULL)
		return FALSE;

	if (group_id == NULL)
		return FALSE;

	return (g_list_find_custom(user->group_ids, group_id, (GCompareFunc)strcmp)) != NULL;
}

gboolean
msn_user_is_in_list(MsnUser *user, MsnListId list_id)
{
	if (user == NULL)
		return FALSE;

	return (user->list_op & (1 << list_id));
}

