// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)

#ifndef EVAL_IR_H
#define EVAL_IR_H

////////////////////////////////
//~ rjf: Bytecode Operation Types

enum
{
  E_IRExtKind_Bytecode = RDI_EvalOp_COUNT,
  E_IRExtKind_SetSpace,
  E_IRExtKind_COUNT
};

typedef struct E_Op E_Op;
struct E_Op
{
  E_Op *next;
  RDI_EvalOp opcode;
  E_Value value;
  String8 string;
};

typedef struct E_OpList E_OpList;
struct E_OpList
{
  E_Op *first;
  E_Op *last;
  U64 op_count;
  U64 encoded_size;
};

////////////////////////////////
//~ rjf: IR Tree Types

typedef struct E_IRNode E_IRNode;
struct E_IRNode
{
  E_IRNode *first;
  E_IRNode *last;
  E_IRNode *next;
  RDI_EvalOp op;
  E_Space space;
  String8 string;
  E_Value value;
};

typedef struct E_IRTreeAndType E_IRTreeAndType;
struct E_IRTreeAndType
{
  E_IRNode *root;
  E_TypeKey type_key;
  E_Mode mode;
  E_MsgList msgs;
};

////////////////////////////////
//~ rjf: Member/Index Lookup Hooks

typedef struct E_LookupInfo E_LookupInfo;
struct E_LookupInfo
{
  void *user_data;
  U64 named_expr_count;
  U64 idxed_expr_count;
};

typedef struct E_LookupAccess E_LookupAccess;
struct E_LookupAccess
{
  E_IRTreeAndType irtree_and_type;
};

#define E_LOOKUP_INFO_FUNCTION_SIG(name) E_LookupInfo name(Arena *arena, E_IRTreeAndType *lhs, String8 filter)
#define E_LOOKUP_INFO_FUNCTION_NAME(name) e_lookup_info_##name
#define E_LOOKUP_INFO_FUNCTION_DEF(name) internal E_LOOKUP_INFO_FUNCTION_SIG(E_LOOKUP_INFO_FUNCTION_NAME(name))
typedef E_LOOKUP_INFO_FUNCTION_SIG(E_LookupInfoFunctionType);
E_LOOKUP_INFO_FUNCTION_DEF(default);

#define E_LOOKUP_ACCESS_FUNCTION_SIG(name) E_LookupAccess name(Arena *arena, E_ExprKind kind, E_Expr *lhs, E_Expr *rhs, void *user_data)
#define E_LOOKUP_ACCESS_FUNCTION_NAME(name) e_lookup_access_##name
#define E_LOOKUP_ACCESS_FUNCTION_DEF(name) internal E_LOOKUP_ACCESS_FUNCTION_SIG(E_LOOKUP_ACCESS_FUNCTION_NAME(name))
typedef E_LOOKUP_ACCESS_FUNCTION_SIG(E_LookupAccessFunctionType);
E_LOOKUP_ACCESS_FUNCTION_DEF(default);

#define E_LOOKUP_RANGE_FUNCTION_SIG(name) void name(Arena *arena, E_Expr *lhs, Rng1U64 idx_range, E_Expr **exprs, String8 *exprs_strings, void *user_data)
#define E_LOOKUP_RANGE_FUNCTION_NAME(name) e_lookup_range_##name
#define E_LOOKUP_RANGE_FUNCTION_DEF(name) internal E_LOOKUP_RANGE_FUNCTION_SIG(E_LOOKUP_RANGE_FUNCTION_NAME(name))
typedef E_LOOKUP_RANGE_FUNCTION_SIG(E_LookupRangeFunctionType);
E_LOOKUP_RANGE_FUNCTION_DEF(default);

#define E_LOOKUP_ID_FROM_NUM_FUNCTION_SIG(name) U64 name(U64 num, void *user_data)
#define E_LOOKUP_ID_FROM_NUM_FUNCTION_NAME(name) e_lookup_id_from_num_##name
#define E_LOOKUP_ID_FROM_NUM_FUNCTION_DEF(name) internal E_LOOKUP_ID_FROM_NUM_FUNCTION_SIG(E_LOOKUP_ID_FROM_NUM_FUNCTION_NAME(name))
typedef E_LOOKUP_ID_FROM_NUM_FUNCTION_SIG(E_LookupIDFromNumFunctionType);
E_LOOKUP_ID_FROM_NUM_FUNCTION_DEF(default);

