#define MYURL "http://www.bortzmeyer.org/velib-rest.html"

/*

Un client REST pour acceder aux informations d'une station Velib (le
service de locations de velo en libre-service a Paris,
http://velib.paris.fr/).

Usage: 
get-station station-number

Il depend des bibliotheques curl et libxml2.

Pour compiler, une solution possible est:
gcc `curl-config --cflags` `xml2-config --cflags` -o get-station \
          `curl-config --libs` `xml2-config --libs` get-station.c

Licence : faites ce que vous voulez.

Auteur : Stephane Bortzmeyer <stephane@bortzmeyer.org>

*/


#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <curl/curl.h>
#include <libxml/parser.h>

#define URL "http://www.velib.paris.fr/service/stationdetails/%s"
#define PROGRAM "get-station"
#define VERSION "0.1"
#define MAX_FILE_SIZE 1000000
#define MAX_URL_SIZE 2048
#define MAX_STATION_SIZE 128
#define IMPOSSIBLE_VALUE 999999

char            data[MAX_FILE_SIZE];
char           *data_ptr = data;
size_t          written = 0;

size_t
write_data(void *inputbuffer, size_t size, size_t sizescale, void *userp)
{
	size_t          nbytes = size * sizescale;
	if ((written + nbytes) > MAX_FILE_SIZE) {
		fprintf(stderr, "Not enough room in data (maximum %i bytes)\n",
			MAX_FILE_SIZE);
		return 0;
	}
	strncpy(data_ptr, inputbuffer, nbytes);
	data_ptr += nbytes;
	written += nbytes;
	return nbytes;
}

unsigned int
get_number_from(xmlNode * node)
{
	xmlNode        *subnode;
	unsigned int    value = IMPOSSIBLE_VALUE;
	subnode = node->children;
	if (subnode->type == XML_TEXT_NODE) {
		errno = 0;
		value = (unsigned int)strtol((char *)subnode->content, NULL, 10);
		if (errno != 0) {
			fprintf(stderr,
				"Wrong numeric content for element <%s>: %s\n",
				node->name, subnode->content);
			return IMPOSSIBLE_VALUE;
		}
	}
	return value;
}

int
main(int argc, char **argv)
{
	CURL           *curl;
	CURLcode        result;
	xmlDoc         *doc;
	xmlNode        *root, *first_child, *node;
	char            errbuf[1024];
	char           *url = malloc(MAX_URL_SIZE);
	char           *station = malloc(MAX_STATION_SIZE);
	unsigned int    available, free, total, maintenance = IMPOSSIBLE_VALUE;
	char            c_compiler[MAX_URL_SIZE], user_agent[MAX_URL_SIZE];

	if (argc != 2) {
		fprintf(stderr, "Usage: %s station-number\n", argv[0]);
		return 1;
	}
	station = argv[1];
	sprintf(url, URL, station);

	curl = curl_easy_init();
	if (!curl) {
		fprintf(stderr, "Cannot initialize curl\n");
		return 1;
	}
#ifdef __GNUC__
	sprintf(c_compiler, " compiled with gcc %i.%i.%i", __GNUC__,
		__GNUC_MINOR__, __GNUC_PATCHLEVEL__);
#else
	c_compiler = "";
#endif
	sprintf(user_agent,
		"%s/%s (Bortzmeyer's get-station.c; C%s; libcurl %s; %s)",
		PROGRAM, VERSION, c_compiler, LIBCURL_VERSION, MYURL);
	curl_easy_setopt(curl, CURLOPT_USERAGENT, user_agent);
#if DEBUG
	curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
#endif
	curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf);
	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
	curl_easy_setopt(curl, CURLOPT_URL, url);
	result = curl_easy_perform(curl);
	if (result != 0) {
		fprintf(stderr, "Error for %s: %s (%i)\n", url, errbuf, result);
		curl_easy_cleanup(curl);
		return 1;
	}
	// If the station number is wrong, the Web service does not report
	// an error, it just sends back an empty body :-(
	if (written == 0) {
		fprintf(stderr,
			"No data for station %s, are you sure it really exists?\n",
			station);
		curl_easy_cleanup(curl);
		return 1;
	}
	curl_easy_cleanup(curl);
	data[written + 1] = '\0';	// Just to be sure
	doc = xmlReadMemory(data, written, "XML data from Velib", NULL, 0);
	root = xmlDocGetRootElement(doc);
	if (strcmp((char *)root->name, "station") != 0) {
		fprintf(stderr, "Wrong XML root element for station %s: <%s>\n",
			station, root->name);
		return 1;
	}
	first_child = root->children;
	for (node = first_child; node; node = node->next) {
		if (node->type == XML_ELEMENT_NODE) {
			if (strcmp((char *)node->name, "available") == 0) {
				available = get_number_from(node);
			} else if (strcmp((char *)node->name, "free") == 0) {
				free = get_number_from(node);
			} else if (strcmp((char *)node->name, "total") == 0) {
				total = get_number_from(node);
			} else if (strcmp((char *)node->name, "ticket") == 0) {
				maintenance = get_number_from(node);
			}
		}
	}
	if (total == 0) {	/* Unfortunately, there is no other way to report
				 * that the station number is wrong */
		fprintf(stderr,
			"Station %s has zero slots, are you sure this station really exists?\n",
			station);
		return 1;
	}
	fprintf(stdout,
		"Available bikes - Free slots - Total slots  at station %s\n",
		station);
	fprintf(stdout, "           %4u         %4u          %4u\n", available,
		free, total);
	return 0;
}
