#include <stdio.h>
#include <time.h>

/*** opaque liblastfm.so session handle ***/
#ifndef LASTFM_PRIV_H
#define LASTFM_PRIV_H
typedef void  LASTFM_SESSION;
#endif

#ifndef CLASTFM_H
#define CLASTFM_H

/* Scrobbler API 2.0 URL */
#define API_ROOT		"http://ws.audioscrobbler.com/2.0/"

enum LASTFM_PERIOD {
	LASTFM_PERIOD_7DAY=0,
	LASTFM_PERIOD_3MONTH,
	LASTFM_PERIOD_6MONTH,
	LASTFM_PERIOD_12MONTH,
	LASTFM_PERIOD_OVERALL,
	LASTFM_PERIOD_COUNT
};

/* If the image size you want is not available, the next smaller size is fetched */
enum LASTFM_IMAGE_SIZE {
	LASTFM_IMAGE_OG=0,	// original
	LASTFM_IMAGE_XXL,	// mega
	LASTFM_IMAGE_XL,	// extralarge
	LASTFM_IMAGE_L,		// large
	LASTFM_IMAGE_M,		// medium
	LASTFM_IMAGE_S,		// small
	LASTFM_IMAGE_COUNT,	// Internal use only
	// Fetching URLs does not add any network overhead as the URLs
	// are already provided in the method result. Just pick one.
	LASTFM_IMAGE_URL_OG,	// Don't download image, only get URL (original)
	LASTFM_IMAGE_URL_XXL,	// Don't download image, only get URL (mega)
	LASTFM_IMAGE_URL_XL,	// Don't download image, only get URL (extralarge)
	LASTFM_IMAGE_URL_L,	// Don't download image, only get URL (large)
	LASTFM_IMAGE_URL_M,	// Don't download image, only get URL (medium)
	LASTFM_IMAGE_URL_S,	// Don't download image, only get URL (small)
	LASTFM_IMAGE_URL_COUNT	// Internal use only
};

enum LASTFM_STATUS_CODES{
	/* Everything is just fine and dandy */
	LASTFM_STATUS_OK=0,
	/* Lastfm returned an error */
	LASTFM_STATUS_ERROR,
	/* Invalid argument/parameters supplied */
	LASTFM_STATUS_INVALID,
	/* Something bad happened in libclastfm */
	LASTFM_INTERNAL_ERROR
};

/*** LFMList API ***/
typedef struct _List LFMList;

struct _List {
	void *data;
	LFMList *next;
};

typedef void (*LFMFunc)(void *,void *); 

void LFMList_append(LFMList **list, void *data);
void LFMList_prepend(LFMList **list, void *data);
void LFMList_foreach(LFMList *list, LFMFunc func,void *udata);
void LFMList_free(LFMList *list);

/*** Album Information struct ***/
typedef struct {
        char            *name;          // Album name
        char            *artist;        // Artist name 
        char            *summary;       // Album summary
        char            *releasedate;   // Album release date 
	unsigned	playcount;	// Playcount if applicable
        unsigned char   *image;         // Actual image file (binary)
        size_t		image_size;     // Size in bytes
	char		*image_url;	// Image url
} LASTFM_ALBUM_INFO;

/*** Artist Information struct ***/
typedef struct {
	char		*name;		// Artist name
	char		*summary;	// Summary information
	unsigned	playcount;	// Playcount if applicable
	unsigned char	*image;		// Artist image (binary)
	size_t		image_size;	// Size in bytes of the image
	char		*image_url;	// Image url
	char		**similar;	// NULL terminated array of similar artist names
} LASTFM_ARTIST_INFO;

/*** Track Information struct ***/
typedef struct {
	char		*name;		// Track title
	char		*artist;	// Artist name
	char		*album;		// Album name
	unsigned	playcount;	// Playcount
	time_t		time;		// time stamp
	double		match;		// 0.0 to 1.0 match (see getsimilar)
} LASTFM_TRACK_INFO;

