
per_thread UI_State* ui_state;

global F32 ui_g_dt;

////////////////////////////////
//~ "Generated"/meta functions
#include "ui_meta.c"

////////////////////////////////
//~ Basic type functions

//- Boxes 
root_function B32 
UI_box_is_nil(UI_Box *box)
{
  return box == 0 || box == &ui_g_nil_box;
}

root_function UI_BoxRec
UI_box_recurse_depth_first(UI_Box *box, UI_Box *stopper, MemberOffset sib, MemberOffset child)
{
  UI_BoxRec rec = {0};
  rec.next = &ui_g_nil_box;
  // We check what we get from the child offset.
  // If it is not nil we set the next pointer to that box.
  if(!UI_box_is_nil(MemberFromOff(box, UI_Box *, child)))
  {
    rec.next = MemberFromOff(box, UI_Box *, child);
    rec.push_count = 1;
  }
  else
  {
    // If the child is nil, we loop over all boxes going up the parent chain,
    // until we hit stopper.
    // As soon as we hit a sibling that is non-nil, we put that in next and return.
    for(UI_Box *b = box; !UI_box_is_nil(b) && b != stopper; b = b->parent)
    {
      if(!UI_box_is_nil(MemberFromOff(b, UI_Box *, sib)))
      {
        rec.next = MemberFromOff(b, UI_Box *, sib);
        break;
      }
      rec.pop_count += 1;
    }
  }
  
  return rec;
}

//- sizes
root_function UI_Size 
UI_size_make(UI_SizeKind kind, F32 value, F32 strictness) 
{
  UI_Size result = {0};
  result.kind = kind;
  result.value = value;
  result.strictness = strictness;
  return result;
}



//- ID strings 
root_function String8 
UI_hash_part_from_box_string(String8 string)
{
  // TODO(anton): Implement ryans stuff here with substrings
  return string;
}

//- Keys 
root_function UI_Key 
UI_key_zero(void)
{
  UI_Key key = {0};
  return key;
}

root_function UI_Key 
UI_key_from_string(UI_Key seed, String8 string)
{
  UI_Key key = {0};
  if(string.size > 0)
  {
    MemoryCopyStruct(&key, &seed);
    for(U64 i = 0; i < string.size; i += 1) 
    {
      key.u64[0] = ((key.u64[0] << 5) + key.u64[0]) + string.str[i];
    }
  }
  return key;
}

root_function B32
UI_key_match(UI_Key a, UI_Key b)
{
  return a.u64[0] == b.u64[0];
}


