--[[

	TechAge
	=======

	Copyright (C) 2019 Joachim Stolberg

	AGPL v3
	See LICENSE.txt for more information
	
	Helper functions

]]--

-- for lazy programmers
local P = minetest.string_to_pos
local M = minetest.get_meta
local S = techage.S

-- Input data to generate the Param2ToDir table
local Input = {
	8,9,10,11,    -- 1
	16,17,18,19,  -- 2
	4,5,6,7,      -- 3
	12,13,14,15,  -- 4
	0,1,2,3,      -- 5
	20,21,22,23,  -- 6
}

-- allowed for digging
local RegisteredNodesToBeDug = {}

function techage.register_node_to_be_dug(name)
	RegisteredNodesToBeDug[name] = true
end

-- translation from param2 to dir (out of the node upwards)
local Param2Dir = {}
for idx,val in ipairs(Input) do
	Param2Dir[val] = math.floor((idx - 1) / 4) + 1
end

-- used by lamps and power switches
function techage.determine_node_bottom_as_dir(node)
	return tubelib2.Turn180Deg[Param2Dir[node.param2] or 1]
end

function techage.determine_node_top_as_dir(node)
	return Param2Dir[node.param2] or 1
end

-- rotation rules (screwdriver) for wallmounted "facedir" nodes
function techage.rotate_wallmounted(param2)
	local offs = math.floor(param2 / 4) * 4
	local rot = ((param2 % 4) + 1) % 4
	return offs + rot
end

function techage.in_range(val, min, max)
	val = tonumber(val)
	if val < min then return min end
	if val > max then return max end
	return val
end

function techage.one_of(val, selection)
	for _,v in ipairs(selection) do
		if val == v then return val end
	end
	return selection[1]
end

function techage.index(list, x)
	for idx, v in pairs(list) do
		if v == x then return idx end
	end
	return nil
end

function techage.in_list(list, x)
	for idx, v in pairs(list) do
		if v == x then return true end
	end
	return false
end

function techage.add_to_set(set, x)
	if not techage.index(set, x) then
		table.insert(set, x)
	end
end

function techage.get_node_lvm(pos)
	local node = minetest.get_node_or_nil(pos)
	if node then
		return node
	end
	local vm = minetest.get_voxel_manip()
	local MinEdge, MaxEdge = vm:read_from_map(pos, pos)
	local data = vm:get_data()
	local param2_data = vm:get_param2_data()
	local area = VoxelArea:new({MinEdge = MinEdge, MaxEdge = MaxEdge})
	local idx = area:indexp(pos)
	if data[idx] and param2_data[idx] then
		return {
			name = minetest.get_name_from_content_id(data[idx]),
			param2 = param2_data[idx]
		}
	end
	return {name="ignore", param2=0}
end

--
-- Functions used to hide electric cable and biogas pipes
--
-- Overridden method of tubelib2!
function techage.get_primary_node_param2(pos, dir)
	local npos = vector.add(pos, tubelib2.Dir6dToVector[dir or 0])
	local param2 = M(npos):get_int("tl2_param2")
	if param2 ~= 0 then
		return param2, npos
	end
end

-- Overridden method of tubelib2!
function techage.is_primary_node(pos, dir)
	local npos = vector.add(pos, tubelib2.Dir6dToVector[dir or 0])
	local param2 = M(npos):get_int("tl2_param2")
	return param2 ~= 0
end

function techage.is_air_like(name)
	local ndef = minetest.registered_nodes[name]
	if ndef and ndef.buildable_to then
		return true
	end
	return false
end

-- returns true, if node can be dug, otherwise false
function techage.can_node_dig(node, ndef)
	if RegisteredNodesToBeDug[node.name] then 
		return true 
	end
	if not ndef then return false end
	if node.name == "ignore" then return false end
	if node.name == "air" then return true end
	if ndef.buildable_to == true then return true end
	if ndef.diggable == false then return false end
	if ndef.after_dig_node then return false end
	-- add it to the white list
	RegisteredNodesToBeDug[node.name] = true
	return true
end

techage.dig_states = {
	NOT_DIGGABLE = 1,
	INV_FULL = 2,
	DUG = 3
}

