-- ========================================================================
-- TPAD MOD v1.2 (Final, Reworked Delete Logic)
-- ========================================================================

tpad = {}
tpad.version = "1.2" -- As requested, version is not incremented
tpad.mod_name = minetest.get_current_modname()
tpad.texture = "tpad-texture.png"
tpad.mesh = "tpad-mesh.obj"
tpad.nodename = "tpad:tpad"
tpad.mod_path = minetest.get_modpath(tpad.mod_name)
tpad.sound_teleport = tpad.mod_name .. "_teleport"
tpad.particle_texture = tpad.mod_name .. "_particle.png"

-- Temporäre Variable zur sicheren Datenübergabe an den Bestätigungsdialog
tpad.pending_deletion = {}

-- ========================================================================
-- Constants
-- ========================================================================

local PRIVATE_PAD_STRING = "Privat (nur Besitzer)"
local PUBLIC_PAD_STRING = "Lokal (nur eigenes Netzwerk)"
local GLOBAL_PAD_STRING = "Global (beliebiges Netzwerk)"

local PRIVATE_PAD = 1
local PUBLIC_PAD = 2
local GLOBAL_PAD = 4

local RED_ESCAPE = minetest.get_color_escape_sequence("#FF0000")
local YELLOW_ESCAPE = minetest.get_color_escape_sequence("#FFFF00")
local CYAN_ESCAPE = minetest.get_color_escape_sequence("#00FFFF")
local WHITE_ESCAPE = minetest.get_color_escape_sequence("#FFFFFF")
local OWNER_ESCAPE_COLOR = CYAN_ESCAPE

local padtype_flag_to_string = {
	[PRIVATE_PAD] = PRIVATE_PAD_STRING,
	[PUBLIC_PAD] = PUBLIC_PAD_STRING,
	[GLOBAL_PAD] = GLOBAL_PAD_STRING,
}
local padtype_string_to_flag = {
	[PRIVATE_PAD_STRING] = PRIVATE_PAD,
	[PUBLIC_PAD_STRING] = PUBLIC_PAD,
	[GLOBAL_PAD_STRING] = GLOBAL_PAD,
}
local short_padtype_string = {
	[PRIVATE_PAD] = "private",
	[PUBLIC_PAD] = "public",
	[GLOBAL_PAD] = "global",
}

-- ========================================================================
-- Dependencies and Libs
-- ========================================================================

local smartfs = dofile(tpad.mod_path .. "/lib/smartfs.lua")
local notify = dofile(tpad.mod_path .. "/notify.lua")

local waypoint_hud_ids = {}

minetest.register_privilege("tpad_admin", {
	description = "Can edit and destroy any tpad",
	give_to_singleplayer = true,
})

-- ========================================================================
-- Original Helper Functions
-- ========================================================================

local function copy_file(source, dest)
	local src_file = io.open(source, "rb")
	if not src_file then return false, "copy_file() unable to open source for reading" end
	local src_data = src_file:read("*all")
	src_file:close()
	local dest_file = io.open(dest, "wb")
	if not dest_file then return false, "copy_file() unable to open dest for writing" end
	dest_file:write(src_data)
	dest_file:close()
	return true, "files copied successfully"
end

local function custom_or_default(modname, path, filename)
	local default_filename = "default/" .. filename
	local full_filename = path .. "/custom." .. filename
	local full_default_filename = path .. "/" .. default_filename
	local file_exists_at_path = io.open(path .. "/" .. filename, "r")
	if file_exists_at_path then
		file_exists_at_path:close()
		os.rename(path .. "/" .. filename, full_filename)
	end
	local file = io.open(full_filename, "rb")
	if not file then
		minetest.debug("[" .. modname .. "] Copying " .. default_filename .. " to " .. filename .. " (path: " .. path .. ")")
		local success, err = copy_file(full_default_filename, full_filename)
		if not success then
			minetest.debug("[" .. modname .. "] " .. err)
			return false
		end
		file = io.open(full_filename, "rb")
		if not file then
			minetest.debug("[" .. modname .. "] Unable to load " .. filename .. " file from path " .. path)
			return false
		end
	end
	file:close()
	return full_filename
end

dofile(tpad.mod_path .. "/storage.lua")

-- ========================================================================
-- Load Custom Recipe
-- ========================================================================

