#include "include/native.h"
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>

struct Imported_Handle {
	int value;
};

static struct Imported_Handle handle = {7};
int imported_global = 0;
const int imported_const_global = 9;
const int imported_const_array[2] = {10, 20};
const Imported_Const_Array imported_typedef_const_array = {11, 12};
int imported_redeclared_array[4] = {13, 14, 15, 16};
Imported_Value imported_record_global = {40};
const Imported_Array_Record imported_const_array_record = {{20, 21}};
Imported_Array_Record imported_mutable_array_record = {{0, 1}};
Imported_Enum imported_enum_global = IMPORTED_ENUM_VALUE;
int child_shared_global = 0;

int child_value(int value) {
	return value;
}

int imported_add(imported_int left, int right) {
	return left + right;
}

unsigned long imported_scalar(unsigned char value, unsigned long extra) {
	return value + extra;
}

Imported_Handle *imported_handle(void) {
	return &handle;
}

int imported_read(const Imported_Handle *value) {
	return value->value;
}

int imported_string(const char *value) {
	if (strcmp(value, "hello") != 0) {
		abort();
	}
	return 0;
}

int imported_apply(Imported_Mapper mapper, int value) {
	if (mapper == NULL) {
		abort();
	}
	int result = mapper(value);
	if (result != value * 2) {
		abort();
	}
	return result;
}

int imported_enum_check(Imported_Enum first, Imported_Enum second) {
	if (!(first == IMPORTED_ENUM_NEGATIVE && second == IMPORTED_ENUM_VALUE)) {
		abort();
	}
	return 0;
}

int imported_enum_variadic(int expected, ...) {
	va_list args;
	va_start(args, expected);
	int value = va_arg(args, int);
	va_end(args);
	if (value != expected) {
		abort();
	}
	return 0;
}

Imported_Enum_Record imported_enum_record(Imported_Enum_Record value) {
	if (value.value != IMPORTED_ENUM_BACK) {
		abort();
	}
	return value;
}

Imported_Enum imported_enum_apply(Imported_Enum_Callback callback, Imported_Enum value) {
	if (callback == NULL || callback(value) != value) {
		abort();
	}
	return value;
}

int imported_check_state(
	Imported_Color color,
	int macro_value,
	int const_value,
	int global_value,
	int record_value,
	int octal_value,
	unsigned int uint_value,
	int redefined_value,
	int inactive_undef_value,
	int repeated_value,
	unsigned int hex_e_value,
	long negative_decimal_value,
	unsigned int negative_hex_value,
	unsigned int negative_uint_value,
	float float_value,
	double double_value
) {
	if (!(color.r == 255 &&
	      color.g == 255 &&
	      color.b == 255 &&
	      color.a == 255 &&
	      macro_value == 42 &&
	      const_value == 9 &&
	      global_value == 42 &&
	      record_value == 42 &&
	      octal_value == 8 &&
	      uint_value == 4294967295U &&
	      redefined_value == 2 &&
	      inactive_undef_value == 77 &&
	      repeated_value == 123 &&
	      hex_e_value == 0xDEADBEEF &&
	      negative_decimal_value == -2147483648L &&
	      negative_hex_value == 0x80000000U &&
	      negative_uint_value == 0xFFFFFFFFU &&
	      float_value == 2.5f &&
	      double_value == 6.25)) {
		abort();
	}
	return 0;
}

int imported_check_zero_state(
	Imported_Color partial,
	Imported_Zero_Outer nested,
	Imported_Zero_Union first_union
) {
	if (!(partial.r == 7 &&
	      partial.g == 0 &&
	      partial.b == 0 &&
	      partial.a == 0 &&
	      nested.head == 5 &&
	      nested.inner.weight == 0.0f &&
	      nested.inner.values[0] == 0 &&
	      nested.inner.values[1] == 0 &&
	      nested.inner.label == NULL &&
	      nested.choice.integer == 0 &&
	      first_union.integer == 9)) {
		abort();
	}
	return 0;
}

int imported_check_array_record(void) {
	if (!(imported_const_array_record.values[0] == 20 &&
	      imported_const_array_record.values[1] == 21 &&
	      imported_mutable_array_record.values[0] == 42 &&
	      imported_mutable_array_record.values[1] == 1)) {
		abort();
	}
	return 0;
}

int imported_check_conversions(Imported_Conversions value) {
	if (!(value.wrapped == 255 &&
	      value.truncated == 3 &&
	      value.from_integer == 42.0f &&
	      value.from_float == 16777216.0 &&
	      value.pointer == NULL)) {
		abort();
	}
	return 0;
}

int imported_check_inline(int scalar, int record) {
	if (!(scalar == 6 && record == 14)) {
		abort();
	}
	return 0;
}

int imported_variadic(int marker, ...) {
	va_list args;
	va_start(args, marker);
	int narrow_signed = va_arg(args, int);
	int narrow_unsigned = va_arg(args, int);
	double float_value = va_arg(args, double);
	double c_float_value = va_arg(args, double);
	const unsigned char *pointer = va_arg(args, const unsigned char *);
	const unsigned char *nullable = va_arg(args, const unsigned char *);
	va_end(args);
	if (!(marker == 7 &&
	      narrow_signed == -2 &&
	      narrow_unsigned == 3 &&
	      float_value == 4.0 &&
	      c_float_value == 5.0 &&
	      pointer[0] == 'o' &&
	      nullable == pointer)) {
		abort();
	}
	return 0;
}

#ifdef BROLANG_FEATURE
int configured_value(int value) {
	return value;
}
#endif
