-- Get current translator
local S = atlatc.translate

local ac = {nodes={}}

function ac.load(data)
	if data then
		ac.nodes=data.nodes
	end
end
function ac.save()
	return {nodes = ac.nodes}
end

function ac.after_place_node(pos, player)
	local meta=minetest.get_meta(pos)
	meta:set_string("formspec", ac.getform(pos, meta))
	meta:set_string("infotext", S("Unconfigured LuaATC component"))
	local ph=minetest.pos_to_string(pos)
	--just get first available key!
	for en,_ in pairs(atlatc.envs) do
		ac.nodes[ph]={env=en}
		return
	end
end
function ac.getform(pos, meta_p)
	local meta = meta_p or minetest.get_meta(pos)
	local envs_asvalues={}
	
	local ph=minetest.pos_to_string(pos)
	local nodetbl = ac.nodes[ph]
	local env, code, err = nil, "", ""
	if nodetbl then
		code=nodetbl.code or ""
		err=nodetbl.err or ""
		env=nodetbl.env or ""
	end
	local sel = 1
	for n,_ in pairs(atlatc.envs) do
		envs_asvalues[#envs_asvalues+1]=minetest.formspec_escape(n)
		if n==env then
			sel=#envs_asvalues
		end
	end
	local form = "size["..atlatc.CODE_FORM_SIZE.."]"
		.."style[code;font=mono]"
		.."label[0,-0.1;"..S("LuaATC Environment").."]"
		.."dropdown[0,0.3;3;env;"..table.concat(envs_asvalues, ",")..";"..sel.."]"
		.."button[5,0.2;2,1;save;"..S("Save").."]"
		.."button[7,0.2;3,1;cle;"..S("Clear Local Environment").."]"
		.."textarea[0.3,1.5;"..atlatc.CODE_FORM_SIZE..";code;"..S("Code")..";"..minetest.formspec_escape(code).."]"
		.."label["..atlatc.CODE_FORM_ERRLABELPOS..";"..err.."]"
	return form
end

function ac.after_dig_node(pos, node, player)
	advtrains.invalidate_all_paths(pos)
	advtrains.ndb.clear(pos)
	atlatc.interrupt.clear_ints_at_pos(pos)
	if advtrains.lines and advtrains.lines.sched then
		advtrains.lines.sched.discard_all(advtrains.encode_pos(pos))
	end
	local ph=minetest.pos_to_string(pos)
	ac.nodes[ph]=nil
end

function ac.on_receive_fields(pos, formname, fields, player)
	if not minetest.check_player_privs(player:get_player_name(), {atlatc=true}) then
		minetest.chat_send_player(player:get_player_name(), S("You are not allowed to configure this LuaATC component without the @1 privilege.", "atlatc"))
		return
	end
	
	local meta=minetest.get_meta(pos)
	local ph=minetest.pos_to_string(pos)
	local nodetbl = ac.nodes[ph] or {}
	--if fields.quit then return end
	if fields.env then
		nodetbl.env=fields.env
	end
	if fields.code then
		nodetbl.code=fields.code
	end
	if fields.save then
		-- reset certain things
		nodetbl.err=nil
		if advtrains.lines and advtrains.lines.sched then
			-- discard all schedules for this node
			advtrains.lines.sched.discard_all(advtrains.encode_pos(pos))
		end
	end
	if fields.cle then
		nodetbl.data={}
	end
	
	ac.nodes[ph]=nodetbl
	
	meta:set_string("formspec", ac.getform(pos, meta))
	if nodetbl.env then
		meta:set_string("infotext", S("LuaATC component assigned to environment '@1'", nodetbl.env))
	else
		meta:set_string("infotext", S("LuaATC component assigned to an invalid environment"))
	end
end

function ac.run_in_env(pos, evtdata, customfct_p, ignore_no_code)
	local ph=minetest.pos_to_string(pos)
	local nodetbl = ac.nodes[ph]
	if not nodetbl then
		atwarn("LuaATC component at",ph,": Data not in memory! Please visit component and click 'Save'!")
		return
	end
	
	local meta
	if advtrains.is_node_loaded(pos) then
		meta=minetest.get_meta(pos)
	end
	
	if not nodetbl.env or not atlatc.envs[nodetbl.env] then
		atwarn("LuaATC component at",ph,": Not an existing environment: "..(nodetbl.env or "<nil>"))
		return false
	end
	local env = atlatc.envs[nodetbl.env]
	if not nodetbl.code or nodetbl.code=="" then
		if not ignore_no_code then
			env:log("warning", "LuaATC component at",ph,": No code to run! (insert -- to suppress warning)")
		end
		return false
	end
	
	local customfct=customfct_p or {}
	-- add interrupt function
	customfct.interrupt=function(t, imesg)
		assertt(t, "number")
		assert(t >= 0)
		atlatc.interrupt.add(t, pos, {type="int", int=true, message=imesg, msg=imesg}) --Compatiblity "message" field.
	end
	customfct.interrupt_safe=function(t, imesg)
		assertt(t, "number")
		assert(t >= 0)
		if atlatc.interrupt.has_at_pos(pos) then
			return false
		end
		atlatc.interrupt.add(t, pos, {type="int", int=true, message=imesg, msg=imesg}) --Compatiblity "message" field.
		return true
	end
	customfct.clear_interrupts=function()
		atlatc.interrupt.clear_ints_at_pos(pos)
	end
	customfct.atc_pos = {
		x = pos.x,
		y = pos.y,
		z = pos.z,
	}
	-- add digiline_send function, if digiline is loaded
	if minetest.global_exists("digiline") then
		customfct.digiline_send=function(channel, msg)
			assertt(channel, "string")
			if advtrains.is_node_loaded(pos) then
				digiline:receptor_send(pos, digiline.rules.default, channel, msg)
			end
		end
	end
	-- add lines scheduler if enabled
	if advtrains.lines and advtrains.lines.sched then
		customfct.schedule = function(rwtime, msg)
			return advtrains.lines.sched.enqueue(rwtime, "atlatc_env", {pos=pos, msg=msg}, advtrains.encode_pos(pos), 1)
		end
		customfct.schedule_in = function(rwtime, msg)
			return advtrains.lines.sched.enqueue_in(rwtime, "atlatc_env", {pos=pos, msg=msg}, advtrains.encode_pos(pos), 1)
		end
	end
	
	local datain=nodetbl.data or {}
	local succ, dataout = env:execute_code(datain, nodetbl.code, evtdata, customfct)
	if succ then
		atlatc.active.nodes[ph].data=atlatc.remove_invalid_data(dataout)
	else
		atlatc.active.nodes[ph].err=dataout
		env:log("error", "LuaATC component at",ph,": LUA Error:",dataout)
		if meta then
			meta:set_string("infotext", S("LuaATC component with error: @1", dataout))
		end
		--TODO temporary
		--if customfct.atc_id then
		--	advtrains.drb_dump(customfct.atc_id)
		--	error("Debug: LuaATC error hit!")
		--end
	end
	if meta then
		meta:set_string("formspec", ac.getform(pos, meta))
	end
end

function ac.on_digiline_receive(pos, node, channel, msg)
	atlatc.interrupt.add(0, pos, {type="digiline", digiline=true, channel = channel, msg = msg})
end

if advtrains.lines and advtrains.lines.sched then
	advtrains.lines.sched.register_callback("atlatc_env", function(data)
		-- This adds another interrupt to the atlatc queue... there might be a better way
		atlatc.interrupt.add(0, data.pos, {type="schedule",schedule=true, msg=data.msg})
	end)
end

ac.trackdef_advtrains_defs = {
	on_train_enter = function(pos, train_id)
		--do async. Event is fired in train steps
		atlatc.interrupt.add(0, pos, {type="train", train=true, id=train_id})
	end,
}

atlatc.active=ac