#define E_LOOKUP_NUM_FROM_ID_FUNCTION_SIG(name) U64 name(U64 id, void *user_data)
#define E_LOOKUP_NUM_FROM_ID_FUNCTION_NAME(name) e_lookup_num_from_id_##name
#define E_LOOKUP_NUM_FROM_ID_FUNCTION_DEF(name) internal E_LOOKUP_NUM_FROM_ID_FUNCTION_SIG(E_LOOKUP_NUM_FROM_ID_FUNCTION_NAME(name))
typedef E_LOOKUP_NUM_FROM_ID_FUNCTION_SIG(E_LookupNumFromIDFunctionType);
E_LOOKUP_NUM_FROM_ID_FUNCTION_DEF(default);

typedef struct E_LookupRule E_LookupRule;
struct E_LookupRule
{
  String8 name;
  E_LookupInfoFunctionType *info;
  E_LookupAccessFunctionType *access;
  E_LookupRangeFunctionType *range;
  E_LookupIDFromNumFunctionType *id_from_num;
  E_LookupNumFromIDFunctionType *num_from_id;
};

typedef struct E_LookupRuleNode E_LookupRuleNode;
struct E_LookupRuleNode
{
  E_LookupRuleNode *next;
  E_LookupRule v;
};

typedef struct E_LookupRuleSlot E_LookupRuleSlot;
struct E_LookupRuleSlot
{
  E_LookupRuleNode *first;
  E_LookupRuleNode *last;
};

typedef struct E_LookupRuleMap E_LookupRuleMap;
struct E_LookupRuleMap
{
  E_LookupRuleSlot *slots;
  U64 slots_count;
};

typedef struct E_LookupRuleTagPair E_LookupRuleTagPair;
struct E_LookupRuleTagPair
{
  E_LookupRule *rule;
  E_Expr *tag;
};

////////////////////////////////
//~ rjf: IR Generation Hooks

#define E_IRGEN_FUNCTION_SIG(name) E_IRTreeAndType name(Arena *arena, E_Expr *expr, E_Expr *tag)
#define E_IRGEN_FUNCTION_NAME(name) e_irgen_##name
#define E_IRGEN_FUNCTION_DEF(name) internal E_IRGEN_FUNCTION_SIG(E_IRGEN_FUNCTION_NAME(name))
typedef E_IRGEN_FUNCTION_SIG(E_IRGenFunctionType);
E_IRGEN_FUNCTION_DEF(default);

typedef struct E_IRGenRule E_IRGenRule;
struct E_IRGenRule
{
  String8 name;
  E_IRGenFunctionType *irgen;
};

typedef struct E_IRGenRuleNode E_IRGenRuleNode;
struct E_IRGenRuleNode
{
  E_IRGenRuleNode *next;
  E_IRGenRule v;
};

typedef struct E_IRGenRuleSlot E_IRGenRuleSlot;
struct E_IRGenRuleSlot
{
  E_IRGenRuleNode *first;
  E_IRGenRuleNode *last;
};

typedef struct E_IRGenRuleMap E_IRGenRuleMap;
struct E_IRGenRuleMap
{
  U64 slots_count;
  E_IRGenRuleSlot *slots;
};

////////////////////////////////
//~ rjf: Type Pattern -> Hook Key Data Structure (Auto View Rules)

typedef struct E_AutoHookNode E_AutoHookNode;
struct E_AutoHookNode
{
  E_AutoHookNode *hash_next;
  E_AutoHookNode *pattern_order_next;
  E_TypeKey type_key;
  String8List type_pattern_parts;
  E_Expr *tag_expr;
};

typedef struct E_AutoHookSlot E_AutoHookSlot;
struct E_AutoHookSlot
{
  E_AutoHookNode *first;
  E_AutoHookNode *last;
};

typedef struct E_AutoHookMap E_AutoHookMap;
struct E_AutoHookMap
{
  U64 slots_count;
  E_AutoHookSlot *slots;
  E_AutoHookNode *first_pattern;
  E_AutoHookNode *last_pattern;
};

typedef struct E_AutoHookParams E_AutoHookParams;
struct E_AutoHookParams
{
  E_TypeKey type_key;
  String8 type_pattern;
  String8 tag_expr_string;
};

////////////////////////////////
//~ rjf: Used Tag Map Data Structure

typedef struct E_UsedTagNode E_UsedTagNode;
struct E_UsedTagNode
{
  E_UsedTagNode *next;
  E_UsedTagNode *prev;
  E_Expr *tag;
};

typedef struct E_UsedTagSlot E_UsedTagSlot;
struct E_UsedTagSlot
{
  E_UsedTagNode *first;
  E_UsedTagNode *last;
};

typedef struct E_UsedTagMap E_UsedTagMap;
struct E_UsedTagMap
{
  U64 slots_count;
  E_UsedTagSlot *slots;
};

////////////////////////////////
//~ rjf: Type Key -> Auto Hook Expr List Cache

typedef struct E_TypeAutoHookCacheNode E_TypeAutoHookCacheNode;
struct E_TypeAutoHookCacheNode
{
  E_TypeAutoHookCacheNode *next;
  E_TypeKey key;
  E_ExprList exprs;
};

