#ifndef MR4TH_TINKER_H
#define MR4TH_TINKER_H

////////////////////////////////
// Tinker Types

#define TNK_MAX_NODE_COUNT (1 << 30)

typedef enum TNK_NodeKind{
  TNK_NodeKind_NULL = 0,
  TNK_NodeKind_Section,
  TNK_NodeKind_Symbol,
  TNK_NodeKind_Reloc,
  TNK_NodeKind_COUNT
} TNK_NodeKind;

typedef struct TNK_Object{
  Arena *arena;
  BUFFER strcons;
  BUFFER_TYPED(struct TNK__Node, nodes);
  struct TNK__Node *free_nodes;
  struct TNK__Buffer *free_buffers;
  
  struct TNK__Node *node_first[TNK_NodeKind_COUNT];
  struct TNK__Node *node_last[TNK_NodeKind_COUNT];
  
  U32 strcons_count;
  U32 node_alloc_pos;
  U32 node_count[TNK_NodeKind_COUNT];
} TNK_Object;

typedef union TNK_ID{
  struct{ U32 idx; U32 gen; };
  U64 xu64;
} TNK_ID;

typedef U32 TNK_SectionFlags;
enum{
  TNK_SectionFlag_R = (1 << 0),
  TNK_SectionFlag_W = (1 << 1),
  TNK_SectionFlag_X = (1 << 2)
};

typedef U32 TNK_SymbolFlags;
enum{
  TNK_SymbolFlag_Function = (1 << 0),
  TNK_SymbolFlag_External = (1 << 1),
};

typedef struct TNK_Position{
  TNK_ID section;
  U32 offset;
} TNK_Position;

typedef enum TNK_Format{
  TNK_Format_NULL = 0,
  TNK_Format_COFF,
  TNK_Format_ELF,
  TNK_Format_COUNT
} TNK_Format;

typedef struct TNK__Node{
  struct TNK__Node *next;
  struct TNK__Node *prev;
  U32 gen;
  TNK_NodeKind kind;
  String8 name;
  U32 flags;
  
  union{
    struct{ struct TNK__Buffer *buffer; } section;
    struct{ TNK_ID section; U32 offset; } symbol;
    struct{ TNK_ID symbol; TNK_ID section; U32 offset; } reloc;
  } u;
} TNK__Node;

typedef struct TNK__Buffer{
  union{
    struct TNK__Buffer *next;
    U32 size;
  };
  BUFFER_TYPED(U8, data);
} TNK__Buffer;

////////////////////////////////
// Tinker Functions

// tinker object read/write

MR4TH_SYMBOL TNK_Object* tnk_object_new(void);
MR4TH_SYMBOL void        tnk_object_release(TNK_Object *obj);

MR4TH_SYMBOL TNK_ID tnk_node_new(TNK_Object *obj, TNK_NodeKind kind);
MR4TH_SYMBOL void   tnk_node_delete(TNK_Object *obj, TNK_ID node);

MR4TH_SYMBOL TNK_ID tnk_node_first(TNK_Object *obj, TNK_NodeKind kind);
MR4TH_SYMBOL TNK_ID tnk_node_next(TNK_Object *obj, TNK_ID node);

/*
** Section: { name, flags, --------, ------, data }
** Symbol:  { name, flags, position, ------, ---- }
** Reloc:   { ----, flags, position, symbol, ---- }
*/

MR4TH_SYMBOL String8 tnk_node_get_name(TNK_Object *obj, TNK_ID node);
MR4TH_SYMBOL U32     tnk_node_get_flags(TNK_Object *obj, TNK_ID node);
MR4TH_SYMBOL void    tnk_node_get_position(TNK_Object *obj, TNK_ID node, TNK_Position *out);
MR4TH_SYMBOL TNK_ID  tnk_node_get_symbol(TNK_Object *obj, TNK_ID node);
MR4TH_SYMBOL String8 tnk_node_read_data(TNK_Object *obj, TNK_ID node);

MR4TH_SYMBOL void tnk_node_set_name(TNK_Object *obj, TNK_ID node, String8 name);
MR4TH_SYMBOL void tnk_node_set_flags(TNK_Object *obj, TNK_ID node, U32 flags);
MR4TH_SYMBOL void tnk_node_set_position(TNK_Object *obj, TNK_ID node, TNK_ID section, U32 offset);
MR4TH_SYMBOL void tnk_node_set_symbol(TNK_Object *obj, TNK_ID node, TNK_ID symbol);
MR4TH_SYMBOL void tnk_node_data_edit(TNK_Object *obj, TNK_ID node, U32 first, U32 opl, String8 data);

MR4TH_SYMBOL TNK_ID     tnk__id_from_ptr(TNK_Object *obj, TNK__Node *ptr);
MR4TH_SYMBOL TNK__Node* tnk__ptr_from_id(TNK_Object *obj, TNK_ID id);

MR4TH_SYMBOL String8 tnk__str_cons(TNK_Object *obj, String8 str);

MR4TH_SYMBOL TNK__Buffer* tnk__buffer_alloc(TNK_Object *obj);
MR4TH_SYMBOL void         tnk__buffer_free(TNK_Object *obj, TNK__Buffer *buffer);
MR4TH_SYMBOL void         tnk__buffer_edit(TNK__Buffer *buffer, U32 first, U32 opl, String8 data);
MR4TH_SYMBOL String8      tnk__buffer_read(TNK__Buffer *buffer);

// generate

MR4TH_SYMBOL String8 tnk_generate(Arena *arena, TNK_Object *obj, TNK_Format format);

MR4TH_SYMBOL String8 tnk_generate_coff(Arena *arena, TNK_Object *obj);
MR4TH_SYMBOL String8 tnk_generate_elf(Arena *arena, TNK_Object *obj);

MR4TH_SYMBOL TNK__Node*** tnk__flatten_node_lists(Arena *arena, TNK_Object *obj);
MR4TH_SYMBOL U32          tnk__reindex(TNK_Object *obj, U32 *reindex_map, TNK_ID id);

#endif //MR4TH_TINKER_H
