---@diagnostic disable: duplicate-set-field
local core = require("core")
local command = require("core.command")
local keymap = require("core.keymap")

local handled_events = {
	["keydown"] = true,
	["keyup"] = true,
	["textinput"] = true,
}

local state = "stopped"
local event_buffer = {}
local modkeys = {}

local on_event = core.on_event

core.on_event = function(type, ...)
	local res = on_event(type, ...)
	if state == "recording" and handled_events[type] then
		table.insert(event_buffer, { type, ... })
	end
	return res
end

local function clone(t)
	local res = {}
	for k, v in pairs(t) do
		res[k] = v
	end
	return res
end

local function macro_is_not_playing()
	-- Macro commands depend on recorder runtime state, so requires tokens cannot replace this predicate
	return state ~= "playing"
end

command.register({
	["macro:toggle-record"] = {
		predicate = macro_is_not_playing,
		perform = function()
			if state == "stopped" then
				state = "recording"
				event_buffer = {}
				modkeys = clone(keymap.modkeys)
				core.log("Recording macro...")
			else
				state = "stopped"
				core.log("Stopped recording macro (%d events)", #event_buffer)
			end
		end,
	},

	["macro:play"] = {
		predicate = macro_is_not_playing,
		perform = function()
			state = "playing"
			core.log("Playing macro... (%d events)", #event_buffer)
			local previous_modkeys = keymap.modkeys
			keymap.modkeys = clone(modkeys)
			local ok, playback_error = xpcall(function()
				for _, event in ipairs(event_buffer) do
					-- Current wrappers must see replayed input just like live input
					core.on_event(table.unpack(event))
					core.workspace_view:update()
				end
			end, debug.traceback)

			keymap.modkeys = previous_modkeys
			state = "stopped"

			if not ok then
				error(playback_error, 0)
			end
		end,
	},
})

keymap.bind("plugin", {
	global = {
		["ctrl+shift+;"] = "macro:toggle-record",
		["ctrl+;"] = "macro:play",
	},
})