/*** Image Information struct ***/
typedef struct {
	unsigned char	*image;
	size_t		image_size;
	char		*image_url;	// Image url
	int		thumbs_up;
	int		thumbs_down;
	char		*title;
	time_t		time;
} LASTFM_IMAGE_INFO;

/*** Tag Information struct ***/
typedef struct {
	char		*name;
	char		*url;
	char		*summary;
	unsigned	reach;
	unsigned	count;
} LASTFM_TAG_INFO;
	

/*********** liblastfm.so service API ***********/

/* init - Creates a new lastfm session 
 *
 * api_key = A 32 character (+1 null) string representing your API Key
 * secret = A 32 character (+1 null) string representing your API Secret 
 * Return: A new liblastfm.so session handle. Dinitialise with LASTFM_dinit() */
LASTFM_SESSION *LASTFM_init(const char *api_key, const char *secret);

/* login - Authenticates a user lastfm session 
 *
 * user = Null terminated lastfm username string
 * pass = Null terminated lastfm password string (clear text)
 * Return: 0 on success, non-zero otherwise */
int LASTFM_login(LASTFM_SESSION *s, const char *user,const char *pass);

/* login - Authenticates a user lastfm session 
 *
 * user = Null terminated lastfm username string
 * pass = Null terminated md5 string of a lastfm password
 * Return: 0 on success, non-zero otherwise */
int LASTFM_login_MD5(LASTFM_SESSION *s,const char *user,const char *pass_hash);

/* dinit - Shutdown and deallocates memory of an existing lastfm session
 *
 * s = LASTFM_SESSION handle
 * Return: 0 on success, non-zero otherwise */
int LASTFM_dinit(LASTFM_SESSION *s);

/* print_session - Prints out session information 
 *
 * out = open FILE handle with write permission
 * s = LASTFM_SESSION handle */
void LASTFM_print_session(FILE *out, LASTFM_SESSION *s);

/* set_verbose - Enables or disables printing of log messages
 *
 * flag = 0 to disable or 1 to enable */
void LASTFM_set_verbose(int flag);

/* progress - Returns a decimal between 0.0 to 1.0 representing
 *            the network download/upload progress.
 *            -1 if idle
 * s = LASTFM_SESSION handle */
double LASTFM_progress(LASTFM_SESSION *s);

/* status - Returns pointers to internal buffers that contain the lastfm status
 *	    message (i.e. "ok" or "failed"), error code and the error text. 
 *	    You only need to call this function once per LASTFM_SESSION.
 * 	    Do not free the results.
 *
 * s = LASTFM_SESSION handle */
void LASTFM_status(LASTFM_SESSION *s, const char **status, const int **error_code,
	const char **error_text);


/* Functions to deallocate memory to various libclastfm data structures */
void LASTFM_free_tag_info   (LASTFM_TAG_INFO    *a);
void LASTFM_free_track_info (LASTFM_TRACK_INFO  *a);
void LASTFM_free_album_info (LASTFM_ALBUM_INFO  *a);
void LASTFM_free_image_info (LASTFM_IMAGE_INFO  *a);
void LASTFM_free_artist_info(LASTFM_ARTIST_INFO *a);

void LASTFM_free_tag_info_list   (LFMList *list);
void LASTFM_free_track_info_list (LFMList *list);
void LASTFM_free_album_info_list (LFMList *list);
void LASTFM_free_image_info_list (LFMList *list);
void LASTFM_free_artist_info_list(LFMList *list);

/* out = An open FILE handle with write permission */
void LASTFM_print_tag_info   (FILE *out, LASTFM_TAG_INFO    *a);
void LASTFM_print_track_info (FILE *out, LASTFM_TRACK_INFO  *a);
void LASTFM_print_album_info (FILE *out, LASTFM_ALBUM_INFO  *a);
void LASTFM_print_image_info (FILE *out, LASTFM_IMAGE_INFO  *a);
void LASTFM_print_artist_info(FILE *out, LASTFM_ARTIST_INFO *a);