typedef struct E_TypeAutoHookCacheSlot E_TypeAutoHookCacheSlot;
struct E_TypeAutoHookCacheSlot
{
  E_TypeAutoHookCacheNode *first;
  E_TypeAutoHookCacheNode *last;
};

typedef struct E_TypeAutoHookCacheMap E_TypeAutoHookCacheMap;
struct E_TypeAutoHookCacheMap
{
  U64 slots_count;
  E_TypeAutoHookCacheSlot *slots;
};

////////////////////////////////
//~ rjf: IR Context

typedef struct E_IRCtx E_IRCtx;
struct E_IRCtx
{
  E_String2ExprMap *macro_map;
  E_LookupRuleMap *lookup_rule_map;
  E_IRGenRuleMap *irgen_rule_map;
  E_AutoHookMap *auto_hook_map;
};

////////////////////////////////
//~ rjf: IR State

typedef struct E_IRTreeAndTypeCacheNode E_IRTreeAndTypeCacheNode;
struct E_IRTreeAndTypeCacheNode
{
  E_IRTreeAndTypeCacheNode *next;
  E_Expr *expr;
  E_IRTreeAndType irtree_and_type;
};

typedef struct E_IRTreeAndTypeCacheSlot E_IRTreeAndTypeCacheSlot;
struct E_IRTreeAndTypeCacheSlot
{
  E_IRTreeAndTypeCacheNode *first;
  E_IRTreeAndTypeCacheNode *last;
};

typedef struct E_IRState E_IRState;
struct E_IRState
{
  Arena *arena;
  U64 arena_eval_start_pos;
  
  // rjf: ir context
  E_IRCtx *ctx;
  
  // rjf: caches
  E_UsedTagMap *used_tag_map;
  E_TypeAutoHookCacheMap *type_auto_hook_cache_map;
  U64 irtree_and_type_cache_slots_count;
  E_IRTreeAndTypeCacheSlot *irtree_and_type_cache_slots;
};

////////////////////////////////
//~ rjf: Globals

local_persist read_only E_LookupRule e_lookup_rule__nil =
{
  str8_lit_comp("nil"),
  E_LOOKUP_INFO_FUNCTION_NAME(default),
  E_LOOKUP_ACCESS_FUNCTION_NAME(default),
  E_LOOKUP_RANGE_FUNCTION_NAME(default),
  E_LOOKUP_ID_FROM_NUM_FUNCTION_NAME(default),
  E_LOOKUP_NUM_FROM_ID_FUNCTION_NAME(default),
};
local_persist read_only E_LookupRule e_lookup_rule__default =
{
  str8_lit_comp("default"),
  E_LOOKUP_INFO_FUNCTION_NAME(default),
  E_LOOKUP_ACCESS_FUNCTION_NAME(default),
  E_LOOKUP_RANGE_FUNCTION_NAME(default),
  E_LOOKUP_ID_FROM_NUM_FUNCTION_NAME(default),
  E_LOOKUP_NUM_FROM_ID_FUNCTION_NAME(default),
};
local_persist read_only E_IRGenRule e_irgen_rule__default =
{
  str8_lit_comp("default"),
  E_IRGEN_FUNCTION_NAME(default),
};
global read_only E_IRNode e_irnode_nil = {&e_irnode_nil, &e_irnode_nil, &e_irnode_nil};
thread_static E_IRState *e_ir_state = 0;

////////////////////////////////
//~ rjf: Expr Kind Enum Functions

internal RDI_EvalOp e_opcode_from_expr_kind(E_ExprKind kind);
internal B32        e_expr_kind_is_comparison(E_ExprKind kind);

////////////////////////////////
//~ rjf: Context Selection Functions (Selection Required For All Subsequent APIs)

internal E_IRCtx *e_selected_ir_ctx(void);
internal void e_select_ir_ctx(E_IRCtx *ctx);

////////////////////////////////
//~ rjf: Lookups

internal E_LookupRuleMap e_lookup_rule_map_make(Arena *arena, U64 slots_count);
internal void e_lookup_rule_map_insert(Arena *arena, E_LookupRuleMap *map, E_LookupRule *rule);
#define e_lookup_rule_map_insert_new(arena, map, name_, ...) e_lookup_rule_map_insert((arena), (map), &(E_LookupRule){.name = (name_), __VA_ARGS__})

internal E_LookupRule *e_lookup_rule_from_string(String8 string);

////////////////////////////////
//~ rjf: IR Gen Rules

