/*
   user.c - Implements the User API
   See README for Copyright and License
*/

#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include "lfm_helper.h"

extern const char *LASTFM_PERIOD_STRINGS[];

extern LASTFM_TAG_INFO    *_new_LASTFM_TAG_INFO();
extern LASTFM_ALBUM_INFO  *_new_LASTFM_ALBUM_INFO();
extern LASTFM_TRACK_INFO  *_new_LASTFM_TRACK_INFO();
extern LASTFM_ARTIST_INFO *_new_LASTFM_ARTIST_INFO();

int LASTFM_user_get_top_tags(LASTFM_SESSION *s, const char *user, LFMList **result){
	XMLNode *xml=NULL, *xi, *xj;
	WebData *data = NULL;
	LASTFM_TAG_INFO *a = NULL;
	LFMList *out = NULL;
	char *buffer;
	int rv = LASTFM_STATUS_ERROR;

	if(s == NULL || ( strisspace(user) && strisspace(s->username)) ) 
		return LASTFM_STATUS_INVALID;
	
	buffer = malloc(LARGE_BUFFER);
	snprintf(buffer,LARGE_BUFFER,
		"%s?method=user.gettoptags&api_key=%s&user=%s",
		API_ROOT,s->api_key,
			strisspace(user) ? s->username : user );
	
	data = lfm_helper_get_page(buffer,s);
	free(buffer);

	xml = tinycxml_parse(data->page);
	if(lfm_helper_get_status(s,xml))goto done;

	xi = xmlnode_get(xml, CCA { "lfm","toptags","tag",NULL },NULL,NULL);
	for(;xi;xi=xi->next){
		a = _new_LASTFM_TAG_INFO();
		LFMList_append(&out,a);

		xj = xmlnode_get(xi,CCA { "tag","name",NULL } ,NULL,NULL);
		if(xj && xj->content)
			a->name = unescape_HTML(strdup(xj->content));

		xj = xmlnode_get(xi,CCA {"tag","url",NULL},NULL,NULL);
		if(xj && xj->content)
			a->url = strdup(xj->content);
		
		xj = xmlnode_get(xi,CCA { "tag","count",NULL } ,NULL,NULL);
		if(xj && xj->content)
			a->count = atoi(xj->content);
	}

	rv = LASTFM_STATUS_OK;
	done:
	s->fraction = -1;
	xmlnode_free(xml);
	lfm_helper_free_page(data);
	*result = out;
	return rv;
}

int LASTFM_user_shout(LASTFM_SESSION *s,const char *user,const char *msg){
	WebData *wpage=NULL;
	XMLNode *xml = NULL;
	char api_sig[MD5_BUFFER];
	char *buffer;
	int rv=0;
	
	if(s==NULL) return LASTFM_STATUS_INVALID;

	buffer = malloc(LARGE_BUFFER);

	/* Build up the api_sig */
	rv = snprintf(buffer,LARGE_BUFFER,
		"api_key%smessage%smethod%ssk%suser%s%s",
		s->api_key,
		msg,
		"user.shout",
		s->session_key,
		user,
		s->secret);
	
	string2MD5(buffer,api_sig);

	rv = snprintf(buffer,LARGE_BUFFER,
		"&user=%s&message=%s&api_key=%s&api_sig=%s&sk=%s&method=user.shout",
		user,msg,s->api_key,api_sig,s->session_key);

	wpage = lfm_helper_post_page(s->curl,s,API_ROOT,buffer);
	free(buffer);
		
	if(!wpage)return LASTFM_STATUS_ERROR;
	if(!wpage->page) return LASTFM_STATUS_ERROR;

	xml = tinycxml_parse(wpage->page);
	if(lfm_helper_get_status(s,xml)){
		rv = LASTFM_STATUS_ERROR;
	}else{
		rv = LASTFM_STATUS_OK;	
	}

	s->fraction = -1;
	xmlnode_free(xml);
	lfm_helper_free_page(wpage);
	return rv;
}


