#include "gui.h"
#include "../render/2d.h"
#include "../render/render.h"
#include "../lib/math.h"
#include "../lib/color.h"
#include "text_draw.h"
#include "../debug/logger.h"
#include "stdio.h"
#include "string.h"
#include "../platform.h"
#include "../lib/hashing.h"

Gui_State global_gui_state;

void gui_button_draw_inner_text(Gui_Context *ctx, Rect r, const char *text, v4 color, Rect *actual_drawn_rect = NULL);

bool gui_init()
{
	gui_context_init(&global_gui_state.default_context);
	global_gui_state.selected_context = &global_gui_state.default_context;
	bool success = gui_text_draw_init();
	return success;
}

void gui_deinit()
{
	gui_text_draw_deinit();
}

void gui_context_init(Gui_Context *ctx)
{
	ctx->width  = 100;
	ctx->height = 100;

	ctx->   last_frame_time = 0;
	ctx->current_frame_time = 0;

	ctx->      active = NULL;
	ctx->         hot = NULL;
	ctx->possibly_hot = NULL;
	ctx->active_start_time = 0;
	ctx->   hot_start_time = 0;
	ctx->active_status = 0;

	ctx->text_cursor_position = 0;
	ctx->text_length = 0;

	ctx->windows         = NULL;
	ctx->window_count    = 0;
	ctx->window_capacity = 0;
	ctx->current_window  = NULL;

	ctx->id_count    = 0;
	ctx->id_capacity = 8;
	ctx->id_stack    = (Gui_Id*)p_alloc(sizeof(Gui_Id) * ctx->id_capacity);

	ctx->input.pointer_position          = {0, 0};
	ctx->input.absolute_pointer_position = {0, 0};
	ctx->input.mouse_pressed             = false;
	ctx->input.mouse_pressed_this_frame  = false;
	ctx->input.mouse_released_this_frame = false;
	ctx->input.text_cursor_move          = 0;
	ctx->input.text[0]                   = '\0';

	ctx->input.absolute_pointer_position_last_frame = {0, 0};


	ctx->style.font_size = 12;
	ctx->style.animation_base_time = 0.100;

	ctx->style.text_color = v4{1.0, 1.0, 1.0, 1.0};
	ctx->style.text_align = GUI_ALIGN_CENTER;


	ctx->style.button_color              = v4{0.4f, 0.4f, 0.4f, 1.0f}*0.8f;
	ctx->style.button_color_hovered      = v4{0.3f, 0.3f, 0.3f, 1.0f}*0.9f;
	ctx->style.button_color_pressed      = v4{0.1f, 0.1f, 0.1f, 1.0f};
	ctx->style.button_text_color         = v4{1.0f, 1.0f, 1.0f, 1.0f};
	ctx->style.button_text_color_hovered = v4{1.0f, 1.0f, 1.0f, 1.0f};
	ctx->style.button_text_color_pressed = v4{1.0f, 1.0f, 1.0f, 1.0f};
	ctx->style.button_radius = 3;

	ctx->style.slider_fill_color = {0.0f, 0.3f, 0.9f, 1.0f};

	ctx->style.window_background_color          = {0.01,0.01,0.01, 0.98};
	ctx->style.window_border_color              = {1.0,0.06,0.0,1.0};
	ctx->style.window_background_color_inactive = {0.05,0.05,0.05, 0.95};
	ctx->style.window_border_color_inactive     = {0.3,0.3,0.3, 1.0};
	ctx->style.window_corner_radius             = 5;
	ctx->style.window_titlebar_color            = ctx->style.window_border_color;
	ctx->style.window_titlebar_color_inactive   = {0.1,0.1,0.1,0.1};
}

void gui_context_select(Gui_Context *ctx)
{
	global_gui_state.selected_context = ctx;
}

void gui_frame_begin(Gui_Context *ctx, f64 curr_time)
{
	ctx->   last_frame_time = ctx->current_frame_time;
	ctx->current_frame_time = curr_time;
}

void gui_frame_begin(f64 curr_time)
{
	gui_frame_begin(&global_gui_state.default_context, curr_time);
}

