package gui

import clay "clay:."
import "base:runtime"
import "core:fmt"
import "core:math"
import "core:strings"
import rl "vendor:raylib"

// --- Font IDs (indices into raylib_fonts) ---
CLAY_FONT_BODY  :: u16(0)
CLAY_FONT_TITLE :: u16(1)
CLAY_FONT_MONO  :: u16(2)

// --- Font registry (shared between layout and renderer) ---
Raylib_Font :: struct {
	fontId: u16,
	font:   rl.Font,
}

raylib_fonts: [dynamic]Raylib_Font


// --- Color conversion ---
clay_color_to_rl :: proc(color: clay.Color) -> rl.Color {
	return {u8(color[0]), u8(color[1]), u8(color[2]), u8(color[3])}
}

// --- Clay Error Handler ---
clay_error_handler :: proc "c" (errorData: clay.ErrorData) {
	context = runtime.default_context()
	fmt.eprintf("CLAY ERROR: %v\n", errorData)
}

// --- Clay State ---
Clay_State :: struct {
	arena_memory: [dynamic]u8,
	font_default: rl.Font,
	font_title:  rl.Font,
	font_mono:  rl.Font,
}

clay_state: Clay_State

load_font_or_default :: proc(path: cstring) -> rl.Font {
	font := rl.LoadFont(path)
	if font.glyphCount > 0 {
		rl.SetTextureFilter(font.texture, .BILINEAR)
		return font
	}
	return rl.GetFontDefault()
}

// --- Init / Shutdown ---
clay_init :: proc(screen_w: i32, screen_h: i32) {
	min_memory_size := clay.MinMemorySize()
	clay_state.arena_memory = make([dynamic]u8, min_memory_size)
	arena: clay.Arena = clay.CreateArenaWithCapacityAndMemory(uint(min_memory_size), raw_data(clay_state.arena_memory))

	clay.Initialize(arena, {f32(screen_w), f32(screen_h)}, {handler = clay_error_handler})
	clay.SetMeasureTextFunction(clay_measure_text, nil)

	clay_state.font_default = load_font_or_default("/usr/share/fonts/Adwaita/AdwaitaSans-Regular.ttf")
	clay_state.font_title = load_font_or_default("/usr/share/fonts/Adwaita/AdwaitaSans-Regular.ttf")
	clay_state.font_mono = load_font_or_default("/usr/share/fonts/Adwaita/AdwaitaMono-Regular.ttf")

	raylib_fonts = make([dynamic]Raylib_Font, 0, 3)
	append(&raylib_fonts, Raylib_Font{fontId = CLAY_FONT_BODY, font = clay_state.font_default})
	append(&raylib_fonts, Raylib_Font{fontId = CLAY_FONT_TITLE, font = clay_state.font_title})
	append(&raylib_fonts, Raylib_Font{fontId = CLAY_FONT_MONO, font = clay_state.font_mono})
}

clay_shutdown :: proc() {
	for f in raylib_fonts {
		if f.font.glyphCount > 0 && f.font.glyphs != nil {
			rl.UnloadFont(f.font)
		}
	}
	delete(raylib_fonts)
	delete(clay_state.arena_memory)
}

clay_update_dimensions :: proc(screen_w: i32, screen_h: i32) {
	clay.SetLayoutDimensions({f32(screen_w), f32(screen_h)})
}

clay_update_input :: proc() {
	mouse_pos := rl.GetMousePosition()
	is_down := rl.IsMouseButtonDown(.LEFT)
	clay.SetPointerState({mouse_pos.x, mouse_pos.y}, is_down)

	scroll_delta := rl.GetMouseWheelMove()
	clay.UpdateScrollContainers(true, {f32(scroll_delta) * 40, 0}, rl.GetFrameTime())
}

// --- Text Measurement ---
clay_measure_text :: proc "c" (text: clay.StringSlice, config: ^clay.TextElementConfig, userData: rawptr) -> clay.Dimensions {
	context = runtime.default_context()

	if config.fontId >= u16(len(raylib_fonts)) {
		return {width = f32(text.length) * f32(config.fontSize) * 0.6, height = f32(config.fontSize)}
	}

	line_width: f32 = 0
	font := raylib_fonts[config.fontId].font
	text_str := string(text.chars[:text.length])

	for i in 0 ..< len(text_str) {
		ch := text_str[i]
		glyph_index := rl.GetGlyphIndex(font, rune(ch))

		if glyph_index < 0 || glyph_index >= i32(font.glyphCount) {
			line_width += f32(config.fontSize) * 0.6
			continue
		}

		glyph := font.glyphs[glyph_index]
		if glyph.advanceX != 0 {
			line_width += f32(glyph.advanceX)
		} else if glyph_index < i32(font.glyphCount) {
			line_width += font.recs[glyph_index].width + f32(font.glyphs[glyph_index].offsetX)
		}
	}

	scaleFactor := f32(config.fontSize) / f32(font.baseSize)
	total_spacing := f32(len(text_str)) * f32(config.letterSpacing)

	return {width = line_width * scaleFactor + total_spacing, height = f32(config.fontSize)}
}

