package main

import fixed_pool "libraries/pool"
import rl "libraries/raylib"
import ui "libraries/snths_ui"

Field :: struct($T: typeid) {
	value: f32,
	input: FieldInput,
}

FIELD_BYTE_COUNT :: 8
FieldInput :: [FIELD_BYTE_COUNT]byte
FieldPool :: struct($T: typeid) {
	values: fixed_pool.Pool(T),
	inputs: fixed_pool.Pool(FieldInput),
}

field_pool_get :: proc(pool: ^FieldPool($T), count: int) -> (values: []T, inputs: []FieldInput) {
	return fixed_pool.get(&pool.values, count), fixed_pool.get(&pool.inputs, count)
}

field_pool_clear :: proc(pool: ^FieldPool($T)) {
	fixed_pool.clear(&pool.values)
	fixed_pool.clear(&pool.inputs)
}

field_pool_init :: proc(pool: ^FieldPool($T), capacity: int, allocator := context.allocator) {
	fixed_pool.init(&pool.values, capacity, allocator)
	fixed_pool.init(&pool.inputs, capacity, allocator)
}

field_pool_free_range :: proc(pool: ^FieldPool($T), from: int, count: int) {
	fixed_pool.free_range(&pool.values, from, count)
	fixed_pool.free_range(&pool.inputs, from, count)
}

field_pool_get_allocated_elems :: proc(pool: ^FieldPool($T)) -> #soa[]Field(T) {
	values := fixed_pool.get_allocated_elems(pool.values)
	fields := fixed_pool.get_allocated_elems(pool.inputs)
	return soa_zip(value = values, input = fields)
}

FontStyle :: struct {
	font: rl.Font,
	size, spacing: f32
}

ProgramState :: enum {
	Normal,
	Message,
}

ProgramMessageType :: enum {
	ChangeMatrixTypeClearData,
}

@rodata program_messages := [ProgramMessageType]cstring {
	.ChangeMatrixTypeClearData = "Changing the matrix type will delete all current matrices.\nAre you sure you want to do this?",
}

program_set_message_state :: proc(program: ^Program, message: ProgramMessageType) {
	program.state = .Message
	program.message_type = message
}

program: Program

Program :: struct {
	ui_context: ui.Context,
	matrix_field_pool: FieldPool(f32),
	grid: Grid,
	font_style: FontStyle,
	state: ProgramState,
	message_type: ProgramMessageType,
}

Grid :: struct {
	area:        rect2,
	inner_area:  rect2,
	zoom:        f32,
}

program_init :: proc(allocator := context.allocator) {
	field_pool_init(&program.matrix_field_pool, 4096, allocator)
	program.font_style = {
		font = rl.GetFontDefault(),
		size = 16.0,
		spacing = 1.0
	}
	program.grid = {
		area = {0, 0, WINDOW_WIDTH, WINDOW_HEIGHT},
		inner_area = {-1, -1, 2, 2},
		zoom = 1.0,
	}
	ui.init(&program.ui_context, 4000, allocator)
}

program_update :: proc() {
	ui_elements := program_build_ui()
	program_render_ui(ui_elements)
}

program_build_ui :: proc() -> []ui.Element {
	container_margin: f32 = 4.0
	container_config := ui.ElementConfiguration {
		margin = V2_ONE * container_margin,
	}

	ui.start(&program.ui_context, {0, 0}, {WINDOW_WIDTH, WINDOW_HEIGHT})
	top_bar_container := ui.container_begin({ui.Size_Percentage(1.0), ui.Size_Pixels(64.0)}, container_config)
	top_bar_inner_height := ui.get_element_inner_area(top_bar_container^).height
	top_bar_button_dimensions := ui.Dimensions {ui.Size_Pixels(top_bar_inner_height), ui.Size_Pixels(top_bar_inner_height)}
	top_bar_button_config := ui.ElementConfiguration {margin = {4.0, 4.0}}
	{
		config := top_bar_button_config
		config.label = "+"
		ui.button(top_bar_button_dimensions, config)
		config.label = "-"
		ui.same_line()
		ui.button(top_bar_button_dimensions, config)
	}
	ui.container_end()

	side_bar_container := ui.container_begin({ui.Size_Percentage(0.3), ui.Size_Percentage(1.0)}, container_config)
	ui.container_end()
	return ui.end()
}

program_render_ui :: proc(elements: []ui.Element) {
	for element in elements {
		area := rl.Rectangle(element.area)
		switch element.type {
		case .Container:
			rl.GuiGroupBox(area, element.label)
		case .Button:
			rl.GuiButton(area, element.label)
		case .Slider:
			data := element.data.float_input_data
			rl.GuiSlider(area, element.label, "", data.value, data.min, data.max)
		}
	}
}