void gui_frame_end(Gui_Context *ctx)
{
	// Render windows
	for(u32 i = 0; i < ctx->window_count; i++)
	{
		Gui_Window *w = &ctx->windows[i];
		if(w->still_open)
		{
			Rect r_uv = Rect{0,0,1,-1}; // y is flipped when rendering framebuffer's textures
			r_2d_immediate_rectangle(w->r, v4{1,1,1,1}, r_uv, &w->framebuffer.color_texture);
			w->still_open = false; // Will be set to true if still open
		}
	}
	// @Performance: cleanup unused windows

	// Fix state
	if(ctx->hot != ctx->possibly_hot)
	{
		ctx->hot = ctx->possibly_hot;
		ctx->hot_start_time = ctx->current_frame_time;
	}

	ctx->input.mouse_pressed_this_frame  = false;
	ctx->input.mouse_released_this_frame = false;

	ctx->input.absolute_pointer_position_last_frame = ctx->input.absolute_pointer_position;

	ctx->input.text[0] = '\0';
	ctx->input.text_cursor_move = 0;

	ctx->possibly_hot = NULL;
}

void gui_frame_end()
{
	gui_frame_end(&global_gui_state.default_context);
}

void gui_handle_event(Gui_Context *ctx, Event *e)
{
	switch(e->type)
	{
		case EVENT_MOUSE_MOVE: {
			if(!e->mouse_move.relative)
			{
				ctx->input.pointer_position          = e->mouse_move.position;
				ctx->input.absolute_pointer_position = e->mouse_move.position;
			}
		} break;
		case EVENT_KEY: {
			switch(e->key.key_code)
			{
				case KEY_MOUSE_LEFT: {
					ctx->input.mouse_pressed = e->key.pressed;
					if(e->key.pressed)
						ctx->input.mouse_pressed_this_frame = true;
					else
						ctx->input.mouse_released_this_frame = true;
				} break;
				case KEY_ARROW_LEFT: {
					if(e->key.pressed)
						ctx->input.text_cursor_move--;
				} break;
				case KEY_ARROW_RIGHT: {
					if(e->key.pressed)
						ctx->input.text_cursor_move++;
				} break;
				default: {
				} break;
			}
		} break;
		case EVENT_RESIZE: {

		} break;
		case EVENT_TEXT: {
			strcat(ctx->input.text, e->text.data);
		} break;
		default: {
		} break;
	}
}

void gui_handle_event(Event *e)
{
	gui_handle_event(&global_gui_state.default_context, e);
}

// ### Widgets ###
// Text
void gui_text(Gui_Context *ctx, Rect r, const char *text)
{
	// @Feature: Clip text to Rect r
	gui_text_draw(r, text, ctx->style.font_size, ctx->style.text_color);
}

void gui_text(Rect r, const char *text)
{
	gui_text(&global_gui_state.default_context, r, text);
}

void gui_text_aligned(Gui_Context *ctx, Rect r, const char *text, Gui_Text_Align alignment)
{
	// @Cleanup: this should not depend on setting state. We should have a function that gets alignment as an argument
	Gui_Text_Align old_alignment = ctx->style.text_align;
	ctx->style.text_align = alignment;
	gui_button_draw_inner_text(ctx, r, text, ctx->style.text_color);
	ctx->style.text_align = old_alignment;
}

void gui_text_aligned(Rect r, const char *text, Gui_Text_Align alignment)
{
	gui_text_aligned(&global_gui_state.default_context, r, text, alignment);
}


v2 gui_text_compute_size(Gui_Context *ctx, const char *text)
{
	return gui_text_draw_size(text, ctx->style.font_size);
}

v2 gui_text_compute_size(const char *text)
{
	return gui_text_compute_size(&global_gui_state.default_context, text);
}