int LASTFM_user_get_top_albums(LASTFM_SESSION *s,const char *user,unsigned period, 
				unsigned limit, unsigned page, LFMList **results){
	LASTFM_ALBUM_INFO *r = NULL;
	WebData *data=NULL;
	XMLNode *xml = NULL, *xi, *xj;
	LFMList *out = NULL;
	int cpage  = 0;  // current page
	int rpages = 0;  // remaining pages
	int tpages = 0;  // total pages
	char *buffer;
	int rv;

	if(s == NULL){
		return -1;
	}

	if(period >= LASTFM_PERIOD_COUNT ){
		snprintf(s->status,SMALL_BUFFER,"Invalid period");	
		return -1;
	}
	if(strisspace(user) && strisspace(s->username)){
		snprintf(s->status,SMALL_BUFFER,"No user specified");
		return -1;
	}

	if(page == 0) page = 1;

	buffer = malloc(LARGE_BUFFER);
	rv = snprintf(buffer,LARGE_BUFFER,
		"%s?method=user.gettopalbums&api_key=%s&user=%s&period=%s&page=%u",
			API_ROOT,
			s->api_key,
			(strisspace(user)) ? s->username : user,
			LASTFM_PERIOD_STRINGS[period],
			page);

	if(limit)
		rv += snprintf(buffer+rv,LARGE_BUFFER-rv,"&limit=%u",limit);

	data = lfm_helper_get_page(buffer,s);
	free(buffer);

	xml = tinycxml_parse(data->page);
	if(lfm_helper_get_status(s,xml))goto done;

	xi = xmlnode_get(xml,CCA {"lfm","topalbums",NULL }, "totalPages", NULL);
	if(xi && xi->content)
		tpages = atoi(xi->content);

	xi = xmlnode_get(xml,CCA {"lfm","topalbums",NULL }, "page", NULL);
	if(xi && xi->content)
		cpage = atoi(xi->content);

	rpages = tpages - cpage;

	xi = xmlnode_get(xml,CCA { "lfm","topalbums","album",NULL},NULL,NULL);
	for(;xi;xi=xi->next){
		r = _new_LASTFM_ALBUM_INFO();
		LFMList_append(&out,r);
		
		xj = xmlnode_get(xi,CCA { "album","name",NULL},NULL,NULL);
		if(xj && xj->content)
			r->name = unescape_HTML(strdup(xj->content));

		xj = xmlnode_get(xi,CCA { "album","playcount",NULL},NULL,NULL);
		if(xj && xj->content)
			r->playcount = atoi(xj->content);

		xj = xmlnode_get(xi,CCA { "album","artist","name",NULL},NULL,NULL);
		if(xj && xj->content)
			r->artist = unescape_HTML(strdup(xj->content));

	}

	done:
	*results = out;
	s->fraction = -1;
	xmlnode_free(xml);
	lfm_helper_free_page(data);
	return rpages;
}

int LASTFM_user_get_top_artists(LASTFM_SESSION *s,const char *user,unsigned period,
					unsigned limit, unsigned page, LFMList **result){
	LASTFM_ARTIST_INFO *r = NULL;
	WebData *data=NULL;
	XMLNode *xml = NULL, *xi, *xj;
	LFMList *out = NULL;
	int cpage  = 0;  // current page
	int rpages = 0;  // remaining pages
	int tpages = 0;  // total pages
	char *buffer;
	int rv;

	if(s == NULL){
		return -1;
	}

	if(period >= LASTFM_PERIOD_COUNT ){
		snprintf(s->status,SMALL_BUFFER,"Invalid period");	
		return -1;
	}
	
	if(strisspace(user) && strisspace(s->username)){
		snprintf(s->status,SMALL_BUFFER,"No user specified");
		return -1;
	}
	
	if(page == 0) page = 1;

	buffer = malloc(LARGE_BUFFER);
	rv = snprintf(buffer,LARGE_BUFFER,
		"%s?method=user.gettopartists&api_key=%s&user=%s&period=%s&page=%u",
			API_ROOT,
			s->api_key,
			(strisspace(user)) ? s->username : user,
			LASTFM_PERIOD_STRINGS[period],
			page);

	if(limit)
		rv += snprintf(buffer+rv,LARGE_BUFFER-rv,"&limit=%u",limit);

	data = lfm_helper_get_page(buffer,s);
	free(buffer);

	xml = tinycxml_parse(data->page);
	if(lfm_helper_get_status(s,xml))goto done;

	xi = xmlnode_get(xml,CCA {"lfm","topartists",NULL }, "totalPages", NULL);
	if(xi && xi->content)
		tpages = atoi(xi->content);

	xi = xmlnode_get(xml,CCA {"lfm","topartists",NULL }, "page", NULL);
	if(xi && xi->content)
		cpage = atoi(xi->content);

	rpages = tpages - cpage;

	xi = xmlnode_get(xml,CCA { "lfm","topartists","artist",NULL},NULL,NULL);

	for(;xi;xi=xi->next){
		r = _new_LASTFM_ARTIST_INFO();
		LFMList_append(&out,r);
		
		xj = xmlnode_get(xi,CCA { "artist","name",NULL},NULL,NULL);
		if(xj && xj->content)
			r->name = unescape_HTML(strdup(xj->content));

		xj = xmlnode_get(xi,CCA { "artist","playcount",NULL},NULL,NULL);
		if(xj && xj->content)
			r->playcount = atoi(xj->content);
	}

	done:
	*result = out;
	s->fraction = -1;
	xmlnode_free(xml);
	lfm_helper_free_page(data);
	return rpages;
}

