class_name CustomLineEdit
extends Control

# Internal state
var cursor_pos: int = 0
var selection: Vector2i = Vector2i(-1, -1)
var cursor_visible: bool = false
var has_focus: bool = false

# Visual settings
var cursor_color: Color = Color.BLACK

@export var text: String = "":
	set(v):
		text = v.left(max_length)
		cursor_pos = clamp(cursor_pos, 0, text.length())
		queue_redraw()

@export_multiline var placeholder: String = "":
	set(v):
		placeholder = v
		queue_redraw()

@export_enum("Left", "Center", "Right") var align: int = 0:
	set(v):
		align = v
		queue_redraw()

@export_range(0, 1024) var max_length: int = 256

@export var secret: bool = false:
	set(v):
		secret = v
		queue_redraw()

@export var secret_char: String = "*" :
	set(v):
		secret_char = v[0] if v.length() > 0 else "*"
		queue_redraw()

@export var editable: bool = true
@export var context_menu_enabled: bool = true
@export var clear_button_enabled: bool = false
@export var shortcut_keys_enabled: bool = true
@export var virtual_keyboard_enabled: bool = true
@export var caret_blink: bool = true
@export_range(0.1, 2.0) var caret_blink_interval: float = 0.5

@export var expand_to_text_length: bool = false
@export var select_all_on_focus: bool = false

@export_group("Colors")
@export var text_color: Color = Color.BLACK:
	set(v):
		text_color = v
		queue_redraw()
		
@export var placeholder_color: Color = Color.DIM_GRAY:
	set(v):
		placeholder_color = v
		queue_redraw()

@export var caret_color: Color = Color.BLACK:
	set(v):
		caret_color = v
		queue_redraw()

@export var selection_color: Color = Color(0.7, 0.8, 1.0, 0.5):
	set(v):
		selection_color = v
		queue_redraw()

@export_group("Font")
@export var font: Font = ThemeDB.fallback_font:
	set(v):
		font = v
		queue_redraw()

@export_range(8, 64) var font_size: int = 16:
	set(v):
		font_size = v
		queue_redraw()

func _ready():
	focus_entered.connect(_on_focus_entered)
	focus_exited.connect(_on_focus_exited)
	set_process_input(true)
	start_blink_timer()

func _on_focus_entered():
	has_focus = true
	cursor_visible = true
	queue_redraw()

func _on_focus_exited():
	has_focus = false
	cursor_visible = false
	queue_redraw()

func start_blink_timer():
	var timer = Timer.new()
	add_child(timer)
	timer.wait_time = 0.5
	timer.timeout.connect(_toggle_cursor_visibility)
	timer.start()

func _toggle_cursor_visibility():
	if has_focus:
		cursor_visible = !cursor_visible
		queue_redraw()

func _gui_input(event):
	if event is InputEventKey and has_focus:
		handle_key_input(event)
	
	if event is InputEventMouseButton and event.pressed:
		if get_rect().has_point(event.position) or true:
			grab_focus()
			handle_mouse_click(event)
		else:
			release_focus()

func handle_mouse_click(event: InputEventMouseButton):
	var click_pos = get_local_mouse_position().x
	var text_to_use = get_display_text()
	var offset = get_text_offset(text_to_use)

	var pos = 0
	var accumulated_width = 0.0
	var found = false

	for i in range(text_to_use.length()):
		var char_width = font.get_string_size(text_to_use.substr(i, 1), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).x
		if accumulated_width + char_width/2 > click_pos - offset:
			pos = i
			found = true
			break
		accumulated_width += char_width
	
	if not found:
		pos = text_to_use.length()
	
	cursor_pos = pos
	selection = Vector2i(-1, -1)
	cursor_visible = true
	queue_redraw()

func handle_key_input(event: InputEventKey):
	var shift_pressed = event.shift_pressed
	var ctrl_pressed = event.ctrl_pressed
	
	# Handle selection
	if shift_pressed and selection.x == -1:
		selection = Vector2i(cursor_pos, cursor_pos)
	
	# Handle text input
	if event.unicode != 0 and !event.echo:
		insert_text_at_cursor(char(event.unicode))
	
	# Handle special keys
	match event.keycode:
		KEY_BACKSPACE:
			delete_prev_char()
		KEY_DELETE:
			delete_next_char()
		KEY_LEFT:
			move_cursor(-1, ctrl_pressed, shift_pressed)
		KEY_RIGHT:
			move_cursor(1, ctrl_pressed, shift_pressed)
		KEY_HOME:
			cursor_pos = 0
			adjust_selection(shift_pressed)
		KEY_END:
			cursor_pos = text.length()
			adjust_selection(shift_pressed)
		KEY_C:
			if ctrl_pressed:
				copy_to_clipboard()
		KEY_V:
			if ctrl_pressed:
				paste_from_clipboard()
	
	queue_redraw()