// Button
bool gui_button(Gui_Context *ctx, Rect r, const char *text)
{
	Gui_Id widget_id = gui_id_from_pointer(ctx, text);
	bool behaviuor = gui_button_behaviuor(ctx, widget_id, r);

	// Compute colors
	v4 button_color = ctx->style.button_color;
	v4 text_color = ctx->style.button_text_color;
	{
		if(ctx->hot == widget_id)
		{
			f64 delta_t = (ctx->current_frame_time - ctx->hot_start_time);
			f32 interpolation = clamp(0, 1, delta_t / ctx->style.animation_base_time);
			button_color = lerp(ctx->style.button_color, ctx->style.button_color_hovered, interpolation);
			text_color   = lerp(ctx->style.button_text_color, ctx->style.button_text_color_hovered, interpolation);
		}
		if(ctx->active == widget_id)
		{
			f64 delta_t = (ctx->current_frame_time - ctx->active_start_time);
			f32 interpolation = clamp(0, 1, delta_t / ctx->style.animation_base_time);
			button_color = lerp(ctx->style.button_color_hovered, ctx->style.button_color_pressed, interpolation * 0.4 + 0.6);
			text_color   = lerp(ctx->style.button_text_color_hovered, ctx->style.button_text_color_pressed, interpolation * 0.4 + 0.6);
		}
	}

	// Draw button and text
	r_2d_immediate_rounded_rectangle(r, ctx->style.button_radius, button_color);
	gui_button_draw_inner_text(ctx, r, text, text_color);

	return behaviuor;
}

bool gui_button(Rect r, const char *text)
{
	return gui_button(&global_gui_state.default_context, r, text);
}

void gui_button_draw_inner_text(Gui_Context *ctx, Rect r, const char *text, v4 color, Rect *actual_drawn_rect)
{
	v2 text_size = gui_text_draw_size(text, ctx->style.font_size);
	// Alignment (center, left, right)
	v2 text_position = r.position + (r.size - text_size) * v2{0.5, 0.5};
	if(ctx->style.text_align == GUI_ALIGN_LEFT)
		text_position = r.position + (r.size - text_size) * v2{0, 0.5};
	if(ctx->style.text_align == GUI_ALIGN_RIGHT)
		text_position = r.position + (r.size - text_size) * v2{1, 0.5};
	// Draw
	Rect text_rect = { .position = text_position, .size = text_size };
	// @Feature: Clip text to Rect r
	gui_text_draw(text_rect, text, ctx->style.font_size, color);

	if(actual_drawn_rect)
		*actual_drawn_rect = text_rect;
}

// Slider
bool gui_slider(Gui_Context *ctx, Rect r, f32 min, f32 max, f32 *value)
{
	Gui_Id widget_id = gui_id_from_pointer(ctx, value);
	bool behaviour = gui_button_behaviuor(ctx, widget_id, r);

	if(ctx->active == widget_id)
	{
		f32 pointer_ratio = (ctx->input.pointer_position.x - r.position.x) / r.size.x;
		*value = clamp(0.0f, 1.0f, pointer_ratio) * (max - min) + min;
	}

	// Colors
	v4 button_color = ctx->style.button_color;
	v4 text_color = ctx->style.button_text_color;
	{
		f64 delta_t = (ctx->current_frame_time - ctx->hot_start_time);
		f32 interpolation = sin(10 * delta_t) * 0.5 + 0.5;
		if(ctx->hot == widget_id)
		{
			button_color = lerp(ctx->style.button_color, ctx->style.button_color_hovered, interpolation);
			text_color   = lerp(ctx->style.button_text_color, ctx->style.button_text_color_hovered, interpolation);
		}
		if(ctx->active == widget_id)
		{
			button_color = lerp(ctx->style.button_color_hovered, ctx->style.button_color_pressed, interpolation * 0.4 + 0.6);
			text_color   = lerp(ctx->style.button_text_color_hovered, ctx->style.button_text_color_pressed, interpolation * 0.4 + 0.6);
		}
	}

	// Draw
	f32 border = 2;
	f32 radius = ctx->style.button_radius;

	// Draw background
	v4 background_color = ctx->style.button_color;
	r_2d_immediate_rounded_rectangle(r, radius, background_color); // Background

	// Draw fill
	f32 ratio = (*value - min) / (max - min);
	Rect fill_r = r;
	fill_r.position += v2{border, border};
	fill_r.size = v2{maximum(0, fill_r.size.x - 2*border), maximum(0, fill_r.size.y - 2*border)};
	f32 fill_radius = maximum(0, radius - border);
	fill_r.size.x = fill_r.size.x * ratio;
	r_2d_immediate_rounded_rectangle(fill_r, fill_radius, ctx->style.slider_fill_color);

	// Draw border
	v4 border_color = ctx->style.button_color_pressed;
	Rect border_r = r;
	border_r.position += v2{border, border} * 0.5;
	border_r.size = v2{maximum(0, border_r.size.x - border), maximum(0, border_r.size.y - border)};
	f32 border_radius = maximum(0, radius - border*0.5);
	r_2d_immediate_rounded_rectangle_outline(border_r, border_radius, border_color, border);

	// Draw value text
	char text[64];
	sprintf(text, "%f", *value);
	gui_button_draw_inner_text(ctx, r, text, text_color);

	return behaviour || ctx->active == widget_id;
}