void LASTFM_print_image_info_list(FILE *out, LFMList *list);

/*********** Last.fm Web API Methods ***********/

/*** Album API ***/

/* Album.getInfo 
 * s = LASTFM_SESSION handle created with LASTFM_init
 * artist = Null terminated Artist name string
 * album = Null terminated Album name string
 * Return: A newly allocated LASTFM_ALBUM_INFO struct. 
 *         Free with LASTFM_free_album_info  */
LASTFM_ALBUM_INFO *LASTFM_album_get_info(LASTFM_SESSION *s,
        const char *artist, const char *album);

/* album.search
 * http://www.last.fm/api/show?service=357
 *
 * s = LASTFM_SESSION handle created with LASTFM_init
 * album = Album name to search for
 * size = See LASTFM_IMAGE_SIZE enum
 * limit = Number of results per page (1 to 30)
 * page  = Page number to fetch from
 * results = Return location of a new LFMList of LASTFM_ALBUM_INFO structs
 *           Free with LASTFM_free_album_info_list()
 *
 * Return: The number of pages remaining, -1 on error  */
int LASTFM_album_search(LASTFM_SESSION *s,const char *album, 
			unsigned size,  unsigned limit, 
			unsigned page, LFMList **results);

/* album.getTopTags
 * http://www.last.fm/api/show?service=438
 *
 * s = LASTFM_SESSION handle created with LASTFM_init
 * artist = Album's artist name
 * album = Album name
 * results = Return location of a new LFMList of LASTFM_TAG_INFO structs
 *           Free with LASTFM_free_tag_info_list()
 *
 * Return: A LASTFM_STATUS code  */
int LASTFM_album_get_top_tags(LASTFM_SESSION *s, const char *artist, 
			const char *album, LFMList **result);


/*** Artist API ***/

/* Artist.getInfo 
 * s = LASTFM_SESSION handle created with LASTFM_init
 * artist = Null terminated Artist name string
 * lang = ISO 639 alpha-2 language code. e.g "es". Defaults to English
 * Return: A newly allocated LASTFM_ARTIST_INFO struct. 
 *         Free with LASTFM_free_artist_info  */
LASTFM_ARTIST_INFO *LASTFM_artist_get_info(LASTFM_SESSION *s, const char *artist, 
								const char *lang);

/* Artist.getImages
 * s = LASTFM_SESSION handle created with LASTFM_init
 * artist = Artist name string
 * size = A value from LASTFM_IMAGE_SIZE enum
 * limit = Number of results per page (1 to n). Defaults to 50
 * page = Page number to fetch from (ie. 1 to n )
 * results = A LFMList of LASTFM_IMAGE_INFO structs. 
 *           Free with LASTFM_free_image_info_list()
 *
 * Return: The number of pages remaining, -1 on error  */
int LASTFM_artist_get_images(LASTFM_SESSION *s, const char *artist, 
				unsigned size, unsigned limit,
				unsigned page, LFMList **results);

/* artist.getTopTags
 * http://www.last.fm/api/show?service=288 
 *
 * s = LASTFM_SESSION handle created with LASTFM_init
 * artist = artist name
 * results = Return location of a new LFMList of LASTFM_TAG_INFO structs
 *           Free with LASTFM_free_tag_info_list()
 *
 * Return: A LASTFM_STATUS code  */
int LASTFM_artist_get_top_tags(LASTFM_SESSION *s, const char *artist, 
				LFMList **result);

/*** User API ***/

/* user.shout
 * s = LASTFM_SESSION handle created with LASTFM_init
 * user = Null terminated username string of the shout recipient
 * msg = Null terminated shout message
 * Return: 0 on success, non-zero otherwise */
int LASTFM_user_shout(LASTFM_SESSION *s,const char *user,const char *msg);

