#pragma once
#ifndef _CRT_SECURE_NO_WARNINGS
    #define _CRT_SECURE_NO_WARNINGS
#endif
#include <stdint.h>
#include <string.h>
#include <stdarg.h>
#include <signal.h>
#include <stddef.h>
#include <stdlib.h>


#if defined(__APPLE__) && defined(__MACH__)
    #define OS_POSIX 1
    #define OS_MAC 1
#elif defined(_WIN32)
    #define OS_WINDOWS 1
#elif defined(__linux__)
    #define OS_POSIX 1
    #define OS_LINUX 1
#elif defined(__EMSCRIPTEN__)
    #define OS_WASM 1
#else
    #error Unsupported platform
#endif

#if defined(__clang__)
    #define COMPILER_CLANG 1
#elif defined(__GNUC__) || defined(__GNUG__)
    #define COMPILER_GCC 1
#elif defined(_MSC_VER)
    #define COMPILER_MSVC 1
#else
    #error Unsupported compiler
#endif

#ifndef OS_WASM
    #define OS_WASM 0
#endif

#ifndef OS_MAC
    #define OS_MAC 0
#endif

#ifndef OS_WINDOWS
    #define OS_WINDOWS 0
#endif

#ifndef OS_LINUX
    #define OS_LINUX 0
#endif

#ifndef OS_POSIX
    #define OS_POSIX 0
#endif

#ifndef COMPILER_MSVC
    #define COMPILER_MSVC 0
#endif

#ifndef COMPILER_CLANG
    #define COMPILER_CLANG 0
#endif

#ifndef COMPILER_GCC
    #define COMPILER_GCC 0
#endif

#ifndef DEBUG_BUILD
    #define DEBUG_BUILD 1
#endif

#if DEBUG_BUILD
    #define IF_DEBUG(x) x
#else
    #define IF_DEBUG(x)
#endif

#if OS_WINDOWS
#define BREAK() __debugbreak()
#elif OS_LINUX
#define BREAK() raise(SIGTRAP)
#elif OS_WASM
#include <emscripten.h>
EM_JS(void, JS_Breakpoint, (), {
    debugger;
})
#define BREAK() JS_Breakpoint()
#endif

#define API
#define Assert(x)       \
    if (!(x)) {         \
        BREAK(); \
    }
#define InvalidCodepath() Assert(!"invalid codepath")
#define ElseInvalidCodepath() else {InvalidCodepath();}

#define KiB(x) ((x##ull) * 1024ull)
#define MiB(x) (KiB(x) * 1024ull)
#define GiB(x) (MiB(x) * 1024ull)
#define TiB(x) (GiB(x) * 1024ull)
#define Lengthof(x) ((int64_t)((sizeof(x) / sizeof((x)[0]))))
#define SLICE_LAST INT64_MAX

using U8   = uint8_t;
using U16  = uint16_t;
using U32  = uint32_t;
using U64  = uint64_t;
using S8   = int8_t;
using S16  = int16_t;
using S32  = int32_t;
using S64  = int64_t;
using Int  = S64;
using UInt = U64;
using Float = double;

template <class T>
T Min(T a, T b) {
    if (a > b) return b;
    return a;
}

template <class T>
T ClampTop(T a, T top) {
    return Min(a, top);
}

template <class T>
T Max(T a, T b) {
    if (a > b) return a;
    return b;
}

template <class T>
T ClampBottom(T bottom, T b) {
    return Max(bottom, b);
}

template <class T>
T Clamp(T value, T min, T max) {
    if (value < min) return min;
    if (value > max) return max;
    return value;
}

template <class T>
void Swap(T *a, T *b) {
    T temp = *a;
    *a     = *b;
    *b     = temp;
}

inline bool IsPowerOf2(size_t x) {
    size_t result = (((x) & ((x)-1)) == 0);
    return result;
}

inline size_t WrapAroundPowerOf2(size_t x, size_t pow2) {
    Assert(IsPowerOf2(pow2));
    size_t result = (((x) & ((pow2)-1llu)));
    return result;
}

inline uint64_t HashBytes(void *data, unsigned size) {
    uint8_t *data8 = (uint8_t *)data;
    uint64_t hash  = (uint64_t)14695981039346656037ULL;
    for (unsigned i = 0; i < size; i++) {
        hash = hash ^ (uint64_t)(data8[i]);
        hash = hash * (uint64_t)1099511628211ULL;
    }
    return hash;
}

inline size_t GetAlignOffset(size_t size, size_t align) {
    Assert(IsPowerOf2(align));
    size_t mask = align - 1;
    size_t val  = size & mask;
    if (val) {
        val = align - val;
    }
    return val;
}

inline size_t AlignUp(size_t size, size_t align) {
    size_t result = size + GetAlignOffset(size, align);
    return result;
}

inline size_t AlignDown(size_t size, size_t align) {
    size += 1; // Make sure when align is 8 doesn't get rounded down to 0
    size_t result = size - (align - GetAlignOffset(size, align));
    return result;
}

template <typename T>
struct DEFER_ExitScope {
    T lambda;
    DEFER_ExitScope(T lambda) : lambda(lambda) {}
    ~DEFER_ExitScope() { lambda(); }
    DEFER_ExitScope(const DEFER_ExitScope &i) : lambda(i.lambda){};

  private:
    DEFER_ExitScope &operator=(const DEFER_ExitScope &);
};

class DEFER_ExitScopeHelp {
  public:
    template <typename T>
    DEFER_ExitScope<T> operator+(T t) { return t; }
};

#define DEFER_CONCAT_INTERNAL(x, y) x##y
#define DEFER_CONCAT(x, y) DEFER_CONCAT_INTERNAL(x, y)
#define defer const auto DEFER_CONCAT(defer__, __LINE__) = DEFER_ExitScopeHelp() + [&]()

struct RandomSeed {
    uint64_t a;
};

inline uint64_t GetRandomU64(RandomSeed *state) {
    uint64_t x = state->a;
    x ^= x << 13;
    x ^= x >> 7;
    x ^= x << 17;
    return state->a = x;
}

#define STRINGIFY_(x) x
#define STRINGIFY(x) STRINGIFY_(x)
#define CONCAT_(a, b) a ## b
#define CONCAT(a, b) CONCAT_(a, b)
