#pragma once

#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>

/* ------------------------------------------------------------
   Base types
   ------------------------------------------------------------ */

typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
typedef int8_t i8;
typedef int16_t i16;
typedef int32_t i32;
typedef int64_t i64;

typedef i8 b8;
typedef int b32;

typedef float f32;
typedef double f64;
/* ------------------------------------------------------------
   Size helpers
   ------------------------------------------------------------ */

#define KiB(x) ((u64)(x) * 1024ULL)
#define MiB(x) (KiB(x) * 1024ULL)

/* ------------------------------------------------------------
   Min / Max helpers
   ------------------------------------------------------------ */

#ifndef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif

#ifndef MAX
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#endif

/* ------------------------------------------------------------
   Alignment helpers
   ------------------------------------------------------------ */

#define ALIGN_UP_POW2(x, a) (((x) + ((a) - 1)) & ~((a) - 1))

/* ------------------------------------------------------------
   Assert
   ------------------------------------------------------------ */

#ifndef ASSERT
#define ASSERT(x) assert(x)
#endif

/* ------------------------------------------------------------
   Some helper functions
   ------------------------------------------------------------ */

#if defined(_WIN32) || defined(_WIN64)

#include <Windows.h>

static u32 plat_get_pagesize(void) {
  SYSTEM_INFO sysinfo = {0};
  GetSystemInfo(&sysinfo);

  return sysinfo.dwPageSize;
}

static void *plat_mem_reserve(u64 size) {
  return VirtualAlloc(NULL, size, MEM_RESERVE, PAGE_READWRITE);
}

static b32 plat_mem_commit(void *ptr, u64 size) {
  void *ret = VirtualAlloc(ptr, size, MEM_COMMIT, PAGE_READWRITE);
  return ret != NULL;
}

static b32 plat_mem_decommit(void *ptr, u64 size) {
  return VirtualFree(ptr, size, MEM_DECOMMIT);
}

static b32 plat_mem_release(void *ptr, u64 size) {
  return VirtualFree(ptr, size, MEM_RELEASE);
}

#elif defined(__linux__)

#ifndef _DEFAULT_SOURCE
#define _DEFAULT_SOURCE
#endif

#include <sys/mman.h>
#include <unistd.h>

static u32 plat_get_pagesize(void) { return (u32)sysconf(_SC_PAGESIZE); }

static void *plat_mem_reserve(u64 size) {
  void *out = mmap(NULL, size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  if (out == MAP_FAILED) {
    return NULL;
  }
  return out;
}

static b32 plat_mem_commit(void *ptr, u64 size) {
  i32 ret = mprotect(ptr, size, PROT_READ | PROT_WRITE);
  return ret == 0;
}

static b32 plat_mem_decommit(void *ptr, u64 size) {
  i32 ret = mprotect(ptr, size, PROT_NONE);
  if (ret != 0)
    return false;
  ret = madvise(ptr, size, MADV_DONTNEED);
  return ret == 0;
}

static b32 plat_mem_release(void *ptr, u64 size) {
  i32 ret = munmap(ptr, size);
  return ret == 0;
}

#endif