bool gui_slider(Rect r, f32 min, f32 max, f32 *value)
{
	return gui_slider(&global_gui_state.default_context, r, min, max, value);
}


// Images
bool gui_image(Gui_Context *ctx, Rect r, r_texture *texture)
{
	Gui_Id widget_id = gui_id_from_pointer(ctx, texture);

	v4 color = {1,1,1,1};
	r_2d_immediate_rectangle(r, color, {0,0,1,1}, texture);

	return gui_button_behaviuor(ctx, widget_id, r);;
}

bool gui_image(Rect r, r_texture *texture)
{
	return gui_image(&global_gui_state.default_context, r, texture);
}

bool gui_image(Gui_Context *ctx, Rect r, const u8 *bmp, u32 width, u32 height, u32 channels, u32 flags)
{
	r_texture texture = r_texture_create((u8*)bmp, {width, height}, flags | R_TEXTURE_DONT_OWN);
	bool result = gui_image(ctx, r, &texture);
	r_texture_destroy(&texture);
	return result;
}

bool gui_image(Rect r, const u8 *bmp, u32 width, u32 height, u32 channels, u32 flags)
{
	return gui_image(&global_gui_state.default_context, r, bmp, width, height, channels, flags);
}


// Text input
bool gui_text_input(Gui_Context *ctx, Rect r, char *text, u64 max_size)
{
	Gui_Id widget_id = gui_id_from_pointer(ctx, text);
	bool behaviour = gui_text_input_behaviuor(ctx, widget_id, r);
	bool edited = false;

	// Cursor, mouse click, input from keyboard/os
	if(ctx->active == widget_id && ctx->input.mouse_pressed_this_frame)
	{
		ctx->text_length = strlen(text);
		ctx->text_cursor_position = ctx->text_length;
	}

	// Move cursors between UTF8 codepoints (not bytes)
	if(ctx->input.text_cursor_move != 0)
	{
		while(ctx->input.text_cursor_move > 0)
		{
			if(text[ctx->text_cursor_position] == '\0')
			{
				ctx->input.text_cursor_move = 0;
				break;
			}
			ctx->text_cursor_position += utf8_bytes_to_next_valid_codepoint(text, ctx->text_cursor_position);
			ctx->input.text_cursor_move--;
		}
		while(ctx->input.text_cursor_move < 0)
		{
			if(ctx->text_cursor_position == 0)
			{
				ctx->input.text_cursor_move = 0;
				break;
			}
			ctx->text_cursor_position -= utf8_bytes_to_prev_valid_codepoint(text, ctx->text_cursor_position);
			ctx->input.text_cursor_move++;
		}
	}

	if(ctx->active == widget_id && ctx->input.text[0] != 0)
	{
		// @Bug: Should iterate on utf8 codepoints. If we don't, there's the possibility
		// of inserting half of a multi-byte codepoint.
		for(char *c = ctx->input.text; *c != 0; c++)
		{
			if(*c == 0x08) // Backspace
			{
				if(ctx->text_cursor_position > 0)
				{
					u32 codepoint_bytes = utf8_bytes_to_prev_valid_codepoint(text, ctx->text_cursor_position);
					u64 from_index = ctx->text_cursor_position;
					u64 to_index   = ctx->text_cursor_position - codepoint_bytes;
					memmove(text + to_index, text + from_index, ctx->text_length + 1 - from_index);
					ctx->text_length -= codepoint_bytes;
					ctx->text_cursor_position -= codepoint_bytes;
				}
				continue;
			}

			if(*c == 0x7F) // Delete
			{
				if(ctx->text_cursor_position < ctx->text_length)
				{
					u32 codepoint_bytes = utf8_bytes_to_next_valid_codepoint(text, ctx->text_cursor_position);
					u64 from_index = ctx->text_cursor_position + codepoint_bytes;
					u64 to_index   = ctx->text_cursor_position;
					memmove(text + to_index, text + from_index, ctx->text_length + 1 - from_index);
					ctx->text_length -= codepoint_bytes;
				}
				continue;
			}

			if(ctx->text_length < max_size - 1) // Leave space for 0 terminator
			{
				memmove(text + ctx->text_cursor_position + 1, text + ctx->text_cursor_position, ctx->text_length + 1 - ctx->text_cursor_position);
				text[ctx->text_cursor_position] = *c;
				ctx->text_length += 1;
				ctx->text_cursor_position += 1;
			}
		}

		edited = true;
	}

	r_2d_immediate_rounded_rectangle(r, ctx->style.button_radius, ctx->style.button_color);
	Rect text_rect;
	gui_button_draw_inner_text(ctx, r, text, ctx->style.button_text_color, &text_rect);

	if(ctx->active == widget_id)
	{
		// Draw cursor
		f64 delta_t = ctx->current_frame_time - ctx->active_start_time;
		f32 u = clamp(0, 1, sin(delta_t * 5) * 0.7 + 0.6);
		v4 cursor_color = ctx->style.button_text_color;
		cursor_color *= lerp(0, cursor_color.a, u);

		char replaced = text[ctx->text_cursor_position];
		text[ctx->text_cursor_position] = 0;
		v2 cursor_position;
		v2 text_size = gui_text_draw_size(text, ctx->style.font_size, &cursor_position);
		text[ctx->text_cursor_position] = replaced;

		Rect cursor_rect =
		{
			.position = text_rect.position + cursor_position - v2{0, ctx->style.font_size},
			.size = ctx->style.font_size * v2{0.1, 0.9}
		};
		r_2d_immediate_rectangle(cursor_rect, cursor_color);
	}

	return edited;
}