int LASTFM_user_get_top_tracks(LASTFM_SESSION *s,const char *user,unsigned period,
					unsigned limit, unsigned page, LFMList **result){
	LASTFM_TRACK_INFO *r;
	WebData *data=NULL;
	XMLNode *xml = NULL, *xi, *xj;
	char *buffer;
	LFMList *out = NULL;
	int cpage  = 0;  // current page
	int rpages = 0;  // remaining pages
	int tpages = 0;  // total pages
	int rv;

	if(s == NULL){
		return -1;
	}

	if(period >= LASTFM_PERIOD_COUNT ){
		snprintf(s->status,SMALL_BUFFER,"Invalid period");	
		return -1;
	}
	
	if(strisspace(user) && strisspace(s->username)){
		snprintf(s->status,SMALL_BUFFER,"No user specified");
		return -1;
	}
	
	if(page == 0) page = 1;

	buffer = malloc(LARGE_BUFFER);
	rv = snprintf(buffer,LARGE_BUFFER,
		"%s?method=user.gettoptracks&api_key=%s&user=%s&period=%s&page=%u",
			API_ROOT,
			s->api_key,
			(strisspace(user)) ? s->username : user,
			LASTFM_PERIOD_STRINGS[period],
			page);

	if(limit)
		rv += snprintf(buffer+rv,LARGE_BUFFER-rv,"&limit=%u",limit);

	data = lfm_helper_get_page(buffer,s);
	free(buffer);

	xml = tinycxml_parse(data->page);
	if(lfm_helper_get_status(s,xml))goto done;

	xi = xmlnode_get(xml,CCA {"lfm","toptracks",NULL }, "totalPages", NULL);
	if(xi && xi->content)
		tpages = atoi(xi->content);

	xi = xmlnode_get(xml,CCA {"lfm","toptracks",NULL }, "page", NULL);
	if(xi && xi->content)
		cpage = atoi(xi->content);

	rpages = tpages - cpage;

	xi = xmlnode_get(xml, CCA { "lfm","toptracks","track",NULL},NULL,NULL);
	for(;xi;xi=xi->next){
		r = _new_LASTFM_TRACK_INFO();
		LFMList_append(&out,r);

		xj = xmlnode_get(xi,CCA { "track","name",NULL},NULL,NULL);
		if(xj && xj->content)
			r->name = unescape_HTML(strdup(xj->content));

		xj = xmlnode_get(xi,CCA { "track","artist","name",NULL},NULL,NULL);
		if(xj && xj->content)
			r->artist = unescape_HTML(strdup(xj->content));

		xj = xmlnode_get(xi,CCA { "track","playcount",NULL},NULL,NULL);
		if(xj && xj->content)
			r->playcount = atoi(xj->content);
	}

	done:
	*result = out;
	s->fraction = -1;
	xmlnode_free(xml);
	lfm_helper_free_page(data);
	return rpages;
}

