@tool
extends Node

## Captures screenshots from the editor viewports and the running game.
## Editor screenshots: direct viewport capture.
## Game screenshots: non-blocking file-based protocol with golem_capture.gd autoload.

const CAPTURE_REQUEST_PATH := "user://golem_capture_request"
const CAPTURE_SCREENSHOT_PATH := "user://golem_game_screenshot.jpg"
const MAX_RETRIES := 3

var _retry_count := 0
var _frames_since_request := 0


func register(server: Node) -> void:
	server.register_handler("get_editor_screenshot", _get_editor_screenshot)
	server.register_handler("get_game_screenshot", _get_game_screenshot)


func _get_editor_screenshot(_args: Dictionary) -> Dictionary:
	return _capture_editor_viewport()


func _get_game_screenshot(_args: Dictionary) -> Dictionary:
	if not EditorInterface.is_playing_scene():
		return {"ok": false, "error": "No scene is currently running"}

	# Delete any stale screenshot
	if FileAccess.file_exists(CAPTURE_SCREENSHOT_PATH):
		DirAccess.remove_absolute(CAPTURE_SCREENSHOT_PATH)

	# Reset retry state
	_retry_count = 0
	_frames_since_request = 0

	# Create capture request flag
	_create_capture_request()

	# Return a deferred response — the server will poll _check_capture every frame
	return {
		"_deferred": true,
		"_check_fn": _check_capture,
		"_timeout": 5.0,
		"_timeout_msg": "Game did not respond to capture request after %d retries. Is GolemCapture autoload active?" % MAX_RETRIES,
	}


func _create_capture_request() -> bool:
	var flag := FileAccess.open(CAPTURE_REQUEST_PATH, FileAccess.WRITE)
	if flag == null:
		return false
	flag.store_string("capture")
	flag.close()
	return true


func _check_capture():
	"""Called every frame by the server. Returns Dictionary when ready, null otherwise."""
	_frames_since_request += 1

	if not FileAccess.file_exists(CAPTURE_SCREENSHOT_PATH):
		# If no response after ~60 frames (~1 second), retry the request
		if _frames_since_request > 60 and _retry_count < MAX_RETRIES:
			_retry_count += 1
			_frames_since_request = 0
			_create_capture_request()
		return null

	var file := FileAccess.open(CAPTURE_SCREENSHOT_PATH, FileAccess.READ)
	if file == null:
		return null

	var file_size := file.get_length()
	if file_size == 0:
		# File exists but is empty — GolemCapture is still writing, wait
		file.close()
		return null

	var bytes := file.get_buffer(file_size)
	file.close()

	# Validate we got actual data
	if bytes.size() == 0:
		# Read failed or file was truncated
		DirAccess.remove_absolute(CAPTURE_SCREENSHOT_PATH)
		return {"ok": false, "error": "Screenshot file was empty or corrupted"}

	# Cleanup
	DirAccess.remove_absolute(CAPTURE_SCREENSHOT_PATH)

	var b64 := Marshalls.raw_to_base64(bytes)
	if b64.is_empty():
		return {"ok": false, "error": "Failed to encode screenshot to base64"}

	return {
		"ok": true,
		"type": "image",
		"result": b64,
		"mime": "image/jpeg",
	}


func _capture_editor_viewport() -> Dictionary:
	var viewport := EditorInterface.get_editor_viewport_2d()
	if viewport == null:
		viewport = EditorInterface.get_editor_viewport_3d(0)
	if viewport == null:
		return {"ok": false, "error": "No editor viewport available"}

	var texture := viewport.get_texture()
	if texture == null:
		return {"ok": false, "error": "No viewport texture available"}

	var image := texture.get_image()
	if image == null:
		return {"ok": false, "error": "Failed to capture screenshot"}

	var max_dim := 1920
	if image.get_width() > max_dim or image.get_height() > max_dim:
		var scale_factor: float = float(max_dim) / float(maxi(image.get_width(), image.get_height()))
		image.resize(
			int(image.get_width() * scale_factor),
			int(image.get_height() * scale_factor),
			Image.INTERPOLATE_BILINEAR
		)

	var buffer := image.save_jpg_to_buffer(0.85)
	return {
		"ok": true,
		"type": "image",
		"result": Marshalls.raw_to_base64(buffer),
		"mime": "image/jpeg",
	}
