---
--- Generated by EmmyLua.
--- Created by Michieal (FaerRaven).
--- DateTime: 10/14/22 4:05 PM
---

local DEBUG = minetest.settings:get_bool("mcl_logging_mcl_signs", false) -- special debug setting.
if DEBUG then
	minetest.log("action", "[mcl_signs] Signs API Loading")
end

-- LOCALIZATION
local S = minetest.get_translator("mcl_signs")
-- Signs form
local F = minetest.formspec_escape

-- PATHs
local modpath = minetest.get_modpath("mcl_signs")

-- CONSTANTS
local SIGN_WIDTH = 115

local LINE_LENGTH = 15
local NUMBER_OF_LINES = 4

local LINE_HEIGHT = 14
local CHAR_WIDTH = 5
local TIMER_INTERVAL = 40.0
-- -----------------------
-- CACHE LOCAL COPIES
local table = table
local string = string

-- CACHE NODE_SOUNDS
local node_sounds
if minetest.get_modpath("mcl_sounds") then
	node_sounds = mcl_sounds.node_sound_wood_defaults()
end

-- SET UP THE CHARACTER MAPPING
-- Load the characters map (characters.txt)
--[[ File format of characters.txt:
It's an UTF-8 encoded text file that contains metadata for all supported characters. It contains a sequence of info
    blocks, one for each character. Each info block is made out of 3 lines:
Line 1: The literal UTF-8 encoded character
Line 2: Name of the texture file for this character minus the “.png” suffix; found in the “textures/” sub-directory
Line 3: Currently ignored. Previously this was for the character width in pixels

After line 3, another info block may follow. This repeats until the end of the file.

All character files must be 5 or 6 pixels wide (5 pixels are preferred)
]]

local chars_file = io.open(modpath .. "/characters.txt", "r")
-- FIXME: Support more characters (many characters are missing). Currently ASCII and Latin-1 Supplement are supported.
local charmap = {}
if not chars_file then
	minetest.log("error", "[mcl_signs] : character map file not found")
else
	while true do
		local char = chars_file:read("*l")
		if char == nil then
			break
		end
		local img = chars_file:read("*l")
		chars_file:read("*l")
		charmap[char] = img
	end
end

local pi = 3.1415926 -- enough accuracy, to build an engine for a car.

local math = math

-- locally cached copy of the official colors; this way, it updates as mcl_colors updates.
local mcl_colors_official = mcl_colors
if DEBUG then
	minetest.log("verbose", "[mcl_signs]Official MCL_Colors:\n" .. dump(mcl_colors_official))
end

-- INITIALIZE THE GLOBAL API FOR SIGNS.
mcl_signs = {}

-- GLOBALS
mcl_signs.sign_groups = { handy = 1, axey = 1, deco_block = 1, material_wood = 1, attached_node = 1, flammable = -1 }
--- colors used for wools.
mcl_signs.mcl_wool_colors = {
	unicolor_white = "#FFFFFF",
	unicolor_dark_orange = "#502A00",
	unicolor_grey = "#5B5B5B",
	unicolor_darkgrey = "#303030",
	unicolor_blue = "#0000CC",
	unicolor_dark_green = "#005000",
	unicolor_green_or_lime = "#50CC00",
	unicolor_violet_purple = "#5000CC",
	unicolor_light_red_pink = "#FF5050",
	unicolor_yellow = "#CCCC00",
	unicolor_orange = "#CC5000",
	unicolor_red = "#CC0000",
	unicolor_cyan = "#00CCCC",
	unicolor_red_violet_magenta = "#CC0050",
	unicolor_black = "#000000",
	unicolor_light_blue = "#5050FF",
}
mcl_signs.signtext_info_wall = {}
mcl_signs.signtext_info_standing = {} -- built in build_signs_info().
-- the rotational levels for all of the standing signs.
mcl_signs.standing_rotation_levels = {}

-- data structure block for dynamically registered signs.
mcl_signs.registered_signs = {}
mcl_signs.registered_signs.wall_signs = {}
mcl_signs.registered_signs.standing_signs = {}
mcl_signs.registered_signs.hanging_signs = {} -- unused. prepping for future use.

function mcl_signs.build_signs_info()
	local n = 23 / 56 - 1 / 128 -- some required magic number from the original code.
	local m = -1 / 16 + 1 / 64  -- "     "        "     "       "    "   "       "

	mcl_signs.signtext_info_wall = {
		{ delta = { x = 0, y = 0, z = n }, yaw = 0 },
		{ delta = { x = n, y = 0, z = 0 }, yaw = pi / -2 },
		{ delta = { x = 0, y = 0, z = -n }, yaw = pi },
		{ delta = { x = -n, y = 0, z = 0 }, yaw = pi / 2 },
	}

	-- PLACE YAW VALUES INTO THE TABLE.
	for rot = 0, 15 do
		local yaw = pi * 2 - (((pi * 2) / 16) * rot)
		local delta = vector.multiply(minetest.yaw_to_dir(yaw), m)
		-- Offset because sign is a bit above node boundaries
		delta.y = delta.y + 2 / 28
		table.insert(mcl_signs.signtext_info_standing, { delta = delta, yaw = yaw })
	end

end

-- DEFINE SIGN BASE TYPES
local common_definition = {
	_mcl_hardness = 1,
	_mcl_blast_resistance = 1,
	stack_max = 16,
	sounds = node_sounds,
	groups = mcl_signs.sign_groups,
	use_texture_alpha = minetest.features.use_texture_alpha_string_modes and "opaque" or false,
	drawtype = "mesh",
	paramtype = "light",
	tiles = { "mcl_signs_sign.png" },
	sunlight_propagates = true,
	walkable = false,
	is_ground_content = false,

	on_rightclick = function (pos, node, clicker, itemstack, pointed_thing)
		if DEBUG then
			minetest.log("verbose", "[mcl_signs] Sign Right Click event.")
		end
	
		-- make sure player is clicking
		if not clicker or not clicker:is_player() then
			return
		end
	
		local item = clicker:get_wielded_item()
		local iname = item:get_name()
	
		local protected = mcl_util.check_position_protection(pos, clicker)
	
		if node and not protected then
			if DEBUG then
				minetest.log("verbose", "[mcl_signs] Sign Right Click event on valid node.")
			end
	
			-- handle glow from glow_ink_sac *first*
			if (iname == "mcl_mobitems:glow_ink_sac") then
				clicker:set_wielded_item(item)
				local success = mcl_signs:glow_sign(pos)
				if success then
					if DEBUG then
						minetest.log("verbose", "[mcl_signs] Sign Glow Success.")
					end
					itemstack:take_item()
				end
				return
			end
	
			-- check the wielded item to make sure that it is a dye.
			local txt_color = mcl_signs:get_color_for_sign(iname)
			if txt_color ~= "false" then
				clicker:set_wielded_item(item)
				local success = mcl_signs:color_sign(pos, txt_color)
				-- "mcl_dye:black" is a special case: it makes the sign's lettering black AND removes glow.
				if (iname == "mcl_dye:black") then
					success = mcl_signs:glow_sign(pos, true)
					if success and DEBUG then
						minetest.log("verbose", "[mcl_signs] Sign Glow removal Success.")
					end
				end
				if success then
					if DEBUG then
						minetest.log("verbose", "[mcl_signs] Sign Color Success.")
					end
					itemstack:take_item()
				end
				return
			end
	
			-- No modifier item in hand, open the sign for edition
			local old_text = minetest.get_meta(pos):get_string("text")
			mcl_signs:show_formspec(clicker, pos, old_text)
		end
	end,

	on_destruct = function(pos)
		mcl_signs:destruct_sign(pos)
	end,

	-- Not Useless Code. this force updates the sign.
	on_punch = function(pos, node, puncher)
		mcl_signs:update_sign(pos)
	end,
}

-- wall signs' & hanging signs' base (definition)
mcl_signs.wall_standard = table.copy(common_definition)
mcl_signs.wall_standard.description = S("Sign")
mcl_signs.wall_standard._tt_help = S("Can be written")
mcl_signs.wall_standard._doc_items_longdesc = S("Signs can be written and come in two variants: Wall sign and sign on a sign post. Signs can be placed on the top and the sides of other blocks, but not below them.")
mcl_signs.wall_standard._doc_items_usagehelp = S("After placing the sign, you can write something on it. You have 4 lines of text with up to 15 characters for each line; anything beyond these limits is lost. Not all characters are supported. The text can not be changed once it has been written; you have to break and place the sign again. Can be colored and made to glow.")
mcl_signs.wall_standard.inventory_image = "mcl_signs_default_sign.png"
mcl_signs.wall_standard.wield_image = "mcl_signs_default_sign.png"
mcl_signs.wall_standard.node_placement_prediction = ""
mcl_signs.wall_standard.paramtype2 = "wallmounted"
mcl_signs.wall_standard.mesh = "mcl_signs_signonwallmount.obj"
mcl_signs.wall_standard.selection_box = { type = "wallmounted", wall_side = { -0.5, -7 / 28, -0.5, -23 / 56, 7 / 28, 0.5 } }
mcl_signs.wall_standard.on_timer = function(pos)
		-- fix for /ClearObjects
		mcl_signs:update_sign(pos)
		-- note: update_sign decides to keep the timer running based on if there is text.
		--		This prevents every sign from having a timer, when not needed.
	end