root_function UI_Signal UI_signal_from_box(UI_Box *box)
{
  UI_Signal sig = {box};
  // TODO(anton): possibly clipped box rect
  Rng2_F32 rect = box->rect;

  
  B32 ctx_menu_is_ancestor = 0;
  {
    for(UI_Box *parent = box; !UI_box_is_nil(parent); parent = parent->parent)
    {
      if(parent == ui_state->ctx_menu_root)
      {
        ctx_menu_is_ancestor = 1;
        break;
      }
    }
  }
  
  for(OS_Event *event = ui_state->events->first, *next = 0; event !=0; event = next)
  {
    B32 taken = 0; // flag for consume
    next = event->next;
    
    //- unpack event
    Vec2_F32 event_mouse = event->position;
    B32 event_mouse_in_bounds = rng2_contains_vec2_F32(rect, event_mouse);
    UI_MouseButtonKind event_mouse_button_kind = (event->key == OS_Key_MouseLeft ? UI_MouseButtonKind_Left :
                                                  event->key == OS_Key_MouseRight ? UI_MouseButtonKind_Right :
                                                  event->key == OS_Key_MouseMiddle ?  UI_MouseButtonKind_Middle :
                                                  UI_MouseButtonKind_Left);
    B32 event_key_is_mouse = (event->key == OS_Key_MouseLeft  ||
                              event->key == OS_Key_MouseRight ||
                              event->key == OS_Key_MouseMiddle);
    //- Mouse presses
    if(box->flags & UI_BoxFlag_MouseClickable &&
       event->kind == OS_EventKind_Press &&
       event_mouse_in_bounds && 
       event_key_is_mouse)
    {
      ui_state->hot_box_key = box->key;
      ui_state->active_box_key[event_mouse_button_kind]  = box->key;
      sig.flag |= (UI_SignalFlag_LeftPressed<<event_mouse_button_kind);
      
      taken = 1;
    }
    
    if(taken)
    {
      OS_consume_event(ui_state->events, event);
    }
  }
  
  
  //////////////////////////////
  //- mouse is over this box's rect -> always mark mouse-over
  //  But the rect may be non-visible
  Vec2_F32 mouse_pos = ui_state->mouse;
  B32 rect_contains_mouse = rng2_contains_vec2_F32(rect, mouse_pos);
  if(rect_contains_mouse)
  {
    sig.flag |= UI_SignalFlag_MouseOver;
  }
  
  //////////////////////////////
  //- mouse is over this box's rect, no other hot key? -> set hot key, mark hovering
  //
  if(box->flags & UI_BoxFlag_MouseClickable &&
     rect_contains_mouse) // &&
     //(UI_key_match(ui_state->hot_box_key, UI_key_zero()) || UI_key_match(ui_state->hot_box_key, box->key)) //&&
     //(UI_key_match(ui_state->active_box_key[UI_MouseButtonKind_Left], UI_key_zero()) || UI_key_match(ui_state->active_box_key[UI_MouseButtonKind_Left], box->key)) &&
     //(UI_key_match(ui_state->active_box_key[UI_MouseButtonKind_Middle], UI_key_zero()) || UI_key_match(ui_state->active_box_key[UI_MouseButtonKind_Middle], box->key)) &&
     //(UI_key_match(ui_state->active_box_key[UI_MouseButtonKind_Right], UI_key_zero()) || UI_key_match(ui_state->active_box_key[UI_MouseButtonKind_Right], box->key)))
     
     {
    ui_state->hot_box_key = box->key;
    sig.flag |= UI_SignalFlag_Hovering;
  }
  
  //////////////////////////////
  //- rjf: clicking on something outside the context menu kills the context menu
  //
  if(!ctx_menu_is_ancestor && sig.flag & (UI_SignalFlag_LeftPressed|UI_SignalFlag_RightPressed|UI_SignalFlag_MiddlePressed))
  {
    UI_ctx_menu_close();
  }
  
  return sig;
}

////////////////////////////////
//~ UI State functions
root_function UI_State*
UI_state_alloc(void)
{
  ui_g_nil_box.hash_next = &ui_g_nil_box;
  ui_g_nil_box.hash_prev = &ui_g_nil_box;
  ui_g_nil_box.first     = &ui_g_nil_box;
  ui_g_nil_box.last      = &ui_g_nil_box;
  ui_g_nil_box.next      = &ui_g_nil_box;
  ui_g_nil_box.prev      = &ui_g_nil_box;
  ui_g_nil_box.parent    = &ui_g_nil_box;
  
  Arena* arena = m_make_arena_reserve(Gigabytes(1));
  UI_State *state = PushArrayZero(arena, UI_State, 1);
  state->arena = arena;
  state->box_table_size = 4096;
  state->box_table = PushArrayZero(arena, UI_BoxSlot, state->box_table_size);
  
  // TODO(anton): make some better system for setting and getting theme colors
  state->colors[UI_Color_Null]            = vec4_F32(0, 0, 0, 1);
  state->colors[UI_Color_PlainBackground] = vec4_F32(0.3f, 0.2f, 0.2f, 1);
  state->colors[UI_Color_PlainBorder]     = vec4_F32(0.4f, 0.4f, 0.4f, 1);
  state->colors[UI_Color_PlainText]       = vec4_F32(0.85f, 0.85f, 0.85f, 1);
  state->colors[UI_Color_PlainOverlay]    = vec4_F32(0.9f, 0.6f, 0.3f, 1);

  for(U64 index = 0; index < ArrayCount(state->frame_arenas); index += 1)
  {
    state->frame_arenas[index] = m_make_arena_reserve(Gigabytes(1));
  }
  state->last_frame_arena_index = 1;
  state->current_frame_arena_index = 0;
  // TODO(anton): drag data arena
  UI_init_stack_nils(state);
  return state;
}

root_function void 
UI_state_set(UI_State *ui)
{
  ui_state = ui;
}

