--[[
	## StreetsMod 2.0 ##
	Submod: matrix_screen
	Optional: true
]]

-- This submod was made by HybridDog and modified by Thomas S.

local matrix_px = 16

-- gets the object at pos
local function get_screen(pos)
	local object
	local objects = minetest.get_objects_inside_radius(pos, 0.5) or {}
	for _, obj in pairs(objects) do
		local ent = obj:get_luaentity()
		if ent then
			if ent.name == "streets:matrix_screen_lights" then
				-- Remove duplicates
				if object then
					obj:remove()
				else
					object = obj
				end
			end
		end
	end
	return object
end

-- used to get the texture for the object
local function generate_textures(data)
	local texture, n = { "streets_matrix_screen_front.png^[combine:" .. matrix_px .. "x" .. matrix_px }, 2
	for y = 1, matrix_px do
		local xs = data[y]
		for x = 1, matrix_px do
			if xs[x] then
				texture[n] = ":" .. x - 1 .. "," .. y - 1 .. "=streets_matrix_px.png"
				n = n + 1
			end
		end
	end
	texture[n] = "^streets_matrix_screen_lines.png"
	texture = table.concat(texture, "")
	return { texture, texture, texture, texture, texture, texture }
end

-- updates texture of screen
local function update_screen(pos, data)
	local obj = get_screen(pos) or minetest.add_entity(pos, "streets:matrix_screen_lights")
	obj:set_properties({ textures = generate_textures(data) })
end

-- returns an empty toggleds table
local function new_toggleds()
	local t = {}
	for y = 1, matrix_px do
		t[y] = {}
	end
	return t
end

-- gets the toggleds of the node from meta
local function get_data(meta)
	local data = minetest.deserialize(minetest.decompress(meta:get_string("toggleds")))
	if type(data) ~= "table" then
		data = new_toggleds()
	end
	return data
end

-- update toggleds via a table sent by digiline
local function apply_changes(toggleds, t)
	if type(t) ~= "table" then
		return false, "matrix screen: got unsupported thing: " .. dump(t)
	end
	for y = 1, matrix_px do
		toggleds[y] = {}
	end
	for y = 1, 16 do
		local xs = t[y]
		if type(xs) == "table" then
			for x = 1, 16 do
				local enabled = xs[x]
				if enabled and enabled ~= 0 then
					toggleds[y][x] = true
				elseif enabled == false or enabled == 0 then
					toggleds[y][x] = nil
				end
			end
		end
	end
	return true
end

-- sets the toggleds of the node to meta
local function set_data(meta, toggleds)
	meta:set_string("toggleds", minetest.compress(minetest.serialize(toggleds)))
end

minetest.register_node("streets:matrix_screen_base", {
	description = "digiline controllable matrix screen",
	tiles = { "streets_matrix_screen_front.png" },
	drawtype = "nodebox",
	paramtype = "light",
	paramtype2 = "facedir",
	groups = { cracky = 3, oddly_breakable_by_hand = 2 },
	sounds = default.node_sound_stone_defaults(),
	light_source = 14,
	node_box = {
		type = "fixed",
		fixed = {
			{ -0.5, -0.5, -0.49, 0.5, 0.5, 0.5 },
		},
	},
	on_receive_fields = function(pos, formname, fields, sender)
		local name = sender:get_player_name()
		if minetest.is_protected(pos, name) and not minetest.check_player_privs(name, { protection_bypass = true }) then
			minetest.record_protection_violation(pos, name)
			return
		end
		if (fields.channel) then
			minetest.get_meta(pos):set_string("channel", fields.channel)
		end
	end,
	on_construct = function(pos)
		local meta = minetest.get_meta(pos)
		meta:set_string("formspec", "field[channel;Channel;${channel}]")
		meta:set_string("channel", "matrix_screen")
		minetest.add_entity(pos, "streets:matrix_screen_lights")
	end,
	on_destruct = function(pos)
		local obj = get_screen(pos)
		if obj then
			obj:remove()
		end
	end,
	digiline = {
		receptor = {},
		effector = {
			action = function(pos, node, channel, t)
				local meta = minetest.get_meta(pos)
				local toggleds = get_data(meta)
				if channel == meta:get_string("channel") .. "_reset" then
					for y = 1, matrix_px do
						toggleds[y] = {}
					end
					set_data(meta, toggleds)
					update_screen(pos, toggleds)
				end
				if channel ~= meta:get_string("channel") then
					return
				end
				apply_changes(toggleds, t)
				set_data(meta, toggleds)
				update_screen(pos, toggleds)
			end,
			rules = {
				{ x = 1, y = 0, z = 0 },
				{ x = 1, y = 1, z = 0 },
				{ x = 1, y = -1, z = 0 },
				{ x = -1, y = 0, z = 0 },
				{ x = -1, y = 1, z = 0 },
				{ x = -1, y = -1, z = 0 },
				{ x = 0, y = 1, z = 0 },
				{ x = 0, y = 1, z = 1 },
				{ x = 0, y = 1, z = -1 },
				{ x = 0, y = -1, z = 0 },
				{ x = 0, y = -1, z = 1 },
				{ x = 0, y = -1, z = -1 },
				{ x = 0, y = 0, z = 1 },
				{ x = 0, y = 0, z = -1 },
			}
		},
	},
})

-- ensure the screen existence
minetest.register_abm({
	interval = 5,
	chance = 1,
	nodenames = { "streets:matrix_screen_base" },
	action = function(pos, node)
		if not get_screen(pos) then
			local toggleds = get_data(minetest.get_meta(pos))
			update_screen(pos, toggleds)
			minetest.log("info", "[streets] matrix screen object was missing")
		end
	end,
})

-- the screen
minetest.register_entity("streets:matrix_screen_lights", {
	collisionbox = { 0, 0, 0, 0, 0, 0 },
	physical = false,
	visual = "cube",
	visual_size = { x = 0.99, y = 0.99 },
	on_activate = function(self, staticdata)
		local pos = self.object:getpos()
		if not vector.equals(pos, vector.round(pos)) then
			self.object:remove()
			return
		end
		local node = minetest.get_node(pos)
		if node.name ~= "streets:matrix_screen_base"
				and node.name ~= "ignore" then
			self.object:remove()
			return
		end
		minetest.after(0, function(pos)
			update_screen(pos, get_data(minetest.get_meta(pos)))
		end, pos)
	end,
})

minetest.register_craft({
	output = "streets:matrix_screen_base",
	recipe = {
		{ "dye:yellow", "default:steel_ingot", "dye:yellow" },
		{ "default:steel_ingot", "digilines:lcd", "default:steel_ingot" },
		{ "dye:yellow", "default:steel_ingot", "dye:yellow" },
	}
})