mcl_signs.wall_standard.on_place = function(itemstack, placer, pointed_thing)
		local above = pointed_thing.above
		local under = pointed_thing.under

		-- Use pointed node's on_rightclick function first, if present
		local node_under = minetest.get_node(under)
		if placer and not placer:get_player_control().sneak then
			if minetest.registered_nodes[node_under.name] and minetest.registered_nodes[node_under.name].on_rightclick then
				return minetest.registered_nodes[node_under.name].on_rightclick(under, node_under, placer, itemstack) or itemstack
			end
		end

		local dir = vector.subtract(under, above)

		-- Only build when it's legal
		local abovenodedef = minetest.registered_nodes[minetest.get_node(above).name]
		if not abovenodedef or abovenodedef.buildable_to == false then
			return itemstack
		end

		local wdir = minetest.dir_to_wallmounted(dir)
		local fdir = minetest.dir_to_facedir(dir)

		local sign_info
		local nodeitem = ItemStack(itemstack)
		-- Ceiling
		if wdir == 0 then
			--how would you add sign to ceiling?
			return itemstack
			-- Floor
		end

		if wdir == 1 then
			-- Standing sign

			-- Determine the sign rotation based on player's yaw
			local yaw = pi * 2 - placer:get_look_horizontal()

			-- Select one of 16 possible rotations (0-15)
			local rotation_level = mcl_signs:round((yaw / (pi * 2)) * 16)

			if rotation_level > 15 then
				rotation_level = 0
			elseif rotation_level < 0 then
				rotation_level = 15
			end

			-- The actual rotation is a combination of predefined mesh and facedir (see node definition)
			if rotation_level % 4 == 0 then
				nodeitem:set_name("mcl_signs:standing_sign")
			elseif rotation_level % 4 == 1 then
				nodeitem:set_name("mcl_signs:standing_sign22_5")
			elseif rotation_level % 4 == 2 then
				nodeitem:set_name("mcl_signs:standing_sign45")
			elseif rotation_level % 4 == 3 then
				nodeitem:set_name("mcl_signs:standing_sign67_5")
			end
			fdir = math.floor(rotation_level / 4)

			-- Place the node!
			local _, success = minetest.item_place_node(nodeitem, placer, pointed_thing, fdir)
			if not success then
				return itemstack
			end
			if not minetest.is_creative_enabled(placer:get_player_name()) then
				itemstack:take_item()
			end
			sign_info = mcl_signs.signtext_info_standing[rotation_level + 1]
			-- Side
		else
			-- Wall sign
			local _, success = minetest.item_place_node(itemstack, placer, pointed_thing, wdir)
			if not success then
				return itemstack
			end
			sign_info = mcl_signs.signtext_info_wall[fdir + 1]
		end

		-- Determine spawn position of entity
		local place_pos
		if minetest.registered_nodes[node_under.name].buildable_to then
			place_pos = under
		else
			place_pos = above
		end

		local text_entity = minetest.add_entity({
			x = place_pos.x + sign_info.delta.x,
			y = place_pos.y + sign_info.delta.y,
			z = place_pos.z + sign_info.delta.z }, "mcl_signs:text")
		text_entity:set_yaw(sign_info.yaw)
		text_entity:get_luaentity()._signnodename = nodeitem:get_name()
		if DEBUG then
			minetest.log("verbose", "[mcl_signs]Placed position:" .. dump(place_pos) .. "\nSign_info: " .. dump(sign_info))
		end

		minetest.sound_play({ name = "default_place_node_hard", gain = 1.0 }, { pos = place_pos }, true)

		mcl_signs:show_formspec(placer, place_pos, "")
		return itemstack
	end
mcl_signs.wall_standard.on_rotate = function(pos, node, user, mode)
		if mode == screwdriver.ROTATE_FACE then
			local r = screwdriver.rotate.wallmounted(pos, node, mode)
			node.param2 = r
			minetest.swap_node(pos, node)
			mcl_signs:update_sign(pos, nil, nil, true)
			return true
		else
			return false
		end
	end


-- standing sign base (definition)
mcl_signs.standing_standard = table.copy(common_definition)
mcl_signs.standing_standard.paramtype2 = "facedir"
mcl_signs.standing_standard.mesh = "mcl_signs_sign.obj"
mcl_signs.standing_standard.selection_box = { type = "fixed", fixed = { -0.2, -0.5, -0.2, 0.2, 0.5, 0.2 } }
mcl_signs.standing_standard.drop = "mcl_signs:wall_sign"
mcl_signs.standing_standard.on_timer = function(pos)
		-- fix for /ClearObjects
		mcl_signs:update_sign(pos)
		minetest.get_node_timer(pos):start(40.0)
	end
mcl_signs.standing_standard.on_rotate = function(pos, node, user, mode)
		if mode == screwdriver.ROTATE_FACE then
			node.name = "mcl_signs:standing_sign22_5"
			minetest.swap_node(pos, node)
		elseif mode == screwdriver.ROTATE_AXIS then
			return false
		end
		mcl_signs:update_sign(pos, nil, nil, true)
		return true
	end

-- HELPER FUNCTIONS' VARIABLES
local sign_glow = 6
local Dyes_table = {
	{ "mcl_dye:aqua", mcl_colors_official.AQUA },
	{ "mcl_dye:black", mcl_colors_official.BLACK },
	{ "mcl_dye:blue", mcl_colors_official.BLUE },
	{ "mcl_dye:brown", mcl_colors_official.brown },
	{ "mcl_dye:cyan", mcl_signs.mcl_wool_colors.unicolor_cyan },
	{ "mcl_dye:green", mcl_colors_official.GREEN },
	{ "mcl_dye:dark_green", mcl_colors_official.DARK_GREEN },
	{ "mcl_dye:grey", mcl_colors_official.GRAY },
	{ "mcl_dye:dark_grey", mcl_colors_official.DARK_GRAY },
	{ "mcl_dye:lightblue", mcl_signs.mcl_wool_colors.unicolor_light_blue },
	{ "mcl_dye:lime", mcl_signs.unicolor_green_or_lime },
	{ "mcl_dye:magenta", mcl_colors_official.LIGHT_PURPLE },
	{ "mcl_dye:orange", mcl_signs.mcl_wool_colors.unicolor_orange },
	{ "mcl_dye:pink", mcl_signs.mcl_wool_colors.unicolor_light_red_pink },
	{ "mcl_dye:purple", mcl_colors_official.LIGHT_PURPLE },
	{ "mcl_dye:red", mcl_signs.mcl_wool_colors.unicolor_red },
	{ "mcl_dye:silver", mcl_signs.mcl_wool_colors.unicolor_grey },
	{ "mcl_dye:violet", mcl_colors_official.DARK_PURPLE },
	{ "mcl_dye:white", mcl_colors_official.WHITE },
	{ "mcl_dye:yellow", mcl_colors_official.YELLOW },
}

local function update_sign_registry(type, name)
	if type == "wall" then
		table.insert(mcl_signs.registered_signs.wall_signs, name)
	end
	if type == "standing" then
		table.insert(mcl_signs.registered_signs.standing_signs, name)
	end
	if type == "hanging" then
		table.insert(mcl_signs.registered_signs.hanging_signs, name)
	end
end

function mcl_signs.make_lbm()

	local registered_sign_nodenames = {}

	for i = 0, #mcl_signs.registered_signs.wall_signs do
		table.insert(registered_sign_nodenames, mcl_signs.registered_signs.wall_signs[i])
	end

	for i = 0, #mcl_signs.registered_signs.standing_signs do
		table.insert(registered_sign_nodenames, mcl_signs.registered_signs.standing_signs[i])
	end

	for i = 0, #mcl_signs.registered_signs.hanging_signs do
		table.insert(registered_sign_nodenames, mcl_signs.registered_signs.hanging_signs[i])
	end

	-- the above is not yet used.
	minetest.register_lbm({
		name = "mcl_signs:respawn_entities",
		label = "Respawn sign text entities",
		run_at_every_load = true,
		nodenames = registered_sign_nodenames,
		action = function(pos, node)
			mcl_signs:update_sign(pos)
		end,
	})

end

function mcl_signs.register_dye (modname, item_name, color_code)
	if minetest.get_modpath(modname) then
		table.insert(Dyes_table, { item_name, color_code })
	end
end