////////////////////////////////
//~ Build phase
root_function void
UI_build_begin(OS_Handle window, OS_EventList  *events)
{
  
  //- Reset per frame state 
  {
    UI_init_stacks(ui_state);
    ui_state->build_gen += 1;
    m_arena_clear(UI_frame_arena());
    ui_state->root = &ui_g_nil_box;
    ui_state->ctx_menu_touched_this_frame = 0;
    ui_state->ctx_menu_changed = 0;

  }
  
  //- Fill per build parameters
  {
    ui_state->events = events;
    ui_state->window = window;
    ui_state->mouse  = OS_mouse_from_window(window);// TODO(anton): window focused and last time moved stuff.
    
  }
  
  //- "Prune stale boxes" 
  
  for(U64 slot = 0; slot < ui_state->box_table_size; slot += 1)
  {
    for(UI_Box *box = ui_state->box_table[slot].first, *next = 0;
        !UI_box_is_nil(box);
        box = next)
    {
      // Set the next box
      next = box->hash_next;
      // Any boxes existing in the hash table, that has a zero key or has a last_gen_touched+1 that is less
      // than the current build gen, are removed from the table. The resulting free slots in the table are pushed
      // onto the free list.
      if(UI_key_match(box->key, UI_key_zero()) || box->last_gen_touched+1 < ui_state->build_gen)
      {
        DLLRemove_NPZ(ui_state->box_table[slot].first, ui_state->box_table[slot].last, 
                      box, hash_next, hash_prev,
                      UI_box_is_nil, UI_box_set_nil);
        StackPush(ui_state->first_free_box, box);
        ui_state->free_box_list_count += 1;
      }
    }
  }
  
  //- Build root 
  // This is a UI box that extends the whole of the window.
  
  {
    Rng2_F32 client_rect = OS_client_rect_from_window(window);
    Vec2_F32 client_rect_size = dim2_F32(client_rect);
    UI_set_next_pref_width(UI_pixels(client_rect_size.x, 1));
    UI_set_next_pref_height(UI_pixels(client_rect_size.y, 1));
    UI_set_next_child_layout_axis(Axis2_Y);
    String8 root_name = str8_lit("root_box");
    UI_Box *root = UI_box_make(0 /* zero box flags for root box */, root_name);
    UI_push_parent(root);
    ui_state->root = root;
  }
  //- Context menu setup 
  ui_state->ctx_menu_open = ui_state->next_ctx_menu_open;
  ui_state->ctx_menu_anchor_key = ui_state->next_ctx_menu_anchor_key;
  {
    UI_Box *anchor_box = UI_box_from_key(ui_state->ctx_menu_anchor_key);
    if(!UI_box_is_nil(anchor_box))
    {
      ui_state->ctx_menu_anchor_box_last_pos = anchor_box->rect.p0; 
    }
    Vec2_F32 anchor = add2_F32(ui_state->ctx_menu_anchor_box_last_pos, ui_state->ctx_menu_anchor_offset);
    
    UI_fixed_x(anchor.x) UI_fixed_y(anchor.y) 
      UI_pref_width(UI_size_by_children(0, 1)) UI_pref_height(UI_size_by_children(0, 1))
    {
      UI_set_next_child_layout_axis(Axis2_Y);
      
      ui_state->ctx_menu_root = UI_box_make(UI_BoxFlag_FloatingX|UI_BoxFlag_FloatingY, str8_lit("ctx_menu"));
    }
    
  }
  
  // Reset hot if we don't have an active widget
  B32 has_active = 0;
  for(EachEnumVal(UI_MouseButtonKind, k))
  {
    if(!UI_key_match(ui_state->active_box_key[k], UI_key_zero()))
    {
      has_active = 1;
    }
  }
  if(!has_active)
  {
    ui_state->hot_box_key = UI_key_zero();
  }
  
  //- rjf: reset active keys if they have been pruned
  for(EachEnumVal(UI_MouseButtonKind, k))
  {
    UI_Box *box = UI_box_from_key(ui_state->active_box_key[k]);
    if(UI_box_is_nil(box))
    {
      ui_state->active_box_key[k] = UI_key_zero();
    }
  }
  
}

