

/////////////////////////
//~ Draw globals
global D_State *d_state = 0;
per_thread D_ThreadCtx *d_thread_ctx = 0;

// temp color stuff
global F32 g_text_color[4] = {0.95f, 0.9f, 0.94f, 1.0f};
global F32 g_rect_color[4] = {0.6f, 0.5f, 0.6f, 1.0f};


////////////////////////////////
//~ Stack helper macros

#define D_StackPush(node_type, val_type, name, new_val) \
{\
Arena *arena = D_active_arena();\
D_Bucket *bucket = D_active_bucket();\
node_type *node_v = bucket->name##_free;\
if(node_v != 0) {StackPop(bucket->name##_free);}\
else {node_v = PushArray(arena, node_type, 1);}\
node_v->v = new_val;\
new_val = bucket->name##_stack_top->v;\
StackPush(bucket->name##_stack_top, node_v);\
bucket->last_cmd_stack_gen = bucket->current_stack_gen;\
bucket->current_stack_gen += 1;\
return new_val;\
}

#define D_StackPop(node_type, val_type, name) \
{\
val_type result = d_thread_ctx->name##_nil_stack_top.v;\
D_Bucket *bucket = D_active_bucket();\
if(bucket->name##_stack_top != &d_thread_ctx->name##_nil_stack_top)\
{\
node_type *node = bucket->name##_stack_top;\
result = node->v;\
StackPop(bucket->name##_stack_top);\
StackPush(bucket->name##_free, node);\
bucket->last_cmd_stack_gen = bucket->current_stack_gen;\
bucket->current_stack_gen += 1;\
}\
return result;\
}

#define D_StackTop(node_type, val_type, name) \
{\
D_Bucket *bucket = D_active_bucket();\
val_type result = bucket->name##_stack_top->v;\
return result;\
}

/////////////////////////
//~ "Generated" code
#include "draw_meta.c"

/////////////////////////
//~ Layer init
root_function D_InitReceipt
D_init(R_InitReceipt r_init_receipt, F_InitReceipt f_init_receipt)
{
  if(is_main_thread() && d_state == 0)
  {
    Arena* arena = m_make_arena_reserve(Megabytes(2));
    d_state = PushArrayZero(arena, D_State, 1);
    d_state->arena = arena;
    d_state->font_texture = F_atlas_texture_handle();
    D_ensure_thread_initialised();
  }
  D_InitReceipt receipt = {0};
  return receipt;
}

root_function void
D_ensure_thread_initialised(void)
{
  if(d_thread_ctx == 0)
  {
    Arena *arena = m_make_arena_reserve(Megabytes(1));
    d_thread_ctx = PushArrayZero(arena, D_ThreadCtx, 1);
    d_thread_ctx->arena = arena;
    d_thread_ctx->fallback_arena = m_make_arena_reserve(Megabytes(256));
    D_InitBucketStacks(&d_thread_ctx->fallback_bucket);
    d_thread_ctx->bucket_selection_fallback.arena  = d_thread_ctx->fallback_arena;
    d_thread_ctx->bucket_selection_fallback.bucket = &d_thread_ctx->fallback_bucket;
    d_thread_ctx->bucket_selection_top = &d_thread_ctx->bucket_selection_fallback;
    D_InitThreadStackTops;
  }
}

/////////////////////////
//~ Draw functions


root_function void
D_frame_begin() {
  D_ensure_thread_initialised();
}

root_function void
D_frame_end()
{
  
}

//- Rect 
root_function R_Rect2DInst *
D_rect2D_(Rng2_F32 rect, D_RectParams *rect_params)
{
  Arena* arena = D_active_arena();
  D_Bucket *bucket  = D_active_bucket();
  R_Pass *pass = D_pass_from_bucket(arena, bucket, R_PassKind_UI);
  R_PassParams_UI *pass_params = pass->params_ui;
  R_BatchGroup2DNode *batch_group = pass_params->rects.last;
  R_Handle tex = d_state->font_texture;
  
  // If we don't have a batch group yet we initialise it here.
  // Eventually we will also check for what different textures we have, since
  // we batch by texture. What about transform? 
  // TODO(anton): understand buckets in rjf codebase.
  if(batch_group == 0 || bucket->last_cmd_stack_gen != bucket->current_stack_gen)
  {
    batch_group = PushArrayZero(arena, R_BatchGroup2DNode, 1);
    QueuePush(pass_params->rects.first, pass_params->rects.last, batch_group);
    pass_params->rects.count += 1;
    batch_group->params.albedo_tex = tex;
    batch_group->params.albedo_tex_sample_kind = bucket->tex2d_sample_kind_stack_top->v;
    Mat3x3_F32 xform2d = {0.0f};
    batch_group->params.xform2d = xform2d;
  }
  // Here we get the available memory in a batch of memory that can be filled with differen things.
  // We get a chunk of that memory of the appropriate size for a R_Rect2DInst and fill it out.
  R_Rect2DInst *inst = R_batch_list_push_struct(arena, &batch_group->batches, 256 /* capacity */, R_Rect2DInst);
  inst->dst_rect = rect;
  inst->src_rect = rect_params->src_rect;
  inst->colors[Corner_00] = rect_params->color;//vec4_F32(0.6f, 0.5f, 0.6f, 1.0f);
  inst->colors[Corner_01] = rect_params->color;//vec4_F32(0.6f, 0.5f, 0.6f, 1.0f);
  inst->colors[Corner_10] = rect_params->color;//vec4_F32(0.6f, 0.5f, 0.6f, 1.0f);
  inst->colors[Corner_11] = rect_params->color;//vec4_F32(0.6f, 0.5f, 0.6f, 1.0f);
  inst->corner_radii[Corner_00] = rect_params->corner_radius; 
  inst->corner_radii[Corner_01] = rect_params->corner_radius; 
  inst->corner_radii[Corner_10] = rect_params->corner_radius; 
  inst->corner_radii[Corner_11] = rect_params->corner_radius; 
  inst->softness = rect_params->softness;
  inst->border_thickness = rect_params->border_thickness;
  inst->omit_texture = rect_params->omit_texture;
  
  return inst;
}

root_function F32 
D_text2D(Vec2_F32 position, String8 string, Vec4_F32 color)
{
  Arena *arena = D_active_arena();
  ArenaTemp scratch = scratch_get(&arena, 1);
  F_Run run = F_run_from_string(scratch.arena, string, position);
  // The position we get in will be the start of where we want to put the text.
  // Then we put a draw rectangle for each glyph in the string, and advance the pos.
  // The return value from this function then is the length of the text string.
  Vec2_F32 p = position;
  for(F_Piece *piece = run.first_piece; piece != 0; piece = piece->next)
  {
    //break_debugger();
    D_RectParams params;
    params.src_rect = piece->src_rect;
    params.omit_texture = 0;
    params.color = color;
    Vec2_F32 dst_p0 = piece->dst_p0;
    Vec2_F32 dst_p1 = piece->dst_p1; 
    Rng2_F32 rect = rng2_F32(dst_p0, dst_p1);
    D_rect2D_(rect, &params);
    p.x += piece->advance;
  }
  scratch_release(scratch);
  F32 result = p.x - position.x;
  return result;
}

///////////////////////////////////////////////////
//~ Buckets

root_function D_Bucket *
D_bucket_make(Arena *arena)
{
  D_Bucket *bucket = PushArrayZero(arena, D_Bucket, 1);
  D_InitBucketStacks(bucket);
  String8 name = str8_pushf(arena, "HejBucket");
  bucket->bucket_name = name;
  return bucket;
}
// TODO(anton): bucket and pass list concat in place when needed

root_function void 
D_push_bucket(Arena *arena, D_Bucket *bucket)
{
  D_BucketSelectionNode *node = d_thread_ctx->bucket_selection_free;
  if(node != 0)
  {
    StackPop(d_thread_ctx->bucket_selection_free);
  }
  else
  {
    node = PushArray(d_thread_ctx->arena, D_BucketSelectionNode, 1);
  }
  node->arena = arena;
  node->bucket = bucket;
  StackPush(d_thread_ctx->bucket_selection_top, node);
}

root_function void 
D_pop_bucket(void)
{
  // Get the top bucket node, pop it and put it on the free list.
  // If the top node is the fallback arena we will clear and reinit the fallback.
  D_BucketSelectionNode *node = d_thread_ctx->bucket_selection_top;
  if(node != &d_thread_ctx->bucket_selection_fallback)
  {
    StackPop(d_thread_ctx->bucket_selection_top);
    StackPush(d_thread_ctx->bucket_selection_free, node);
  }
  if(d_thread_ctx->bucket_selection_top == &d_thread_ctx->bucket_selection_fallback)
  {
    m_arena_clear(d_thread_ctx->fallback_arena);
    MemoryZeroStruct(&d_thread_ctx->fallback_bucket);
    D_InitBucketStacks(&d_thread_ctx->fallback_bucket);
  }
}

root_function R_Pass *
D_pass_from_bucket(Arena *arena, D_Bucket *bucket, R_PassKind kind)
{
  R_PassNode *node = bucket->passes.last;
  R_Pass *pass = 0;
  // If we dont have a pass node at the last position, we push the pass to the list.
  if(node == 0)
  {
    pass = R_pass_list_push(arena, &bucket->passes, kind);
  }
  else
  {
    pass = &node->v;
  }
  return pass;
}

root_function Arena *
D_active_arena()
{
  return d_thread_ctx->bucket_selection_top->arena;
}

root_function D_Bucket *
D_active_bucket(void)
{
  return d_thread_ctx->bucket_selection_top->bucket; 
}

root_function void 
D_submit(R_Handle window_r, D_Bucket *bucket)
{
  R_window_submit(window_r, &bucket->passes);
}