int LASTFM_user_get_recent_tracks(LASTFM_SESSION *s, const char *user,
				time_t from, time_t to, unsigned page, 
				LFMList **result){
	XMLNode *xml  = NULL, *xi, *xj;
	WebData *data = NULL;
	LFMList *out  = NULL;
	LASTFM_TRACK_INFO *r;
	char *buffer       = NULL;
	int cur_page        = 0;
	int total_pages     = 0;
	int remaining_pages = 0;
	int rv,i;

	if(s == NULL){
		return -1;
	}
	if(user == NULL && s->username == NULL){
		snprintf(s->status,SMALL_BUFFER,"No user specified");
		return -1;
	}

	if(page == 0) page = 1;

	buffer = malloc(LARGE_BUFFER);
	rv = snprintf(buffer,LARGE_BUFFER,
		"%s?method=user.getrecenttracks&api_key=%s&user=%s&limit=%u&page=%u",
			API_ROOT,
			s->api_key,
			(user) ? user : s->username,
			200,
			page);
	
	if(to){
		rv += snprintf(buffer+rv,LARGE_BUFFER-rv,"&to=%lu",to);
	}

	if(from){
		rv += snprintf(buffer+rv,LARGE_BUFFER-rv,"&from=%lu",from);
	}

	data = lfm_helper_get_page(buffer,s);
	free(buffer);

	xml = tinycxml_parse(data->page);
	if(lfm_helper_get_status(s,xml))goto done;

	xi = xmlnode_get(xml,CCA {"lfm","recenttracks",NULL }, "totalPages", NULL);
	if(xi && xi->content)
		total_pages = atoi(xi->content);

	xi = xmlnode_get(xml,CCA {"lfm","recenttracks",NULL }, "page", NULL);
	if(xi && xi->content)
		cur_page = atoi(xi->content);

	remaining_pages = total_pages - cur_page;
	
	xi = xmlnode_get(xml,CCA {"lfm","recenttracks","track",NULL},NULL,NULL);
	for(i=0;xi;i++){
		r = _new_LASTFM_TRACK_INFO();
		
		xj = xmlnode_get(xi,CCA {"track","name",NULL},NULL,NULL);
		if(xj && xj->content)
			r->name = unescape_HTML(strdup(xj->content));

		xj = xmlnode_get(xi,CCA {"track","artist",NULL},NULL,NULL);
		if(xj && xj->content)
			r->artist = unescape_HTML(strdup(xj->content));

		xj = xmlnode_get(xi,CCA {"track","album",NULL},NULL,NULL);
		if(xj && xj->content)
			r->album = unescape_HTML(strdup(xj->content));
		
		xj = xmlnode_get(xi,CCA {"track","date",NULL},"uts",NULL);
		if(xj && xj->content)
			r->time = atol(xj->content);

		xi = xi->next;
		LFMList_append(&out,r);
	}

	done:
	*result = out;
	s->fraction = -1;
	xmlnode_free(xml);
	lfm_helper_free_page(data);
	return remaining_pages;



}

