/* This header is precompiled and automatically included into all source files */

/* NOTE: Include guards disabled since it breaks editor parsing */
//#ifndef COMMON_H
//#define COMMON_H

#ifdef __cplusplus
extern "C" {
#endif

/* ========================== *
 * Compiler headers
 * ========================== */

/* Intrinsic header info:
 * <mmintrin.h>  MMX
 * <xmmintrin.h> SSE
 * <emmintrin.h> SSE2
 * <pmmintrin.h> SSE3
 * <tmmintrin.h> SSSE3
 * <smmintrin.h> SSE4.1
 * <nmmintrin.h> SSE4.2
 * <ammintrin.h> SSE4A
 * <wmmintrin.h> AES
 * <immintrin.h> AVX, AVX2, FMA
*/

#include <stddef.h>
#include <stdint.h>
#include <stdarg.h>
#include <intrin.h>
#include <nmmintrin.h> /* SSE4.2 */

/* ========================== *
 * Flag defaults
 * ========================== */

/* Compile definition defaults */
#ifndef RTC
# define RTC 0
#endif

#ifndef ASAN
# define ASAN 0
#endif

#ifndef CRTLIB
# define CRTLIB 0
#endif

#ifndef DEBINFO
# define DEBINFO 0
#endif

#ifndef DEVELOPER
# define DEVELOPER 0
#endif

#ifndef PROFILING
# define PROFILING 0
#endif

#ifndef UNOPTIMIZED
# define UNOPTIMIZED 0
#endif

#ifndef RUN_TESTS
# define RUN_TESTS 0
#endif

#ifndef INCBIN_DIR_RAW
# define INCBIN_DIR ""
#else
# define INCBIN_DIR STRINGIZE(INCBIN_DIR_RAW)
#endif

/* ========================== *
 * Machine context
 * ========================== */

/* Compiler */
#if defined(__clang__)
# define COMPILER_CLANG 1
# define COMPILER_MSVC 0
#elif defined(_MSC_VER)
# define COMPILER_CLANG 0
# define COMPILER_MSVC 1
#else
# error "Unknown compiler"
#endif

/* Operating system */
#if defined(_WIN32)
# define PLATFORM_WINDOWS 1
# define PLATFORM_MAC 0
# define PLATFORM_LINUX 0
#elif defined(__APPLE__) && defined(__MACH__)
# define PLATFORM_WINDOWS 0
# define PLATFORM_MAC 1
# define PLATFORM_LINUX 0
#elif defined(__gnu_linux__)
# define PLATFORM_WINDOWS 0
# define PLATFORM_MAC 0
# define PLATFORM_LINUX 1
#else
# error "Unknown OS"
#endif

#if defined(__cplusplus)
# define LANGUAGE_CPP 1
# define LANGUAGE_C 0
#else
# define LANGUAGE_CPP 0
# define LANGUAGE_C 1
#endif

/* ========================== *
 * Debug
 * ========================== */

/* Compile time assert */
#define CT_ASSERT3(cond, line) struct CT_ASSERT_____##line {int foo[(cond) ? 1 : -1];}
#define CT_ASSERT2(cond, line) CT_ASSERT3(cond, line)
#define CT_ASSERT(cond) CT_ASSERT2(cond, __LINE__)

#if COMPILER_MSVC
# if DEBINFO
#  define DEBUG_ALIAS(var, alias) *(alias) = &(var);
# else
#  define DEBUG_ALIAS(var, alias) *(alias) = &(var);
# endif
#else
# if DEBINFO
#  define DEBUG_ALIAS(var, alias) __attribute((used)) *(alias) = &(var);
# else
#  define DEBUG_ALIAS(var, alias) __attribute((unused)) *(alias) = &(var);
# endif
#endif

#if RTC
# if COMPILER_MSVC
#  define ASSERT(cond) ((cond) ? 1 : ((*(volatile int *)0) = 0, 0))
#  define DEBUGBREAK __debugbreak
# else
#  define ASSERT(cond) ((cond) ? 1 : (__builtin_trap(), 0))
#  define DEBUGBREAK __builtin_debugtrap()
# endif
# define DEBUGBREAKABLE { volatile i32 __DEBUGBREAKABLE_VAR = 0; (UNUSED) __DEBUGBREAKABLE_VAR; }
#else
# define ASSERT(cond) (void)(0)
#endif

/* Address sanitization */
#if ASAN
void __asan_poison_memory_region(void const volatile *, size_t);
void __asan_unpoison_memory_region(void const volatile *add, size_t);
# define ASAN_POISON(addr, size) __asan_poison_memory_region((addr), (size));
# define ASAN_UNPOISON(addr, size) __asan_unpoison_memory_region((addr), (size));
#else
# define ASAN_POISON(addr, size)
# define ASAN_UNPOISON(addr, size)
#endif

/* ========================== *
 * Common macros
 * ========================== */

#if COMPILER_MSVC && LANGUAGE_CPP
#  define CPPFRIENDLY_INITLIST_TYPE(type) type
# else
#  define CPPFRIENDLY_INITLIST_TYPE(type) (type)
#endif

#if 1
# define INLINE static inline
#else
/* TODO: benchmark benefits of forced inlining */
# define INLINE static inline __attribute((always_inline))
#endif

#if COMPILER_MSVC
# define FORCE_INLINE static inline __forceinline
#else
# define FORCE_INLINE static inline __attribute((always_inline))
#endif

/* Separate `static` usage into different keywords for easier grepping */
#define LOCAL_PERSIST static
#define INTERNAL static
#define GLOBAL static

/* Read-only */
#if PLATFORM_WINDOWS
# if COMPILER_MSVC
#   pragma section(".rdata$", read)
#   define READONLY __declspec(allocate(".rdata$"))
# else
#  define READONLY __declspec(allocate(".rdata"))
# endif
#elif PLATFORM_MAC
#define READONLY __attribute((section("__TEXT,__const")))
#else
#define READONLY __attribute((section(".rodata")))
#endif

/* Markup */
#define UNUSED void

#if COMPILER_CLANG
#define FALLTHROUGH __attribute((fallthrough))
#else
#define FALLTHROUGH
#endif

/* Sizes */
#define KILOBYTE(n) (n*1024ULL)
#define MEGABYTE(n) (n*KILOBYTE(1024ULL))
#define GIGABYTE(n) (n*MEGABYTE(1024ULL))
#define TERABYTE(n) (n*GIGABYTE(1024ULL))

/* typeof */

#if COMPILER_MSVC
/* Typeof not supported in MSVC */
#  define typeof(type) ASSERT(false)
#  define TYPEOF_DEFINED 0
#else
# if LANGUAGE_CPP ||  (__STDC_VERSION__ < 202311L)
#  define typeof(type) __typeof__(type)
#  define TYPEOF_DEFINED 1
# endif
#endif

/* alignof */
#if COMPILER_MSVC || (LANGUAGE_C && (__STDC_VERSION__ < 202311L))
# define alignof(type) __alignof(type)
#endif

#define ARRAY_COUNT(a) (sizeof(a) / sizeof((a)[0]))

/* Field macros */
#define FIELD_SIZEOF(type, field) sizeof(((type *)0)->field)

#if COMPILER_MSVC && !defined _CRT_USE_BUILTIN_OFFSETOF
# define FIELD_OFFSETOF(type, field) ((u64)&(((type *)0)->field))
#else
# define FIELD_OFFSETOF(type, field) __builtin_offsetof(type, field)
#endif

/* Bool */
#if !LANGUAGE_CPP
# define true 1
# define false 0
#endif

/* Array */
#define IS_INDEXABLE(a) (sizeof(a[0]))
#define IS_ARRAY(a) (IS_INDEXABLE(a) && (((void *)&a) == ((void *)a)))

/* Pack */
#if COMPILER_MSVC
# define PACK(s) __pragma(pack(push, 1)) s __pragma(pack(pop))
#else
# define PACK(s) s __attribute((__packed__))
#endif

/* Color */
#define RGBA_32(r, g, b, a) (u32)((u32)(r) | ((u32)(g) << 8) | ((u32)(b) << 16) | ((u32)(a) << 24))
#define RGB_32(r, g, b) RGBA_32(r, g, b, 0xFF)

#define _RGB_F_TO_U8(f) ((u8)((f * 255.0f) + 0.5f))
#define RGBA_32_F(r, g, b, a) RGBA_32(_RGB_F_TO_U8(r), _RGB_F_TO_U8(g), _RGB_F_TO_U8(b), _RGB_F_TO_U8(a))
#define RGB_32_F(r, g, b) RGBA_32_F(r, g, b, 1.f)

#define COLOR_WHITE     RGB_32(0xFF, 0xFF, 0xFF)
#define COLOR_BLACK     RGB_32(0,    0,    0   )
#define COLOR_RED       RGB_32(0xFF, 0,    0   )
#define COLOR_GREEN     RGB_32(0,    0xFF, 0   )
#define COLOR_BLUE      RGB_32(0,    0,    0xFF)

/* Barrier */
#if COMPILER_MSVC
# define WRITE_BARRIER() _WriteBarrier()
# define READ_BARRIER() _ReadBarrier()
#elif defined(__x86_64) || defined(__i386__)
# define WRITE_BARRIER() __asm__ volatile("" ::: "memory")
# define READ_BARRIER() __asm__ volatile("" ::: "memory")
#else
# error "Memory barriers not implemented"
#endif

/* Cat */
#define CAT1(a, b) a ## b
#define CAT(a, b) CAT1(a, b)

/* Stringize */
#define STRINGIZE2(x) #x
#define STRINGIZE(x) STRINGIZE2(x)


#if 0
/* ========================== *
 * Bit utils
 * ========================== */

#define bit_set(i, mask) (i |= mask)
#define bit_clear(i, mask) (i &= ~mask)
#define bit_flip(i, mask) (i ^= mask)
#endif

/* ========================== *
 * Primitive types
 * ========================== */

typedef int8_t      i8;
typedef int16_t     i16;
typedef int32_t     i32;
typedef int64_t     i64;
typedef uint8_t     u8;
typedef uint16_t    u16;
typedef uint32_t    u32;
typedef uint64_t    u64;
typedef float       f32;
typedef double      f64;
typedef i8          b8;
typedef i32         b32;

#define U8_MAX          (0xFF)
#define U16_MAX         (0xFFFF)
#define U32_MAX         (0xFFFFFFFF)
#define U64_MAX         (0xFFFFFFFFFFFFFFFFULL)

#define I8_MAX          (0x7F)
#define I16_MAX         (0x7FFF)
#define I32_MAX         (0x7FFFFFFF)
#define I64_MAX         (0x7FFFFFFFFFFFFFFFLL)

#define I8_MIN          ((i8)-0x80)
#define I16_MIN         ((i16)-0x8000)
#define I32_MIN         ((i32)-0x80000000)
#define I64_MIN         ((i64)-0x8000000000000000LL)

GLOBAL const u32 _f32_infinity_u32 = 0x7f800000;
GLOBAL const f32 *_f32_infinity = (f32 *)&_f32_infinity_u32;
GLOBAL const u64 _f64_infinity_u64 = 0x7ff0000000000000ULL;
GLOBAL const f64 *_f64_infinity = (f64 *)&_f64_infinity_u64;
#define F32_INFINITY    (*_f32_infinity)
#define F64_INFINITY    (*_f64_infinity)

#define PI              ((f32)3.14159265358979323846)
#define TAU             ((f32)6.28318530717958647693)

/* ========================== *
 * Big int
 * ========================== */

#if COMPILER_CLANG

#define U128(hi64, lo64) (((u128)(hi64) << 64) | (lo64))
#define U128_HI64(a) ((u64)((a) >> 64))
#define U128_LO64(a) ((u64)(a))

typedef __uint128_t u128;

INLINE b32 u128_eq(u128 a, u128 b) { return a == b; }
INLINE u128 u128_xor_u8(u128 a, u8 b) { return a ^ b; }
INLINE u128 u128_mul(u128 a, u128 b) { return a * b; }

#elif COMPILER_MSVC

#if LANGUAGE_CPP
# define U128(hi64, lo64) { (hi64), (lo64) }
#else
# define U128(hi64, lo64) ((u128) { .hi = (hi64), .lo = (lo64) })
#endif
#define U128_HI64(a) ((a).hi)
#define U128_LO64(a) ((a).lo)

typedef struct { u64 hi; u64 lo; } u128;

INLINE b32 u128_eq(u128 a, u128 b) { return a.hi == b.hi && a.lo == b.lo; }
INLINE u128 u128_xor_u8(u128 a, u8 b) { return U128(a.hi, a.lo ^ b); }

/* https://www.codeproject.com/Tips/784635/UInt-Bit-Operations */

INLINE u128 u128_mul(u128 a, u128 b)
{
    u64 a1 = (a.lo & 0xffffffff);
    u64 b1 = (b.lo & 0xffffffff);
    u64 t = (a1 * b1);
    u64 w3 = (t & 0xffffffff);
    u64 k = (t >> 32);

    a.lo >>= 32;
    t = (a.lo * b1) + k;
    k = (t & 0xffffffff);
    u64 w1 = (t >> 32);

    b.lo >>= 32;
    t = (a1 * b.lo) + k;
    k = (t >> 32);

    u128 res = U128((a.lo * b.lo) + w1 + k, (t << 32) + w3);
    res.hi += (a.hi * b.lo) + (a.lo * b.hi);

    return res;
}

#endif

/* ========================== *
 * Atomics
 * ========================== */

struct atomic_i32 {
    volatile i32 _v;
};

struct atomic_i64 {
    volatile i64 _v;
};

struct atomic_u32 {
    volatile u32 _v;
};

struct atomic_u64 {
    volatile u64 _v;
};

struct atomic_ptr {
    volatile void *_v;
};

/* ========================== *
 * Common structs
 * ========================== */

struct arena {
    u64 pos;
    u64 committed;
    u64 reserved;
    u8 *base;
#if RTC
    b32 readonly;
#endif
};

struct string {
    u64 len;
    u8 *text;
};

struct string16 {
    u64 len;
    u16 *text;
};

struct string32 {
    u64 len;
    u32 *text;
};

struct image_rgba {
    u32 width;
    u32 height;
    u32 *pixels;  /* Array of [width * height] pixels */
};

struct pcm {
    u64 count;
    i16 *samples;
};

struct buffer {
    u64 size;
    u8 *data;
};

struct renderer_handle {
    u64 v[1];
};

/* ========================== *
 * Tag structs
 * ========================== */

struct sprite_tag {
    u128 hash;
    struct string path;
};

/* ========================== *
 * Buffer utils
 * ========================== */

/* Utility buffer constructor */
#define BUFFER(size, data) (CPPFRIENDLY_INITLIST_TYPE(struct buffer) { (size), (data) })

/* Utility buffer constructor from static array */
#define BUFFER_FROM_ARRAY(a) \
    ( \
        /* Must be array */ \
        ASSERT(IS_ARRAY(a)), \
        /* Must be array of bytes */ \
        ASSERT(sizeof((a)[0]) == sizeof(u8)), \
        ((struct buffer) { .size = ARRAY_COUNT(a), .data = (u8 *)(a) }) \
    )

#define BUFFER_FROM_STRING(str) (CPPFRIENDLY_INITLIST_TYPE(struct buffer) { (str).len, (str).text })

#define BUFFER_FROM_POINTERS(p1, p2) (CPPFRIENDLY_INITLIST_TYPE(struct buffer) { (u8 *)(p2) - (u8 *)(p1), (u8 *)p1 })

#define BUFFER_FROM_STRUCT(ptr) (CPPFRIENDLY_INITLIST_TYPE(struct buffer) { sizeof(*(ptr)), (ptr) })

/* ========================== *
 * String utils
 * ========================== */

/* Expand C string literal with size for string initialization */
#define STR(cstr_lit) CPPFRIENDLY_INITLIST_TYPE(struct string) { (sizeof((cstr_lit)) - 1), (u8 *)(cstr_lit) }

/* Same as `STR`, but works with static variable initialization */
#define STR_NOCAST(cstr_lit) { .len = (sizeof((cstr_lit)) - 1), .text = (u8 *)(cstr_lit) }

#define STRING_FROM_BUFFER(buff) (CPPFRIENDLY_INITLIST_TYPE(struct string) { buff.size, buff.data })

#define STRING_FROM_ARRAY(a) STRING_FROM_BUFFER(BUFFER_FROM_ARRAY(a))

/* ========================== *
 * Math types
 * ========================== */

#define V2(x, y) ((struct v2) { (x), (y) })
struct v2 {
    f32 x, y;
};

struct v2_array {
    struct v2 *points;
    u64 count;
};

#define V3(x, y, z) ((struct v3) { (x), (y), (z) })
struct v3 {
    f32 x, y, z;
};

struct v3_array {
    struct v3 *points;
    u64 count;
};

#define V4(x, y, z, w) ((struct v4) { (x), (y), (z), (w) })
struct v4 {
    f32 x, y, z, w;
};

struct v4_array {
    struct v4 *points;
    u64 count;
};

struct xform {
    struct v2 bx;  /* X basis vector (x axis) */
    struct v2 by;  /* Y basis vector (y axis)*/
    struct v2 og;  /* Translation vector (origin) */
};

struct mat4x4 {
    f32 e[4][4];
};

#define RECT(_x, _y, _width, _height) (struct rect) { .x = (_x), .y = (_y), .width = (_width), .height = (_height) }
#define RECT_FROM_V2(_pos, _size) (struct rect) { .pos = (_pos), .size = (_size) }
struct rect {
    union {
        struct { f32 x, y, width, height; };
        struct { struct v2 pos, size; };
    };
};

INLINE b32 rect_eq(struct rect r1, struct rect r2) { return r1.x == r2.x && r1.y == r2.y && r1.width == r2.width && r1.height == r2.height; }

/* Values expected to be normalized 0.0 -> 1.0 */
#define CLIP_ALL ((struct clip_rect) { { 0.0f, 0.0f }, { 1.0f, 1.0f } })
struct clip_rect {
    struct v2 p1, p2;
};

#define QUAD_UNIT_SQUARE (struct quad) { V2(0, 0), V2(0, 1), V2(1, 1), V2(1, 0) }
#define QUAD_UNIT_SQUARE_CENTERED (struct quad) { V2(-0.5f, -0.5f), V2(0.5f, -0.5f), V2(0.5f, 0.5f), V2(-0.5f, 0.5f) }
struct quad {
    struct v2 p1, p2, p3, p4;
};

/* (T)ranslation, (R)otation, (S)cale */
#define TRS(...) ((struct trs) { .t = V2(0,0), .s = V2(1, 1), .r = 0, __VA_ARGS__ })
struct trs {
    struct v2 t;
    struct v2 s;
    f32 r;
};

/* ========================== *
 * Common utilities
 * ========================== */

INLINE u8 min_u8(u8 a, u8 b) { return a <= b ? a : b; }
INLINE u8 max_u8(u8 a, u8 b) { return a >= b ? a : b; }
INLINE u32 min_u32(u32 a, u32 b) { return a <= b ? a : b; }
INLINE u32 max_u32(u32 a, u32 b) { return a >= b ? a : b; }
INLINE u64 min_u64(u64 a, u64 b) { return a <= b ? a : b; }
INLINE u64 max_u64(u64 a, u64 b) { return a >= b ? a : b; }
INLINE i32 min_i32(i32 a, i32 b) { return a <= b ? a : b; }
INLINE i32 max_i32(i32 a, i32 b) { return a >= b ? a : b; }
INLINE i64 min_i64(i64 a, i64 b) { return a <= b ? a : b; }
INLINE i64 max_i64(i64 a, i64 b) { return a >= b ? a : b; }
INLINE f32 min_f32(f32 a, f32 b) { return a <= b ? a : b; }
INLINE f32 max_f32(f32 a, f32 b) { return a >= b ? a : b; }
INLINE f64 min_f64(f64 a, f64 b) { return a <= b ? a : b; }
INLINE f64 max_f64(f64 a, f64 b) { return a >= b ? a : b; }

INLINE u32 clamp_u32(u32 v, u32 min, u32 max) { return v < min ? min : v > max ? max : v; }
INLINE u64 clamp_u64(u64 v, u64 min, u64 max) { return v < min ? min : v > max ? max : v; }
INLINE i32 clamp_i32(i32 v, i32 min, i32 max) { return v < min ? min : v > max ? max : v; }
INLINE i64 clamp_i64(i64 v, i64 min, i64 max) { return v < min ? min : v > max ? max : v; }
INLINE f32 clamp_f32(f32 v, f32 min, f32 max) { return v < min ? min : v > max ? max : v; }
INLINE f64 clamp_f64(f64 v, f64 min, f64 max) { return v < min ? min : v > max ? max : v; }

/* ========================== *
 * Profiling
 * ========================== */

#if PROFILING

#include "third_party/tracy/tracy/TracyC.h"

#define PROFILING_CAPTURE_FRAME_IMAGE 1

/* Clang/GCC cleanup macros */
#if COMPILER_MSVC
# error "MSVC not supported for profiling (cleanup attributes are required for profiling markup)"
#else
# ifdef TRACY_NO_CALLSTACK
#  define __prof static const struct ___tracy_source_location_data CAT(__tracy_source_location,__LINE__) = { NULL, __func__,  __FILE__, (uint32_t)__LINE__, 0 }; __attribute((cleanup(__prof_zone_cleanup_func))) TracyCZoneCtx __tracy_ctx = ___tracy_emit_zone_begin( &CAT(__tracy_source_location,__LINE__), true );
#  define __profscope(name) static const struct ___tracy_source_location_data CAT(__tracy_source_location,__LINE__) = { NULL, #name,  __FILE__, (uint32_t)__LINE__, 0 }; __attribute((cleanup(__prof_zone_cleanup_func))) TracyCZoneCtx __tracy_ctx = ___tracy_emit_zone_begin( &CAT(__tracy_source_location,__LINE__), true );
# else
#  define __prof static const struct ___tracy_source_location_data CAT(__tracy_source_location,__LINE__) = { NULL, __func__,  __FILE__, (uint32_t)__LINE__, 0 }; __attribute((cleanup(__prof_zone_cleanup_func))) TracyCZoneCtx __tracy_ctx = ___tracy_emit_zone_begin_callstack( &CAT(__tracy_source_location,__LINE__), TRACY_CALLSTACK, true );
#  define __profscope(name) static const struct ___tracy_source_location_data CAT(__tracy_source_location,__LINE__) = { NULL, #name,  __FILE__, (uint32_t)__LINE__, 0 }; __attribute((cleanup(__prof_zone_cleanup_func))) TracyCZoneCtx __tracy_ctx = ___tracy_emit_zone_begin_callstack( &CAT(__tracy_source_location,__LINE__), TRACY_CALLSTACK, true );
# endif
#endif
INLINE void __prof_zone_cleanup_func(TracyCZoneCtx *__tracy_ctx) { TracyCZoneEnd(*__tracy_ctx); }

#define __profalloc(ptr, size)      TracyCAlloc(ptr, size)
#define __proffree(ptr)             TracyCFree(ptr)
#define __profmsg(txt, len, col)    TracyCMessageC(txt, len, col);
#define __profframe(name)           TracyCFrameMarkNamed(name)

#if PROFILING_CAPTURE_FRAME_IMAGE
# define __profframeimage(image, width, height, offset, flipped)    TracyCFrameImage(image, width, height, offset, flipped);
#else
# define __profframeimage(image, width, height, offset, flipped)
#endif  /* PROFILING_CAPTURE_FRAME_IMAGE */

#else

#define PROFILING_CAPTURE_FRAME_IMAGE 0

#define __prof
#define __profscope(name)
#define __profalloc(ptr, size)
#define __proffree(ptr)
#define __profmsg(txt, len, col)
#define __profframe(name)
#define __profframeimage(image, width, height, offset, flipped)

#endif  /* PROFILING */

/* ========================== *
 * Configurable constants
 * ========================== */

#include "config.h"

#ifdef __cplusplus
}
#endif