// --- Rendering ---
clay_raylib_render :: proc(render_commands: ^clay.ClayArray(clay.RenderCommand)) {
	for i in 0 ..< render_commands.length {
		render_command := clay.RenderCommandArray_Get(render_commands, i)
		bounds := render_command.boundingBox

		switch render_command.commandType {
		case .None:
		case .Text:
			config := render_command.renderData.text
			text := string(config.stringContents.chars[:config.stringContents.length])
			cstr_text := strings.clone_to_cstring(text)
			if config.fontId < u16(len(raylib_fonts)) {
				font := raylib_fonts[config.fontId].font
				rl.DrawTextEx(
					font,
					cstr_text,
					{bounds.x, bounds.y},
					f32(config.fontSize),
					f32(config.letterSpacing),
					clay_color_to_rl(config.textColor),
				)
			}
		case .Image:
			config := render_command.renderData.image
			texture := (^rl.Texture2D)(config.imageData)
			rl.DrawTextureEx(texture^, {bounds.x, bounds.y}, 0, bounds.width / f32(texture.width), rl.WHITE)
		case .ScissorStart:
			rl.BeginScissorMode(
				i32(math.round(bounds.x)),
				i32(math.round(bounds.y)),
				i32(math.round(bounds.width)),
				i32(math.round(bounds.height)),
			)
		case .ScissorEnd:
			rl.EndScissorMode()
		case .Rectangle:
			config := render_command.renderData.rectangle
			if config.cornerRadius.topLeft > 0 {
				radius := (config.cornerRadius.topLeft * 2) / min(bounds.width, bounds.height)
				rl.DrawRectangleRounded(
					{bounds.x, bounds.y, bounds.width, bounds.height},
					radius, 8,
					clay_color_to_rl(config.backgroundColor),
				)
			} else {
				rl.DrawRectangle(
					i32(math.round(bounds.x)),
					i32(math.round(bounds.y)),
					i32(math.round(bounds.width)),
					i32(math.round(bounds.height)),
					clay_color_to_rl(config.backgroundColor),
				)
			}
		case .Border:
			config := render_command.renderData.border
			if config.width.left > 0 {
				rl.DrawRectangle(
					i32(math.round(bounds.x)),
					i32(math.round(bounds.y + config.cornerRadius.topLeft)),
					i32(config.width.left),
					i32(math.round(bounds.height - config.cornerRadius.topLeft - config.cornerRadius.bottomLeft)),
					clay_color_to_rl(config.color),
				)
			}
			if config.width.right > 0 {
				rl.DrawRectangle(
					i32(math.round(bounds.x + bounds.width - f32(config.width.right))),
					i32(math.round(bounds.y + config.cornerRadius.topRight)),
					i32(config.width.right),
					i32(math.round(bounds.height - config.cornerRadius.topRight - config.cornerRadius.bottomRight)),
					clay_color_to_rl(config.color),
				)
			}
			if config.width.top > 0 {
				rl.DrawRectangle(
					i32(math.round(bounds.x + config.cornerRadius.topLeft)),
					i32(math.round(bounds.y)),
					i32(math.round(bounds.width - config.cornerRadius.topLeft - config.cornerRadius.topRight)),
					i32(config.width.top),
					clay_color_to_rl(config.color),
				)
			}
			if config.width.bottom > 0 {
				rl.DrawRectangle(
					i32(math.round(bounds.x + config.cornerRadius.bottomLeft)),
					i32(math.round(bounds.y + bounds.height - f32(config.width.bottom))),
					i32(math.round(bounds.width - config.cornerRadius.bottomLeft - config.cornerRadius.bottomRight)),
					i32(config.width.bottom),
					clay_color_to_rl(config.color),
				)
			}
			if config.cornerRadius.topLeft > 0 {
				rl.DrawRing(
					{math.round(bounds.x + config.cornerRadius.topLeft), math.round(bounds.y + config.cornerRadius.topLeft)},
					math.round(config.cornerRadius.topLeft - f32(config.width.top)),
					config.cornerRadius.topLeft,
					180, 270, 10, clay_color_to_rl(config.color),
				)
			}
			if config.cornerRadius.topRight > 0 {
				rl.DrawRing(
					{math.round(bounds.x + bounds.width - config.cornerRadius.topRight), math.round(bounds.y + config.cornerRadius.topRight)},
					math.round(config.cornerRadius.topRight - f32(config.width.top)),
					config.cornerRadius.topRight,
					270, 360, 10, clay_color_to_rl(config.color),
				)
			}
			if config.cornerRadius.bottomLeft > 0 {
				rl.DrawRing(
					{math.round(bounds.x + config.cornerRadius.bottomLeft), math.round(bounds.y + bounds.height - config.cornerRadius.bottomLeft)},
					math.round(config.cornerRadius.bottomLeft - f32(config.width.top)),
					config.cornerRadius.bottomLeft,
					90, 180, 10, clay_color_to_rl(config.color),
				)
			}
			if config.cornerRadius.bottomRight > 0 {
				rl.DrawRing(
					{math.round(bounds.x + bounds.width - config.cornerRadius.bottomRight), math.round(bounds.y + bounds.height - config.cornerRadius.bottomRight)},
					math.round(config.cornerRadius.bottomRight - f32(config.width.bottom)),
					config.cornerRadius.bottomRight,
					0.1, 90, 10, clay_color_to_rl(config.color),
				)
			}
		case .OverlayColorStart:
			config := render_command.renderData.overlayColor
			rl.DrawRectangle(
				i32(math.round(bounds.x)),
				i32(math.round(bounds.y)),
				i32(math.round(bounds.width)),
				i32(math.round(bounds.height)),
				clay_color_to_rl(config.color),
			)
		case .OverlayColorEnd:
		case .Custom:
		}
	}
}

