/* date = March 25th 2024 10:14 pm */

#ifndef UI_CORE_H
#define UI_CORE_H

////////////////////////////////
//~ Keys
typedef enum UI_Color
{
  UI_Color_Null,
  UI_Color_PlainBackground,
  UI_Color_PlainText,
  UI_Color_PlainBorder,
  UI_Color_PlainOverlay,
  UI_Color_COUNT
} UI_Color;



////////////////////////////////
//~ Keys

// The UI Key is used to hash a "widget" so we can get events from it.
typedef struct UI_Key UI_Key;
struct UI_Key
{
  U64 u64[1];
};

////////////////////////////////
//~ Mouse Button Kinds

typedef enum UI_MouseButtonKind
{
  UI_MouseButtonKind_Left,
  UI_MouseButtonKind_Middle,
  UI_MouseButtonKind_Right,
  UI_MouseButtonKind_COUNT
}
UI_MouseButtonKind;

////////////////////////////////
//~ Focus Types (for hot, active widget states)

typedef enum UI_FocusKind
{
  UI_FocusKind_Null,
  UI_FocusKind_Off,
  UI_FocusKind_On,
  UI_FocusKind_Root,
  UI_FocusKind_COUNT
}
UI_FocusKind;

////////////////////////////////
//~ Semantic sizes

// The size kind specifies how the size of a box should be computed
typedef enum UI_SizeKind
{
  UI_SizeKind_Pixels,
  UI_SizeKind_TextDim,
  UI_SizeKind_Percent,
  UI_SizeKind_SizeByChildren,
  UI_SizeKind_COUNT
}
UI_SizeKind;

typedef struct UI_Size UI_Size;
struct UI_Size
{
  UI_SizeKind kind;
  F32 value;
  F32 strictness;
};

////////////////////////////////
//~ Main UI hierarchy

typedef enum UI_TextAlignment
{
  UI_TextAlignment_Left,
  UI_TextAlignment_COUNT,
}
UI_TextAlignment;

typedef U32 UI_BoxFlags;
enum
{
  // Interaction
  UI_BoxFlag_Disabled               = (1<<0),
  UI_BoxFlag_MouseClickable         = (1<<1),
  UI_BoxFlag_FocusHot               = (1<<3),  
  UI_BoxFlag_FocusActive            = (1<<4),  
  
  // Layout
  UI_BoxFlag_FloatingX              = (1<<5),  
  UI_BoxFlag_FloatingY              = (1<<6),
  UI_BoxFlag_FixedWidth             = (1<<7),
  UI_BoxFlag_FixedHeight            = (1<<8),
  UI_BoxFlag_OverflowX              = (1<<9),  
  UI_BoxFlag_OverflowY              = (1<<10),  
  
  // Appearance
  UI_BoxFlag_Clip                   = (1<<11),
  UI_BoxFlag_DrawText               = (1<<12),
  UI_BoxFlag_DrawBackground         = (1<<13),
  UI_BoxFlag_DrawBorder             = (1<<14),
  UI_BoxFlag_DrawHotEffects         = (1<<15),
  UI_BoxFlag_DrawActiveEffects      = (1<<16),
  UI_BoxFlag_DisableTextTruncate    = (1<<17),
  UI_BoxFlag_DrawDropShadow         = (1<<18),
  
  // Helpers, when either X or Y is active etc.
  UI_BoxFlag_Floating               = UI_BoxFlag_FloatingX | UI_BoxFlag_FloatingY,
  UI_BoxFlag_Clickable              = UI_BoxFlag_MouseClickable, //| UI_BoxFlag_KeyboardClickable,
};

// UI Box is the big struct that handles all of the information of a "node" in the ui hierarchy.
// The box is a part of the composition that can be described as a "widget".
// It has both the tree information adn the state information in it.
typedef struct UI_Box UI_Box;
struct UI_Box
{
  // Hash links, persistent across frames
  UI_Box *hash_next;
  UI_Box *hash_prev;
  
  // Tree link data, updates every frame.
  // This enables us to encode an n-ary tree to describe the "box" hierarchy that defines the UI.
  UI_Box *first;
  UI_Box *last;
  UI_Box *next;
  UI_Box *prev;
  UI_Box *parent;
  U64 child_count;
  
  // Key and generation info
  UI_Key key;
  U64 last_frame_touched_index;
  
  // Per-build parameters
  UI_BoxFlags flags;
  String8 string;
  Vec2_F32 fixed_position;
  Vec2_F32 fixed_size;
  UI_Size pref_size[Axis2_COUNT];
  Axis2 child_layout_axis;
  
  Vec4_F32 background_color;
  Vec4_F32 text_color;
  Vec4_F32 border_color;
  Vec4_F32 overlay_color;
  F32 corner_radii[Corner_COUNT];
  