root_function void
UI_build_end(void)
{
  
  ui_state->ctx_menu_touched_this_frame = 1;
  if(ui_state->ctx_menu_open != 0 || ui_state->ctx_menu_touched_this_frame == 0)
  {
    //UI_ctx_menu_close();
  }
  
  UI_layout();
  
  // TODO(anton): When I need this and understand why
  //if(ui_state->ctx_menu_touched_this_frame && !ui_state->ctx_menu_changed)
  //{
    //UI_Box *anchor_box = UI_box_from_key(ui_state->ctx_menu_anchor_key);
    //if(!UI_box_is_nil(anchor_box))
    //{
      //Rng2_F32 root_rect = ui_state->ctx_menu_root->rect;
      //Vec2_F32 pos =
      //{
        //anchor_box->rect.x0 + ui_state->ctx_menu_anchor_offset.x,
        //anchor_box->rect.y0 + ui_state->ctx_menu_anchor_offset.y,
      //};
      //Vec2_F32 shift = sub2_F32(pos, root_rect.p0);
      //Rng2_F32 new_root_rect = shift2_F32(root_rect, shift);
      //ui_state->ctx_menu_root->fixed_position = new_root_rect.p0;
      //ui_state->ctx_menu_root->fixed_size = dim2_F32(new_root_rect);
      //ui_state->ctx_menu_root->rect = new_root_rect;
    //}
  //}
  //
}

////////////////////////////////
//~ Context menu
root_function void
UI_ctx_menu_open(UI_Key key, UI_Key anchor_key, Vec2_F32 anchor_offset)
{
  anchor_offset.x = (F32)(int)anchor_offset.x;
  anchor_offset.y = (F32)(int)anchor_offset.y;
  ui_state->next_ctx_menu_open = 1;
  ui_state->ctx_menu_changed = 1;
  // TODO(anton): time parameter for ctx menu animation
  ui_state->ctx_menu_key = key;
  ui_state->next_ctx_menu_anchor_key = anchor_key;
  ui_state->ctx_menu_anchor_offset = anchor_offset;
  ui_state->ctx_menu_touched_this_frame = 1;
  ui_state->ctx_menu_anchor_box_last_pos =  vec2_F32(100, 100);
}

root_function void
UI_ctx_menu_close(void)
{
  ui_state->next_ctx_menu_open = 0;
}

root_function B32
UI_begin_ctx_menu(UI_Key key)
{
  UI_push_parent(ui_state->root);
  UI_push_parent(ui_state->ctx_menu_root);
  B32 result = UI_key_match(key, ui_state->ctx_menu_key) && ui_state->ctx_menu_open;
  if(result)
  {
    ui_state->ctx_menu_touched_this_frame = 1;
    ui_state->ctx_menu_root->flags |=  UI_BoxFlag_DrawBackground;
    ui_state->ctx_menu_root->flags |=  UI_BoxFlag_DrawDropShadow;
    UI_push_pref_width(UI_pixels(100, 0));
    UI_push_pref_height(UI_pixels(200, 0));
  }

  
  return result;
}

root_function B32
UI_ctx_menu_is_open(UI_Key key)
{
  return ui_state->ctx_menu_open && UI_key_match(key, ui_state->ctx_menu_key);
}

root_function void
UI_end_ctx_menu(void)
{
  //UI_Box* top_parent = UI_top_parent();
  UI_pop_pref_width();
  UI_pop_pref_height();
  UI_pop_parent();
  //top_parent = UI_top_parent();
  UI_pop_parent();
  //top_parent = UI_top_parent();
}

////////////////////////////////
//~ UI Frame
root_function Arena *
UI_frame_arena(void)
{
  return ui_state->frame_arenas[ui_state->current_frame_arena_index];
}

root_function void
UI_frame_begin(F32 delta_time)
{
  ui_g_dt = delta_time;
}

root_function void
UI_frame_end(void)
{
  U32 last = ui_state->last_frame_arena_index;
  ui_state->last_frame_arena_index = ui_state->current_frame_arena_index;
  ui_state->current_frame_arena_index = last;
}


////////////////////////////////
//~ Box hierarchy construction

