--[[

	TechAge
	=======

	Copyright (C) 2019-2022 Joachim Stolberg

	AGPL v3
	See LICENSE.txt for more information

	For tupe/pipe blocks with teleport support

]]--

-- for lazy programmers
local S2P = minetest.string_to_pos
local P2S = minetest.pos_to_string
local NDEF = function(pos) return minetest.registered_nodes[techage.get_node_lvm(pos).name] or {} end
local M = minetest.get_meta
local S = techage.S
local menu = techage.menu

techage.teleport = {}

local PairingList = {}  -- {owner = {node_type = {channel = pos}}}

local function get_pairing_table1(meta)
    local owner = meta:get_string("owner")
    local node_type = meta:get_string("tele_node_type")
	PairingList[owner] = PairingList[owner] or {}
	PairingList[owner][node_type] = PairingList[owner][node_type] or {}
	return PairingList[owner][node_type]
end

local function get_pairing_table2(oldmetadata)
    local owner = oldmetadata.fields.owner
    local node_type = oldmetadata.fields.tele_node_type
	PairingList[owner] = PairingList[owner] or {}
	PairingList[owner][node_type] = PairingList[owner][node_type] or {}
	return PairingList[owner][node_type]
end

local function get_free_server_list(pos)
	local tbl = {""}
	for name, pos in pairs(get_pairing_table1(M(pos))) do
		table.insert(tbl, name)
	end
	return table.concat(tbl, ",")
end

local TELE_MENU = {
	{
		type = "label",
		label = S("Enter a block name or select an existing one"),
		tooltip = "",
		name = "l1",
	},
	{
		type = "ascii",
		name = "conn_name",
		label = S("Block name"),
		tooltip = S("Connection name for this block"),
		default = "",
	},
	{
		type = "dropdown",
		choices = "",
		on_dropdown = get_free_server_list,
		name = "remote_name",
		label = S("Remote name"),
		tooltip = S("Connection name of the remote block"),
	},
	{
		type = "output",
		label = S("Status"),
		tooltip = S("Connection status"),
		name = "status",
		default = "",
	},
}

function techage.teleport.formspec(pos)
	local ndef = NDEF(pos)
	return menu.generate_formspec(pos, ndef, TELE_MENU)
end

local function store_connection(pos, peer_pos)
	local meta = M(pos)
	local status = S("connected to") .. " " .. P2S(peer_pos)
	meta:set_string("tele_status", status)
	meta:set_string("tele_peer_pos", P2S(peer_pos))
	meta:set_string("formspec", "")
end

function techage.teleport.prepare_pairing(pos, node_type, status)
	local meta = M(pos)
	if node_type then
		meta:set_string("tele_node_type", node_type)
	end
	status = status or S("not connected")
	meta:set_string("tele_status", status)
	meta:set_string("tele_peer_pos", "")
	meta:set_string("formspec", techage.teleport.formspec(pos))
end

function techage.teleport.stop_pairing(pos, oldmetadata)
	-- disconnect peer node
	if oldmetadata and oldmetadata.fields then
		if oldmetadata.fields.tele_peer_pos then
			local peer_pos = S2P(oldmetadata.fields.tele_peer_pos)
			local meta = M(peer_pos)
			if meta:get_string("conn_name") ~= "" then -- Server
				local tbl = get_pairing_table1(meta)
				tbl[meta:get_string("conn_name")] = peer_pos
				techage.teleport.prepare_pairing(peer_pos, nil, S("server not connected"))
			else
				techage.teleport.prepare_pairing(peer_pos)
			end
		elseif oldmetadata.fields.conn_name then
			local tbl = get_pairing_table2(oldmetadata)
			tbl[oldmetadata.fields.conn_name] = nil
		end
	end
end

function techage.teleport.is_connected(pos)
	return M(pos):get_string("tele_peer_pos") ~= ""
end

function techage.teleport.get_remote_pos(pos)
	local s = M(pos):get_string("tele_peer_pos")
	if s ~= "" then
		return S2P(s)
	end
end

function techage.teleport.after_formspec(pos, player, fields, max_dist, ex_points)
	if techage.get_expoints(player) >= ex_points then
		if techage.menu.eval_input(pos, TELE_MENU, fields) then
			if not techage.teleport.is_connected(pos) then
				local meta = M(pos)
				if fields.remote_name ~= "" then -- Client
					local tbl = get_pairing_table1(meta)
					local peer_pos = tbl[fields.remote_name]
					if peer_pos then
						if vector.distance(pos, peer_pos) <= max_dist then
							tbl[fields.remote_name] = nil
							store_connection(pos, peer_pos)
							store_connection(peer_pos, pos)
							M(pos):set_string("status", S("Connected"))
						else
							M(pos):set_string("status", S("Distance > @1 blocks", max_dist))
							meta:set_string("formspec", techage.teleport.formspec(pos))
						end
					end
				elseif fields.conn_name ~= "" then -- Server
					local tbl = get_pairing_table1(meta)
					tbl[fields.conn_name] = pos
					techage.teleport.prepare_pairing(pos, nil, S("server not connected"))
				end
			end
		end
	else
		M(pos):set_string("status", S("Ex-points missing (@1 < @2)", techage.get_expoints(player), ex_points))
		M(pos):set_string("formspec", techage.teleport.formspec(pos))
	end
end