function digilines.getspec(node)
	local def = minetest.registered_nodes[node.name]
	if not def then return false end
	return def.digilines or def.digiline
end

function digilines.importrules(spec, node)
	if type(spec) == 'function' then
		return spec(node)
	elseif spec then
		return spec
	else
		return digilines.rules.default
	end
end

function digilines.getAnyInputRules(pos)
	local node = digilines.get_node_force(pos)
	local spec = digilines.getspec(node)
	if not spec then return end

	if spec.wire then
		return digilines.importrules(spec.wire.rules, node)
	end
	if spec.effector then
		return digilines.importrules(spec.effector.rules, node)
	end
end

function digilines.getAnyOutputRules(pos)
	local node = digilines.get_node_force(pos)
	local spec = digilines.getspec(node)
	if not spec then return end

	if spec.wire then
		return digilines.importrules(spec.wire.rules, node)
	end
	if spec.receptor then
		return digilines.importrules(spec.receptor.rules, node)
	end
end

function digilines.rules_link(output, input)
	local outputrules = digilines.getAnyOutputRules(output)
	local inputrules  = digilines.getAnyInputRules (input)

	if not outputrules or not inputrules then return false end


	for _, orule in ipairs(outputrules) do
		if digilines.cmpPos(digilines.addPosRule(output, orule), input) then
			for _, irule in ipairs(inputrules) do
				if digilines.cmpPos(digilines.addPosRule(input, irule), output) then
					return true
				end
			end
		end
	end
	return false
end

function digilines.rules_link_anydir(output, input)
	return digilines.rules_link(output, input)
	or     digilines.rules_link(input, output)
end

local function queue_new()
	return {nextRead = 1, nextWrite = 1}
end

local function queue_empty(queue)
	return queue.nextRead == queue.nextWrite
end

local function queue_enqueue(queue, object)
	local nextWrite = queue.nextWrite
	queue[nextWrite] = object
	queue.nextWrite = nextWrite + 1
end

local function queue_dequeue(queue)
	local nextRead = queue.nextRead
	local object = queue[nextRead]
	queue[nextRead] = nil
	queue.nextRead = nextRead + 1
	return object
end

function digilines.transmit(pos, channel, msg, checked)
	local checkedID = minetest.hash_node_position(pos)
	if checked[checkedID] then
		return
	end
	checked[checkedID] = true

	digilines.vm_begin()
	local queue = queue_new()
	queue_enqueue(queue, pos)
	while not queue_empty(queue) do
		local curPos = queue_dequeue(queue)
		local node = digilines.get_node_force(curPos)
		local spec = digilines.getspec(node)
		if spec then
			-- Effector actions --> Receive
			if spec.effector then
				spec.effector.action(curPos, node, channel, msg)
			end

			-- Cable actions --> Transmit
			if spec.wire then
				local rules = digilines.importrules(spec.wire.rules, node)
				for _, rule in ipairs(rules) do
					local nextPos = digilines.addPosRule(curPos, rule)
					if digilines.rules_link(curPos, nextPos) then
						local checkedID2 = minetest.hash_node_position(nextPos)
						if not checked[checkedID2] then
							checked[checkedID2] = true
							queue_enqueue(queue, nextPos)
						end
					end
				end
			end
		end
	end
	digilines.vm_end()
end