root_function UI_Box *
UI_box_from_key(UI_Key key)
{
  UI_Box *result = &ui_g_nil_box;
  // The box table contains all the hashed boxes, so we mod by the size to get the slot in the table.
  U64 slot = key.u64[0] % ui_state->box_table_size;
  if(!UI_key_match(key, UI_key_zero()))
  {
    // We check if we have a box corresponding to the key in the hash table, and return that if we find it.
    for(UI_Box *b = ui_state->box_table[slot].first; !UI_box_is_nil(b); b = b->hash_next)
    {
      if(UI_key_match(b->key, key))
      {
        result = b;
        break;
      }
    }
  }
  return result;
}

root_function UI_Box *
UI_box_make_from_key(UI_BoxFlags flags, UI_Key key)
{
  // Get the nil box or a box from the hash table.
  UI_Box *box = UI_box_from_key(key);
  
  // If the key has already been used, we "trample" over the key/box pair to make an id-less box.
  if(box->last_gen_touched == ui_state->build_gen)
  {
    box = &ui_g_nil_box;
    key = UI_key_zero();
  }
  
  // Allocate the box if it's not allocated, or if it's a duplicate key
  B32 first_frame = 0;
  if(UI_box_is_nil(box))
  {
    U64 slot = key.u64[0] % ui_state->box_table_size;
    first_frame = 1;
    box = ui_state->first_free_box;
    // If the first free box is nil we push a new box on the arena.
    if(UI_box_is_nil(box))
    {
      box = PushArrayZero(ui_state->arena, UI_Box, 1);
    }
    else
    {
      // If the first free box is non-nil, we pop it from the stack.
      // This macro works since the first_free_box is a UI_Box type, which has a next member.
      // So this will but the ui_state->first_free_box pointer to the next member of the previous 
      // element pointed to by ui_state->first_free_box.
      StackPop(ui_state->first_free_box);
      MemoryZeroStruct(box);
      ui_state->free_box_list_count -= 1;
    }
    // We push back the box to the doubly linked list in the table slot, using custom functions for
    // zero check and zero set.
    DLLPushBack_NPZ(ui_state->box_table[slot].first, 
                    ui_state->box_table[slot].last, box, hash_next, hash_prev, 
                    UI_box_is_nil, UI_box_set_nil);
    box->key = key;
  }
  
  //- Link to the tree by getting the parent. If the parent is nil, the current box should actually be the root.
  UI_Box *parent = UI_top_parent();
  if(UI_box_is_nil(parent))
  {
    ui_state->root = box;
  }
  else 
  {
    DLLPushBack_NPZ(parent->first, parent->last, box, next, prev, UI_box_is_nil, UI_box_set_nil);
    parent->child_count += 1;
    box->parent = parent;
  }
  
  //- Fill the state of the current box
  if(!UI_box_is_nil(box))
  {
    
    if(first_frame)
    {
      box->first_gen_touched = ui_state->build_gen;
    }
    
    box->child_count = 0;
    box->first = box->last = &ui_g_nil_box;
    box->flags = flags | UI_top_flags();
    
    if(ui_state->fixed_width_stack.top != &ui_state->fixed_width_nil_stack_top)
    {
      box->flags |= UI_BoxFlag_FixedWidth;
      box->fixed_size.x = ui_state->fixed_width_stack.top->v;
    } else {
      box->pref_size[Axis2_X] = UI_top_pref_width();
    }
    
    if(ui_state->fixed_height_stack.top != &ui_state->fixed_height_nil_stack_top)
    {
      box->flags |= UI_BoxFlag_FixedHeight;
      box->fixed_size.y = ui_state->fixed_height_stack.top->v;
    } else {
      box->pref_size[Axis2_Y] = UI_top_pref_height();
    }
    
    
    box->calc_rel_pos.x = UI_top_fixed_x();
    box->calc_rel_pos.y = UI_top_fixed_y();
    
    
    box->child_layout_axis  = UI_top_child_layout_axis();
    box->last_gen_touched = ui_state->build_gen;
    
    // TODO(anton): Text and color drawing properties here
    box->background_color  = ui_state->colors[UI_Color_PlainBackground];
    box->text_color        = ui_state->colors[UI_Color_PlainText];
    box->border_color      = ui_state->colors[UI_Color_PlainBorder];
    box->overlay_color     = ui_state->colors[UI_Color_PlainOverlay];
    
  }
  
  UI_auto_pop_stacks(ui_state);
  
  return box;
}