local recipes_filename = custom_or_default(tpad.mod_name, tpad.mod_path, "recipes.lua")
if recipes_filename then
	local recipes = dofile(recipes_filename)
	if type(recipes) == "table" and recipes[tpad.nodename] then
		minetest.register_craft({
			output = tpad.nodename,
			recipe = recipes[tpad.nodename],
		})
	end
end

-- ========================================================================
-- Chat Command
-- ========================================================================

function tpad.command(playername, param)
	tpad.hud_off(playername)
	if(param == "off") then return end
	local player = minetest.get_player_by_name(playername)
	local pads = tpad._get_stored_pads(playername)
	local shortest_distance = nil
	local closest_pad = nil
	local playerpos = player:getpos()
	for strpos, pad in pairs(pads) do
		local pos = minetest.string_to_pos(strpos)
		local distance = vector.distance(pos, playerpos)
		if not shortest_distance or distance < shortest_distance then
			closest_pad = {
				pos = pos,
				name = pad.name .. " " .. strpos,
			}
			shortest_distance = distance
		end
	end
	if closest_pad then
		waypoint_hud_ids[playername] = player:hud_add({
			hud_elem_type = "waypoint",
			name = closest_pad.name,
			world_pos = closest_pad.pos,
			number = 0xFF0000,
		})
		notify(playername, "Waypoint to " .. closest_pad.name .. " displayed")
	end
end

function tpad.hud_off(playername)
	local player = minetest.get_player_by_name(playername)
	local hud_id = waypoint_hud_ids[playername]
	if hud_id then
		player:hud_remove(hud_id)
	end
end

-- ========================================================================
-- Teleport Logic
-- ========================================================================

function tpad.do_teleport(player, destination_pos, destination_name, form_context)
	local playername = player:get_player_name()

	minetest.after(0.1, function()
		if not player or not player:is_player() then return end
		local start_pos = player:getpos()
		minetest.sound_play(tpad.sound_teleport, {pos = start_pos, max_hear_distance = 10, gain = 1.0})
		minetest.add_particlespawner({
			amount = 60, time = 0.5,
			minpos = vector.add(start_pos, -0.5), maxpos = vector.add(start_pos, 0.5),
			minvel = {x=-1, y=0, z=-1}, maxvel = {x=1, y=2, z=1},
			minacc = {x=0, y=0, z=0}, maxacc = {x=0, y=0, z=0},
			minexptime = 0.5, maxexptime = 2,
			minsize = 1, maxsize = 3,
			texture = tpad.particle_texture,
		})

		player:move_to(destination_pos)

		minetest.sound_play(tpad.sound_teleport, {pos = destination_pos, max_hear_distance = 10, gain = 1.0})
		minetest.add_particlespawner({
			amount = 60, time = 0.5,
			minpos = vector.add(destination_pos, {x = -0.5, y = 0, z = -0.5}),
			maxpos = vector.add(destination_pos, {x = 0.5, y = 1, z = 0.5}),
			minvel = {x=-1, y=1, z=-1}, maxvel = {x=1, y=2, z=1},
			minacc = {x=0, y=0, z=0}, maxacc = {x=0, y=0, z=0},
			minexptime = 0.5, maxexptime = 2,
			minsize = 1, maxsize = 3,
			texture = tpad.particle_texture,
		})
		tpad.hud_off(playername)
		
		-- NEU: Nach Ankunft am Ziel die Ansicht mit neuem Kontext aktualisieren
		minetest.after(0.2, function()
			if not player or not player:is_player() then return end
			
			local dest_pad_data = tpad.get_pad_data(destination_pos)
			if not dest_pad_data then return end

			local new_context = {
				playername = playername,
				clicker = player,
				ownername = dest_pad_data.owner,
				clicked_pos = destination_pos,
				node = minetest.get_node(destination_pos),
				page = 1,
				-- Behalte den Netzwerk-Typ (lokal/global) bei
				network_type = form_context.network_type 
			}
			tpad.show_network_view(new_context, new_context.network_type)
		end)
	end)
end

-- ========================================================================
-- Node Placement and Data Helpers
-- ========================================================================