--- Register a new sign, tint the textures, and gives it an unique node name. Creates both wall and standing signs.
--- modname: optional (pass "" or "false" to ignore), for use with other mods to
--- allow the creation of a sign from the mod's wood (if installed).
---
--- color: the color code to color the base sign textures. must be a valid html color code.
---
--- _name: the sign's name suffix, such as "_dark" or "_red", etc., appended to "wall_sign" or "standing_sign"
---
--- ttsign: the tool tip of the sign that gets translated. Shown when the mouse hovers the inventory sign.
--- For example: the basic, default oak (wood) sign is just "Sign"; and a spruce sign would be "Spruce Sign"
function mcl_signs.register_sign (modname, color, _name, ttsign)
	local mod_name_pass = false
	if modname ~= "" and modname ~= "false" then
		if minetest.get_modpath(modname) then
			mod_name_pass = true
		end
		if mod_name_pass == false then
			return
		end
	end
	local new_sign = {}

	if color == nil or color == "" then
		color = "#FFFFFF"
	end

	new_sign = table.copy(mcl_signs.wall_standard)
	new_sign.description = ttsign

	new_sign.wield_image = "(mcl_signs_default_sign.png^[multiply:" .. color .. ")"
	new_sign.tiles = { "(mcl_signs_sign.png^[multiply:" .. color .. ")" }
	new_sign.inventory_image = "(mcl_signs_default_sign.png^[multiply:" .. color .. ")"

	-- currently have to do this, because of how the base node placement works.
	new_sign.on_place = function(itemstack, placer, pointed_thing)
		local above = pointed_thing.above
		local under = pointed_thing.under

		-- Use pointed node's on_rightclick function first, if present
		local node_under = minetest.get_node(under)
		if placer and not placer:get_player_control().sneak then
			if minetest.registered_nodes[node_under.name] and minetest.registered_nodes[node_under.name].on_rightclick then
				return minetest.registered_nodes[node_under.name].on_rightclick(under, node_under, placer, itemstack) or itemstack
			end
		end

		local dir = vector.subtract(under, above)

		-- Only build when it's legal
		local abovenodedef = minetest.registered_nodes[minetest.get_node(above).name]
		if not abovenodedef or abovenodedef.buildable_to == false then
			return itemstack
		end

		local wdir = minetest.dir_to_wallmounted(dir)
		local fdir = minetest.dir_to_facedir(dir)

		local sign_info
		local nodeitem = ItemStack(itemstack)

		local yaw = 0

		-- Ceiling
		if wdir == 0 then
			--how would you add sign to ceiling? simple - hanging sign.
			-- add code for placement underneath a node.

			return itemstack
			-- Floor
		elseif wdir == 1 then
			-- Standing sign

			-- Determine the sign rotation based on player's yaw
			yaw = pi * 2 - placer:get_look_horizontal()

			-- Select one of 16 possible rotations (0-15)
			local rotation_level = mcl_signs:round((yaw / (pi * 2)) * 16)

			if rotation_level > 15 then
				rotation_level = 0
			elseif rotation_level < 0 then
				rotation_level = 15
			end

			-- The actual rotation is a combination of predefined mesh and facedir (see node definition)
			if rotation_level % 4 == 0 then
				nodeitem:set_name("mcl_signs:standing_sign" .. _name)
			elseif rotation_level % 4 == 1 then
				nodeitem:set_name("mcl_signs:standing_sign22_5" .. _name)
			elseif rotation_level % 4 == 2 then
				nodeitem:set_name("mcl_signs:standing_sign45" .. _name)
			elseif rotation_level % 4 == 3 then
				nodeitem:set_name("mcl_signs:standing_sign67_5" .. _name)
			end
			fdir = math.floor(rotation_level / 4)

			-- Place the node!
			local _, success = minetest.item_place_node(nodeitem, placer, pointed_thing, fdir)
			if not success then
				return itemstack
			end
			if not minetest.is_creative_enabled(placer:get_player_name()) then
				itemstack:take_item()
			end
			sign_info = mcl_signs.signtext_info_standing[rotation_level + 1]
			-- Side
		else
			-- Wall sign
			local _, success = minetest.item_place_node(itemstack, placer, pointed_thing, wdir)
			if not success then
				return itemstack
			end
			sign_info = mcl_signs.signtext_info_wall[fdir + 1]
		end

		-- Determine spawn position of entity
		local place_pos
		if minetest.registered_nodes[node_under.name].buildable_to then
			place_pos = under
		else
			place_pos = above
		end

		if DEBUG then
			minetest.log("action", "[mcl_signs] Register_Sign::Placed position:" .. dump(place_pos) .. "\nSign_info: " .. dump(sign_info))
		end

		local text_entity = minetest.add_entity({
			x = place_pos.x + sign_info.delta.x,
			y = place_pos.y + sign_info.delta.y,
			z = place_pos.z + sign_info.delta.z }, "mcl_signs:text")
		text_entity:set_yaw(sign_info.yaw)
		text_entity:get_luaentity()._signnodename = nodeitem:get_name()

		minetest.sound_play({ name = "default_place_node_hard", gain = 1.0 }, { pos = place_pos }, true)

		mcl_signs:show_formspec(placer, place_pos, "")
		return itemstack
	end

	minetest.register_node(":mcl_signs:wall_sign" .. _name, new_sign)
	update_sign_registry("wall", "mcl_signs:wall_sign" .. _name)

	-- debug step
	if DEBUG then
		minetest.log("action", "[mcl_signs] Registered: mcl_signs:wall_sign" .. _name .. color .. "\n" .. dump(new_sign))
		minetest.log("action", "[mcl_signs] mcl_signs:wall_sign_standard\n" .. dump(mcl_signs.wall_standard))
	end

	-- standing sign base.
	local new_sign_standing = {}
	new_sign_standing = table.copy(mcl_signs.standing_standard)
	new_sign_standing.drop = "mcl_signs:wall_sign" .. _name
	new_sign_standing.wield_image = "(mcl_signs_default_sign.png^[multiply:" .. color .. ")"
	new_sign_standing.tiles = { "(mcl_signs_sign.png^[multiply:" .. color .. ")" }
	new_sign_standing.inventory_image = "(mcl_signs_default_sign.png^[multiply:" .. color .. ")"

	new_sign_standing.on_rotate = function(pos, node, user, mode)
		if mode == screwdriver.ROTATE_FACE then
			node.name = "mcl_signs:standing_sign22_5" .. _name
			minetest.swap_node(pos, node)
		elseif mode == screwdriver.ROTATE_AXIS then
			return false
		end
		mcl_signs:update_sign(pos, nil, nil, true)
		return true
	end,

	minetest.register_node(":mcl_signs:standing_sign" .. _name, new_sign_standing)
	update_sign_registry("standing", "mcl_signs:standing_sign" .. _name)
	-- debug step
	if DEBUG then
		minetest.log("action", "[mcl_signs] Registered: mcl_signs:standing_sign" .. _name .. color .. "\n" .. dump(new_sign_standing))
	end

	-- 22.5°
	local ssign22_5d = table.copy(new_sign_standing)
	ssign22_5d.mesh = "mcl_signs_sign22.5.obj"
	ssign22_5d.on_rotate = function(pos, node, user, mode)
		if mode == screwdriver.ROTATE_FACE then
			node.name = "mcl_signs:standing_sign45" .. _name
			minetest.swap_node(pos, node)
		elseif mode == screwdriver.ROTATE_AXIS then
			return false
		end
		mcl_signs:update_sign(pos, nil, nil, true)
		return true
	end
	minetest.register_node(":mcl_signs:standing_sign22_5" .. _name, ssign22_5d)
	update_sign_registry("standing", "mcl_signs:standing_sign22_5" .. _name)

	-- 45°
	local ssign45d = table.copy(new_sign_standing)
	ssign45d.mesh = "mcl_signs_sign45.obj"
	ssign45d.on_rotate = function(pos, node, user, mode)
		if mode == screwdriver.ROTATE_FACE then
			node.name = "mcl_signs:standing_sign67_5" .. _name
			minetest.swap_node(pos, node)
		elseif mode == screwdriver.ROTATE_AXIS then
			return false
		end
		mcl_signs:update_sign(pos, nil, nil, true)
		return true
	end
	minetest.register_node(":mcl_signs:standing_sign45" .. _name, ssign45d)
	update_sign_registry("standing", "mcl_signs:standing_sign45" .. _name)

	-- 67.5°
	local ssign67_5d = table.copy(new_sign_standing)
	ssign67_5d.mesh = "mcl_signs_sign67.5.obj"
	ssign67_5d.on_rotate = function(pos, node, user, mode)
		if mode == screwdriver.ROTATE_FACE then
			node.name = "mcl_signs:standing_sign" .. _name
			node.param2 = (node.param2 + 1) % 4
			minetest.swap_node(pos, node)
		elseif mode == screwdriver.ROTATE_AXIS then
			return false
		end
		mcl_signs:update_sign(pos, nil, nil, true)
		return true
	end
	minetest.register_node(":mcl_signs:standing_sign67_5" .. _name, ssign67_5d)
	update_sign_registry("standing", "mcl_signs:standing_sign67_5" .. _name)

	-- register Doc entry
	if minetest.get_modpath("doc") then
		doc.add_entry_alias("nodes", "mcl_signs:wall_sign", "nodes", "mcl_signs:wall_sign" .. _name)
		doc.add_entry_alias("nodes", "mcl_signs:wall_sign", "nodes", "mcl_signs:standing_sign" .. _name)
		doc.add_entry_alias("nodes", "mcl_signs:wall_sign", "nodes", "mcl_signs:standing_sign22_5" .. _name)
		doc.add_entry_alias("nodes", "mcl_signs:wall_sign", "nodes", "mcl_signs:standing_sign45" .. _name)
		doc.add_entry_alias("nodes", "mcl_signs:wall_sign", "nodes", "mcl_signs:standing_sign67_5" .. _name)
	end

	--register standing sign's rotation_levels
	table.insert(mcl_signs.standing_rotation_levels, { "mcl_signs:standing_sign22_5" .. _name, 1 })
	table.insert(mcl_signs.standing_rotation_levels, { "mcl_signs:standing_sign45" .. _name, 2 })
	table.insert(mcl_signs.standing_rotation_levels, { "mcl_signs:standing_sign67_5" .. _name, 3 })

	-- register as unpushable
	if minetest.get_modpath("mesecons_mvps") then
		mesecon.register_mvps_stopper("mcl_signs:wall_sign" .. _name)
		mesecon.register_mvps_stopper("mcl_signs:standing_sign" .. _name)
		mesecon.register_mvps_stopper("mcl_signs:standing_sign22_5" .. _name)
		mesecon.register_mvps_stopper("mcl_signs:standing_sign45" .. _name)
		mesecon.register_mvps_stopper("mcl_signs:standing_sign67_5" .. _name)
	end
end

