#ifndef BASE_TYPES_H
#define BASE_TYPES_H

#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>

/////////////////////////
//~ Macros

/////////////////////////
//- Linking keywords 

// TODO(anton): Understand this, yoinked from rjf's layer.
#if LANG_CPP
# define no_name_mangle extern "C"
#else 
# define no_name_mangle
#endif 

// TODO(anton): OS_WINDOWS dll import/export macros

/////////////////////////
//- Keywords 
// Static is stupid and means different things depending on context in C and C++.
// These defines increases readability.
#define function static // Function internal to compilation unit.
#define local_persist static
#define global static
#define fallthrough // for use in switch statements, for clarity..

// TODO(anton): Understand and add good comment on this.
#if LANG_CPP
# define root_global no_name_mangle
# define root_function function
#else 
# define root_global extern
# define root_function function
#endif

#define inline_function inline static

#if OS_WINDOWS
# pragma section(".roglob", read)
# define read_only __declspec(allocate(".roglob"))
#else
# define read_only
#endif

#if COMPILER_MSVC
# define per_thread __declspec(thread)
#else
# error Thread keyword not abstracted on compiler.
#endif

/////////////////////////
//- Memory operations 
// It's nice to put these in macros, so we can swap out the functionality from standard library, eventually.
#define MemoryCopy memcpy
#define MemoryMove memmove
#define MemorySet  memset

// NOTE(anton): This gives a 4127 compiler warning for the sizeof conditional. This should be ignored
#define MemoryCopyStruct(dst, src) do { Assert(sizeof(*(dst)) == sizeof(*(src))); MemoryCopy((dst), (src), sizeof(*(dst))); } while(0)

#define MemoryZero(ptr, size) MemorySet((ptr), 0, (size))
#define MemoryZeroStruct(ptr) MemoryZero((ptr), sizeof(*(ptr)))
#define MemoryZeroArray(arr)  MemoryZero((arr), sizeof(arr))

/////////////////////////
//- Integer/pointer/array/type manipulations 

#define ArrayCount(a) (sizeof(a) / sizeof((a)[0]))
#define IntFromPtr(p) (U64)(((U8*)p) - 0)
#define PtrFromInt(i) (void*)(((U8*)0) + i)
#define Member(type, member_name) ((type  *)0)->member_name
// TODO(anton): Understand why this becomes offset actually
#define OffsetOf(type, member_name) IntFromPtr(&Member(type, member_name))
// TODO(anton): Understand this
#define BaseFromMember(type, member_name, ptr) (type *)((U8 *)(ptr) - OffsetOf(type, member_name))

#define Bytes(n)     (n)
#define Kilobytes(n) (n << 10) // 2^10 == 1024 etc
#define Megabytes(n) (n << 20)
#define Gigabytes(n) (((U64)n) << 30)
#define Terabytes(n) (((U64)n) << 40)

#define Thousand(n) ((n)*1000)
#define Million(n)  ((n)*1000000)
#define Billion(n)  ((n)*1000000000LL)

#define AbsoluteValueU64(x) (U64)llabs((U64)(x))

/////////////////////////
//- Linked list helpers 

#define CheckNull(p) ((p)==0)
#define SetNull(p) ((p)=0)
// Link list helper macros that are a bit involved

// Suffixes N,P,Z means that we have (N)ext, (P)rev arguments and/or a (Z)ero check and/or set argument
// f, l, n are "first", "last", "node" I think?
// DLL
// Doubly Linked List: Each node has a prev and next pointer. Operations: Push back, Push front, remove
#define DLLInsert_NPZ(f,l,p,n,next,prev,zchk,zset) \
(zchk(f) ? (((f) = (l) = (n)), zset((n)->next), zset((n)->prev)) :\
zchk(p) ? (zset((n)->prev), (n)->next = (f), (zchk(f) ? (0) : ((f)->prev = (n))), (f) = (n)) :\
((zchk((p)->next) ? (0) : (((p)->next->prev) = (n))), (n)->next = (p)->next, (n)->prev = (p), (p)->next = (n),\
((p) == (l) ? (l) = (n) : (0))))