root_function UI_Box *
UI_box_make(UI_BoxFlags flags, String8 string)
{
  UI_Key seed = UI_top_seed_key();
  
  String8 string_hash_part = UI_hash_part_from_box_string(string);
  UI_Key key = UI_key_from_string(seed, string_hash_part);
  
  UI_Box *box = UI_box_make_from_key(flags, key);
  
  box->string = str8_copy(UI_frame_arena(), string);
  
  return box;
}

////////////////////////////////
//~ layout

root_function void
UI_solve_independent_sizes(UI_Box *root, Axis2 axis)
{
  switch(root->pref_size[axis].kind)
  {
    default:break;
    case UI_SizeKind_Pixels:
    {
      root->calc_size.v[axis] = root->pref_size[axis].value;
      root->calc_size.v[axis] = floor_F32(root->calc_size.v[axis]);
    } break;
  }
  
  // Recurse
  for(UI_Box *child = root->first; !UI_box_is_nil(child); child = child->next)
  {
    UI_solve_independent_sizes(child, axis);
  }
  
}

root_function void
UI_solve_upward_dependent_sizes(UI_Box *root, Axis2 axis)
{
  switch(root->pref_size[axis].kind)
  {
    default:break;
    case UI_SizeKind_Percent:
    {
      UI_Box *ancestor = &ui_g_nil_box;
      // Move up the parents and get the first ancestor that does not have 
      // size by children.
      for(UI_Box *p = root->parent; !UI_box_is_nil(p); p = p->parent)
      {
        if(p->pref_size[axis].kind != UI_SizeKind_SizeByChildren)
        {
          ancestor = p;
          break;
        }
      }
      // The calculated size of the argument box to this function is then 
      // the ancestor calculated size, scaled by the preferred size of this box, which is 
      // assumed to be given as a percentage by the SizeKind
      if(!UI_box_is_nil(ancestor))
      {
        root->calc_size.v[axis] = ancestor->calc_size.v[axis] * root->pref_size[axis].value;
        root->calc_size.v[axis] = floor_F32(root->calc_size.v[axis]);
      }
    } break;
  }
  
  for(UI_Box *child = root->first; !UI_box_is_nil(child); child = child->next)
  {
    UI_solve_upward_dependent_sizes(child, axis);
  }
}

root_function void
UI_solve_downward_dependent_sizes(UI_Box *root, Axis2 axis)
{
  // Here we first recurse since we will depend on the result for this input.
  for(UI_Box *child = root->first; !UI_box_is_nil(child); child = child->next)
  {
    UI_solve_downward_dependent_sizes(child, axis);
  }
  
  switch(root->pref_size[axis].kind)
  {
    default:break;
    case UI_SizeKind_SizeByChildren:
    {
      F32 value = 0;
      {
        // We will calculate the size to be the sum of the child sizes on this axis,
        // if we are on the layout axis.
        // If we are not on the layout axis the size will just be the maximum size of any child on that axis.
        if(axis == root->child_layout_axis)
        {
          for(UI_Box *child = root->first; !UI_box_is_nil(child); child = child->next)
          {
            value += child->calc_size.v[axis];
          }
        }
        else
        {
          for(UI_Box *child = root->first; !UI_box_is_nil(child); child = child->next)
          {
            value = Max(value, child->calc_size.v[axis]);
          }
        }
      }
      root->calc_size.v[axis] = value;
      root->calc_size.v[axis] = floor_F32(root->calc_size.v[axis]);
    } break;
  }
}