bool gui_text_input(Rect r, char *text, u64 max_size)
{
	return gui_text_input(&global_gui_state.default_context, r, text, max_size);
}



// Windows
bool gui_window_start(Gui_Context *ctx, Rect r, Gui_Id id)
{
	gui_id_stack_push(ctx, id);
	Gui_Window *window = gui_window_by_id(ctx, r, id);
	window->still_open = true;
	gui_window_update_rect(ctx, window, r);
	u32 window_index = window - ctx->windows;

	bool hovered = gui_is_hovered(ctx, id, r);
	if(hovered && ctx->input.mouse_pressed_this_frame)
	{
		// Bring window on top
		u32 move_count = ctx->window_count - 1 - window_index;
		if(move_count > 0)
		{
			Gui_Window tmp = *window;
			memmove(ctx->windows + window_index, ctx->windows + window_index + 1, sizeof(Gui_Window) * move_count);

			ctx->windows[ctx->window_count - 1] = tmp;
			window_index = ctx->window_count - 1;
			window = &ctx->windows[window_index];
		}
	}

	ctx->current_window = window;
	ctx->input.pointer_position = ctx->input.absolute_pointer_position - window->r.position;

	ctx->old_framebuffer = r_render_state.current_framebuffer;
	r_framebuffer_select(&window->framebuffer);


	bool is_inactive = window_index != ctx->window_count-1;
	v4 background_color = is_inactive ? ctx->style.window_background_color_inactive :
ctx->style.window_background_color;
	v4 border_color     = is_inactive ? ctx->style.window_border_color_inactive     :
ctx->style.window_border_color;
	r_clear({0,0,0,0});
	Rect background_rect = {0.5, 0.5, floor(r.w)-1.0, floor(r.h)-1.0};
	r_2d_immediate_rounded_rectangle(background_rect, ctx->style.window_corner_radius, background_color);
	r_2d_immediate_rounded_rectangle_outline(background_rect, ctx->style.window_corner_radius, border_color, 1.0);

	return true;
}

bool gui_window_start(Rect r, Gui_Id id)
{
	return gui_window_start(&global_gui_state.default_context, r, id);
}