  // Post size determination
  Vec2_F32 calc_size;
  Vec2_F32 calc_rel_pos;
  
  // Post-layout data
  Rng2_F32 rel_rect;
  Rng2_F32 rect;
  
  // State that is persistent across frames
  F32 hot_t;
  F32 active_t;
  F32 disabled_t;
  F32 focus_hot_t;
  F32 focus_active_t;
  U64 first_gen_touched;
  U64 last_gen_touched;
  Vec2_F32 view_offset;
  Vec2_F32 target_view_offset;
  
  
  
};

typedef struct UI_BoxRec UI_BoxRec;
struct UI_BoxRec
{
  UI_Box *next;
  S32 push_count;
  S32 pop_count;
};


//~ Signal  
typedef U32 UI_SignalFlags;
enum
{
  // mouse press -> box was pressed while hovering
  UI_SignalFlag_LeftPressed     = (1<<0),
  UI_SignalFlag_MiddlePressed   = (1<<1),
  UI_SignalFlag_RightPressed    = (1<<2),
  
  // released -> box was previously pressed & user released, in or out of bounds
  UI_SignalFlag_LeftReleased    = (1<<12),
  UI_SignalFlag_MiddleReleased  = (1<<13),
  UI_SignalFlag_RightReleased   = (1<<14),
  
  // clicked -> box was previously pressed & user released, in bounds
  UI_SignalFlag_LeftClicked     = (1<<15),
  UI_SignalFlag_MiddleClicked   = (1<<16),
  UI_SignalFlag_RightClicked    = (1<<17),
  
  UI_SignalFlag_Hovering        = (1<<25), // hovering specifically this box
  UI_SignalFlag_MouseOver       = (1<<26), // mouse is over, but may be occluded
  
  UI_SignalFlag_Pressed         = UI_SignalFlag_LeftPressed | UI_SignalFlag_MiddlePressed | UI_SignalFlag_RightPressed,
  UI_SignalFlag_Clicked         = UI_SignalFlag_LeftClicked | UI_SignalFlag_MiddleClicked | UI_SignalFlag_RightClicked 
};

// The UI Signal is the struct which carries the information about user interaction with a box
typedef struct UI_Signal UI_Signal;
struct UI_Signal
{
  UI_Box *box;
  UI_SignalFlags flag;
};

#define UI_hovering(s)          !!((s).flag & UI_SignalFlag_Hovering)
#define UI_pressed(s)           !!((s).flag & UI_SignalFlag_Pressed) 
/////////////////////////////////
//~ Generated/meta

// This is "metaprogramming" code that eventually will be generated externally.
// Right now I am doing some thing by hand, but it basically contains macros that define 
// different stack members for the UI state.
#include "ui_meta.h"


////////////////////////////////
//~ UI State

// This is a slot in the hash table that helps caching of UI boxes between frames.
typedef struct UI_BoxSlot UI_BoxSlot;
struct UI_BoxSlot
{
  UI_Box *first;
  UI_Box *last;
};

typedef struct UI_State UI_State;
struct UI_State 
{
  // Permanent state
  U64 build_gen;
  Arena *arena;
  
  // Frame arenas. We have two since we want to use information from the previous frame to
  // compute things in the current frame.
  U32 last_frame_arena_index;
  U32 current_frame_arena_index;
  Arena *frame_arenas[2];
  
  //- Persistent box state
  UI_Box *first_free_box;
  U64 free_box_list_count;
  UI_BoxSlot *box_table;
  U64 box_table_size;
  
  //- Per UI build parameters
  OS_Handle window;
  OS_EventList *events;
  UI_Box *root;
  UI_Box *ctx_menu_root;
  B32 ctx_menu_touched_this_frame;
  Vec2_F32 mouse;
  
  //- User interaction state
  UI_Key hot_box_key;
  UI_Key active_box_key[UI_MouseButtonKind_COUNT];
  
  //- Color, rendering properties 
  Vec4_F32 colors[UI_Color_COUNT];
  
  //- Context menu state
  UI_Key ctx_menu_anchor_key;
  UI_Key next_ctx_menu_anchor_key;
  Vec2_F32 ctx_menu_anchor_box_last_pos;
  Vec2_F32 ctx_menu_anchor_offset;
  B32 ctx_menu_open;
  B32 next_ctx_menu_open;
  UI_Key ctx_menu_key;
  B32 ctx_menu_changed;
  
  // TODO(anton): tooltip root
  
  // Stack state. Here Ryan uses generated code from metadesk. I will do it by hand to start with.
  UI_declare_stack_nils;
  UI_declare_stacks;
};

////////////////////////////////
//~ Globals

// Nil structs

global UI_Box ui_g_nil_box;

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

//- Boxes 
root_function B32 UI_box_is_nil(UI_Box *box);
#define UI_box_set_nil(b) ((b) = &ui_g_nil_box)