int LASTFM_user_get_artist_tracks(LASTFM_SESSION *s,const char *user, 
				const char *artist,time_t start, time_t end,
				unsigned page, LFMList **result){

	XMLNode *xml  = NULL, *xi, *xj;
	WebData *data = NULL;
	LFMList *out  = NULL;
	LASTFM_TRACK_INFO *r;
	char *quote_artist = NULL;
	char *buffer       = NULL;
	int tpages  = 0; // Total pages
	int rpages  = 0; // Remaining pages
	int cpage   = 0; // Current page
	int rv;

	if(s == NULL){
		return -1;
	}
	if(artist == NULL){
		snprintf(s->status,SMALL_BUFFER,"No artist specified");
		return -1;
	}
	if(user == NULL && s->username == NULL){
		snprintf(s->status,SMALL_BUFFER,"No user specified");
		return -1;
	}

	quote_artist = curl_easy_escape(s->curl,artist,0);

	buffer = malloc(LARGE_BUFFER);
	rv = snprintf(buffer,LARGE_BUFFER,
		"%s?method=user.getartisttracks&api_key=%s&artist=%s&user=%s&page=%u",
			API_ROOT,
			s->api_key,
			quote_artist,
			(user) ? user : s->username,
			(page) ? page : 1 );
	free(quote_artist);
	
	if(start){
		rv += snprintf(buffer+rv,LARGE_BUFFER-rv,"&startTimestamp=%lu",start);
	}

	if(end){
		rv += snprintf(buffer+rv,LARGE_BUFFER-rv,"&endTimestamp=%lu",end);
	}

	data = lfm_helper_get_page(buffer,s);
	free(buffer);

	xml = tinycxml_parse(data->page);
	if(lfm_helper_get_status(s,xml))goto done;

	xi = xmlnode_get(xml,CCA {"lfm","artisttracks",NULL }, "totalPages", NULL);
	if(xi && xi->content)
		tpages = atoi(xi->content);

	xi = xmlnode_get(xml,CCA {"lfm","artisttracks",NULL }, "page", NULL);
	if(xi && xi->content)
		cpage = atoi(xi->content);

	rpages = tpages - cpage;
	
	xi = xmlnode_get(xml,CCA {"lfm","artisttracks","track",NULL},NULL,NULL);
	for(;xi;xi=xi->next){
		r = _new_LASTFM_TRACK_INFO();
		
		xj = xmlnode_get(xi,CCA {"track","name",NULL},NULL,NULL);
		if(xj && xj->content)
			r->name = unescape_HTML(strdup(xj->content));

		xj = xmlnode_get(xi,CCA {"track","album",NULL},NULL,NULL);
		if(xj && xj->content)
			r->album = unescape_HTML(strdup(xj->content));
		
		xj = xmlnode_get(xi,CCA {"track","date",NULL},"uts",NULL);
		if(xj && xj->content)
			r->time = atol(xj->content);

		LFMList_append(&out,r);
	}

	done:
	*result = out;
	s->fraction = -1;
	xmlnode_free(xml);
	lfm_helper_free_page(data);
	return rpages;
}

int LASTFM_user_get_loved_tracks(LASTFM_SESSION *s,const char *user,
				unsigned page, LFMList **result){
	XMLNode *xml  = NULL, *xi, *xj;
	WebData *data = NULL;
	LFMList *out  = NULL;
	LASTFM_TRACK_INFO *r;
	char *buffer;
	int tpages = 0; // Total pages
	int rpages = 0; // Remaining pages
	int cpage =  0; // Current page

	if(s == NULL){
		return -1;
	}
	if(strisspace(user) && strisspace(s->username) ){
		snprintf(s->status,SMALL_BUFFER,"No user specified");
		return -1;
	}

	buffer = malloc(LARGE_BUFFER);
	snprintf(buffer,LARGE_BUFFER,
		"%s?method=user.getlovedtracks&api_key=%s&user=%s&page=%u",
		API_ROOT,
		s->api_key,
		(user) ? user : s->username,
		(page) ? page : 1 );
	
	data = lfm_helper_get_page(buffer,s);
	free(buffer);

	xml = tinycxml_parse(data->page);
	if(lfm_helper_get_status(s,xml))goto done;

	xi = xmlnode_get(xml,CCA {"lfm","lovedtracks",NULL }, "totalPages", NULL);
	if(xi && xi->content)
		tpages = atoi(xi->content);

	xi = xmlnode_get(xml,CCA {"lfm","lovedtracks",NULL }, "page", NULL);
	if(xi && xi->content)
		cpage = atoi(xi->content);

	rpages = tpages - cpage;

	xi = xmlnode_get(xml,CCA {"lfm","lovedtracks","track",NULL},NULL,NULL);

	for(;xi;xi=xi->next){
		r = _new_LASTFM_TRACK_INFO();
		LFMList_append(&out,r);

		xj = xmlnode_get(xi,CCA {"track","name",NULL},NULL,NULL);
		if(xj && xj->content)
			r->name = unescape_HTML(strdup(xj->content));

		xj = xmlnode_get(xi,CCA {"track","artist","name",NULL},NULL,NULL);
		if(xj && xj->content)
			r->artist = unescape_HTML(strdup(xj->content));

		xj = xmlnode_get(xi,CCA {"track","date",NULL},"uts",NULL);
		if(xj && xj->content)
			r->time = atol(xj->content);
	}

	done:
	*result = out;
	s->fraction = -1;
	xmlnode_free(xml);
	lfm_helper_free_page(data);
	return rpages;
}