void gui_window_end(Gui_Context *ctx)
{
	gui_id_stack_pop(ctx);
	ctx->current_window = NULL;
	ctx->input.pointer_position = ctx->input.absolute_pointer_position;

	r_framebuffer_select(ctx->old_framebuffer);
}

void gui_window_end()
{
	return gui_window_end(&global_gui_state.default_context);
}


bool gui_window_titlebar(Gui_Context *ctx, Rect r, const char *title, bool *close, v2 *move)
{
	Gui_Id widget_id = gui_id_from_pointer(ctx, title);
	bool behaviour = gui_button_behaviuor(ctx, widget_id, r);
	if(close)
		*close = false;
	if(move)
		*move = {0,0};

	// Background
	v4 titlebar_color = ctx->style.window_titlebar_color_inactive;
	if(ctx->current_window && ctx->current_window - ctx->windows == ctx->window_count - 1)
	{
		titlebar_color = ctx->style.window_titlebar_color;
	}
	r_2d_immediate_rounded_rectangle(r, ctx->style.window_corner_radius, titlebar_color);

	// Title
	v2 title_size = gui_text_compute_size(title);
	Rect title_r = r;
	title_r.size = title_size;
	title_r.position = r.position + (r.size - title_r.size) / 2;
	gui_text(title_r, title);

	// Exit button
	f32 smallest_side = minimum(r.w, r.h);
	f32 exit_size = smallest_side;
	Gui_Style exit_style = ctx->style;
	exit_style.button_color              = v4{0.8f, 0.8f, 0.8f, 1.0f}*0.0f;
	exit_style.button_color_hovered      = v4{1.0f, 0.0f, 0.0f, 1.0f}*1.0f;
	exit_style.button_color_pressed      = v4{0.8f, 0.0f, 0.0f, 1.0f};
	exit_style.button_text_color         = v4{1.0f, 1.0f, 1.0f, 1.0f};
	exit_style.button_text_color_hovered = v4{1.0f, 1.0f, 1.0f, 1.0f};
	exit_style.button_text_color_pressed = v4{1.0f, 1.0f, 1.0f, 1.0f};
	exit_style.button_radius = ctx->style.window_corner_radius;
	Gui_Style old_style = ctx->style;
	ctx->style = exit_style;
	Rect exit_button_r = {r.x + r.w - exit_size, r.y, exit_size, exit_size};
	if(gui_button(ctx, exit_button_r, "⨯"))
	{
		if(close)
			*close = true;
		behaviour = true;
	}
	ctx->style = old_style;

	// Move
	if(ctx->active == widget_id && !ctx->input.mouse_pressed_this_frame)
	{
		if(move)
		{
			*move = ctx->input.absolute_pointer_position - ctx->input.absolute_pointer_position_last_frame;
		}
	}

	return behaviour || ctx->active == widget_id;
}

bool gui_window_titlebar(Rect r, const char *title, bool *close, v2 *move)
{
	return gui_window_titlebar(&global_gui_state.default_context, r, title, close, move);
}



Gui_Window *gui_window_by_id(Gui_Context *ctx, Rect r, Gui_Id id)
{
	Gui_Window *window = NULL;
	for(u32 i = 0; i < ctx->window_count; i++)
	{
		if(ctx->windows[i].id == id)
		{
			window = &ctx->windows[i];
			break;
		}
	}

	if(!window)
	{
		if(ctx->window_count >= ctx->window_capacity)
		{
			if(ctx->window_capacity == 0)
				ctx->window_capacity = 1;
			ctx->window_capacity *= 2;
			ctx->windows = (Gui_Window*) p_realloc(ctx->windows, sizeof(Gui_Window) * ctx->window_capacity);
		}

		window = &ctx->windows[ctx->window_count];
		ctx->window_count++;

		window->id = id;
		window->r  = r;
		window->framebuffer = r_framebuffer_create(V2S(r.size), 0);
	}

	return window;
}

void gui_window_update_rect(Gui_Context *ctx, Gui_Window *window, Rect r)
{
	if(window->r.size != r.size)
	{
		r_framebuffer_update_size(&window->framebuffer, V2S(r.size));
	}
	window->r = r;
}