root_function UI_BoxRec UI_box_recurse_depth_first(UI_Box *box, UI_Box *stopper, MemberOffset sib, MemberOffset child);
#define UI_box_recurse_depth_first_post(box, stopper) UI_box_recurse_depth_first((box), (stopper), MemberOff(UI_Box, prev), MemberOff(UI_Box, last));

//- Sizes 
root_function                         UI_Size UI_size_make(UI_SizeKind kind, F32 value, F32 strictness);
#define UI_pixels(v, strictness)              UI_size_make(UI_SizeKind_Pixels, (v), (strictness))
#define UI_size_by_children(v, strictness)    UI_size_make(UI_SizeKind_SizeByChildren, (v), (strictness))
#define UI_pct(v, strictness)                 UI_size_make(UI_SizeKind_Percent, (v), (strictness))

//- ID strings 
root_function String8 UI_hash_part_from_box_string(String8 string);

//- Keys 
root_function UI_Key UI_key_zero(void);
root_function UI_Key UI_key_from_string(UI_Key seed, String8 string);
root_function B32 UI_key_match(UI_Key a, UI_Key b);

//- Signal 
root_function UI_Signal UI_signal_from_box(UI_Box *box);


////////////////////////////////
//~ UI State functions
root_function UI_State *UI_state_alloc(void);
root_function void UI_state_set(UI_State *ui);

////////////////////////////////
//~ Build phase
root_function void UI_build_begin(OS_Handle window, OS_EventList *events);
root_function void UI_build_end(void);

////////////////////////////////
//~ Context menu
root_function void UI_ctx_menu_open(UI_Key key, UI_Key anchor_key, Vec2_F32 anchor_offset);
root_function void UI_ctx_menu_close(void);
root_function B32 UI_begin_ctx_menu(UI_Key key);
root_function void UI_end_ctx_menu(void);

////////////////////////////////
//~ UI Frame
root_function Arena *UI_frame_arena(void);
root_function void UI_frame_begin(F32 delta_time);
root_function void UI_frame_end(void);

////////////////////////////////
//~ Box hierarchy construction
root_function UI_Box *UI_box_from_key(UI_Key key);
root_function UI_Box *UI_box_make_from_key(UI_BoxFlags flags, UI_Key key);
root_function UI_Box *UI_box_make(UI_BoxFlags flags, String8 string);

////////////////////////////////
//~ layout
root_function void UI_solve_independent_sizes(UI_Box *root, Axis2 axis);
root_function void UI_solve_upward_dependent_sizes(UI_Box *root, Axis2 axis);
root_function void UI_solve_downward_dependent_sizes(UI_Box *root, Axis2 axis);
root_function void UI_solve_size_violations(UI_Box *root, Axis2 axis);
root_function void UI_layout_root(UI_Box *root, Axis2 axis);
root_function void UI_layout(void);

////////////////////////////////
//~ Compositions
root_function void UI_push_pref_size(Axis2 axis, UI_Size v);
root_function void UI_pop_pref_size(Axis2 axis);
root_function void UI_set_next_pref_size(Axis2 axis, UI_Size v);
root_function void UI_push_fixed_pos(Vec2_F32 v);
root_function void UI_pop_fixed_pos();
root_function void UI_set_next_fixed_pos(Vec2_F32 v);
root_function void UI_push_fixed_rect(Rng2_F32 rect);
root_function void UI_pop_fixed_rect();
root_function void UI_set_next_fixed_rect(Rng2_F32 rect);


////////////////////////////////
//~ Drawing and text
root_function Vec2_F32 UI_text_pos_from_box(UI_Box *box);
root_function String8 UI_display_string_from_box(UI_Box *box);
////////////////////////////////
//~ Defer helpers

//- base 
#define UI_parent(v) DeferLoop(UI_push_parent(v), UI_pop_parent())
#define UI_pref_width(v) DeferLoop(UI_push_pref_width(v), UI_pop_pref_width())
#define UI_pref_height(v) DeferLoop(UI_push_pref_height(v), UI_pop_pref_height())

//- pane 
#define UI_pane(r, s) DeferLoop(UI_pane_begin((r), (s)), UI_pane_end())
//- compositions 
#define UI_width_fill UI_pref_width(UI_pct(1, 0))
#define UI_height_fill UI_pref_height(UI_pct(1, 0))
#define UI_fixed_x(v) DeferLoop(UI_push_fixed_x((v)), UI_pop_fixed_x())
#define UI_fixed_y(v) DeferLoop(UI_push_fixed_y((v)), UI_pop_fixed_y())
#define UI_fixed_pos(v) DeferLoop(UI_push_fixed_pos(v), UI_pop_fixed_pos())
#define UI_ctx_menu(key) DeferLoopChecked(UI_begin_ctx_menu(key), UI_end_ctx_menu())
#endif //UI_CORE_H