#define DLLPushBack_NPZ(f,l,n,next,prev,zchk,zset) DLLInsert_NPZ(f,l,l,n,next,prev,zchk,zset)

#define DLLPushBack_NP(f, l, n, next, prev, zchk) \
(zchk(f) ? ((f)=(l)=(n),(n)->next=(n)->prev=0) : ((n)->prev=(l),(l)->next=(n),(l)=(n),(n)->next=0))

// If f == n we put f to f->next, and f->prev = 0.
// Else if l == n, we put l=l->prev, l->next = 0.
// If l != n and f != n we set n->next->prev to n->prev, and n->prev->next to n->next

#define DLLRemove_NP(f, l, n, next, prev) (((f) == (n) ? \
((f)=(f)->next, (f)->prev=0) : \
(l) == (n) ? \
((l)=(l)->prev, (l)->next=0) : \
((n)->next->prev=(n)->prev, \
(n)->prev->next=(n)->next) ))

#define DLLRemove_NPZ(f,l,n,next,prev,zchk,zset) (((f)==(n))?\
((f)=(f)->next, (zchk(f) ? (zset(l)) : zset((f)->prev))):\
((l)==(n))?\
((l)=(l)->prev, (zchk(l) ? (zset(f)) : zset((l)->next))):\
((zchk((n)->next) ? (0) : ((n)->next->prev=(n)->prev)),\
(zchk((n)->prev) ? (0) : ((n)->prev->next=(n)->next))))

#define DLLPushBack(f, l, n) DLLPushBack_NPZ(f, l, n, next, prev, CheckNull, SetNull)
// For front push I can just switch prev/next!
#define DLLPushFront(f, l, n) DLLPushBack_NPZ(l, f, n, prev, next, CheckNull, SetNull)
#define DLLRemove(f, l, n) DLLRemove_NPZ(f, l, n, next, prev, CheckNull, SetNull)


// SLL, queue or stack.
// These are from rjf's layer.

////////////////
// Queue
// Queue has only a next pointer. But we can push from front also.
// zchk = zero check, zset = zero set
#define QueuePush_NZ(f, l, n, next, zchk, zset) (zchk(f)?\
(((f)=(l)=(n)), zset((n)->next)):\
((l)->next=(n),(l)=(n),zset((n)->next)))

#define QueuePushFront_NZ(f, l, n, next, zchk, zset) ( zchk(f) ? \
((f)=(l)=(n)), zset((n)->next) :  \
((n)->next = (f)), ((f) = (n)) ) 

#define QueuePop_NZ(f, l, next, zchk, zset) ( (f)==(l) ? \
(zset(f), zset(l)) : ((f)=(f)->next))

#define QueuePush(f, l, n) QueuePush_NZ(f, l, n, next, CheckNull, SetNull)
#define QueuePushFront(f, l, n) QueuePushFront_NZ(f, l, n, next, CheckNull, SetNull)
#define QueuePop(f, l) QueuePop_NZ(f, l, next, CheckNull, SetNull)

////////////////
// Stack
#define StackPush_N(f, n, next) ((n)->next=(f), (f)=(n)) // Take the first element and set it to n->next, and set the first element to the node n.
#define StackPop_NZ(f, next, zchk) (zchk(f) ? 0 : ((f)=(f)->next)) // If first element is not zero we say that the first element is f->next, ie we pop f and put f->next on top.

#define StackPush(f, n) StackPush_N(f, n, next)
#define StackPop(f) StackPop_NZ(f, next, CheckNull)