-- Digs a node like a player would by utilizing a fake player object.
-- add_to_inv(itemstacks) is a method that should try to add the dropped stacks to an appropriate inventory.
-- The node will only be dug, if add_to_inv(itemstacks) returns true.
function techage.dig_like_player(pos, fake_player, add_to_inv)
	local node = techage.get_node_lvm(pos)
	local ndef = minetest.registered_nodes[node.name]
	if not ndef or ndef.diggable == false or (ndef.can_dig and not ndef.can_dig(pos, fake_player)) then
		return techage.dig_states.NOT_DIGGABLE
	end
	local drop_as_strings = minetest.get_node_drops(node)
	local drop_as_stacks = {}
	for _,itemstring in ipairs(drop_as_strings) do
		drop_as_stacks[#drop_as_stacks+1] = ItemStack(itemstring)
	end
	local meta = M(pos)
	if ndef.preserve_metadata then
		ndef.preserve_metadata(pos, node, meta, drop_as_stacks)
	end

	if add_to_inv(drop_as_stacks) then
		local oldmeta = meta:to_table()
		minetest.remove_node(pos)

		if ndef.after_dig_node then
			ndef.after_dig_node(pos, node, oldmeta, fake_player)
		end
		return techage.dig_states.DUG
	end
	return techage.dig_states.INV_FULL
end

local function handle_drop(drop)
	-- To keep it simple, return only the item with the lowest rarity
	if drop.items then
		local rarity = 9999
		local name
		for idx,item in ipairs(drop.items) do
			if item.rarity and item.rarity < rarity then
				rarity = item.rarity
				name = item.items[1] -- take always the first item
			else
				return item.items[1] -- take always the first item
			end
		end
		return name
	end
	return false
end

-- returns the node name, if node can be dropped, otherwise nil
function techage.dropped_node(node, ndef)
	if node.name == "air" then return end
	--if ndef.buildable_to == true then return end
	if not ndef.diggable then return end
	if ndef.drop == "" then return end
	if type(ndef.drop) == "table" then
		return handle_drop(ndef.drop)
	end
	return ndef.drop or node.name
end	

-- needed for windmill plants
local function determine_ocean_ids()
	techage.OceanIdTbl = {}
	for name, _ in pairs(minetest.registered_biomes) do
		if string.find(name, "ocean") then
			local id = minetest.get_biome_id(name)
			--print(id, name)
			techage.OceanIdTbl[id] = true
		end
	end
end

determine_ocean_ids()

-- check if natural water is on given position (water placed by player has param2 = 1)
function techage.is_ocean(pos)
	if pos.y > 1 then return false end
	local node = techage.get_node_lvm(pos)
	if node.name ~= "default:water_source" then return false end
	if node.param2 == 1 then return false end
	return true
end

function techage.item_image(x, y, itemname, count)
	local name, size = unpack(string.split(itemname, " "))
	size = count and count or size
	size = tonumber(size) or 1
	local label = ""
	local text = minetest.formspec_escape(ItemStack(itemname):get_description())
	local tooltip = "tooltip["..x..","..y..";1,1;"..text..";#0C3D32;#FFFFFF]"

	if minetest.registered_tools[name] and size > 1 then
		local offs = 0
		if size < 10 then
			offs = 0.65
		elseif size < 100 then
			offs = 0.5
		elseif size < 1000 then
			offs = 0.35
		else
			offs = 0.2
		end
		label = "label["..(x + offs)..","..(y + 0.45)..";"..tostring(size).."]"
	end
	
	return "box["..x..","..y..";0.85,0.9;#808080]"..
		"item_image["..x..","..y..";1,1;"..itemname.."]"..
		tooltip..
		label
end

function techage.item_image_small(x, y, itemname, tooltip_prefix)
	local name = unpack(string.split(itemname, " "))
	local tooltip = ""
	local ndef = minetest.registered_nodes[name] or minetest.registered_items[name] or minetest.registered_craftitems[name]
	
	if ndef and ndef.description then
		local text = minetest.formspec_escape(ndef.description)
		tooltip = "tooltip["..x..","..y..";0.8,0.8;"..tooltip_prefix..": "..text..";#0C3D32;#FFFFFF]"
	end
	
	return "box["..x..","..y..";0.65,0.7;#808080]"..
		"item_image["..x..","..y..";0.8,0.8;"..name.."]"..
		tooltip
end

function techage.mydump(o, indent, nested, level)
	local t = type(o)
	if not level and t == "userdata" then
		-- when userdata (e.g. player) is passed directly, print its metatable:
		return "userdata metatable: " .. techage.mydump(getmetatable(o))
	end
	if t ~= "table" then
		return basic_dump(o)
	end
	-- Contains table -> true/nil of currently nested tables
	nested = nested or {}
	if nested[o] then
		return "<circular reference>"
	end
	nested[o] = true
	indent = " "
	level = level or 1
	local t = {}
	local dumped_indexes = {}
	for i, v in ipairs(o) do
		t[#t + 1] = techage.mydump(v, indent, nested, level + 1)
		dumped_indexes[i] = true
	end
	for k, v in pairs(o) do
		if not dumped_indexes[k] then
			if type(k) ~= "string" or not is_valid_identifier(k) then
				k = "["..techage.mydump(k, indent, nested, level + 1).."]"
			end
			v = techage.mydump(v, indent, nested, level + 1)
			t[#t + 1] = k.." = "..v
		end
	end
	nested[o] = nil
	if indent ~= "" then
		local indent_str = string.rep(indent, level)
		local end_indent_str = string.rep(indent, level - 1)
		return string.format("{%s%s%s}",
				indent_str,
				table.concat(t, ","..indent_str),
				end_indent_str)
	end
	return "{"..table.concat(t, ", ").."}"
end

-- title bar help (width is the fornmspec width)
function techage.question_mark_help(width, tooltip)
	local x = width- 0.6
	return "label["..x..",-0.1;"..minetest.colorize("#000000", minetest.formspec_escape("[?]")).."]"..
		"tooltip["..x..",-0.1;0.5,0.5;"..tooltip..";#0C3D32;#FFFFFF]"
end

function techage.wrench_tooltip(x, y)
	local tooltip = S("Block has an\nadditional wrench menu")
	return "label["..x..","..y..";"..minetest.colorize("#000000", minetest.formspec_escape("[?]")).."]"..
		"tooltip["..x..","..y..";0.5,0.5;"..tooltip..";#0C3D32;#FFFFFF]"
end

