#include <uwsgi.h>

extern struct uwsgi_server uwsgi;

static struct uwsgi_rrdtool {
	void *lib;
	char *lib_name;

	int (*create)(int, char **);
	int (*update)(int, char **);

	int freq;

	char *update_area;
	struct uwsgi_string_list *directory;

	struct uwsgi_stats_pusher *pusher;
} u_rrd;

static struct uwsgi_option rrdtool_options[] = {
	{"rrdtool", required_argument, 0, "store rrd files in the specified directory", uwsgi_opt_add_string_list, &u_rrd.directory, UWSGI_OPT_MASTER|UWSGI_OPT_METRICS},
	{"rrdtool-freq", required_argument, 0, "set collect frequency", uwsgi_opt_set_int, &u_rrd.freq, 0},
	{"rrdtool-lib", required_argument, 0, "set the name of rrd library (default: librrd.so)", uwsgi_opt_set_str, &u_rrd.lib_name, 0},
	{0, 0, 0, 0, 0, 0, 0},

};


static int rrdtool_init() {

	if (!u_rrd.lib_name) {
		u_rrd.lib_name = "librrd.so";
	}

	u_rrd.lib = dlopen(u_rrd.lib_name, RTLD_LAZY);
	if (!u_rrd.lib) return -1;

	u_rrd.create = dlsym(u_rrd.lib, "rrd_create");
	if (!u_rrd.create) {
		dlclose(u_rrd.lib);
		return -1;
	}

	u_rrd.update = dlsym(u_rrd.lib, "rrd_update");
	if (!u_rrd.update) {
		dlclose(u_rrd.lib);
		return -1;
	}

	uwsgi_log_initial("*** RRDtool library available at %p ***\n", u_rrd.lib);

	return 0;
}

/*

	create .rrd files if needed

*/
static void rrdtool_post_init() {

	if (!u_rrd.create) return;

	// do not waste time if no --rrdtool option is defined
	if (!u_rrd.directory) return;

	if (!u_rrd.freq) u_rrd.freq = 300;

	char *argv[7];
	argv[0] = "create";


	// create RRA
	argv[3] = "RRA:AVERAGE:0.5:1:288" ;
	argv[4] = "RRA:AVERAGE:0.5:12:168" ;
	argv[5] = "RRA:AVERAGE:0.5:288:31" ;
	argv[6] = "RRA:AVERAGE:0.5:2016:52";

	struct uwsgi_string_list *usl = NULL;
	uwsgi_foreach(usl, u_rrd.directory) {
		char *dir = uwsgi_expand_path(usl->value, strlen(usl->value), NULL);
                if (!dir) {
                        uwsgi_error("rrdtool_post_init()/uwsgi_expand_path()");
                        exit(1);
                }
		struct uwsgi_metric *um = uwsgi.metrics;
		// here locking is useless, but maybe in the future we could move this part
		// somewhere else
		int created = 0;
		uwsgi_rlock(uwsgi.metrics_lock);
		while(um) {
			char *filename = uwsgi_concat4(dir, "/", um->name, ".rrd");
			if (!uwsgi_file_exists(filename)) {
				argv[1] = filename;
				if (um->type == UWSGI_METRIC_GAUGE) {
					argv[2] = "DS:metric:GAUGE:600:0:U";
				}
				else {
					argv[2] = "DS:metric:DERIVE:600:0:U";
				}
				if (u_rrd.create(7, argv)) {
					uwsgi_log("unable to create rrd file for metric \"%s\"\n", um->name);
					uwsgi_error("rrd_create()");
					exit(1);
				}
				created++;
			}
			free(filename);
			um = um->next;
		}
		uwsgi_rwunlock(uwsgi.metrics_lock);
	
		uwsgi_log("created %d new rrd files in %s\n", created, dir);

		struct uwsgi_stats_pusher_instance *uspi = uwsgi_stats_pusher_add(u_rrd.pusher, NULL);
        	uspi->freq = u_rrd.freq;
		uspi->data = dir;
        	// no need to generate the json
        	uspi->raw=1;
	}

}

static void rrdtool_push(struct uwsgi_stats_pusher_instance *uspi, time_t now, char *json, size_t json_len) {

	if (!u_rrd.update) return ;

	// standard stats pusher
	if (!uspi->data) {
		if (!uspi->arg) {
			uwsgi_log("invalid rrdtool stats pusher syntax\n");
			exit(1);
		}
		uspi->data = uwsgi_expand_path(uspi->arg, strlen(uspi->arg), NULL);
		if (!uspi->data) {
			uwsgi_error("rrdtool_push()/uwsgi_expand_path()");
                        exit(1);
		}
		if (!u_rrd.freq) u_rrd.freq = 300;
		uspi->freq = u_rrd.freq;
	}

	// 1k will be more than enough
	char buf[1024];
	char *argv[3];
	argv[0] = "update";

	struct uwsgi_metric *um = uwsgi.metrics;
	while(um) {
		uwsgi_rlock(uwsgi.metrics_lock);
		int ret = snprintf(buf, 1024, "N:%lld", (long long) (*um->value));
		uwsgi_rwunlock(uwsgi.metrics_lock);
		if (um->reset_after_push){
			uwsgi_wlock(uwsgi.metrics_lock);
			*um->value = um->initial_value;
			uwsgi_rwunlock(uwsgi.metrics_lock);
		}
		if (ret < 3 || ret >= 1024) {
			uwsgi_log("unable to update rrdtool metric for %s\n", um->name);
			goto next;
		}		
		char *filename = uwsgi_concat4(uspi->data, "/", um->name, ".rrd");
		argv[1] = filename;
                argv[2] = buf;
                if (u_rrd.update(3, argv)) {
                	uwsgi_log_verbose("ERROR: rrd_update(\"%s\", \"%s\")\n", argv[1], argv[2]);
                }
		free(filename);
next:
		um = um->next;
	}

}

static void rrdtool_register() {
	u_rrd.pusher = uwsgi_register_stats_pusher("rrdtool", rrdtool_push);
	u_rrd.pusher->raw = 1;
}

struct uwsgi_plugin rrdtool_plugin = {

	.name = "rrdtool",
	.options = rrdtool_options,
	
	.on_load = rrdtool_register,

	// this is the best phase to create rrd files (if needed)
	.preinit_apps = rrdtool_post_init,
	// here we get pointers to rrdtool functions
	.init = rrdtool_init,
};