/* user.getTopAlbums
 * s = LASTFM_SESSION handle created with LASTFM_init
 * user = Username or NULL to use currently authenticated user
 * period = See LASTFM_PERIOD values
 * limit = Number of results to return per page (i.e 1 to n). 0 for default.
 * page = Page number to fetch results from (i.e 1 to n)
 * result = Return location of an LFMList of LASTFM_ALBUM_INFO structs
 *           Free with LASTFM_free_album_info_list()
 * Return: The number of pages remaining on the server. -1 on error */
int LASTFM_user_get_top_albums(LASTFM_SESSION *s,const char *user,unsigned period, 
				unsigned limit, unsigned page, LFMList **results);

/* user.getTopArtists
 * http://www.last.fm/api/show?service=300
 *
 * s = LASTFM_SESSION handle created with LASTFM_init
 * user = Username or NULL to use currently authenticated user
 * period = See LASTFM_PERIOD values
 * limit = Number of results to return per page (i.e 1 to n). 0 for default.
 * page = Page number to fetch results from (i.e 1 to n)
 * result = Return location of an LFMList of LASTFM_ARTIST_INFO structs
 *           Free with LASTFM_free_artist_info_list()
 * Return: The number of pages remaining on the server. -1 on error */
int LASTFM_user_get_top_artists(LASTFM_SESSION *s,const char *user,unsigned period,
				unsigned limit, unsigned page, LFMList **results);

/* user.getTopTracks
 * http://www.last.fm/api/show?service=301
 *
 * s = LASTFM_SESSION handle created with LASTFM_init
 * user = Username or NULL to use currently authenticated user
 * period = See LASTFM_PERIOD values
 * limit = Number of results to return per page (i.e 1 to n). 0 for default.
 * page = Page number to fetch results from (i.e 1 to n)
 * result = Return location of an LFMList of LASTFM_TRACK_INFO structs
 *           Free with LASTFM_free_track_info_list()
 * Return: The number of pages remaining on the server. -1 on error */
int LASTFM_user_get_top_tracks(LASTFM_SESSION *s,const char *user,unsigned period,
				unsigned limit, unsigned page, LFMList **result);

/* user.getArtistTracks
 * s = LASTFM_SESSION handle created with LASTFM_init
 * user = Username or NULL for currently authenticated user
 * artist = The artist name you are interested in
 * start/end = Time range in which to look for scrobbles
 * page = Page number to fetch results from (ie. 1 to n )
 * result = Return location of a LFMList of LASTFM_TRACK_INFO structs
 *
 * Return: The number of pages remaining, -1 on error  */
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);

/* user.getRecentTracks
 * http://www.last.fm/api/show?service=278
 *
 * s = LASTFM_SESSION handle created with LASTFM_init
 * user    = Username or NULL for currently authenticated user
 * from/to = Time range in which to look for scrobbles
 * page    = Page number to fetch results from (ie. 1 to n )
 * result  = Return location of a LFMList of LASTFM_TRACK_INFO structs
 *
 * Return: The number of pages remaining, -1 on error  */
int LASTFM_user_get_recent_tracks(LASTFM_SESSION *s, const char *user,
				time_t from, time_t to, unsigned page, 
				LFMList **result);

/* user.getLovedTracks 
 * http://www.last.fm/api/show?service=329
 *
 * s = LASTFM_SESSION handle created with LASTFM_init
 * user    = Username or NULL for currently authenticated user
 * page    = Page number to fetch results from (ie. 1 to n )
 * result  = Return location of a LFMList of LASTFM_TRACK_INFO structs
 *
 * Return: The number of pages remaining, -1 on error  */
int LASTFM_user_get_loved_tracks(LASTFM_SESSION *s,const char *user,
				unsigned page, LFMList **result);

/* user.getTopTags
 * http://www.last.fm/api/show?service=123
 *
 * s = LASTFM_SESSION handle created with LASTFM_init
 * user = User to fetch tags for or NULL to use the current user
 * results = Return location of a new LFMList of LASTFM_TAG_INFO structs
 *           Free with LASTFM_free_tag_info_list()
 *
 * Return: A LASTFM_STATUS code  */