// --- Layout helpers (procs because Clay helpers require runtime evaluation) ---

clay_card_layout :: proc() -> clay.LayoutConfig {
	return clay.LayoutConfig {
		layoutDirection = .TopToBottom,
		sizing = {
			width = clay.SizingGrow({}),
			height = clay.SizingFit({}),
		},
		padding = clay.PaddingAll(16),
		childGap = CLAY_SPACE_8,
	}
}

clay_row_layout :: proc() -> clay.LayoutConfig {
	return clay.LayoutConfig {
		layoutDirection = .LeftToRight,
		sizing = {
			width = clay.SizingGrow({}),
			height = clay.SizingFit({}),
		},
		childGap = CLAY_SPACE_8,
	}
}

clay_input_layout :: proc() -> clay.LayoutConfig {
	return clay.LayoutConfig {
		layoutDirection = .LeftToRight,
		sizing = {
			width = clay.SizingGrow({}),
			height = clay.SizingFixed(40),
		},
		padding = {top = 8, right = 12, bottom = 8, left = 12},
	}
}

clay_button_layout :: proc() -> clay.LayoutConfig {
	return clay.LayoutConfig {
		layoutDirection = .LeftToRight,
		sizing = {
			width = clay.SizingFit({}),
			height = clay.SizingFixed(38),
		},
		padding = {top = 8, right = 16, bottom = 8, left = 16},
		childAlignment = {x = .Center, y = .Center},
	}
}

// --- Style configs ---

clay_card_style :: proc() -> clay.ElementDeclaration {
	return {
		layout = clay_card_layout(),
		backgroundColor = CLAY_BG_CARD,
		cornerRadius = clay.CornerRadiusAll(CLAY_RADIUS_MD),
		border = {color = CLAY_BORDER_CARD, width = clay.BorderOutside(1)},
	}
}

// --- Text helpers ---

clay_body_text :: proc(text: string, color: clay.Color = CLAY_TEXT_PRIMARY, size: u16 = CLAY_FONT_SIZE_MD) {
	clay.Text(text, {fontId = CLAY_FONT_BODY, fontSize = size, textColor = color})
}

clay_title_text :: proc(text: string, color: clay.Color = CLAY_TEXT_BRIGHT, size: u16 = CLAY_FONT_SIZE_LG) {
	clay.Text(text, {fontId = CLAY_FONT_TITLE, fontSize = size, textColor = color})
}

clay_muted_text :: proc(text: string) {
	clay.Text(text, {fontId = CLAY_FONT_BODY, fontSize = CLAY_FONT_SIZE_SM, textColor = CLAY_TEXT_TERTIARY})
}