/////////////////////////
//- Clamp/min/max
#define Min(a, b) (((a)<(b)) ? (a) : (b))
#define Max(a, b) (((a)>(b)) ? (a) : (b))
#define ClampTop(x, a) Min(x,a) // "Top" since we are cutting off anything above Min(x,a)
#define ClampBot(a, x) Max(a,x) // "Bot" since we're cutting off anything below Max(a,x)
// If a > x we get a, else we see if b < x and then get b if true, else x.
// TODO(anton): Is this actually what we want from a Clamp?
#define Clamp(a, x, b) (((a)>(x))?(a):((b)<(x))?(b):(x))

//- loop  
#define DeferLoop(start, end) for(int _i_ = ((start), 0); _i_ == 0; _i_ += 1, (end))
#define DeferLoopChecked(begin, end) for(int _i_ = 2 * !(begin); (_i_ == 2 ? ((end), 0) : !_i_); _i_ += 1, (end))

#define EachEnumVal(type, it) type it = (type)0; it < type##_COUNT; it = (type)(it+1)
#define EachNonZeroEnumVal(type, it) type it = (type)1; it < type##_COUNT; it = (type)(it+1)


/////////////////////////
//~ Base types
typedef int8_t   S8;
typedef int16_t  S16; 
typedef int32_t  S32;
typedef int64_t  S64;
typedef uint8_t  U8;
typedef uint16_t U16;
typedef uint32_t U32;
typedef uint64_t U64;
typedef S8       B8;
typedef S16      B16;
typedef S32      B32;
typedef S64      B64;
typedef float    F32;
typedef double   F64;
typedef void VoidFunction(void);

/////////////////////////
//~ Numerical limits
read_only global U8 U8Max = 0xFF;
read_only global U8 U8Min = 0;
read_only global U32 U32Max = 0xFFFFFFFF;
read_only global U32 U32Min = 0;
read_only global U64 U64Max = 0xFFFFFFFFFFFFFFFF;

// TODO(anton):  Rest of the limits, unsigned and signed integer values
read_only global U32 SignF32 = 0x80000000;


//- compiler, shut up! helpers
#define unused_variable(name) (void)name

/////////////////////////
//~ Base enums

// Describing a 2-coordinate system
typedef enum Axis2 
{
  Axis2_Invalid = -1,
  Axis2_X,
  Axis2_Y,
  Axis2_COUNT
}
Axis2;
#define Axis2_flip(a) ((Axis2)(!(a)))

// Corners of a rectangle.
//     00  -----  10
//       |       |
//     01  -----  11
typedef enum Corner
{
  Corner_Invalid = -1,
  Corner_00,
  Corner_01,
  Corner_10,
  Corner_11,
  Corner_COUNT
}
Corner;

////////////////////////////////
//~ Member Offset Helper

typedef struct MemberOffset MemberOffset;
struct MemberOffset
{
  U64 v;
};

#define MemberOff(S, member) (MemberOffset){OffsetOf(S, member)}
#define MemberOffLit(S, member) {OffsetOf(S, member)}
#define MemberFromOff(ptr, type, memoff) (*(type *)((U8 *)ptr + memoff.v))

/////////////////////////
//~ Assertions

#if OS_WINDOWS
# define break_debugger() __debugbreak()
#else
# error not implemented
#endif

#undef Assert
#define Assert(b) do { if(!(b)) { break_debugger(); } } while(0)

#if !defined(LOG_NOT_IMPLEMENTED)
# define LOG_NOT_IMPLEMENTED printf("\nFATAL ERROR: Not implemented yet.\n"); Assert(false); exit(1);
#endif

/////////////////////////
//~ Bit patterns
#define AlignUpToPow2(bytes_to_align, alignment_bytes) (((bytes_to_align) + (alignment_bytes - 1)) & ~(alignment_bytes - 1))

inline_function F32
absolute_value_F32(F32 f)
{
  union { U32 u; F32 f; } x;
  x.f = f;
  x.u = x.u & ~SignF32;
  return x.f;
}

// TODO(anton): Understand rjf's bit patterns

#endif //BASE_TYPES_H