--- The same as register_sign, except caller defines the textures. Note, there is a greyscale version of the sign,
--- called "mcl_signs_default_sign_greyscale.png" and "mcl_signs_sign_greyscale.png" for optional use in the textures directory.
---
--- modname: optional (pass "" or "false" to ignore), for use with other mods to
--- allow the creation of a sign from the mod's wood (if installed).
---
--- _name: the sign's name suffix, such as "_dark" or "_red", etc., appended to "wall_sign" or "standing_sign"
---
--- tiles: the texture file to use for the sign.
---
--- color: color the texture file to use with this color. Use white (#FFFFFF) to negate the color,
--- and just use the texture as is
---
--- inventory_image: the texture file to use for the sign's display in inventory.
---
--- wield_image: the texture file to use for the sign's weilded (in hand) object.
---
--- inventory_image: the image used for in-inventory and in hand.
---
--- ttsign: the tool tip of the sign that gets translated. Shown when the mouse hovers the inventory sign.
--- For example: the basic, default oak (wood) sign is just "Sign"; and a spruce sign would be "Spruce Sign"
function mcl_signs.register_sign_custom (modname, _name, tiles, color, inventory_image, wield_image, ttsign)
	local mod_name_pass = false
	if modname ~= "" and modname ~= "false" then
		if minetest.get_modpath(modname) then
			mod_name_pass = true
		end
		if mod_name_pass == false then
			return
		end
	end
	local new_sign = {}

	new_sign = table.copy(mcl_signs.wall_standard)

	if not color or color == nil then
		new_sign.wield_image = wield_image
		new_sign.tiles = { tiles }
		new_sign.inventory_image = inventory_image
	else
		new_sign.wield_image = "(" .. wield_image .. "^[multiply:" .. color .. ")"
		new_sign.tiles = { "(" .. tiles .. "^[multiply:" .. color .. ")" }
		new_sign.inventory_image = "(" .. inventory_image .. "^[multiply:" .. color .. ")"
	end

	new_sign.description = ttsign

	-- currently have to do this, because of how the base node placement works.
	new_sign.on_place = function(itemstack, placer, pointed_thing)
		local above = pointed_thing.above
		local under = pointed_thing.under

		-- Use pointed node's on_rightclick function first, if present
		local node_under = minetest.get_node(under)
		if placer and not placer:get_player_control().sneak then
			if minetest.registered_nodes[node_under.name] and minetest.registered_nodes[node_under.name].on_rightclick then
				return minetest.registered_nodes[node_under.name].on_rightclick(under, node_under, placer, itemstack) or itemstack
			end
		end

		local dir = vector.subtract(under, above)

		-- Only build when it's legal
		local abovenodedef = minetest.registered_nodes[minetest.get_node(above).name]
		if not abovenodedef or abovenodedef.buildable_to == false then
			return itemstack
		end

		local wdir = minetest.dir_to_wallmounted(dir)
		local fdir = minetest.dir_to_facedir(dir)

		local sign_info
		local nodeitem = ItemStack(itemstack)
		-- Ceiling
		if wdir == 0 then
			--how would you add sign to ceiling?
			return itemstack
			-- Floor
		elseif wdir == 1 then
			-- Standing sign

			-- Determine the sign rotation based on player's yaw
			local yaw = pi * 2 - placer:get_look_horizontal()

			-- Select one of 16 possible rotations (0-15)
			local rotation_level = mcl_signs:round((yaw / (pi * 2)) * 16)

			if rotation_level > 15 then
				rotation_level = 0
			elseif rotation_level < 0 then
				rotation_level = 15
			end

			-- The actual rotation is a combination of predefined mesh and facedir (see node definition)
			if rotation_level % 4 == 0 then
				nodeitem:set_name("mcl_signs:standing_sign" .. _name)
			elseif rotation_level % 4 == 1 then
				nodeitem:set_name("mcl_signs:standing_sign22_5" .. _name)
			elseif rotation_level % 4 == 2 then
				nodeitem:set_name("mcl_signs:standing_sign45" .. _name)
			elseif rotation_level % 4 == 3 then
				nodeitem:set_name("mcl_signs:standing_sign67_5" .. _name)
			end
			fdir = math.floor(rotation_level / 4)

			-- Place the node!
			local _, success = minetest.item_place_node(nodeitem, placer, pointed_thing, fdir)
			if not success then
				return itemstack
			end
			if not minetest.is_creative_enabled(placer:get_player_name()) then
				itemstack:take_item()
			end
			sign_info = mcl_signs.signtext_info_standing[rotation_level + 1]
			-- Side
		else
			-- Wall sign
			local _, success = minetest.item_place_node(itemstack, placer, pointed_thing, wdir)
			if not success then
				return itemstack
			end
			sign_info = mcl_signs.signtext_info_wall[fdir + 1]
		end

		-- Determine spawn position of entity
		local place_pos
		if minetest.registered_nodes[node_under.name].buildable_to then
			place_pos = under
		else
			place_pos = above
		end

		local text_entity = minetest.add_entity({
			x = place_pos.x + sign_info.delta.x,
			y = place_pos.y + sign_info.delta.y,
			z = place_pos.z + sign_info.delta.z }, "mcl_signs:text")
		text_entity:set_yaw(sign_info.yaw)
		text_entity:get_luaentity()._signnodename = nodeitem:get_name()

		minetest.sound_play({ name = "default_place_node_hard", gain = 1.0 }, { pos = place_pos }, true)

		mcl_signs:show_formspec(placer, place_pos, "")
		return itemstack
	end
	minetest.register_node(":mcl_signs:wall_sign" .. _name, new_sign)
	update_sign_registry("wall", "mcl_signs:wall_sign" .. _name)

	-- standing sign base.
	local new_sign_standing = {}
	new_sign_standing = table.copy(mcl_signs.standing_standard)
	new_sign_standing.drop = "mcl_signs:wall_sign" .. _name
	if not color or color == nil then
		new_sign_standing.wield_image = wield_image
		new_sign_standing.tiles = { tiles }
		new_sign_standing.inventory_image = inventory_image
	else
		new_sign_standing.wield_image = "(" .. wield_image .. "^[multiply:" .. color .. ")"
		new_sign_standing.tiles = { "(" .. tiles .. "^[multiply:" .. color .. ")" }
		new_sign_standing.inventory_image = "(" .. inventory_image .. "^[multiply:" .. color .. ")"
	end
	new_sign_standing.on_rotate = function(pos, node, user, mode)
		if mode == screwdriver.ROTATE_FACE then
			node.name = "mcl_signs:standing_sign22_5" .. _name
			minetest.swap_node(pos, node)
		elseif mode == screwdriver.ROTATE_AXIS then
			return false
		end
		mcl_signs:update_sign(pos, nil, nil, true)
		return true
	end,
	minetest.register_node(":mcl_signs:standing_sign" .. _name, new_sign_standing)
	update_sign_registry("standing", "mcl_signs:standing_sign" .. _name)

	-- 22.5°
	local ssign22_5d = table.copy(new_sign_standing)
	ssign22_5d.mesh = "mcl_signs_sign22.5.obj"
	ssign22_5d.on_rotate = function(pos, node, user, mode)
		if mode == screwdriver.ROTATE_FACE then
			node.name = "mcl_signs:standing_sign45" .. _name
			minetest.swap_node(pos, node)
		elseif mode == screwdriver.ROTATE_AXIS then
			return false
		end
		mcl_signs:update_sign(pos, nil, nil, true)
		return true
	end
	minetest.register_node(":mcl_signs:standing_sign22_5" .. _name, ssign22_5d)
	update_sign_registry("standing", "mcl_signs:standing_sign22_5" .. _name)

	-- 45°
	local ssign45d = table.copy(new_sign_standing)
	ssign45d.mesh = "mcl_signs_sign45.obj"
	ssign45d.on_rotate = function(pos, node, user, mode)
		if mode == screwdriver.ROTATE_FACE then
			node.name = "mcl_signs:standing_sign67_5" .. _name
			minetest.swap_node(pos, node)
		elseif mode == screwdriver.ROTATE_AXIS then
			return false
		end
		mcl_signs:update_sign(pos, nil, nil, true)
		return true
	end
	minetest.register_node(":mcl_signs:standing_sign45" .. _name, ssign45d)
	update_sign_registry("standing", "mcl_signs:standing_sign45" .. _name)

	-- 67.5°
	local ssign67_5d = table.copy(new_sign_standing)
	ssign67_5d.mesh = "mcl_signs_sign67.5.obj"
	ssign67_5d.on_rotate = function(pos, node, user, mode)
		if mode == screwdriver.ROTATE_FACE then
			node.name = "mcl_signs:standing_sign" .. _name
			node.param2 = (node.param2 + 1) % 4
			minetest.swap_node(pos, node)
		elseif mode == screwdriver.ROTATE_AXIS then
			return false
		end
		mcl_signs:update_sign(pos, nil, nil, true)
		return true
	end
	minetest.register_node(":mcl_signs:standing_sign67_5" .. _name, ssign67_5d)
	update_sign_registry("standing", "mcl_signs:standing_sign67_5" .. _name)

	-- register Doc entry
	if minetest.get_modpath("doc") then
		doc.add_entry_alias("nodes", "mcl_signs:wall_sign", "nodes", "mcl_signs:wall_sign" .. _name)
		doc.add_entry_alias("nodes", "mcl_signs:wall_sign", "nodes", "mcl_signs:standing_sign" .. _name)
		doc.add_entry_alias("nodes", "mcl_signs:wall_sign", "nodes", "mcl_signs:standing_sign22_5" .. _name)
		doc.add_entry_alias("nodes", "mcl_signs:wall_sign", "nodes", "mcl_signs:standing_sign45" .. _name)
		doc.add_entry_alias("nodes", "mcl_signs:wall_sign", "nodes", "mcl_signs:standing_sign67_5" .. _name)
	end

	--register standing sign's rotation_levels
	table.insert(mcl_signs.standing_rotation_levels, { "mcl_signs:standing_sign22_5" .. _name, 1 })
	table.insert(mcl_signs.standing_rotation_levels, { "mcl_signs:standing_sign45" .. _name, 2 })
	table.insert(mcl_signs.standing_rotation_levels, { "mcl_signs:standing_sign67_5" .. _name, 3 })

	-- register as unpushable
	if minetest.get_modpath("mesecons_mvps") then
		mesecon.register_mvps_stopper("mcl_signs:wall_sign" .. _name)
		mesecon.register_mvps_stopper("mcl_signs:standing_sign" .. _name)
		mesecon.register_mvps_stopper("mcl_signs:standing_sign22_5" .. _name)
		mesecon.register_mvps_stopper("mcl_signs:standing_sign45" .. _name)
		mesecon.register_mvps_stopper("mcl_signs:standing_sign67_5" .. _name)
	end
end

--- Override an existing sign, tint the textures, and gives it an unique node name. Creates both wall and standing signs.
--- modname: optional (pass "" or "false" to ignore), for use with other mods to
--- allow the creation of a sign from the mod's wood (if installed).
---
--- color: the color code to color the base sign textures. must be a valid html color code.
---
--- _name: the sign's name suffix, such as "_dark" or "_red", etc., appended to "wall_sign" or "standing_sign"
---
--- ttsign: the tool tip of the sign that gets translated. Shown when the mouse hovers the inventory sign.
--- For example: the basic, default oak (wood) sign is just "Sign"; and a spruce sign would be "Spruce Sign"
function mcl_signs.reregister_sign (modname, color, _name, ttsign)
	local mod_name_pass = false
	if modname ~= "" and modname ~= "false" then
		if minetest.get_modpath(modname) then
			mod_name_pass = true
		end
		if mod_name_pass == false then
			return
		end
	end
	local new_sign = {}

	if color == nil or color == "" then
		color = "#FFFFFF"
	end

	new_sign = table.copy(mcl_signs.wall_standard)
	new_sign.description = ttsign

	new_sign.wield_image = "(mcl_signs_default_sign.png^[multiply:" .. color .. ")"
	new_sign.tiles = { "(mcl_signs_sign.png^[multiply:" .. color .. ")" }
	new_sign.inventory_image = "(mcl_signs_default_sign.png^[multiply:" .. color .. ")"

	-- currently have to do this, because of how the base node placement works.
	new_sign.on_place = function(itemstack, placer, pointed_thing)
		local above = pointed_thing.above
		local under = pointed_thing.under

		-- Use pointed node's on_rightclick function first, if present
		local node_under = minetest.get_node(under)
		if placer and not placer:get_player_control().sneak then
			if minetest.registered_nodes[node_under.name] and minetest.registered_nodes[node_under.name].on_rightclick then
				return minetest.registered_nodes[node_under.name].on_rightclick(under, node_under, placer, itemstack) or itemstack
			end
		end

		local dir = vector.subtract(under, above)

		-- Only build when it's legal
		local abovenodedef = minetest.registered_nodes[minetest.get_node(above).name]
		if not abovenodedef or abovenodedef.buildable_to == false then
			return itemstack
		end

		local wdir = minetest.dir_to_wallmounted(dir)
		local fdir = minetest.dir_to_facedir(dir)

		local sign_info
		local nodeitem = ItemStack(itemstack)
		-- Ceiling
		if wdir == 0 then
			--how would you add sign to ceiling?
			return itemstack
			-- Floor
		elseif wdir == 1 then
			-- Standing sign

			-- Determine the sign rotation based on player's yaw
			local yaw = pi * 2 - placer:get_look_horizontal()

			-- Select one of 16 possible rotations (0-15)
			local rotation_level = mcl_signs:round((yaw / (pi * 2)) * 16)

			if rotation_level > 15 then
				rotation_level = 0
			elseif rotation_level < 0 then
				rotation_level = 15
			end

			-- The actual rotation is a combination of predefined mesh and facedir (see node definition)
			if rotation_level % 4 == 0 then
				nodeitem:set_name("mcl_signs:standing_sign" .. _name)
			elseif rotation_level % 4 == 1 then
				nodeitem:set_name("mcl_signs:standing_sign22_5" .. _name)
			elseif rotation_level % 4 == 2 then
				nodeitem:set_name("mcl_signs:standing_sign45" .. _name)
			elseif rotation_level % 4 == 3 then
				nodeitem:set_name("mcl_signs:standing_sign67_5" .. _name)
			end
			fdir = math.floor(rotation_level / 4)

			-- Place the node!
			local _, success = minetest.item_place_node(nodeitem, placer, pointed_thing, fdir)
			if not success then
				return itemstack
			end
			if not minetest.is_creative_enabled(placer:get_player_name()) then
				itemstack:take_item()
			end
			sign_info = mcl_signs.signtext_info_standing[rotation_level + 1]
			-- Side
		else
			-- Wall sign
			local _, success = minetest.item_place_node(itemstack, placer, pointed_thing, wdir)
			if not success then
				return itemstack
			end
			sign_info = mcl_signs.signtext_info_wall[fdir + 1]
		end

		-- Determine spawn position of entity
		local place_pos
		if minetest.registered_nodes[node_under.name].buildable_to then
			place_pos = under
		else
			place_pos = above
		end

		if DEBUG then
			minetest.log("action", "[mcl_signs] Register_Sign::Placed position:" .. dump(place_pos) .. "\nSign_info: " .. dump(sign_info))
		end

		local text_entity = minetest.add_entity({
			x = place_pos.x + sign_info.delta.x,
			y = place_pos.y + sign_info.delta.y,
			z = place_pos.z + sign_info.delta.z }, "mcl_signs:text")
		text_entity:set_yaw(sign_info.yaw)
		text_entity:get_luaentity()._signnodename = nodeitem:get_name()

		minetest.sound_play({ name = "default_place_node_hard", gain = 1.0 }, { pos = place_pos }, true)

		mcl_signs:show_formspec(placer, place_pos, "")
		return itemstack
	end

	minetest.override_item("mcl_signs:wall_sign" .. _name, new_sign)
	update_sign_registry("wall", "mcl_signs:wall_sign" .. _name)

	-- debug step
	if DEBUG then
		minetest.log("action", "[mcl_signs] Registered: mcl_signs:wall_sign" .. _name .. color .. "\n" .. dump(new_sign))
		minetest.log("action", "[mcl_signs] mcl_signs:wall_sign_standard\n" .. dump(mcl_signs.wall_standard))
	end

	-- standing sign base.
	local new_sign_standing = {}
	new_sign_standing = table.copy(mcl_signs.standing_standard)
	new_sign_standing.drop = "mcl_signs:wall_sign" .. _name
	new_sign_standing.wield_image = "(mcl_signs_default_sign.png^[multiply:" .. color .. ")"
	new_sign_standing.tiles = { "(mcl_signs_sign.png^[multiply:" .. color .. ")" }
	new_sign_standing.inventory_image = "(mcl_signs_default_sign.png^[multiply:" .. color .. ")"
	new_sign_standing.on_rotate = function(pos, node, user, mode)
		if mode == screwdriver.ROTATE_FACE then
			node.name = "mcl_signs:standing_sign22_5" .. _name
			minetest.swap_node(pos, node)
		elseif mode == screwdriver.ROTATE_AXIS then
			return false
		end
		mcl_signs:update_sign(pos, nil, nil, true)
		return true
	end,
	minetest.override_item("mcl_signs:standing_sign" .. _name, new_sign_standing)
	update_sign_registry("standing", "mcl_signs:standing_sign" .. _name)
	-- debug step
	if DEBUG then
		minetest.log("action", "[mcl_signs] Registered: mcl_signs:standing_sign" .. _name .. color .. "\n" .. dump(new_sign_standing))
	end

	-- 22.5°
	local ssign22_5d = table.copy(new_sign_standing)
	ssign22_5d.mesh = "mcl_signs_sign22.5.obj"
	ssign22_5d.on_rotate = function(pos, node, user, mode)
		if mode == screwdriver.ROTATE_FACE then
			node.name = "mcl_signs:standing_sign45" .. _name
			minetest.swap_node(pos, node)
		elseif mode == screwdriver.ROTATE_AXIS then
			return false
		end
		mcl_signs:update_sign(pos, nil, nil, true)
		return true
	end
	minetest.override_item("mcl_signs:standing_sign22_5" .. _name, ssign22_5d)
	update_sign_registry("standing", "mcl_signs:standing_sign22_5" .. _name)

	-- 45°
	local ssign45d = table.copy(new_sign_standing)
	ssign45d.mesh = "mcl_signs_sign45.obj"
	ssign45d.on_rotate = function(pos, node, user, mode)
		if mode == screwdriver.ROTATE_FACE then
			node.name = "mcl_signs:standing_sign67_5" .. _name
			minetest.swap_node(pos, node)
		elseif mode == screwdriver.ROTATE_AXIS then
			return false
		end
		mcl_signs:update_sign(pos, nil, nil, true)
		return true
	end
	minetest.override_item("mcl_signs:standing_sign45" .. _name, ssign45d)
	update_sign_registry("standing", "mcl_signs:standing_sign45" .. _name)

	-- 67.5°
	local ssign67_5d = table.copy(new_sign_standing)
	ssign67_5d.mesh = "mcl_signs_sign67.5.obj"
	ssign67_5d.on_rotate = function(pos, node, user, mode)
		if mode == screwdriver.ROTATE_FACE then
			node.name = "mcl_signs:standing_sign" .. _name
			node.param2 = (node.param2 + 1) % 4
			minetest.swap_node(pos, node)
		elseif mode == screwdriver.ROTATE_AXIS then
			return false
		end
		mcl_signs:update_sign(pos, nil, nil, true)
		return true
	end
	minetest.override_item("mcl_signs:standing_sign67_5" .. _name, ssign67_5d)
	update_sign_registry("standing", "mcl_signs:standing_sign67_5" .. _name)

	-- register Doc entry
	if minetest.get_modpath("doc") then
		doc.add_entry_alias("nodes", "mcl_signs:wall_sign", "nodes", "mcl_signs:wall_sign" .. _name)
		doc.add_entry_alias("nodes", "mcl_signs:wall_sign", "nodes", "mcl_signs:standing_sign" .. _name)
		doc.add_entry_alias("nodes", "mcl_signs:wall_sign", "nodes", "mcl_signs:standing_sign22_5" .. _name)
		doc.add_entry_alias("nodes", "mcl_signs:wall_sign", "nodes", "mcl_signs:standing_sign45" .. _name)
		doc.add_entry_alias("nodes", "mcl_signs:wall_sign", "nodes", "mcl_signs:standing_sign67_5" .. _name)
	end

	--register standing sign's rotation_levels
	table.insert(mcl_signs.standing_rotation_levels, { "mcl_signs:standing_sign22_5" .. _name, 1 })
	table.insert(mcl_signs.standing_rotation_levels, { "mcl_signs:standing_sign45" .. _name, 2 })
	table.insert(mcl_signs.standing_rotation_levels, { "mcl_signs:standing_sign67_5" .. _name, 3 })

	-- register as unpushable
	if minetest.get_modpath("mesecons_mvps") then
		mesecon.register_mvps_stopper("mcl_signs:wall_sign" .. _name)
		mesecon.register_mvps_stopper("mcl_signs:standing_sign" .. _name)
		mesecon.register_mvps_stopper("mcl_signs:standing_sign22_5" .. _name)
		mesecon.register_mvps_stopper("mcl_signs:standing_sign45" .. _name)
		mesecon.register_mvps_stopper("mcl_signs:standing_sign67_5" .. _name)
	end
end

--- The same as reregister_sign, except caller defines the textures. Note, there is a greyscale version of the sign,
--- called "mcl_signs_default_sign_greyscale.png" and "mcl_signs_sign_greyscale.png" for optional use in the textures directory.
---
--- modname: optional (pass "" or "false" to ignore), for use with other mods to
--- allow the creation of a sign from the mod's wood (if installed).
---
--- _name: the sign's name suffix, such as "_dark" or "_red", etc., appended to "wall_sign" or "standing_sign"
---
--- tiles: the texture file to use for the sign.
---
--- color: color the texture file to use with this color. Use white (#FFFFFF) to negate the color,
--- and just use the texture as is
---
--- inventory_image: the texture file to use for the sign's display in inventory.
---
--- wield_image: the texture file to use for the sign's weilded (in hand) object.
---
--- inventory_image: the image used for in-inventory and in hand.
---
--- ttsign: the tool tip of the sign that gets translated. Shown when the mouse hovers the inventory sign.
--- For example: the basic, default oak (wood) sign is just "Sign"; and a spruce sign would be "Spruce Sign"
function mcl_signs.reregister_sign_custom (modname, _name, tiles, color, inventory_image, wield_image, ttsign)
	local mod_name_pass = false
	if modname ~= "" and modname ~= "false" then
		if minetest.get_modpath(modname) then
			mod_name_pass = true
		end
		if mod_name_pass == false then
			return
		end
	end
	local new_sign = {}

	new_sign = table.copy(mcl_signs.wall_standard)

	if not color or color == nil then
		new_sign.wield_image = wield_image
		new_sign.tiles = { tiles }
		new_sign.inventory_image = inventory_image
	else
		new_sign.wield_image = "(" .. wield_image .. "^[multiply:" .. color .. ")"
		new_sign.tiles = { "(" .. tiles .. "^[multiply:" .. color .. ")" }
		new_sign.inventory_image = "(" .. inventory_image .. "^[multiply:" .. color .. ")"
	end
	new_sign.description = ttsign
	-- currently have to do this, because of how the base node placement works.
	new_sign.on_place = function(itemstack, placer, pointed_thing)
		local above = pointed_thing.above
		local under = pointed_thing.under

		-- Use pointed node's on_rightclick function first, if present
		local node_under = minetest.get_node(under)
		if placer and not placer:get_player_control().sneak then
			if minetest.registered_nodes[node_under.name] and minetest.registered_nodes[node_under.name].on_rightclick then
				return minetest.registered_nodes[node_under.name].on_rightclick(under, node_under, placer, itemstack) or itemstack
			end
		end

		local dir = vector.subtract(under, above)

		-- Only build when it's legal
		local abovenodedef = minetest.registered_nodes[minetest.get_node(above).name]
		if not abovenodedef or abovenodedef.buildable_to == false then
			return itemstack
		end

		local wdir = minetest.dir_to_wallmounted(dir)
		local fdir = minetest.dir_to_facedir(dir)

		local sign_info
		local nodeitem = ItemStack(itemstack)
		-- Ceiling
		if wdir == 0 then
			--how would you add sign to ceiling?
			return itemstack
			-- Floor
		elseif wdir == 1 then
			-- Standing sign

			-- Determine the sign rotation based on player's yaw
			local yaw = pi * 2 - placer:get_look_horizontal()

			-- Select one of 16 possible rotations (0-15)
			local rotation_level = mcl_signs:round((yaw / (pi * 2)) * 16)

			if rotation_level > 15 then
				rotation_level = 0
			elseif rotation_level < 0 then
				rotation_level = 15
			end

			-- The actual rotation is a combination of predefined mesh and facedir (see node definition)
			if rotation_level % 4 == 0 then
				nodeitem:set_name("mcl_signs:standing_sign" .. _name)
			elseif rotation_level % 4 == 1 then
				nodeitem:set_name("mcl_signs:standing_sign22_5" .. _name)
			elseif rotation_level % 4 == 2 then
				nodeitem:set_name("mcl_signs:standing_sign45" .. _name)
			elseif rotation_level % 4 == 3 then
				nodeitem:set_name("mcl_signs:standing_sign67_5" .. _name)
			end
			fdir = math.floor(rotation_level / 4)

			-- Place the node!
			local _, success = minetest.item_place_node(nodeitem, placer, pointed_thing, fdir)
			if not success then
				return itemstack
			end
			if not minetest.is_creative_enabled(placer:get_player_name()) then
				itemstack:take_item()
			end
			sign_info = mcl_signs.signtext_info_standing[rotation_level + 1]
			-- Side
		else
			-- Wall sign
			local _, success = minetest.item_place_node(itemstack, placer, pointed_thing, wdir)
			if not success then
				return itemstack
			end
			sign_info = mcl_signs.signtext_info_wall[fdir + 1]
		end

		-- Determine spawn position of entity
		local place_pos
		if minetest.registered_nodes[node_under.name].buildable_to then
			place_pos = under
		else
			place_pos = above
		end

		local text_entity = minetest.add_entity({
			x = place_pos.x + sign_info.delta.x,
			y = place_pos.y + sign_info.delta.y,
			z = place_pos.z + sign_info.delta.z }, "mcl_signs:text")
		text_entity:set_yaw(sign_info.yaw)
		text_entity:get_luaentity()._signnodename = nodeitem:get_name()

		minetest.sound_play({ name = "default_place_node_hard", gain = 1.0 }, { pos = place_pos }, true)

		mcl_signs:show_formspec(placer, place_pos, "")
		return itemstack
	end
	minetest.override_item("mcl_signs:wall_sign" .. _name, new_sign)
	update_sign_registry("wall", "mcl_signs:wall_sign" .. _name)

	-- standing sign base.
	local new_sign_standing = {}
	new_sign_standing = table.copy(mcl_signs.standing_standard)
	if not color or color == nil then
		new_sign_standing.wield_image = wield_image
		new_sign_standing.tiles = { tiles }
		new_sign_standing.inventory_image = inventory_image
	else
		new_sign_standing.wield_image = "(" .. wield_image .. "^[multiply:" .. color .. ")"
		new_sign_standing.tiles = { "(" .. tiles .. "^[multiply:" .. color .. ")" }
		new_sign_standing.inventory_image = "(" .. inventory_image .. "^[multiply:" .. color .. ")"
	end
	new_sign_standing.on_rotate = function(pos, node, user, mode)
		if mode == screwdriver.ROTATE_FACE then
			node.name = "mcl_signs:standing_sign22_5" .. _name
			minetest.swap_node(pos, node)
		elseif mode == screwdriver.ROTATE_AXIS then
			return false
		end
		mcl_signs:update_sign(pos, nil, nil, true)
		return true
	end,
	minetest.override_item("mcl_signs:standing_sign" .. _name, new_sign_standing)
	update_sign_registry("standing", "mcl_signs:standing_sign" .. _name)

	-- 22.5°
	local ssign22_5d = table.copy(new_sign_standing)
	ssign22_5d.mesh = "mcl_signs_sign22.5.obj"
	ssign22_5d.on_rotate = function(pos, node, user, mode)
		if mode == screwdriver.ROTATE_FACE then
			node.name = "mcl_signs:standing_sign45" .. _name
			minetest.swap_node(pos, node)
		elseif mode == screwdriver.ROTATE_AXIS then
			return false
		end
		mcl_signs:update_sign(pos, nil, nil, true)
		return true
	end
	minetest.override_item("mcl_signs:standing_sign22_5" .. _name, ssign22_5d)
	update_sign_registry("standing", "mcl_signs:standing_sign22_5" .. _name)

	-- 45°
	local ssign45d = table.copy(new_sign_standing)
	ssign45d.mesh = "mcl_signs_sign45.obj"
	ssign45d.on_rotate = function(pos, node, user, mode)
		if mode == screwdriver.ROTATE_FACE then
			node.name = "mcl_signs:standing_sign67_5" .. _name
			minetest.swap_node(pos, node)
		elseif mode == screwdriver.ROTATE_AXIS then
			return false
		end
		mcl_signs:update_sign(pos, nil, nil, true)
		return true
	end
	minetest.override_item("mcl_signs:standing_sign45" .. _name, ssign45d)
	update_sign_registry("standing", "mcl_signs:standing_sign45" .. _name)

	-- 67.5°
	local ssign67_5d = table.copy(new_sign_standing)
	ssign67_5d.mesh = "mcl_signs_sign67.5.obj"
	ssign67_5d.on_rotate = function(pos, node, user, mode)
		if mode == screwdriver.ROTATE_FACE then
			node.name = "mcl_signs:standing_sign" .. _name
			node.param2 = (node.param2 + 1) % 4
			minetest.swap_node(pos, node)
		elseif mode == screwdriver.ROTATE_AXIS then
			return false
		end
		mcl_signs:update_sign(pos, nil, nil, true)
		return true
	end
	minetest.override_item("mcl_signs:standing_sign67_5" .. _name, ssign67_5d)
	update_sign_registry("standing", "mcl_signs:standing_sign67_5" .. _name)

	-- register Doc entry
	if minetest.get_modpath("doc") then
		doc.add_entry_alias("nodes", "mcl_signs:wall_sign", "nodes", "mcl_signs:wall_sign" .. _name)
		doc.add_entry_alias("nodes", "mcl_signs:wall_sign", "nodes", "mcl_signs:standing_sign" .. _name)
		doc.add_entry_alias("nodes", "mcl_signs:wall_sign", "nodes", "mcl_signs:standing_sign22_5" .. _name)
		doc.add_entry_alias("nodes", "mcl_signs:wall_sign", "nodes", "mcl_signs:standing_sign45" .. _name)
		doc.add_entry_alias("nodes", "mcl_signs:wall_sign", "nodes", "mcl_signs:standing_sign67_5" .. _name)
	end

	--register standing sign's rotation_levels
	table.insert(mcl_signs.standing_rotation_levels, { "mcl_signs:standing_sign22_5" .. _name, 1 })
	table.insert(mcl_signs.standing_rotation_levels, { "mcl_signs:standing_sign45" .. _name, 2 })
	table.insert(mcl_signs.standing_rotation_levels, { "mcl_signs:standing_sign67_5" .. _name, 3 })

	-- register as unpushable
	if minetest.get_modpath("mesecons_mvps") then
		mesecon.register_mvps_stopper("mcl_signs:wall_sign" .. _name)
		mesecon.register_mvps_stopper("mcl_signs:standing_sign" .. _name)
		mesecon.register_mvps_stopper("mcl_signs:standing_sign22_5" .. _name)
		mesecon.register_mvps_stopper("mcl_signs:standing_sign45" .. _name)
		mesecon.register_mvps_stopper("mcl_signs:standing_sign67_5" .. _name)
	end
end

--- Usage: Call this with the mod's name, the wood's item string (for the planks), and with the sign's suffix.
--- Registers the crafting recipe for that sign. for every registered sign, call this function to register the
--- standard recipe for the sign. Otherwise, you have to do your own register craft call.
---
--- modname: optional (pass "" or "false" to ignore), for use with other mods to
--- allow the creation of a sign from the mod's wood (if installed). Example: "mcl_core".
---
--- wood_item_string: example: "mcl_core:wood" or "mcl_core:sprucewood"
---
--- _name: the sign's name suffix, such as "_dark" or "_red", etc., appended to "wall_sign" or "standing_sign"
function mcl_signs.register_sign_craft(modname, wood_item_string, _name)
	local mod_name_pass = false
	if modname ~= "" and modname ~= "false" then
		if minetest.get_modpath(modname) then
			mod_name_pass = true
		end
		if mod_name_pass == false then
			return
		end
	end

	minetest.register_craft({
		type = "fuel",
		recipe = "mcl_signs:wall_sign" .. _name,
		burntime = 10,
	})

	-- debug step
	if DEBUG then
		minetest.log("action", "[mcl_signs] Register Sign Crafts: \n" .. modname .. "\n" .. wood_item_string .. "\n" .. _name)
	end

	-- register crafts (actual recipe)
	if minetest.get_modpath(modname) then

		local itemstring = "mcl_signs:wall_sign"

		minetest.register_craft({
			output = itemstring .. _name .. " 3",
			recipe = {
				{ wood_item_string, wood_item_string, wood_item_string },
				{ wood_item_string, wood_item_string, wood_item_string },
				{ "", "mcl_core:stick", "" },
			},
		})
	end
end

function mcl_signs.register_hanging_sign_craft(modname, wood_item_string, _name)
	local mod_name_pass = false
	if modname ~= "" and modname ~= "false" then
		if minetest.get_modpath(modname) then
			mod_name_pass = true
		end
		if mod_name_pass == false then
			return
		end
	end

	minetest.register_craft({
		type = "fuel",
		recipe = ":mcl_signs:wall_sign" .. _name,
		burntime = 10,
	})

	-- debug step
	if DEBUG then
		minetest.log("action", "[mcl_signs] Register Sign Crafts: \n" .. modname .. "\n" .. wood_item_string .. "\n" .. _name)
	end

	-- register crafts (actual recipe)
	if minetest.get_modpath(modname) then

		local itemstring = ":mcl_signs:hanging_sign"
		local quantity = "6"

		local bamboo = string.find(wood_item_string, "bamboo")
		if bamboo then
			quantity = "2"
		end
		minetest.register_craft({
			output = itemstring .. _name .. " " .. quantity,
			recipe = {
				{ "mcl_lanterns:chain", "", "mcl_lanterns:chain" },
				{ wood_item_string, wood_item_string, wood_item_string },
				{ wood_item_string, wood_item_string, wood_item_string },
			},
		})
	end
end

-- Helper functions
local function string_to_array(str)
	local string_table = {}
	for i = 1, string.len(str) do
		table.insert(string_table, string.sub(str, i, i))
	end
	return string_table
end

local function string_to_line_array(str)
	local linechar_table = {}
	local current = 1
	local linechar = 1
	linechar_table[1] = ""
	for _, char in ipairs(string_to_array(str)) do
		-- New line
		if char == "\n" then
			current = current + 1
			linechar_table[current] = ""
			linechar = 1
		else
			linechar_table[current] = linechar_table[current] .. char
			linechar = linechar + 1
		end
	end
	return linechar_table
end

local function get_rotation_level(facedir, nodename)
	local nnames = mcl_signs.standing_rotation_levels -- functional copy... was easier this way. #LazyAF :P

	local rl
	local offset = 0
	for x = 1, #nnames do
		if nnames[x][1] == nodename then
			offset = nnames[x][2]
			break
		end
	end
	rl = facedir * 4 + offset
	if DEBUG then
		minetest.log("action", "[mcl_signs] GetRotationLevel: NodeName: " .. nodename .. " RL value: " .. rl)
	end
	return rl
end

function mcl_signs:round(num, idp)
	local mult = 10 ^ (idp or 0)
	return math.floor(num * mult + 0.5) / mult
end

function mcl_signs:get_color_for_sign(item_name)

	for d = 1, #Dyes_table do
		if Dyes_table[d][1] == item_name then
			return Dyes_table[d][2]
		end
	end
	return "false"
end

function mcl_signs:color_sign (pos, text_color)

	local success = mcl_signs:update_sign(pos, nil, nil, true, text_color)

	-- debug step
	local meta = minetest.get_meta(pos)
	if not meta then
		minetest.log("error", "[mcl_signs] Sign Color Fail - Metadata.")

		return false
	end
	if DEBUG then
		minetest.log("verbose", "[mcl_signs] Post-Sign Color: " .. meta:get_string("mcl_signs:text_color") .. " " .. meta:get_string("mcl_signs:glowing_sign") .. ".\n" .. dump(pos))
	end

	return success

end

function mcl_signs:glow_sign (pos, remove_glow)
	local success = true
	-- Get Meta Data for the sign.
	local meta = minetest.get_meta(pos)

	if not meta then
		return false
	end
	local text = meta:get_string("text")
	if text == nil then
		text = ""
	end

	-- we can't make the text glow if there isn't any text
	if text == "" then
		return false
	end

	if remove_glow == nil then
		remove_glow = false
	end

	-- set up text glow
	local objects = minetest.get_objects_inside_radius(pos, 0.5)
	local text_entity
	for _, v in ipairs(objects) do
		local ent = v:get_luaentity()
		if ent and ent.name == "mcl_signs:text" then
			text_entity = v
			break
		end
	end
	if remove_glow == true then
		text_entity:set_properties({
			glow = nil,
		})
		meta:set_string("mcl_signs:glowing_sign", "false")
	else
		text_entity:set_properties({
			glow = sign_glow,
		})
		meta:set_string("mcl_signs:glowing_sign", "true")
	end
	if not text_entity then
		return false
	end
	text_entity:get_luaentity()._glowing_sign = meta:get_string("mcl_signs:glowing_sign")

	-- debug step
	if DEBUG then
		minetest.log("verbose", "[mcl_signs] Post-Sign Glow: " .. meta:get_string("mcl_signs:text_color") .. " " .. meta:get_string("mcl_signs:glowing_sign") .. ".\n" .. dump(pos))
	end
	return success
end

function mcl_signs:create_lettering(text, signnodename, sign_color)
	if sign_color == nil then
		sign_color = mcl_colors.BLACK
	end
	local texture = mcl_signs:generate_texture(mcl_signs:create_lines(text), signnodename, sign_color)

	-- debug step
	if DEBUG then
		minetest.log("action", "[mcl_signs] Creating sign text; text:" .. text)
	end

	return texture
end

function mcl_signs:create_lines(text)
	local line_num = 1
	local text_table = {}
	for _, line in ipairs(string_to_line_array(text)) do
		if line_num > NUMBER_OF_LINES then
			break
		end
		table.insert(text_table, line)
		line_num = line_num + 1
	end
	return text_table
end

function mcl_signs:generate_line(s, ypos)
	local i = 1
	local parsed = {}
	local width = 0
	local chars = 0
	local printed_char_width = CHAR_WIDTH + 1
	while chars < LINE_LENGTH and i <= #s do
		local file
		-- Get and render character
		if charmap[s:sub(i, i)] then
			file = charmap[s:sub(i, i)]
			i = i + 1
		elseif i < #s and charmap[s:sub(i, i + 1)] then
			file = charmap[s:sub(i, i + 1)]
			i = i + 2
		else
			-- No character image found.
			-- Use replacement character:
			file = "_rc"
			i = i + 1
			if DEBUG then
				minetest.log("verbose", "[mcl_signs] Unknown symbol in '" .. s .. "' at " .. i)
			end
		end
		if file then
			width = width + printed_char_width
			table.insert(parsed, file)
			chars = chars + 1
		end
	end
	width = width - 1

	local texture = ""
	local xpos = math.floor((SIGN_WIDTH - width) / 2)

	for j = 1, #parsed do
		texture = texture .. ":" .. xpos .. "," .. ypos .. "=" .. parsed[j] .. ".png"
		xpos = xpos + printed_char_width
	end
	return texture
end

function mcl_signs:generate_texture(lines, signnodename, letter_color)
	local texture = "[combine:" .. SIGN_WIDTH .. "x" .. SIGN_WIDTH
	local ypos = 0

	-- Handle all of the dynamically created signs.
	for x = 1, #mcl_signs.registered_signs.wall_signs do
		if signnodename == mcl_signs.registered_signs.wall_signs[x] then
			ypos = 30
			break
		end
	end
	for x = 1, #mcl_signs.registered_signs.standing_signs do
		if signnodename == mcl_signs.registered_signs.standing_signs[x] then
			ypos = 0
			break
		end
	end
	-- for future inclusion, when the hanging sings are made.
	--[[
	for x = 1, #mcl_signs.registered_signs.hanging_signs do
		if signnodename == mcl_signs.registered_signs.hanging_signs[x] then
			ypos = 30
			break
		end
	end
	]]

	-- kept in for now, compatibility with existing hard coded signs. TODO: Remove after done with api.
	if signnodename == "mcl_signs:wall_sign" or signnodename == "mcl_signs:wall_sign_dark" then
		ypos = 30
	end

	-- debug step
	if DEBUG then
		minetest.log("action", "[mcl_signs] Generate_Texture::Debug_Data:\nSignNodeName: " .. dump(signnodename) .. "\nYPOS: " .. ypos)
	end

	for i = 1, #lines do
		texture = texture .. mcl_signs:generate_line(lines[i], ypos)
		ypos = ypos + LINE_HEIGHT
	end

	texture = "(" .. texture .. "^[multiply:" .. letter_color .. ")"
	return texture
end

function mcl_signs:get_wall_signtext_info(param2, nodename)
	local dir = minetest.wallmounted_to_dir(param2)
	if dir.x > 0 then
		return 2
	elseif dir.z > 0 then
		return 1
	elseif dir.x < 0 then
		return 4
	else
		return 3
	end
end

function mcl_signs:destruct_sign(pos)
	local objects = minetest.get_objects_inside_radius(pos, 0.5)
	for _, v in ipairs(objects) do
		local ent = v:get_luaentity()
		if ent and ent.name == "mcl_signs:text" then
			v:remove()
		end
	end
	local players = minetest.get_connected_players()
	for p = 1, #players do
		if vector.distance(players[p]:get_pos(), pos) <= 30 then
			minetest.close_formspec(players[p]:get_player_name(), "mcl_signs:set_text_" .. pos.x .. "_" .. pos.y .. "_" .. pos.z)
		end
	end
end

function mcl_signs:update_sign(pos, fields, sender, force_remove, text_color)
	-- Get Meta Data for the sign.
	local meta = minetest.get_meta(pos)

	if not meta then
		return false
	end
	local text = meta:get_string("text", "")
	if fields and fields.text then
		meta:set_string("text", fields.text)
		text = fields.text
	end
	if text == nil then
		text = ""
	end

	-- find text color.
	local sign_color

	if meta:get_string("mcl_signs:text_color") == "" then
		-- if no sign text color has been assigned, make it black.
		sign_color = mcl_colors.BLACK
		meta:set_string("mcl_signs:text_color", sign_color)
	else
		sign_color = meta:get_string("mcl_signs:text_color")
	end

	if text_color == nil or text == "" then
		text_color = "false"
	end

	if text_color == "false" then
		text_color = sign_color --if a new color hasn't been chosen, then keep the existing color.
	end

	-- find the sign's glow value
	local has_glow = false

	if meta:get_string("mcl_signs:glowing_sign") == "" or meta:get_string("mcl_signs:glowing_sign") == "false" then
		has_glow = false
		meta:set_string("mcl_signs:glowing_sign", "false")
	else
		has_glow = true
	end

	-- debug step
	if DEBUG then
		minetest.log("action", "[mcl_signs] Update_Signs: Pre-Sign Update: " .. sign_color .. " " .. meta:get_string("mcl_signs:glowing_sign") .. ".\n" .. dump(pos))
	end

	local sign_info
	local npos = minetest.get_node(pos)
	local npos_name = npos.name

	-- Handle all of the dynamically created signs.
	for x = 1, #mcl_signs.registered_signs.wall_signs do
		if npos_name == mcl_signs.registered_signs.wall_signs[x] then
			sign_info = mcl_signs.signtext_info_wall[mcl_signs:get_wall_signtext_info(npos.param2)]
			break
		end
	end
	for x = 1, #mcl_signs.registered_signs.standing_signs do
		if npos_name == mcl_signs.registered_signs.standing_signs[x] then
			sign_info = mcl_signs.signtext_info_standing[get_rotation_level(npos.param2, npos_name) + 1]
			break
		end
	end
	-- for future inclusion, when the hanging sings are made.
	--[[
	for x = 1, #mcl_signs.registered_signs.hanging_signs do
		if nn == mcl_signs.registered_signs.hanging_signs[x] then
			sign_info = mcl_signs.signtext_info_wall[mcl_signs:get_wall_signtext_info(n.param2)]
			break
		end
	end
	]]

	-- the following if..elseif..end block is here for compatibility with the old code. TODO: remove this block after the new api is complete.
	if npos_name == "mcl_signs:standing_sign_dark" or npos_name == "mcl_signs:standing_sign22_5_dark" or npos_name == "mcl_signs:standing_sign45_dark" or npos_name == "mcl_signs:standing_sign67_5_dark" then
		sign_info = mcl_signs.signtext_info_standing[get_rotation_level(npos.param2, npos_name) + 1]
	elseif npos_name == "mcl_signs:wall_sign_dark" then
		sign_info = mcl_signs.signtext_info_wall[mcl_signs:get_wall_signtext_info(npos.param2)]
	end
	if sign_info == nil then
		minetest.log("error", "[mcl_signs] Update_Signs: Missing sign_info!")
		return false
	end

	local text_entity
	text_entity = mcl_signs:get_text_entity(pos,force_remove)

	if not text_entity then
		if DEBUG then
			minetest.log("action", "[mcl_signs] Update_Sign: Text_Entity - does not exist, creating it now.")
		end
		text_entity = minetest.add_entity({
			x = pos.x + sign_info.delta.x,
			y = pos.y + sign_info.delta.y,
			z = pos.z + sign_info.delta.z }, "mcl_signs:text")

		if DEBUG then
			minetest.log("action", "[mcl_signs] Update_Sign: Placed position:" .. dump(pos) .. "\nSign_info: " .. dump(sign_info))
		end
	end
	text_entity:get_luaentity()._signnodename = npos_name

	-- set up special case: Dark Oak Sign. Dark Oak signs are soooo dark, they start off with white lettering.
	if npos_name == "mcl_signs:wall_sign_darkwood" or
			npos_name == "mcl_signs:standing_sign67_5_darkwood" or
			npos_name == "mcl_signs:standing_sign45_darkwood" or
			npos_name == "mcl_signs:standing_sign22_5_darkwood" or
			npos_name == "mcl_signs:standing_sign_darkwood"
	then
		if text_color == "#000000" then
			text_color = "#ffffff"
		end
	end

	-- Set the actual properties for the sign

	text_entity:set_properties({
		textures = { mcl_signs:create_lettering(text, npos_name, text_color) },
	})

	if has_glow then
		text_entity:set_properties({
			glow = sign_glow,
		})
	end

	text_entity:set_yaw(sign_info.yaw)
	if DEBUG then
		minetest.log("verbose", "[mcl_signs] Update_Sign: After texture recreation.")
		minetest.log("action", "[mcl_signs] Update_Sign: " .. npos_name .. "\nPlaced position:" .. dump(pos) .. "\nSign_info: " .. dump(sign_info))
	end

	-- save sign metadata.
	meta:set_string("mcl_signs:text_color", text_color)

	-- Moved timer stuff to here, to make sure that it's called and only has one set of code.
	local timer = minetest.get_node_timer(pos)
	if text_entity and text ~= "" then
		-- Do timer related stuff - but only if there is text to display.
		-- Also, prevent excessive use with punching. (see node def.)
		if timer:is_started() == false then
			timer:start(TIMER_INTERVAL)
		else
			timer:stop()
			timer:start(TIMER_INTERVAL)
		end
	else
		if timer:is_started() == true then
			timer:stop()
		end
	end

	-- debug step
	if DEBUG then
		minetest.log("action", "[mcl_signs] Update_Sign: Post-Sign Update: " .. meta:get_string("mcl_signs:text_color") .. " " .. meta:get_string("mcl_signs:glowing_sign") .. ".\n" .. dump(pos))
	end

	return true

end

function mcl_signs:show_formspec(player, pos, old_text)
	minetest.show_formspec(
			player:get_player_name(),
			"mcl_signs:set_text_" .. pos.x .. "_" .. pos.y .. "_" .. pos.z,
			"size[6,3]textarea[0.25,0.25;6,1.5;text;" .. F(S("Enter sign text:")) .. ";".. F(old_text) .. "]" ..
			"label[0,1.5;" .. F(S("Maximum line length: 15")) ..
			 "\n" .. F(S("Maximum lines: 4")) .. "]button_exit[0,2.5;6,1;submit;" .. F(S("Done")) .. "]"
	)
end

function mcl_signs:get_text_entity (pos, force_remove)
	local objects = minetest.get_objects_inside_radius(pos, 0.5)
	local text_entity = false -- just to have a check for failure.
	for _, v in ipairs(objects) do
		local ent = v:get_luaentity()
		if ent and ent.name == "mcl_signs:text" then
			if force_remove ~= nil and force_remove == true then
				v:remove()
			else
				text_entity = v
				break
			end
		end
	end
	return text_entity
end