root_function void
UI_solve_size_violations(UI_Box *root, Axis2 axis)
{
  // Determine maximum available space from the root size.
  F32 available_space = root->calc_size.v[axis];
  
  F32 taken_space = 0.0f;
  F32 total_fix_budget = 0.0f;
  B32 is_overflow_x_on_axis = (root->flags & (UI_BoxFlag_OverflowX<<axis));
  if(!is_overflow_x_on_axis)
  {
    for(UI_Box *child = root->first; !UI_box_is_nil(child); child = child->next)
    {
      if(!(child->flags & (UI_BoxFlag_FloatingX<<axis)))
      {
        if(axis == root->child_layout_axis)
        {
          taken_space += child->calc_size.v[axis];
        }
        else
        {
          taken_space = Max(taken_space, child->calc_size.v[axis]);
        }
        
        F32 fix_budget_this_child = child->calc_size.v[axis] * (1.0f  - child->pref_size[axis].strictness);
        total_fix_budget += fix_budget_this_child;
        
      }
    }
  }
  
  //- Fix children as much as possible within the calculated budget 
  if(!is_overflow_x_on_axis)
  {
    F32 violation = taken_space - available_space;
    if(violation > 0 && total_fix_budget > 0)
    {
      for(UI_Box *child = root->first; !UI_box_is_nil(child); child = child->next)
      {
        if(!(child->flags & (UI_BoxFlag_FloatingX<<axis)))
        {
          F32 fix_budget_this_child = child->calc_size.v[axis] * (1.0f - child->pref_size[axis].strictness);
          F32 fix_size_this_child = 0.0f;
          if(axis == root->child_layout_axis)
          {
            fix_size_this_child = fix_budget_this_child * (violation / total_fix_budget);
          }
          else
          {
            fix_size_this_child = child->calc_size.v[axis] - available_space;
          }
          fix_size_this_child = Clamp(0, fix_size_this_child, fix_budget_this_child);
          child->calc_size.v[axis] -= fix_size_this_child;
          child->calc_size.v[axis] = floor_F32(child->calc_size.v[axis]);
        }
      }
    }
  }
  
  //- Position all children after fixup 
  {
    if(axis == root->child_layout_axis)
    {
      // Determine the relative offset by incrementing p by the size of a child on the relevant axis.
      F32 p = 0.0f;
      for(UI_Box *child = root->first; !UI_box_is_nil(child); child = child->next)
      {
        if(!(child->flags & (UI_BoxFlag_FloatingX<<axis)))
        {
          child->calc_rel_pos.v[axis] = p;
          p += child->calc_size.v[axis];
        }
      }
    }
    else
    {
      // If we're not on the layout axis the relative position is just zero
      for(UI_Box *child = root->first; !UI_box_is_nil(child); child = child->next)
      {
        if(!(child->flags & (UI_BoxFlag_FloatingX<<axis)))
        {
          child->calc_rel_pos.v[axis] = 0;
        }
      }
    }
    
    // Set the actual position values and rectangles
    for(UI_Box *child = root->first; !UI_box_is_nil(child); child = child->next)
    {
      Rng2_F32 last_rel_rect = child->rel_rect;
      unused_variable(last_rel_rect);
      // The relative rectangle starts at the relative position, and ends at the relative rectangle p0 + size.
      // TODO(anton): What does the relative rectangle mean?
      child->rel_rect.p0.v[axis] = child->calc_rel_pos.v[axis];
      child->rel_rect.p1.v[axis] = child->rel_rect.p0.v[axis] + child->calc_size.v[axis];
      // TODO(anton): Corner stuff here
      // The actual rectangle is the root rect p0, plus th relative p0, minus view offset of the root.
      // And the p1 is the p0 + the size of the child.
      child->rect.p0.v[axis] = root->rect.p0.v[axis] + child->rel_rect.p0.v[axis] - root->view_offset.v[axis];
      child->rect.p1.v[axis] = child->rect.p0.v[axis] + child->calc_size.v[axis];
      if(!(child->flags & (UI_BoxFlag_FloatingX<<axis)))
      {
        // If we are not floating on this axis, we floor the rectangle? Because we want to align to pixel sizes?
        child->rect.p0.v[axis] = floor_F32(child->rect.p0.v[axis]);
        child->rect.p1.v[axis] = floor_F32(child->rect.p1.v[axis]);
      }
    }
  }
  
  // Recurse
  for(UI_Box *child = root->first; !UI_box_is_nil(child); child = child->next)
  {
    UI_solve_size_violations(child, axis);
  }

}

root_function void 
UI_layout_root(UI_Box *root, Axis2 axis)
{
  UI_solve_independent_sizes(root, axis);
  UI_solve_upward_dependent_sizes(root, axis);
  UI_solve_downward_dependent_sizes(root, axis);
  UI_solve_size_violations(root, axis);
}