// Helpers
bool gui_is_hovered(Gui_Context *ctx, Gui_Id widget_id, Rect r)
{
	if(is_inside(r, ctx->input.pointer_position))
	{
		s32 current_window_index = -1; // We use -1 to indicate we are not in a window. When we iterate over windows we do a +1 and start from 0, aka the first window. If we used 0, we would start from 1 and skip over window index 0.

		// The ctx->windows array is sorted from back to front. If we are inside a window, only the following windows in the array can overlap up. The ones before are covered by the current window.
		if(ctx->current_window)
			current_window_index = ctx->current_window - ctx->windows;

		// Am I a window? If so, we start checking from us. If ctx->current_window is set and widget_id is a window, it means we are a subwindow.
		// Subwindow are not supported yet though (20 September 2023), so this should be a bug in the user code. Yeah we don't check to prevent this, but anyways.
		for(s32 i = current_window_index + 1; i < ctx->window_count; i++)
		{
			Gui_Id window_id = ctx->windows[i].id;
			if(widget_id == window_id)
			{
				current_window_index = i;
				break;
			}
		}

		// Iterate over windows that cover the current one
		for(u32 i = current_window_index + 1; i < ctx->window_count; i++)
		{
			Gui_Id window_id   = ctx->windows[i].id;
			Rect window_rect = ctx->windows[i].r;
			if(is_inside(window_rect, ctx->input.absolute_pointer_position))
			{
				return false;
			}
		}
		return true;
	}
	return false;
}

bool gui_button_behaviuor(Gui_Context *ctx, Gui_Id widget_id, Rect r)
{
	bool behaviour = false;
	if(gui_is_hovered(ctx, widget_id, r))
	{
		if(!ctx->active || ctx->active == widget_id || !(ctx->active_status & GUI_WIDGET_STATUS_PREVENT_HOT))
			ctx->possibly_hot = widget_id;

		if(ctx->hot == widget_id && ctx->input.mouse_pressed_this_frame)
		{
			ctx->active = widget_id;
			ctx->active_start_time = ctx->current_frame_time;
			ctx->active_status = GUI_WIDGET_STATUS_PREVENT_HOT;
		}

		if(ctx->active == widget_id && ctx->input.mouse_released_this_frame)
		{
			behaviour = true;
		}
	}

	if(ctx->active == widget_id && ctx->input.mouse_released_this_frame)
	{
		ctx->active = NULL;
		ctx->active_status = 0;
	}

	return behaviour;
}


bool gui_text_input_behaviuor(Gui_Context *ctx, Gui_Id widget_id, Rect r)
{
	bool behaviour = false;
	if(gui_is_hovered(ctx, widget_id, r))
	{
		if(!ctx->active || ctx->active == widget_id || !(ctx->active_status & GUI_WIDGET_STATUS_PREVENT_HOT))
			ctx->possibly_hot = widget_id;

		if(ctx->hot == widget_id && ctx->input.mouse_pressed_this_frame)
		{
			ctx->active = widget_id;
			ctx->active_start_time = ctx->current_frame_time;
			ctx->active_status = 0;
		}

		if(ctx->active == widget_id && ctx->input.mouse_released_this_frame)
		{
			behaviour = true;
		}
	}

	if(ctx->active == widget_id && ctx->input.mouse_released_this_frame)
	{
		// ctx->active = NULL;
		// ctx->active_status = 0;
	}

	return behaviour;
}

Gui_Id gui_id_from_pointer(Gui_Context *ctx, const void* ptr)
{
	u32 seed = 0xFFFFFFFF;
	if(ctx->id_count)
		seed = ctx->id_stack[ctx->id_count - 1];
	return hash_crc32(&ptr, sizeof(void*), seed);
}

void gui_id_stack_push(Gui_Context *ctx, Gui_Id id)
{
	if(ctx->id_capacity <= ctx->id_count)
	{
		u32 new_capacity = maximum(ctx->id_count + 1, ctx->id_capacity * 2);
		ctx->id_stack = (Gui_Id*)p_realloc(ctx->id_stack, sizeof(Gui_Id) * new_capacity);
		ctx->id_capacity = new_capacity;
	}

	ctx->id_stack[ctx->id_count] = id;
	ctx->id_count++;
}

void gui_id_stack_pop(Gui_Context *ctx)
{
	if(ctx->id_count > 0)
		ctx->id_count--;
}