function tpad.after_place_node(pos, placer, itemstack)
	local playername = placer:get_player_name()
	if tpad.max_total_pads_reached(placer) then
		notify.warn(playername, "Du kannst keine weiteren TPADs erstellen. Limit erreicht.")
		minetest.remove_node(pos)
		minetest.add_item(placer:get_pos(), itemstack:get_name())
		return
	end

	local meta = minetest.get_meta(pos)
	meta:set_string("owner", playername)
	meta:set_string("infotext", "TPAD Station von " .. playername)
	tpad.set_pad_data(pos, "", PRIVATE_PAD_STRING)
end

local submit = {}

function tpad.max_total_pads_reached(placer)
	local placername = placer:get_player_name()
	if minetest.get_player_privs(placername).tpad_admin then
		return false
	end
	local pads = tpad._get_stored_pads(placername)
	local count = 0
	for _ in pairs(pads) do
		count = count + 1
	end
	return count >= tpad.get_max_total_pads()
end

function tpad.max_global_pads_reached(playername)
	if minetest.get_player_privs(playername).tpad_admin then
		return false
	end
	local pads = tpad._get_stored_pads(playername)
	local count = 0
	for _, pad in pairs(pads) do
		if pad.type == GLOBAL_PAD then
			count = count + 1
		end
	end
	return count >= tpad.get_max_global_pads()
end

-- ========================================================================
-- GUI DATA HELPERS
-- ========================================================================

function submit.global_helper()
	local allpads = tpad._get_all_pads()
	local result = {}
	for ownername, pads in pairs(allpads) do
		for strpos, pad in pairs(pads) do
			if pad.type == GLOBAL_PAD then
				-- Der 'viewername' ist hier nicht bekannt, also wird 'nil' übergeben, was die Funktion korrekt behandelt.
				table.insert(result, tpad.decorate_pad_data(strpos, pad, ownername, nil))
			end
		end
	end
	table.sort(result, function(a, b) return a.global_fullname:lower() < b.global_fullname:lower() end)
	return result
end

function submit.local_helper(ownername, viewername)
	local result = {}
	local added_pads = {}

	local owner_pads = tpad._get_stored_pads(ownername)
	for strpos, pad in pairs(owner_pads) do
		local is_viewer_the_owner = (ownername == viewername)
		if (pad.type == PUBLIC_PAD) or (is_viewer_the_owner and pad.type == PRIVATE_PAD) then
			if pad.type ~= GLOBAL_PAD then
				table.insert(result, tpad.decorate_pad_data(strpos, pad, ownername, viewername))
				added_pads[strpos] = true
			end
		end
	end

	if ownername ~= viewername then
		local viewer_pads = tpad._get_stored_pads(viewername)
		for strpos, pad in pairs(viewer_pads) do
			if (pad.type == PUBLIC_PAD or pad.type == PRIVATE_PAD) and not added_pads[strpos] then
				table.insert(result, tpad.decorate_pad_data(strpos, pad, viewername, viewername))
				added_pads[strpos] = true
			end
		end
	end

	table.sort(result, function(a, b) return a.local_fullname:lower() < b.local_fullname:lower() end)
	return result
end

function submit.management_helper(playername)
    local is_admin = minetest.get_player_privs(playername).tpad_admin
    local result = {}

    if is_admin then
        local allpads = tpad._get_all_pads()
        for ownername, pads in pairs(allpads) do
            for strpos, pad in pairs(pads) do
                table.insert(result, tpad.decorate_pad_data(strpos, pad, ownername, playername))
            end
        end
    else
        local pads = tpad._get_stored_pads(playername)
        for strpos, pad in pairs(pads) do
            table.insert(result, tpad.decorate_pad_data(strpos, pad, playername, playername))
        end
    end

    table.sort(result, function(a, b) return a.local_fullname:lower() < b.local_fullname:lower() end)
    return result
end

-- ========================================================================
-- GUI LOGIC (REFACTORED)
-- ========================================================================

