#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <mysql/mysql.h>

#define NAME        "minacum"
#define N_ARGS      3

#define ARG_IVAL    0
#define ARG_NUM     1
#define ARG_BASE    2

#define INTERVAL(ptr)   ((Interval *) ptr)
#define GET_ARG(n)      (args->args[n] == NULL ? 0 : *((long long*) args->args[n]))

extern "C" {

typedef struct _Interval Interval;
struct _Interval {
	long long num;
	long long sum;
	Interval* next;
};

void free_data(UDF_INIT *initid) {
	Interval* ival = INTERVAL(initid->ptr);
	Interval* cur;
	while (ival != NULL) {
		cur = ival;
		ival = ival->next;
		free(cur);
	}
	initid->ptr = NULL;
}

long long minacum(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error) {
	Interval* ival = INTERVAL(initid->ptr);
	if (ival == NULL) return 0;

	long long min;
	long long base = GET_ARG(ARG_BASE);

	if (ival->num == base) {
		min = ival->sum;
		ival = ival->next;
	} else
		min = 0;

	long long acum = min;

	while (ival != NULL) {
		acum += ival->sum;
		if (acum < min) min = acum;
		ival = ival->next;
	}

	return min;
}

void minacum_add(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error) {
	long long num = GET_ARG(ARG_IVAL);
	long long amount = GET_ARG(ARG_NUM);

	Interval* ival = INTERVAL(initid->ptr);
	Interval* prev = ival;

	while (ival != NULL && ival->num < num) {
		prev = ival;
		ival = ival->next;
	}

	if (ival == NULL || ival->num > num) {
		Interval* new_ival = INTERVAL(malloc(sizeof(Interval)));
		new_ival->num = num;
		new_ival->sum = amount;
		new_ival->next = ival;
		if (prev != ival)
			prev->next = new_ival;
		else
			initid->ptr = (char *) new_ival;
	} else
		ival->sum += amount;
}

void minacum_clear(UDF_INIT *initid, char *is_null, char *error) {
	free_data(initid);
}

bool minacum_init(UDF_INIT *initid, UDF_ARGS *args, char *message) {
	if (args->arg_count == N_ARGS) {
		args->arg_type[ARG_IVAL] = INT_RESULT;
		args->arg_type[ARG_NUM] = INT_RESULT;
		args->arg_type[ARG_BASE] = INT_RESULT;
		initid->maybe_null = 0;
		initid->const_item = 0;
		initid->ptr = NULL;
		return 0;
	} else {
		sprintf(message, "%s must have %d parameters", NAME, N_ARGS);
		return 1;
	}
}

void minacum_deinit(UDF_INIT *initid) {
	free_data(initid);
}

}