#include <erl_nif.h>
#include "median_int.h"
#include "median_float.h"

static ERL_NIF_TERM median(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
	ERL_NIF_TERM list = argv[0];
	ERL_NIF_TERM head;
	ERL_NIF_TERM tail;
	ErlNifTermType t;
	unsigned int len; // can we use size_t instead of unsigned int?
	double *arr_float = NULL;
	int *arr_int = NULL;

	ERL_NIF_TERM res;

	if (!enif_get_list_length(env, list, &len)) {
		return enif_make_badarg(env); // not a proper list
	}

	if (len == 0) {
		return enif_make_atom(env, "nil");
	}

	enif_get_list_cell(env, list, &head, &tail);
	t = enif_term_type(env, head);
	switch (t) {
		case ERL_NIF_TERM_TYPE_FLOAT:
			arr_float = enif_alloc(len * sizeof(double));
			break;
		case ERL_NIF_TERM_TYPE_INTEGER:
			arr_int = enif_alloc(len * sizeof(int));
			break;
		default:
			return enif_make_badarg(env);
	}
	if (!arr_float && !arr_int) {
		return enif_raise_exception(env, enif_make_atom(env, "alloc"));
	}

	for (unsigned int i=0; i<len; i++) {
		enif_get_list_cell(env, list, &head, &tail);
		list = tail;

		if (t == ERL_NIF_TERM_TYPE_FLOAT) {
			if (!enif_get_double(env, head, &arr_float[i])) {
				enif_free(arr_float);
				return enif_make_badarg(env);
			}
		} else {
			if (!enif_get_int(env, head, &arr_int[i])) {
				enif_free(arr_int);
				return enif_make_badarg(env);
			}
		}
	}

	if (t == ERL_NIF_TERM_TYPE_FLOAT) {
		res = enif_make_double(env, median_double(arr_float, len));
		enif_free(arr_float);
	} else {
		res = enif_make_int(env, median_int(arr_int, len));
		enif_free(arr_int);
	}

	return res;
}

static ErlNifFunc nif_funcs[] = {
	{"select", 1, median},
};

ERL_NIF_INIT(Elixir.Median, nif_funcs, NULL, NULL, NULL, NULL)