function tpad.show_network_view(form, network_type)
	local is_admin = minetest.get_player_privs(form.playername).tpad_admin
	local form_key = is_admin and "network_view_admin" or "network_view"
	form.state = tpad.forms[form_key]:show(form.playername)
	form.formname = "tpad.forms." .. form_key

	local pad_list
	local title_text

	local function create_clean_context()
		return {
			playername = form.playername,
			clicker = form.clicker,
			ownername = form.ownername,
			clicked_pos = form.clicked_pos,
			node = form.node,
			page = form.page or 1
		}
	end

	local current_pad_data = tpad.get_pad_data(form.clicked_pos)
	local current_pad_name = current_pad_data.name or "Unnamed"

	local current_pad_type_str
	if current_pad_data.type == GLOBAL_PAD then
		current_pad_type_str = "GLOBALE"
	else
		current_pad_type_str = "LOKALE"
	end
	
	local RED_ESCAPE = minetest.get_color_escape_sequence("#FF0000") or ""
	title_text = RED_ESCAPE .. current_pad_type_str .. " " .. WHITE_ESCAPE .. "TPAD-Station " .. YELLOW_ESCAPE .. current_pad_name .. WHITE_ESCAPE .. ". Wähle ein Ziel:"

	if network_type == "global" then
		form.network_type = "global"
		pad_list = submit.global_helper()
		form.state:get("toggle_network_button"):setText("Lokales Netzwerk")
		form.state:get("toggle_network_button"):onClick(function()
			minetest.after(0, function()
				local clean_form = create_clean_context()
				-- KORREKTUR: Der 'ownername' wird nicht mehr überschrieben.
				-- Er bleibt der des ursprünglichen Pads, was korrekt ist.
				clean_form.page = 1
				tpad.show_network_view(clean_form, "local")
			end)
		end)
	else -- "local"
		form.network_type = "local"
		-- KORREKTUR: Übergebe den Spielernamen (string), nicht das Ergebnis eines Vergleichs (boolean).
		pad_list = submit.local_helper(form.ownername, form.playername)
		form.state:get("toggle_network_button"):setText("Globales Netzwerk")
		form.state:get("toggle_network_button"):onClick(function()
			minetest.after(0, function()
				local clean_form = create_clean_context()
				clean_form.page = 1
				tpad.show_network_view(clean_form, "global")
			end)
		end)
	end

	form.state:get("title_label"):setText(title_text)
	form.state:get("management_button"):onClick(function()
		minetest.after(0, function()
			local clean_form = create_clean_context()
			clean_form.preselect_pos = clean_form.clicked_pos
			submit.management(clean_form)
		end)
	end)

	if is_admin then
		form.state:get("admin_button"):onClick(function()
			minetest.after(0, function() submit.admin_settings(form) end)
		end)
	end

	local max_rows, num_columns = 10, 3
	local buttons_per_page = max_rows * num_columns

	local destination_pads = {}
	for _, pad in ipairs(pad_list) do
		if not vector.equals(pad.pos, form.clicked_pos) then
			table.insert(destination_pads, pad)
		end
	end

	local total_pages = math.ceil(#destination_pads / buttons_per_page)
	if total_pages == 0 then total_pages = 1 end

	local current_page = form.page or 1
	if current_page > total_pages then current_page = total_pages end
	if current_page < 1 then current_page = 1 end
	form.page = current_page

	for i = 1, buttons_per_page do
		local index = ((current_page - 1) * buttons_per_page) + i
		local pad_data = destination_pads[index]
		local button_name = "tpad_btn_" .. i
		local button = form.state:get(button_name)

		if button then
			if pad_data then
				local display_name = (network_type == "global") and pad_data.global_fullname or pad_data.local_fullname
				button:setText(display_name)
				button:setVisible(true)
				button:onClick(function()
					tpad.do_teleport(form.clicker, pad_data.pos, display_name, form)
				end)
			else
				button:setVisible(false)
			end
		end
	end

	if total_pages > 1 then
		form.state:get("page_label"):setText("Seite " .. current_page .. " von " .. total_pages)
		local prev_button, next_button = form.state:get("prev_button"), form.state:get("next_button")
		prev_button:setVisible(current_page > 1)
		next_button:setVisible(current_page < total_pages)
		
		prev_button:onClick(function()
			minetest.after(0, function()
				local clean_form = create_clean_context()
				clean_form.page = current_page - 1
				tpad.show_network_view(clean_form, network_type)
			end)
		end)
		next_button:onClick(function()
			minetest.after(0, function()
				local clean_form = create_clean_context()
				clean_form.page = current_page + 1
				tpad.show_network_view(clean_form, network_type)
			end)
		end)
	else
		form.state:get("page_label"):setText("")
		form.state:get("prev_button"):setVisible(false)
		form.state:get("next_button"):setVisible(false)
	end
end

function submit.management(form)
	form.formname = "tpad_management"
	form.state = tpad.forms.management:show(form.playername)
	local pad_list = submit.management_helper(form.playername)
	local listbox = form.state:get("pads_listbox")
	local is_admin = minetest.get_player_privs(form.playername).tpad_admin
	local selected_pad_data = nil

	listbox:clearItems()
	for _, pad in ipairs(pad_list) do
		local label = pad.name .. " (" .. short_padtype_string[pad.type] .. ")"
		if is_admin then
			label = label .. " [" .. pad.owner .. "]"
		end
		listbox:addItem(label)
	end

	local padname_field = form.state:get("padname_field")
	local padtype_dropdown = form.state:get("padtype_dropdown")

	local function update_fields_for_index(index)
		if not index or index <= 0 then return end
		selected_pad_data = pad_list[index]
		if selected_pad_data then
			padname_field:setText(selected_pad_data.name)
			padtype_dropdown:setSelectedItem(padtype_flag_to_string[selected_pad_data.type])
		end
	end

	if form.preselect_pos then
		for i, pad in ipairs(pad_list) do
			if vector.equals(pad.pos, form.preselect_pos) then
				listbox:setSelected(i)
				update_fields_for_index(i)
				break
			end
		end
	end

	listbox:onClick(function()
		local index = listbox:getSelected()
		update_fields_for_index(index)
	end)

	form.state:get("save_button"):onClick(function()
		if not selected_pad_data then
			notify.warn(form.playername, "Bitte wähle zuerst ein TPAD aus der Liste aus.")
			return
		end
		local new_name = padname_field:getText()
		local new_type_str
		local value_from_dropdown = padtype_dropdown:getSelectedItem()
		if value_from_dropdown then
			local index = tonumber(value_from_dropdown)
			if index then
				new_type_str = padtype_dropdown:getItem(index)
			else
				new_type_str = value_from_dropdown
			end
		end
		if not new_type_str or new_type_str == "" then
			notify.err(form.playername, "Konnte TPAD-Typ nicht lesen. Bitte erneut versuchen.")
			return
		end
		if not minetest.get_player_privs(form.playername).tpad_admin then
			if new_type_str == GLOBAL_PAD_STRING and tpad.max_global_pads_reached(selected_pad_data.owner) then
				notify.warn(form.playername, "Der Besitzer des TPADs hat das Limit für globale TPADs erreicht.")
				return
			end
		end
		tpad.set_pad_data(selected_pad_data.pos, new_name, new_type_str)
		local meta = minetest.get_meta(selected_pad_data.pos)
		if new_name and new_name ~= "" then
			meta:set_string("infotext", "TPAD Station " .. new_name)
		else
			meta:set_string("infotext", "Unbenannte TPAD Station")
		end
		notify(form.playername, "TPAD '" .. new_name .. "' gespeichert.")
		minetest.after(0, function()
			form.preselect_pos = nil
			submit.management(form)
		end)
	end)
		
	form.state:get("delete_button"):onClick(function()
		if not selected_pad_data then
			notify.warn(form.playername, "Bitte wähle zuerst ein TPAD aus der Liste aus.")
			return
		end
		if vector.equals(selected_pad_data.pos, form.clicked_pos) then
			notify.warn(form.playername, "Du kannst das TPAD, an dem du stehst, nicht über dieses Menü löschen.")
			return
		end
		tpad.pending_deletion[form.playername] = {
			pad_data = selected_pad_data,
			original_form_context = form,
		}
		minetest.close_formspec(form.playername, form.formname)
		minetest.after(0.1, function()
			tpad.forms.confirm_pad_deletion:show(form.playername)
		end)
	end)

	form.state:get("back_button"):onClick(function()
		minetest.after(0, function()
			local clean_form_context = {
				playername = form.playername,
				clicker = form.clicker,
				ownername = form.ownername,
				clicked_pos = form.clicked_pos,
				node = form.node,
				page = 1
			}
			tpad.show_network_view(clean_form_context, form.network_type)
		end)
	end)

	-- NEU: Logik für den Admin-Teleport-Button
	if is_admin then
		local admin_teleport_button = form.state:get("admin_teleport_button")
		admin_teleport_button:setVisible(true)
		admin_teleport_button:onClick(function()
			if not selected_pad_data then
				notify.warn(form.playername, "Bitte zuerst ein TPAD aus der Liste auswählen, um dorthin zu teleportieren.")
				return
			end
			
			local dest_pad = selected_pad_data
			
			-- Erstelle den Kontext für die Ansicht, die nach dem Teleport angezeigt werden soll
			local new_context_after_teleport = {
				playername = form.playername,
				clicker = form.clicker,
				ownername = dest_pad.owner,
				clicked_pos = dest_pad.pos,
				node = minetest.get_node(dest_pad.pos),
				page = 1,
				network_type = (dest_pad.type == GLOBAL_PAD) and "global" or "local"
			}
			
			tpad.do_teleport(form.clicker, dest_pad.pos, dest_pad.name, new_context_after_teleport)
		end)
	end
end

function submit.admin_settings(form)
	form.state = tpad.forms.admin:show(form.playername)
	form.formname = "tpad_admin_settings"
	local max_total_field, max_global_field = form.state:get("max_total_field"), form.state:get("max_global_field")
	max_total_field:setText(tpad.get_max_total_pads())
	max_global_field:setText(tpad.get_max_global_pads())

	form.state:get("save_button"):onClick(function()
		tpad.set_max_total_pads(tonumber(max_total_field:getText()))
		tpad.set_max_global_pads(tonumber(max_global_field:getText()))
		minetest.close_formspec(form.playername, form.formname)
	end)
end

-- ========================================================================
-- Main Node Callbacks
-- ========================================================================

function tpad.on_rightclick(clicked_pos, node, clicker)
	local playername = clicker:get_player_name()
	local pad = tpad.get_pad_data(clicked_pos, playername)
	
	if not pad or not pad.owner then
		notify.err(playername, "Fehler! Fehlende oder korrupte TPAD-Daten. Bitte neu platzieren.")
		return
	end

	-- Zugriffsschutz-Prüfung für private Pads
	if pad.type == PRIVATE_PAD and pad.owner ~= playername and not minetest.get_player_privs(playername).tpad_admin then
		local RED_ESCAPE = minetest.get_color_escape_sequence("#FF0000") or ""
		notify.warn(playername, YELLOW_ESCAPE .. "PRIVATE" .. WHITE_ESCAPE .. " TPAD Station von " .. pad.owner .. ". " .. RED_ESCAPE .. "Zugriff verweigert.")
		return
	end

	-- Erstelle das Kontext-Objekt für die Formulare
	local form = {
		playername = playername,
		clicker = clicker,
		ownername = pad.owner,
		clicked_pos = clicked_pos,
		node = node,
		page = 1,
	}

	-- Wenn das Pad neu ist (keinen Namen hat), direkt zur Verwaltung
	if pad.name == "" then
		form.preselect_pos = clicked_pos
		submit.management(form)
	else
		-- Ansonsten zeige die entsprechende Netzwerk-Ansicht
		if pad.type == GLOBAL_PAD then
			tpad.show_network_view(form, "global")
		else
			tpad.show_network_view(form, "local")
		end
	end
end

function tpad.can_dig(pos, player)
	local meta = minetest.get_meta(pos)
	local ownername = meta:get_string("owner")
	local playername = player:get_player_name()
	if ownername == "" or ownername == playername or minetest.get_player_privs(playername).tpad_admin then
		return true
	end
	notify.warn(playername, "Dieses TPAD gehört dir nicht.")
	return false
end

function tpad.on_destruct(pos)
	local meta = minetest.get_meta(pos)
	local ownername = meta:get_string("owner")
	if ownername and ownername ~= "" then
		tpad.del_pad(ownername, pos)
	end
end

-- ========================================================================
-- FORMS (Rebuilt)
-- ========================================================================

tpad.forms = {}

local function create_network_view_form(state)
	state:size(16, 11)
	state:label(0.5, 0.2, "title_label", "")
	local bottom_y = 10.4
	state:button(0.5, bottom_y, 3.0, 0, "toggle_network_button", "")
	state:button(3.6, bottom_y, 2.3, 0, "management_button", "Verwaltung")
	local close_button = state:button(14.0, bottom_y, 1.5, 0, "close_button", "Schließen")
	close_button:setClose(true)
	state:button(6.0, bottom_y, 1.0, 0, "prev_button", "[<<]")
	state:label(7.1, bottom_y, "page_label", "")
	state:button(9.5, bottom_y, 1.0, 0, "next_button", "[>>]")

	local max_rows, num_columns = 10, 3
	local start_x, start_y = 0.5, 1.0
	local button_width, button_height = 4.8, 0.8
	local column_width = 5.0
	for i = 1, max_rows * num_columns do
		local column = math.floor((i - 1) / max_rows)
		local row = (i - 1) % max_rows
		local current_x = start_x + (column * column_width)
		local current_y = start_y + (row * button_height)
		state:button(current_x, current_y, button_width, 0, "tpad_btn_" .. i, ""):setVisible(false)
	end
end

tpad.forms.teleport_success = smartfs.create("tpad.forms.teleport_success", function(state)
	state:size(8, 2)
	local destination_name = state.param.destination_name or "???"
	state:label(0.5, 0.5, "success_label", "Teleport erfolgreich: " .. YELLOW_ESCAPE .. destination_name)
	
	-- Dieser Button hat setClose(true), was das Fenster zuverlässig schließt.
	local close_button = state:button(3, 1.2, 2, 0, "close_button", "Schließen")
	close_button:setClose(true)
end)

tpad.forms.network_view = smartfs.create("tpad.forms.network_view", create_network_view_form)

tpad.forms.network_view_admin = smartfs.create("tpad.forms.network_view_admin", function(state)
	create_network_view_form(state)
	state:button(12.4, 10.4, 1.5, 0, "admin_button", "Admin")
end)

tpad.forms.management = smartfs.create("tpad.forms.management", function(state)
	state:size(12, 9)
	state:label(0.2, 0.2, "management_title", "TPAD Verwaltung")
	state:listbox(0.2, 0.6, 11.6, 5, "pads_listbox", {})
	state:field(0.5, 6.6, 6, 0, "padname_field", "Name", "")

	local padtype_dropdown = state:dropdown(0.25, 6.7, 6.25, 0, "padtype_dropdown")
	padtype_dropdown:addItem(PRIVATE_PAD_STRING)
	padtype_dropdown:addItem(PUBLIC_PAD_STRING)
	padtype_dropdown:addItem(GLOBAL_PAD_STRING)

	state:button(7, 6.3, 2, 0, "save_button", "Speichern")
	state:button(7, 7.1, 2, 0, "delete_button", "Löschen")
	state:button(0.2, 8.4, 2, 0, "back_button", "Zurück")
	
	-- NEU: Teleport-Button für Admins, standardmäßig unsichtbar
	state:button(2.3, 8.4, 3, 0, "admin_teleport_button", "Teleport (Admin)"):setVisible(false)

	local close_button = state:button(9.8, 8.4, 2, 0, "close_button", "Schließen")
	close_button:setClose(true)
end)

-- This form now defines its OWN behavior, making it independent and robust.
tpad.forms.confirm_pad_deletion = smartfs.create("tpad.forms.confirm_pad_deletion", function(state)
	state:size(8, 2.5)
	state:label(0, 0, "intro_label", "Willst du das TPAD wirklich löschen?")
	state:label(0, 0.5, "padname_label", "")
	state:label(0, 1, "outro_label", "(es lässt sich nicht wiederherstellen)")
	
	local confirm_button = state:button(0, 2.2, 2, 0, "confirm_button", "Ja, löschen")
	local deny_button = state:button(6, 2.2, 2, 0, "deny_button", "Nein, abbrechen")
	
	local playername = state.location.player
	local pending_data = tpad.pending_deletion[playername]

	if not pending_data then
		state:close()
		return
	end

	local pad_to_delete = pending_data.pad_data
	local original_form_context = pending_data.original_form_context

	local pad_display_name = pad_to_delete.name .. " (" .. short_padtype_string[pad_to_delete.type] .. ")"
	state:get("padname_label"):setText(YELLOW_ESCAPE .. pad_display_name)
	
	-- Diese Funktion erzwingt einen sauberen Neuaufbau der Verwaltungs-Ansicht
	local function return_to_management_with_fresh_state()
		tpad.pending_deletion[playername] = nil -- Temporäre Daten löschen
		minetest.after(0, function()
			-- Erstelle einen sauberen Kontext, anstatt den alten wiederzuverwenden
			local fresh_context = {
				playername = original_form_context.playername,
				clicker = original_form_context.clicker,
				ownername = original_form_context.ownername,
				clicked_pos = original_form_context.clicked_pos,
				node = minetest.get_node(original_form_context.clicked_pos), -- Node neu holen, falls sich was geändert hat
				network_type = original_form_context.network_type,
				page = 1
			}
			submit.management(fresh_context)
		end)
	end

	confirm_button:onClick(function()
		tpad.del_pad(pad_to_delete.owner, pad_to_delete.pos)
		minetest.remove_node(pad_to_delete.pos)
		notify(playername, "TPAD '" .. pad_to_delete.name .. "' gelöscht.")
		return_to_management_with_fresh_state()
	end)

	deny_button:onClick(return_to_management_with_fresh_state)
end)

tpad.forms.admin = smartfs.create("tpad.forms.admin", function(state)
	state:size(8, 8)
	state:label(0.2, 0.2, "admin_label", "TPAD Einstellungen")
	state:field(0.5, 2, 6, 0, "max_total_field", "Max. Gesamtzahl an TPADs (pro Spieler)")
	state:field(0.5, 3.5, 6, 0, "max_global_field", "Max. globale TPADs (pro Spieler)")
	state:button(6.5, 0.7, 1.5, 0, "save_button", "Speichern")
	local close_button = state:button(6.5, 7, 1.5, 0, "close_button", "Schließen")
	close_button:setClose(true)
end)

-- ========================================================================
-- Data Helper Functions
-- ========================================================================

function tpad.decorate_pad_data(pos, pad, ownername, viewername)
	pad = table.copy(pad)
	if type(pos) == "string" then
		pad.strpos = pos
		pad.pos = minetest.string_to_pos(pos)
	else
		pad.pos = pos
		pad.strpos = minetest.pos_to_string(pos)
	end
	pad.owner = ownername
	pad.name = pad.name or ""
	pad.type = pad.type or PUBLIC_PAD

	-- NEUE LOGIK: Erstellt den Anzeigenamen für das lokale Netzwerk
	local is_own_pad = (viewername and ownername == viewername)
	if is_own_pad then
		if pad.type == PRIVATE_PAD then
			pad.local_fullname = pad.name .. " (meins, privat)"
		elseif pad.type == PUBLIC_PAD then
			pad.local_fullname = pad.name .. " (meins)"
		else
			-- Fallback, falls es weitere Typen gäbe
			pad.local_fullname = pad.name
		end
	else
		-- Bisherige Logik für fremde Pads
		if pad.type == PRIVATE_PAD then
			pad.local_fullname = pad.name .. " (privat)"
		else
			pad.local_fullname = pad.name
		end
	end

	pad.global_fullname = pad.name
	return pad
end

function tpad.get_pad_data(pos)
	local meta = minetest.get_meta(pos)
	local ownername = meta:get_string("owner")
	if not ownername or ownername == "" then return end
	local pads = tpad._get_stored_pads(ownername)
	local strpos = minetest.pos_to_string(pos)
	local pad = pads[strpos]
	if not pad then return end
	return tpad.decorate_pad_data(pos, pad, ownername)
end

function tpad.set_pad_data(pos, padname, padtype_str)
	local meta = minetest.get_meta(pos)
	local ownername = meta:get_string("owner")
	local pads = tpad._get_stored_pads(ownername)
	local strpos = minetest.pos_to_string(pos)
	local pad = pads[strpos] or {}
	pad.name = padname
	pad.type = padtype_string_to_flag[padtype_str]
	pads[strpos] = pad
	tpad._set_stored_pads(ownername, pads)
end

function tpad.del_pad(ownername, pos)
	local pads = tpad._get_stored_pads(ownername)
	pads[minetest.pos_to_string(pos)] = nil
	tpad._set_stored_pads(ownername, pads)
end

-- ========================================================================
-- Register Node and Bind Callbacks
-- ========================================================================

local collision_box = {
	type = "fixed",
	fixed = { -0.5, -0.5, -0.5, 0.5, -0.3, 0.5 },
}

minetest.register_node(tpad.nodename, {
	drawtype = "mesh",
	tiles = { tpad.texture },
	mesh = tpad.mesh,
	paramtype = "light",
	paramtype2 = "facedir",
	on_place = minetest.rotate_and_place,
	after_place_node = tpad.after_place_node,
	collision_box = collision_box,
	selection_box = collision_box,
	description = "Teleporter Pad",
	groups = {choppy = 2, dig_immediate = 2},
	on_rightclick = tpad.on_rightclick,
	can_dig = tpad.can_dig,
	on_destruct = tpad.on_destruct,
})

minetest.register_chatcommand("tpad", {func = tpad.command})