root_function void UI_layout(void)
{
  for(Axis2 axis = (Axis2)0; axis < Axis2_COUNT; axis = (Axis2)(axis+1))
  {
    UI_layout_root(ui_state->root, axis);
  }
}

////////////////////////////////
//~ Compositions
root_function void 
UI_push_pref_size(Axis2 axis, UI_Size v)
{
  if(axis == Axis2_X)
  {
    UI_push_pref_width(v);
  }
  else
  {
    UI_push_pref_height(v);
  }
}

root_function void UI_pop_pref_size(Axis2 axis)
{
  if(axis == Axis2_X)
  {
    UI_pop_pref_width();
  }
  else
  {
    UI_pop_pref_height();
  }
}

root_function void 
UI_set_next_pref_size(Axis2 axis, UI_Size v)
{
  if(axis == Axis2_X)
  {
    UI_set_next_pref_width(v);
  }
  else
  {
    UI_set_next_pref_height(v);
  }
}

root_function void
UI_push_fixed_pos(Vec2_F32 v)
{
  UI_push_fixed_x(v.x);
  UI_push_fixed_y(v.y);
}

root_function void
UI_pop_fixed_pos()
{
  UI_pop_fixed_x();
  UI_pop_fixed_y();
}

root_function void 
UI_set_next_fixed_pos(Vec2_F32 v)
{
  UI_set_next_fixed_x(v.x);
  UI_set_next_fixed_y(v.y);
}

root_function void 
UI_push_fixed_rect(Rng2_F32 rect)
{
  Vec2_F32 dim = dim2_F32(rect);
  UI_push_fixed_pos(rect.p0);
  UI_push_pref_size(Axis2_X, UI_pixels(dim.x, 1));
  UI_push_pref_size(Axis2_Y, UI_pixels(dim.y, 1));
}

root_function void 
UI_pop_fixed_rect()
{
  UI_pop_fixed_pos();
  UI_pop_pref_size(Axis2_X);
  UI_pop_pref_size(Axis2_Y);
}

root_function void 
UI_set_next_fixed_rect(Rng2_F32 rect)
{
  Vec2_F32 dim = dim2_F32(rect);
  UI_set_next_fixed_pos(rect.p0);
  UI_set_next_pref_size(Axis2_X, UI_pixels(dim.x, 1));
  UI_set_next_pref_size(Axis2_Y, UI_pixels(dim.y, 1));
}

root_function Rng2_F32 
UI_push_rect(Rng2_F32 rect)
{
  Rng2_F32 replaced = {0};
  Vec2_F32 size = dim2_F32(rect);
  replaced.x0 = UI_push_fixed_x(rect.x0);
  replaced.y0 = UI_push_fixed_y(rect.y0);
  replaced.x1 = replaced.x0 + UI_push_fixed_width(size.x);
  replaced.y1 = replaced.y0 + UI_push_fixed_height(size.y);
  
  return replaced;
}

root_function Rng2_F32
UI_pop_rect(void)
{
  Rng2_F32 popped = {0};
  popped.x0 = UI_pop_fixed_x();
  popped.y0 = UI_pop_fixed_y();
  popped.x1 = popped.x0 + UI_pop_fixed_width();
  popped.y1 = popped.y0 + UI_pop_fixed_height();
  return popped;
}

////////////////////////////////
//~ Drawing and text
root_function Vec2_F32 
UI_text_pos_from_box(UI_Box *box)
{
  // The stb assumes that the supplied start position will give 
  // the _BASELINE_ of the text that is to be produced.
  // So the text position we give here is adjusted to x of the box rect's p0,
  // and to the y of the box rect's p1.
  F32 offset_x_pixels = 0.1f*(box->rect.x1-box->rect.x0);
  F32 offset_y_pixels = 0.30*(box->rect.y1-box->rect.y0);
  Vec2_F32 result = {0};
  result.x = box->rect.x0 + offset_x_pixels;
  result.y = box->rect.y1 - offset_y_pixels;
  
  return result;
}

root_function String8
UI_display_string_from_box(UI_Box *box)
{
  return box->string;
  
}