#pragma once
#include <stdint.h>

#if defined(__GNUC__)
#include <cpuid.h>
#elif defined(_MSC_VER)
#include <intrin.h>
#endif

#if CPUINFO_MOCK
#include <cpuinfo-mock.h>
#endif
#include <x86/api.h>

#if defined(__GNUC__) || defined(_MSC_VER)
static inline struct cpuid_regs cpuid(uint32_t eax) {
#if CPUINFO_MOCK
	uint32_t regs_array[4];
	cpuinfo_mock_get_cpuid(eax, regs_array);
	return (struct cpuid_regs){
		.eax = regs_array[0],
		.ebx = regs_array[1],
		.ecx = regs_array[2],
		.edx = regs_array[3],
	};
#else
	struct cpuid_regs regs;
#if defined(__GNUC__)
	__cpuid(eax, regs.eax, regs.ebx, regs.ecx, regs.edx);
#else
	int regs_array[4];
	__cpuid(regs_array, (int)eax);
	regs.eax = regs_array[0];
	regs.ebx = regs_array[1];
	regs.ecx = regs_array[2];
	regs.edx = regs_array[3];
#endif
	return regs;
#endif
}

static inline struct cpuid_regs cpuidex(uint32_t eax, uint32_t ecx) {
#if CPUINFO_MOCK
	uint32_t regs_array[4];
	cpuinfo_mock_get_cpuidex(eax, ecx, regs_array);
	return (struct cpuid_regs){
		.eax = regs_array[0],
		.ebx = regs_array[1],
		.ecx = regs_array[2],
		.edx = regs_array[3],
	};
#else
	struct cpuid_regs regs;
#if defined(__GNUC__)
	__cpuid_count(eax, ecx, regs.eax, regs.ebx, regs.ecx, regs.edx);
#else
	int regs_array[4];
	__cpuidex(regs_array, (int)eax, (int)ecx);
	regs.eax = regs_array[0];
	regs.ebx = regs_array[1];
	regs.ecx = regs_array[2];
	regs.edx = regs_array[3];
#endif
	return regs;
#endif
}
#endif

static inline uint64_t xgetbv(uint32_t ext_ctrl_reg) {
#ifdef _MSC_VER
	return (uint64_t)_xgetbv((unsigned int)ext_ctrl_reg);
#else
	uint32_t lo, hi;
	__asm__(".byte 0x0F, 0x01, 0xD0" : "=a"(lo), "=d"(hi) : "c"(ext_ctrl_reg));
	return ((uint64_t)hi << 32) | (uint64_t)lo;
#endif
}