func insert_text_at_cursor(new_text: String):
	var new_text_filtered = new_text.replace("\n", "").replace("\t", "")
	if text.length() + new_text_filtered.length() > max_length:
		return
	
	if selection.x != -1:
		delete_selection()
	
	text = text.insert(cursor_pos, new_text_filtered)
	cursor_pos += new_text_filtered.length()
	selection = Vector2i(-1, -1)

func delete_prev_char():
	if cursor_pos > 0:
		if selection.x != -1:
			delete_selection()
		else:
			text = text.erase(cursor_pos - 1, 1)
			cursor_pos -= 1

func delete_next_char():
	if cursor_pos < text.length():
		if selection.x != -1:
			delete_selection()
		else:
			text = text.erase(cursor_pos, 1)

func delete_selection():
	var start = min(selection.x, selection.y)
	var end = max(selection.x, selection.y)
	text = text.erase(start, end - start)
	cursor_pos = start
	selection = Vector2i(-1, -1)

func move_cursor(direction: int, ctrl: bool, shift: bool):
	var new_pos = cursor_pos
	if ctrl:
		new_pos = find_word_boundary(direction)
	else:
		new_pos += direction
	
	cursor_pos = clamp(new_pos, 0, text.length())
	adjust_selection(shift)

func adjust_selection(shift: bool):
	if shift:
		selection.y = cursor_pos
	else:
		selection = Vector2i(-1, -1)

func find_word_boundary(direction: int) -> int:
	# Simplified word navigation
	var pos = cursor_pos
	if direction == -1:
		while pos > 0 and text[pos-1] == " ":
			pos -= 1
		while pos > 0 and text[pos-1] != " ":
			pos -= 1
	else:
		while pos < text.length() and text[pos] == " ":
			pos += 1
		while pos < text.length() and text[pos] != " ":
			pos += 1
	return pos

func copy_to_clipboard():
	if selection.x != -1:
		var start = min(selection.x, selection.y)
		var end = max(selection.x, selection.y)
		DisplayServer.clipboard_set(text.substr(start, end - start))

func paste_from_clipboard():
	var clipboard = DisplayServer.clipboard_get()
	insert_text_at_cursor(clipboard)

func get_display_text() -> String:
	if secret and text.length() > 0:
		return secret_char.repeat(text.length())
	return text if text.length() > 0 else placeholder

func get_text_offset(display_text: String) -> float:
	var text_width = font.get_string_size(display_text, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).x
	match align:
		HorizontalAlignment.HORIZONTAL_ALIGNMENT_CENTER:
			return max(0, (size.x - text_width) / 2)
		HorizontalAlignment.HORIZONTAL_ALIGNMENT_RIGHT:
			return max(0, size.x - text_width)
		_: # Left align
			return 0.0

func _draw():
	var display_text = get_display_text()
	var text_offset = get_text_offset(display_text)
	
	# Draw selection
	print(selection)
	if selection.x != -1:
		draw_selection(text_offset, display_text)
	
	# Draw text
	var text_color = Color.DIM_GRAY if text == "" else Color.BLACK
	draw_string(font, Vector2(text_offset, size.y / 2 + font_size / 2), display_text, 
				HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, text_color)
	
	# Draw cursor
	if cursor_visible and has_focus:
		draw_cursor(text_offset, display_text)

func draw_selection(offset: float, display_text: String):
	var start = min(selection.x, selection.y)
	var end = max(selection.x, selection.y)
	
	var start_pos = offset + font.get_string_size(display_text.left(start), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).x
	var end_pos = offset + font.get_string_size(display_text.left(end), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).x
	
	draw_rect(Rect2(start_pos, 2, end_pos - start_pos, size.y - 4), selection_color, true)

func draw_cursor(offset: float, display_text: String):
	var cursor_x = offset + font.get_string_size(display_text.left(cursor_pos), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).x
	draw_line(Vector2(cursor_x, 2), Vector2(cursor_x, size.y - 2), cursor_color, 2.0)
