#ifndef MR4TH_SYMBOL_SET_H
#define MR4TH_SYMBOL_SET_H

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~ Copy-Pastable Setups ~~~~~~~~~~~~~~~~~~~~~~*/


/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~ To define a Symbol Set...
~~
~~  copy the code below
~~  replace ZZZ:    to name the symbol set
~~  replace Typezz: to set the type of the symbol set
~~  replace secz:   to name of the data section of the symbol set
~~  complete the sections with "..."s
*/

#if 0

#define SYMBOL_SET_DEFINE ZZZ
#define ZZZ_Type    Typezz
#define ZZZ_section ".sy.secz"
#define ZZZ_marker  secz
#include "mr4th_symbol_set.define.h"

// basic struct def with a name
#define ZZZ_DEF(N,...) SyDefine(ZZZ, N) = { ... }

// def with attached function body
//  replace funczz: to prefix namespace unique functions
//  fill ... sections appropriately
#define ZZZ_DEF(N,...) \
MR4TH_SYMBOL_STATIC void funczz##...(void*); \
SyDefine(ZZZ, N) = { funczz##..., ... }; \
MR4TH_SYMBOL_STATIC void funczz##...(void *ptr)

// nameless def
#define ZZZ_DEF(...)   SyDefineUnnamed(ZZZ) = { ... }

#endif


/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~ To loop over a Symbol Set...
~~
~~  copy the code below
~~  replace ZZZ:    to name the symbol set
~~  replace Typezz: to set the type of the symbol set
~~  optionally replace z: to name the local iteration variables
*/

#if 0

{
  Typezz *z_baseptr = SyBasePtr(ZZZ);
  U64 z_count = SyCount(ZZZ);
  
  Typezz *z = z_baseptr;
  for (U64 i = 0; i < z_count; i += 1, z += 1){
    
  }
}

#endif


// I dropped this here so that if you copy paste any of the
// above and forget to replace Typezz, you'll get auto-int like
// behavior from your error messages.
#define Typezz int


////////////////////////////////
// Symbol Set System

// Symbol Set key terms:
// "count"    - the total number of symbols in this set
// "baseptr"  - the pointer to the base of the array of symbol data
// "oplptr"   - the pointer to the spot one past the last element of the array
// "id"       - the 1-based index of the symbol
//              id=0 stands for the special 'nil' case
//              id values in the range [1,count] stand for a symbol in the set
// "metadata" - a pointer to the slot in the array for this symbol
// "raw"      - address of the metadata as an integer

// user interface

#define SyType(S) Glue(S,_Type)

#define SyDeclare(S,N)     MR4TH_ALIGN_AS_LIT(8) MR4TH_SEC_RW(S##_section) SyType(S) SY__SYMBOL(S,N)
#define SyDefine(S,N)      MR4TH_DO_NOT_ELIMINATE(SY__SYMBOL(S,N)) SyDeclare(S,N)
#define SyDefineUnnamed(S) SyDefine(S,Glue(unnamed,__COUNTER__))

#define SyAddress(S,N) (&SY__SYMBOL(S,N))
#define SyID(S,N)      SyIDFromAddress_Unchecked(S,SyAddress(S,N))
#define SyFlag(S,N)    (1llu << (SyID(S,N) - 1))

#define SyStride(S) AlignUpPow2(sizeof(SyType(S)), 8)

#if OS_WINDOWS
# define SyFirst(S) SY__FIRST(S)
# define SyOpl(S)   SY__OPL(S)
#elif OS_LINUX
# define SyFirst(S) (&SY__FIRST(S))
# define SyOpl(S)   (&SY__OPL(S))
#else
# error missing implementation set locator for this OS
#endif

#define SyCount(S)     (((U8*)SyOpl(S) - (U8*)SyFirst(S))/SyStride(S))
#define SyNext(S,addr) (SyType(S)*)((U8*)addr + SyStride(S))

#define SyEach(S,var)   SyType(S)*var=SyFirst(S); var<SyOpl(S); var=SyNext(S,var)
#define SyEachID(S,var) U32 var=1; var<=SyCount(S); var+=1

#define SyIDFromAddress(S,addr) (SyAddressCheck(S,addr)?SyIDFromAddress_Unchecked(S,addr):0)
#define SyAddressFromID(S,id)   (SyIDCheck(S,id)?SyAddressFromID_Unchecked(S,id):0)

#define SyAddressCheck(S,addr) (SyFirst(S) <= (addr) && (addr) <= SyOpl(S))
#define SyIDCheck(S,id)        (1 <= (id) && (id) <= SyCount(S))

#define SyIDFromAddress_Unchecked(S,addr) ((U32)(1 + ((U8*)(addr) - (U8*)SyFirst(S))/SyStride(S)))
#define SyAddressFromID_Unchecked(S,id)   ((SyType(S)*)((U8*)SyFirst(S) + ((id) - 1)*SyStride(S)))

#define SyRaw(S,N)                   (UAddr)(SyAddress(S,N))
#define SyIDFromRaw(S,raw)           SyIDFromAddress(S,(SyType(S)*)(raw))
#define SyIDFromRaw_Unchecked(S,raw) SyIDFromAddress_Unchecked(S,(SyType(S)*)(raw))

// internal

#define SY__SYMBOL(S,N) Glue(S,Glue(__,N))

#define SY__FIRST_(S) Glue(syfirst__, S##_marker)
#define SY__OPL_(S)   Glue(syopl__, S##_marker)

#define SY__FIRST(S) SY__FIRST_(S)
#define SY__OPL(S)   SY__OPL_(S)

#if OS_WINDOWS
void sy__section_init(char *name, U32 name_size, void **first_out, void **opl_out);
#endif

#endif //MR4TH_SYMBOL_SET_H