internal E_IRGenRuleMap e_irgen_rule_map_make(Arena *arena, U64 slots_count);
internal void e_irgen_rule_map_insert(Arena *arena, E_IRGenRuleMap *map, E_IRGenRule *rule);
#define e_irgen_rule_map_insert_new(arena, map, name_, ...) e_irgen_rule_map_insert((arena), (map), &(E_IRGenRule){.name = (name_), __VA_ARGS__})

internal E_IRGenRule *e_irgen_rule_from_string(String8 string);

////////////////////////////////
//~ rjf: Auto Hooks

internal E_AutoHookMap e_auto_hook_map_make(Arena *arena, U64 slots_count);
internal void e_auto_hook_map_insert_new_(Arena *arena, E_AutoHookMap *map, E_AutoHookParams *params);
#define e_auto_hook_map_insert_new(arena, map, ...) e_auto_hook_map_insert_new_((arena), (map), &(E_AutoHookParams){.type_key = zero_struct, __VA_ARGS__})
internal E_ExprList e_auto_hook_tag_exprs_from_type_key(Arena *arena, E_TypeKey type_key);
internal E_ExprList e_auto_hook_tag_exprs_from_type_key__cached(E_TypeKey type_key);

////////////////////////////////
//~ rjf: IR-ization Functions

//- rjf: op list functions
internal void e_oplist_push_op(Arena *arena, E_OpList *list, RDI_EvalOp opcode, E_Value value);
internal void e_oplist_push_uconst(Arena *arena, E_OpList *list, U64 x);
internal void e_oplist_push_sconst(Arena *arena, E_OpList *list, S64 x);
internal void e_oplist_push_bytecode(Arena *arena, E_OpList *list, String8 bytecode);
internal void e_oplist_push_set_space(Arena *arena, E_OpList *list, E_Space space);
internal void e_oplist_push_string_literal(Arena *arena, E_OpList *list, String8 string);
internal void e_oplist_concat_in_place(E_OpList *dst, E_OpList *to_push);

//- rjf: ir tree core building helpers
internal E_IRNode *e_push_irnode(Arena *arena, RDI_EvalOp op);
internal void e_irnode_push_child(E_IRNode *parent, E_IRNode *child);

//- rjf: ir subtree building helpers
internal E_IRNode *e_irtree_const_u(Arena *arena, U64 v);
internal E_IRNode *e_irtree_unary_op(Arena *arena, RDI_EvalOp op, RDI_EvalTypeGroup group, E_IRNode *c);
internal E_IRNode *e_irtree_binary_op(Arena *arena, RDI_EvalOp op, RDI_EvalTypeGroup group, E_IRNode *l, E_IRNode *r);
internal E_IRNode *e_irtree_binary_op_u(Arena *arena, RDI_EvalOp op, E_IRNode *l, E_IRNode *r);
internal E_IRNode *e_irtree_conditional(Arena *arena, E_IRNode *c, E_IRNode *l, E_IRNode *r);
internal E_IRNode *e_irtree_bytecode_no_copy(Arena *arena, String8 bytecode);
internal E_IRNode *e_irtree_string_literal(Arena *arena, String8 string);
internal E_IRNode *e_irtree_set_space(Arena *arena, E_Space space, E_IRNode *c);
internal E_IRNode *e_irtree_mem_read_type(Arena *arena, E_IRNode *c, E_TypeKey type_key);
internal E_IRNode *e_irtree_convert_lo(Arena *arena, E_IRNode *c, RDI_EvalTypeGroup out, RDI_EvalTypeGroup in);
internal E_IRNode *e_irtree_trunc(Arena *arena, E_IRNode *c, E_TypeKey type_key);
internal E_IRNode *e_irtree_convert_hi(Arena *arena, E_IRNode *c, E_TypeKey out, E_TypeKey in);
internal E_IRNode *e_irtree_resolve_to_value(Arena *arena, E_Mode from_mode, E_IRNode *tree, E_TypeKey type_key);

//- rjf: top-level irtree/type extraction
internal E_IRTreeAndType e_irtree_and_type_from_expr(Arena *arena, E_Expr *expr);

//- rjf: irtree -> linear ops/bytecode
internal void e_append_oplist_from_irtree(Arena *arena, E_IRNode *root, E_Space *current_space, E_OpList *out);
internal E_OpList e_oplist_from_irtree(Arena *arena, E_IRNode *root);
internal String8 e_bytecode_from_oplist(Arena *arena, E_OpList *oplist);

////////////////////////////////
//~ rjf: IRified Expression Cache

internal E_IRTreeAndType e_irtree_and_type_from_expr__cached(E_Expr *expr);

////////////////////////////////
//~ rjf: Expression & IR-Tree => Lookup Rule

internal E_LookupRuleTagPair e_lookup_rule_tag_pair_from_expr_irtree(E_Expr *expr, E_IRTreeAndType *irtree);

#endif // EVAL_IR_H
