-- @author DoomTero 2026

local ZomboRutScene = require "ZomboRut/ZomboRutScene"

local function banditsAvailable()
	return Bandit ~= nil and BanditCustom ~= nil and BanditBrain ~= nil
end

local function findOnTilesAndNeighbors(worldobjects, which, predicate)
	for i = 1, #worldobjects do
		local obj = worldobjects[i]
		if predicate(obj) then return obj end
	end
	for i = 1, #worldobjects do
		local obj = worldobjects[i]
		local sq = obj and obj.getSquare and obj:getSquare()
		local cell = sq and sq:getCell()
		if sq and cell then
			local x, y, z = sq:getX(), sq:getY(), sq:getZ()
			for dx = -1, 1 do
				for dy = -1, 1 do
					local s = cell:getGridSquare(x + dx, y + dy, z)
					if s then
						if which ~= "moving" then
							local statics = s:getStaticMovingObjects()
							if statics then
								for j = 0, statics:size() - 1 do
									local m = statics:get(j)
									if predicate(m) then return m end
								end
							end
						end
						if which ~= "static" then
							local moving = s:getMovingObjects()
							if moving then
								for j = 0, moving:size() - 1 do
									local m = moving:get(j)
									if predicate(m) then return m end
								end
							end
						end
					end
				end
			end
		end
	end
	return nil
end

local function findDeadBodyAt(worldobjects)
	return findOnTilesAndNeighbors(worldobjects, "static", function(obj)
		return instanceof(obj, "IsoDeadBody")
	end)
end

local function isDummyBandit(obj)
	if not banditsAvailable() then return false end
	return instanceof(obj, "IsoZombie")
		and obj:getVariableBoolean("Bandit")
		and obj:getModData().zrDummyBandit
		and not obj:isDead()
end

local function isDummyZombie(obj)
	return instanceof(obj, "IsoZombie")
		and obj:getModData().zrDummyZombie
		and not obj:getVariableBoolean("Bandit")
		and not obj:isDead()
end

local function findLiveSceneTarget(worldobjects)
	local bandit = findOnTilesAndNeighbors(worldobjects, "moving", isDummyBandit)
	if bandit then return bandit, "bandit" end
	local zombie = findOnTilesAndNeighbors(worldobjects, "moving", isDummyZombie)
	if zombie then return zombie, "zombie" end
	return nil, nil
end

local function onFillWorldObjectContextMenu(playerIndex, context, worldobjects, test)
	if test == true then return end

	local playerObj = getSpecificPlayer(playerIndex)
	if not playerObj then return end

	local sceneIds = ZomboRutScene.listLogical()
	if #sceneIds == 0 then return end

	if playerObj:isDraggingCorpse() then
		context:addOption("ZR Scene: End", worldobjects, function()
			ZomboRutScene.endScene(playerObj)
		end)
		return
	end

	local liveTarget, liveKind = findLiveSceneTarget(worldobjects)
	local deadBody = (not liveTarget) and findDeadBodyAt(worldobjects) or nil

	if not liveTarget and not deadBody then return end

	local rootOption = context:addOption("ZR Scene", worldobjects, nil)
	local rootSub = ISContextMenu:getNew(context)
	context:addSubMenu(rootOption, rootSub)

	for _, id in ipairs(sceneIds) do
		local label = id
		local cfg = ZomboRutScene.getLogical(id)
		if cfg and cfg.label then label = cfg.label end
		if liveTarget then
			local launcher = (liveKind == "bandit")
				and ZomboRutScene.playSceneFromLive
				or ZomboRutScene.playSceneFromAttacker
			rootSub:addOption(label, worldobjects, function()
				launcher(playerObj, liveTarget, id)
			end)
		else
			rootSub:addOption(label, worldobjects, function()
				ZomboRutScene.playSceneFromCorpse(playerObj, deadBody, id)
			end)
		end
	end
end

Events.OnFillWorldObjectContextMenu.Add(onFillWorldObjectContextMenu)
