local S = minetest.get_translator("elevator")

local function is_falsey_string(str)
	return not str or str == ""
end

-- called "on_punch" of elevator and elevator
function elevator.update_formspec(pos, puncher_name, fields)
	local meta = minetest.get_meta(pos)

	local this_node   = minetest.get_node(pos)
	local is_elevator = this_node ~= nil and this_node.name == "elevator:elevator"

	if not meta then
		return
	end

	local owner_name      = meta:get_string("owner")
	local station_name    = meta:get_string("station_name")
	local station_network = meta:get_string("station_network")

	if	   not owner_name
		or not station_name
		or is_falsey_string(station_network)
	then
		if is_elevator then
			elevator.add_target(nil, nil, pos, puncher_name, meta, owner_name)
			return
		end

		elevator.reset_formspec(meta)
		elevator.show_message(pos, puncher_name, "Error", S("Update failed! Resetting this box on the elevator."))
		return
	end

	-- if the station got lost from the network for some reason (savefile corrupted?) then add it again
	if not elevator.get_station(owner_name, station_network, station_name) then

		local network = elevator.get_or_create_network(owner_name, station_network)

		local zeit = meta:get_int("timestamp")
		if not zeit or type(zeit) ~= "number" or zeit < 100000 then
			zeit = os.time()
		end

		-- add this station
		network[station_name] = {
			pos = pos,
			timestamp = zeit
		}

		minetest.chat_send_player(owner_name,
				S("Station '@1'" .. " " ..
					"has been reattached to the network '@2'.", station_name, station_network))
		elevator.save_data()
	end


	-- add name of station + network + owner + update-button

	local formspec = ([[
			size[12,10]
			label[3.3,0.0;%s:]
			label[6.3,0.0;%s]
			label[0.3,0.4;%s]
			label[6.3,0.4;%s]
			label[0.3,0.8;%s]
			label[6.3,0.8;%s]
			label[0.3,1.2;%s]
			label[6.3,1.2;%s]
			label[3.3,1.6;%s]
		]]):format(
			S("elevator-Box"),
			S("Punch box to update target list."),
			S("Name of this station:"),
			minetest.formspec_escape(station_name or "?"),
			S("Assigned to Network:"),
			minetest.formspec_escape(station_network or "?"),
			S("Owned by:"),
			minetest.formspec_escape(owner_name or "?"),
			S("Click on target to travel there:")
		)

	local x = 0
	local y = 0
	local i = 0

	-- collect all station names in a table
	local stations = {}
	local network = elevator.targets[owner_name][station_network]

	for k in pairs(network) do
		table.insert(stations, k)
	end

	local ground_level = 1
	if is_elevator then
		table.sort(stations, function(a, b)
			return network[a].pos.y > network[b].pos.y
		end)

		-- find ground level
		local vgl_timestamp = 999999999999
		for index,k in ipairs(stations) do
			local station = network[k]
			if not station.timestamp then
				station.timestamp = os.time()
			end
			if station.timestamp < vgl_timestamp then
				vgl_timestamp = station.timestamp
				ground_level  = index
			end
		end

		for index,k in ipairs(stations) do
			local station = network[k]
			if index == ground_level then
				station.nr = "1"
			else
				station.nr = tostring(ground_level + 1 - index)
			end
		end
	else
		-- sort the table according to the timestamp (=time the station was configured)
		table.sort(stations, function(a, b)
			return network[a].timestamp < network[b].timestamp
		end)
	end

	-- does the player want to move this station one position up in the list?
	-- only the owner and players with the elevator_attach priv can change the order of the list
	-- Note: With elevators, only the "G"(round) marking is actually moved
	if	    fields and (fields.move_up or fields.move_down)
		and not is_falsey_string(owner_name)
		and (
			   (owner_name == puncher_name)
			or (minetest.check_player_privs(puncher_name, { elevator_attach=true }))
		)
	then

		local current_pos = -1
		for index, k in ipairs(stations) do
			if k == station_name then
				current_pos = index
				-- break??
			end
		end

		local swap_with_pos
		if fields.move_up then
			swap_with_pos = current_pos-1
		else
			swap_with_pos = current_pos+1
		end

		-- handle errors
		if swap_with_pos < 1 then
			elevator.show_message(pos, puncher_name, "Info", S("This station is already the first one on the list."))
			return
		elseif swap_with_pos > #stations then
			elevator.show_message(pos, puncher_name, "Info", S("This station is already the last one on the list."))
			return
		else
			local current_station = stations[current_pos]
			local swap_with_station = stations[swap_with_pos]

			-- swap the actual data by which the stations are sorted
			local old_timestamp = network[swap_with_station].timestamp
			network[swap_with_station].timestamp = network[current_station].timestamp
			network[current_station].timestamp = old_timestamp

			-- for elevators, only the "G"(round) marking is moved; no point in swapping stations
			if not is_elevator then
				-- actually swap the stations
				stations[swap_with_pos] = current_station
				stations[current_pos]   = swap_with_station
			end

			-- store the changed order
			elevator.save_data()
		end
	end

	-- if there are only 8 stations (plus this one), center them in the formspec
	if #stations < 10 then
		x = 4
	end

	for _,k in ipairs(stations) do

		i = i+1

		-- new column
		if y == 8 then
			x = x + 4
			y = 0
		end

		-- check if there is an elevator door in front that needs to be opened
		if k == station_name then
			formspec = formspec ..
				("button_exit[%f,%f;1,0.5;open_door;<>]label[%f,%f;%s]")
						:format(x, y + 2.5, x + 0.9, y + 2.35, k)
		elseif is_elevator then
			formspec = formspec ..
				("button_exit[%f,%f;1,0.5;target;%s]label[%f,%f;%s]")
						:format(x, y + 2.5, tostring(network[k].nr), x + 0.9, y + 2.35, k)
		else
			formspec = formspec ..
				("button_exit[%f,%f;4,0.5;target;%s]")
						:format(x, y + 2.5, k)
		end

		y = y+1
	end

	formspec = formspec .. ([[
			label[8.0,1.6;%s]
			button_exit[11.3,0.0;1.0,0.5;station_exit;%s]
			button_exit[10.0,0.5;2.2,0.7;station_dig;%s]
			button[9.6,1.6;1.4,0.5;move_up;%s]
			button[10.9,1.6;1.4,0.5;move_down;%s]
		]]):format(
			S("Position in list:"),
			S("Exit"),
			S("Remove station"),
			S("move up"),
			S("move down")
		)


	meta:set_string("formspec", formspec)

	meta:set_string("infotext",
			S("Station '@1'" .. " " ..
				"on elevator '@2' (owned by @3)" .. " " ..
				"ready for usage. Right-click to travel, punch to update.",
				tostring(station_name), tostring(station_network), tostring(owner_name)))

	-- show the player the updated formspec
	elevator.show_current_formspec(pos, meta, puncher_name)
end
