// Value handling functions

#define INT(ptr)    (*((long long *) ptr))
#define REAL(ptr)   (*((double *) ptr))
#define min(a,b)    (a < b ? a : b)

typedef union {
	long long i;
	double d;
	char * s;
} Val;

typedef struct {
	Val val;
	int is_null;
	Item_result type;
	unsigned long len;
	int free;
} Value;

#define value_ptr(value)    (value->is_null ? NULL : (char *) &value->val)

void value_init(Value * value) {
	value->is_null = 1;
	value->free = 0;
}

void value_free_val(Value *value) {
	if (value->free) {
		free(value->val.s);
		value->free = 0;
	}
}

void value_set_null(Value *value) {
	value_free_val(value);
	value->is_null = 1;
}

void value_set(Value * value, UDF_ARGS *args, int arg_index, int copy) {
	char * arg = args->args[arg_index];

	if (arg != NULL) {
		value->is_null = 0;
		value->type = args->arg_type[arg_index];

		switch (value->type) {
		case INT_RESULT:
			value->val.i = INT(arg);
			break;
		case REAL_RESULT:
			value->val.d = REAL(arg);
			break;
		default:
			value->len = args->lengths[arg_index];
			if (copy) {
				value->free = 1;
				value->val.s = (char *) malloc(value->len);
				memcpy(value->val.s, arg, value->len);
			} else
				value->val.s = arg;
		}
	} else
		value->is_null = 1;
}

void value_from_arg(Value * value, UDF_ARGS *args, int arg_index) {
	value->free = 0;
	value_set(value, args, arg_index, 0);
}

void value_copy_arg(Value * value, UDF_ARGS *args, int arg_index) {
	value_free_val(value);
	value_set(value, args, arg_index, 1);
}

int value_compare(Value *a, Value *b) {
	int cmp;
	double real_cmp;

	if (!a->is_null && !b->is_null && a->type == b->type) {
		switch (a->type) {
		case INT_RESULT:
			cmp = a->val.i - b->val.i;
			break;
		case REAL_RESULT:
			real_cmp = a->val.d - b->val.d;
			cmp = real_cmp == 0.0 ? 0 : (real_cmp > 0.0 ? 1 : -1);
			break;
		default: // STRING_RESULT & DECIMAL_RESULT
			cmp = strncmp(a->val.s, b->val.s, min(a->len, b->len));
			if (cmp == 0) cmp = a->len - b->len;
		}
	} else
		cmp = b->is_null && a->is_null ? 0 : (a->is_null ? -1 : 1);

	return cmp;
}