--[[

	Signs Bot
	=========

	Copyright (C) 2019-2021 Joachim Stolberg

	GPL v3
	See LICENSE.txt for more information

	Node Sensor

]]--

-- for lazy programmers
local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end
local M = minetest.get_meta

-- Load support for I18n.
local S = signs_bot.S

local lib = signs_bot.lib

local CYCLE_TIME = 2

local function update_infotext(pos, dest_pos, cmnd)
	M(pos):set_string("infotext", S("Node Sensor: Connected with ")..P2S(dest_pos).." / "..cmnd)
end

local function swap_node(pos, name)
	local node = minetest.get_node(pos)
	if node.name == name then
		return false
	end
	if string.sub(node.name, 1,21) == "signs_bot:node_sensor" then
		node.name = name
		minetest.swap_node(pos, node)
		return true
	end
	return false
end


local DropdownValues = {
	[S("added")] = 1,
	[S("removed")] = 2,
	[S("added or removed")] = 3,
}

local function formspec(mem)
	local label = S("added")..","..S("removed")..","..S("added or removed")
	return "size[6,3]"..
	default.gui_bg..
	default.gui_bg_img..
	default.gui_slots..
	"label[0.2,0.4;"..S("Send signal if nodes have been:").."]"..
	"dropdown[0.2,1;6,1;mode;"..label..";"..(mem.mode or 3).."]"..
	"button_exit[1.5,2.2;3,1;accept;"..S("accept").."]"
end

local function any_node_changed(pos)
	local mem = tubelib2.get_mem(pos)
	if not mem.pos1 or not mem.pos2 or not mem.num then
		local node = minetest.get_node(pos)
		local param2 = (node.param2 + 2) % 4
		mem.pos1 = lib.dest_pos(pos, param2, {0})
		mem.pos2 = lib.dest_pos(pos, param2, {0,0,0})
		mem.num = #minetest.find_nodes_in_area(mem.pos1, mem.pos2, {"air"})
		return false
	end
	local num = #minetest.find_nodes_in_area(mem.pos1, mem.pos2, {"air"})

	if mem.num ~= num then
		if mem.mode == 1 and num < mem.num then
			mem.num = num
			return true
		elseif mem.mode == 2 and num > mem.num then
			mem.num = num
			return true
		elseif mem.mode == 3 then
			mem.num = num
			return true
		end
		mem.num = num
	end
	return false
end

local function on_receive_fields(pos, formname, fields, player)
	local mem = tubelib2.get_mem(pos)
	local meta = M(pos)
	if minetest.is_protected(pos, player:get_player_name()) then
		return
	end
	if fields.accept then
		mem.mode = DropdownValues[fields.mode] or 3
		mem.num = nil
		minetest.get_node_timer(pos):start(CYCLE_TIME)
		swap_node(pos, "signs_bot:node_sensor")
	end
	meta:set_string("formspec", formspec(mem))
end

local function node_timer(pos)
	if any_node_changed(pos)then
		if swap_node(pos, "signs_bot:node_sensor_on") then
			signs_bot.send_signal(pos)
			signs_bot.lib.activate_extender_nodes(pos, true)
			minetest.after(1, swap_node, pos, "signs_bot:node_sensor")
		end
	end
	return true
end

minetest.register_node("signs_bot:node_sensor", {
	description = S("Node Sensor"),
	inventory_image = "signs_bot_sensor_node_inv.png",
	drawtype = "nodebox",
	node_box = {
		type = "fixed",
		fixed = {
			{ -11/32, -1/2, -11/32, 11/32, -5/16, 11/32},
		},
	},
	tiles = {
		-- up, down, right, left, back, front
		"signs_bot_sensor1.png^signs_bot_sensor_node.png",
		"signs_bot_sensor1.png",
		"signs_bot_sensor1.png^[transformFXR90",
		"signs_bot_sensor1.png^[transformFXR90",
		"signs_bot_sensor1.png^[transformFXR90",
		"signs_bot_sensor1.png^[transformFXR180",
	},

	after_place_node = function(pos, placer)
		local meta = M(pos)
		local mem = tubelib2.init_mem(pos)
		meta:set_string("infotext", S("Node Sensor: Not connected"))
		mem.mode = 3 -- default legacy mode
		meta:set_string("formspec", formspec(mem))
		minetest.get_node_timer(pos):start(CYCLE_TIME)
		any_node_changed(pos)
	end,

	on_timer = node_timer,
	update_infotext = update_infotext,
	on_receive_fields = on_receive_fields,
	on_rotate = screwdriver.disallow,
	paramtype = "light",
	use_texture_alpha = signs_bot.CLIP,
	sunlight_propagates = true,
	paramtype2 = "facedir",
	is_ground_content = false,
	groups = {sign_bot_sensor = 1, cracky = 1},
	sounds = default.node_sound_metal_defaults(),
})

minetest.register_node("signs_bot:node_sensor_on", {
	description = S("Node Sensor"),
	drawtype = "nodebox",
	node_box = {
		type = "fixed",
		fixed = {
			{ -11/32, -1/2, -11/32, 11/32, -5/16, 11/32},
		},
	},
	tiles = {
		-- up, down, right, left, back, front
		"signs_bot_sensor1.png^signs_bot_sensor_node_on.png",
		"signs_bot_sensor1.png",
		"signs_bot_sensor1.png^[transformFXR90",
		"signs_bot_sensor1.png^[transformFXR90",
		"signs_bot_sensor1.png^[transformFXR90",
		"signs_bot_sensor1.png^[transformFXR180",
	},

	on_timer = node_timer,
	update_infotext = update_infotext,
	on_receive_fields = on_receive_fields,
	on_rotate = screwdriver.disallow,
	paramtype = "light",
	use_texture_alpha = signs_bot.CLIP,
	sunlight_propagates = true,
	paramtype2 = "facedir",
	is_ground_content = false,
	drop = "signs_bot:node_sensor",
	groups = {sign_bot_sensor = 1, cracky = 1, not_in_creative_inventory = 1},
	sounds = default.node_sound_metal_defaults(),
})

minetest.register_craft({
	output = "signs_bot:node_sensor",
	recipe = {
		{"", "", ""},
		{"dye:black", "group:stone", "dye:grey"},
		{"default:steel_ingot", "default:mese_crystal_fragment", "default:steel_ingot"}
	}
})

minetest.register_lbm({
	label = "[signs_bot] Restart timer",
	name = "signs_bot:node_sensor_restart",
	nodenames = {"signs_bot:node_sensor", "signs_bot:node_sensor_on"},
	run_at_every_load = true,
	action = function(pos, node)
		minetest.get_node_timer(pos):start(CYCLE_TIME)
		if node.name == "signs_bot:node_sensor_on" then
			signs_bot.send_signal(pos)
			signs_bot.lib.activate_extender_nodes(pos, true)
		end
	end
})

if minetest.get_modpath("doc") then
	doc.add_entry("signs_bot", "node_sensor", {
		name = S("Node Sensor"),
		data = {
			item = "signs_bot:node_sensor",
			text = table.concat({
				S("The node sensor sends cyclical signals when it detects that nodes have appeared or disappeared,"),
				S("but has to be configured accordingly."),
				S("Valid nodes are all kind of blocks and plants."),
				S("The sensor range is 3 nodes/meters in one direction."),
				S("The sensor has an active side (red) that must point to the observed area."),
			}, "\n")
		},
	})
end