int LASTFM_user_get_top_tags(LASTFM_SESSION *s, const char *user,
				LFMList **result);



/*** Track API ***/


/* track.getCorrection - Compare the supplied track data with the last.fm 
 * corrections data to check for errors
 * http://www.last.fm/api/show/track.getCorrection
 *
 * s      = LASTFM_SESSION handle created with LASTFM_init
 * title  = Null terminated song title string
 * artist = Null terminated artist name string
 * result = Return location of an LFMList of LASTFM_TRACK_INFO structs
 * 
 * Return: A LASTFM_STATUS code */
int LASTFM_track_get_correction(LASTFM_SESSION *s, char *artist, char *title, 
							LFMList **result);

/* track.updateNowPlaying - (hint to what you are currently playing)
 * http://www.last.fm/api/show/track.updateNowPlaying
 *
 * s = LASTFM_SESSION handle created with LASTFM_init
 * title   = Null terminated song title string
 * album   = Null terminated album name string
 * artist  = Null terminated artist name string
 * length  = Duration in seconds of the track
 * trackno = Track number on album
 * mbtrack_id = MusicBrainz Track ID
 * result = Optional return location of an LFMList of LASTFM_TRACK_INFO structs
 *          i.e Can be used to check for tag corrections. Pass NULL if result
 *          is not needed.
 *
 * Return: A LASTFM_STATUS code */
int LASTFM_track_update_now_playing(LASTFM_SESSION *s,
		char *title,char *album, char *artist,
		unsigned int length, unsigned short trackno,
		unsigned int mbtrack_id, LFMList **result);

/* track.scrobble - (scrobbles the track)
 *
 * s = LASTFM_SESSION handle created with LASTFM_init
 * title   = Null terminated song title string
 * album   = Null terminated album name string
 * artist  = Null terminated artist name string
 * start   = Unix epoch time of when the track started playing
 * length  = Duration in seconds of the track
 * trackno = Track number on album
 * mbtrack_id = MusicBrainz Track ID 
 * result = Optional return location of an LFMList of LASTFM_TRACK_INFO structs
 *          i.e Can be used to check for tag corrections. Pass NULL if result
 *          is not needed.
 *
 * Return: A LASTFM_STATUS code */
int LASTFM_track_scrobble(LASTFM_SESSION *s,
		char *title, char *album, char *artist,
		time_t start_time,unsigned int length, unsigned int trackno,
		unsigned int mbtrack_id, LFMList **result);

/* track.love - (Love a track)
 *
 * s = LASTFM_SESSION handle created with LASTFM_init
 * title = Null terminated song title string
 * artist = Null terminated artist name string 
 *
 * Return: A LASTFM_STATUS code */
int LASTFM_track_love(LASTFM_SESSION *s,const char *title,const char *artist);

/* track.unlove - (UnLove a track)
 *
 * s = LASTFM_SESSION handle created with LASTFM_init
 * title = Null terminated song title string
 * artist = Null terminated artist name string 
 *
 * Return: A LASTFM_STATUS code */
int LASTFM_track_unlove(LASTFM_SESSION *s,const char *title,const char *artist);


/* track.getSimilar - Get similar tracks based on listening data. 
 * http://www.last.fm/api/show?service=319
 *
 * s = LASTFM_SESSION handle created with LASTFM_init
 * title = Track title string
 * artist = Track artist name string
 * limit = Maximum number of similar tracks to return
 * results = Return location of an LFMList of LASTFM_TRACK_INFO structs .
 *           Free with LASTFM_free_track_info_list()
 *
 * Return: A LASTFM_STATUS code */
int LASTFM_track_get_similar(LASTFM_SESSION *s, const char *track, const char *artist,
			unsigned limit, LFMList **results);

#endif
