local orion_api_enable_half_slabs = minetest.settings:get_bool("orion_api_enable_half_slabs")

function shallow_copy(table)
  local newTable = {}
  for key, value in pairs(table) do
    newTable[key] = value
  end
  return newTable
end

local maskName = "orionapi"
local opaqueNodes = {}

-- slabtypes
SLAB_NONE = 0
SLAB_LOWER = 1
SLAB_UPPER = 2
SLAB_COMBO = 3


local placeCombinedNode = function(position, secondNode)


end

local onPlace = function(itemstack, placer, pointed_thing)
	if pointed_thing.type == "node" and orion_api_enable_half_slabs then
		return on_place_half_slabs(itemstack, placer, pointed_thing)
	else
		return minetest.item_place(itemstack, player, pointed_thing)
	end
end


local on_place_half_slabs = function(itemstack, placer, pointed_thing)
	local under = pointed_thing.under
	local above = pointed_thing.above
	if above.y == under.y +1 then -- looking from above
		local pointedNode = minetest.get_node(under)
		local newSlabName = itemstack:get_name()
		if minetest.registered_nodes[pointedNode.name] and
			minetest.registered_nodes[pointedNode.name].groups and
			minetest.registered_nodes[pointedNode.name].groups.orion_slabtype == SLAB_LOWER then

			if pointedNode.name == newSlabName then -- Same slabs twice, place the normal block
				minetest.swap_node(under, {name = pointedNode.name:gsub("%_lower", "")})
				return itemstack:take_item(itemstack:get_count() -1)
			else
				local lowerName = pointedNode.name:gsub("%_lower", "")
				lowerName = lowerName:gsub(":", "_")
				local upperName = newSlabName:gsub("%_lower", "")
				upperName = upperName:gsub(":", "_")

				local nodeName = maskName .. ":" .. upperName .. "_" .. lowerName
				if minetest.registered_nodes[nodeName] then
					minetest.swap_node(under, {name = nodeName})
					return itemstack:take_item(itemstack:get_count() -1)
				end
			end
		else
			local otherNode = minetest.get_node(above)
			local newSlabName = itemstack:get_name()
			if minetest.registered_nodes[otherNode.name] and
				minetest.registered_nodes[otherNode.name].groups and
				minetest.registered_nodes[otherNode.name].groups.orion_slabtype == SLAB_UPPER then
				if otherNode.name:gsub("%_upper", "_lower") == newSlabName then -- Same slabs twice, place the normal block
					minetest.swap_node(above, {name = otherNode.name:gsub("%_upper", "")})
					return itemstack:take_item(itemstack:get_count() -1)
				else
					local upperName = otherNode.name:gsub("%_upper", "")
					upperName = upperName:gsub(":", "_")
					local lowerName = newSlabName:gsub("%_lower", "")
					lowerName = lowerName:gsub(":", "_")

					local nodeName = maskName .. ":" .. upperName .. "_" .. lowerName
					if minetest.registered_nodes[nodeName] then
						minetest.swap_node(above, {name = nodeName})
						return itemstack:take_item(itemstack:get_count() -1)
					end
				end
			end
		end
	elseif above.y == under.y -1 then -- pointed to lower side
		local pointedNode = minetest.get_node(under)
		local newSlabName = itemstack:get_name()
		if minetest.registered_nodes[pointedNode.name] and
			minetest.registered_nodes[pointedNode.name].groups and
			minetest.registered_nodes[pointedNode.name].groups.orion_slabtype == SLAB_UPPER then

			if pointedNode.name:gsub("%_upper", "_lower") == newSlabName then -- Same slabs twice, place the normal block
				minetest.swap_node(under, {name = pointedNode.name:gsub("%_upper", "")})
				return itemstack:take_item(itemstack:get_count() -1)
			else
				local upperName = pointedNode.name:gsub("%_upper", "")
				upperName = upperName:gsub(":", "_")
				local lowerName = newSlabName:gsub("%_lower", "")
				lowerName = lowerName:gsub(":", "_")

                local nodeName = maskName .. ":" .. upperName .. "_" .. lowerName
				if minetest.registered_nodes[nodeName] then
					minetest.swap_node(under, {name = nodeName})
					return itemstack:take_item(itemstack:get_count() -1)
				end
			end
		else
			local otherNode = minetest.get_node(above)
			local newSlabName = itemstack:get_name()
			if minetest.registered_nodes[otherNode.name] and
				minetest.registered_nodes[otherNode.name].groups and
				minetest.registered_nodes[otherNode.name].groups.orion_slabtype == SLAB_LOWER then

				if otherNode.name == newSlabName then -- Same slabs twice, place the normal block
					minetest.swap_node(above, {name = otherNode.name:gsub("%_lower", "")})
					return itemstack:take_item(itemstack:get_count() -1)
				else
					local upperName = otherNode.name:gsub("%_lower", "")
					upperName = upperName:gsub(":", "_")
					local lowerName = newSlabName:gsub("%_lower", "")
					lowerName = lowerName:gsub(":", "_")

					local nodeName = maskName .. ":" .. lowerName .. "_" .. upperName
					if minetest.registered_nodes[nodeName] then
						minetest.swap_node(above, {name = nodeName})
						return itemstack:take_item(itemstack:get_count() -1)
					end
				end
			else
				if otherNode.name == "air" then
					local newName = newSlabName:gsub("%_lower", "_upper")
					minetest.set_node(above, {name = newName})
					return itemstack:take_item(itemstack:get_count() -1)
				end
			end
		end
	else -- any side was targeted
		-- if there is an "upper" variant here, place an upper variant
		-- if there is a normal node here try to determin magically weather to place upper or lower

	end
	return minetest.item_place(itemstack, player, pointed_thing)
end


orion.register_slab_node = function(name, definition)
	if not orion_api_enable_half_slabs then
		minetest.register_node(name, definition)
		return
	end


	local normalDef = shallow_copy(definition)
	local normalDefTiles = shallow_copy(definition.tiles)
	if #normalDef.tiles == 1 then
		normalDef["tiles"][2] = normalDefTiles[1]
		normalDef["tiles"][3] = normalDefTiles[1]
		normalDef["tiles"][4] = normalDefTiles[1]
		normalDef["tiles"][5] = normalDefTiles[1] .. "^[transformR180"
		normalDef["tiles"][6] = normalDefTiles[1]
	end
	minetest.register_node(name, normalDef)


	local lowerDef = shallow_copy(definition)
	local upperDef = shallow_copy(definition)

	local lowerDefTiles = shallow_copy(lowerDef.tiles)
	lowerDef["paramtype"] = "light"
	lowerDef["description"] = lowerDef["description"] or "" .. " (lower)"
	lowerDef["drawtype"] = "nodebox"
	lowerDef["node_box"] = {
		type = "fixed",
		fixed = {-0.50, -0.50, -0.50, 0.50, 0.00, 0.50} }
	lowerDef["on_place"] = onPlace
	lowerDef["tiles"][1] = "[combine:128x128:0,-64=".. lowerDefTiles[1] .. "\\^[resize\\:128x128:0,64=".. lowerDefTiles[1] .. "\\^[resize\\:128x128"
	if #lowerDefTiles == 1 then
		lowerDef["tiles"][2] = lowerDefTiles[1]
		lowerDef["tiles"][3] = lowerDefTiles[1]
		lowerDef["tiles"][4] = lowerDefTiles[1]
		lowerDef["tiles"][5] = lowerDefTiles[1] .. "^[transformR180"
		lowerDef["tiles"][6] = lowerDefTiles[1]
	end
	local lowerGroups = shallow_copy(definition["groups"])
	lowerDef.node_placement_prediction = "air"
	lowerGroups.orion_slabtype = SLAB_LOWER
	lowerDef["groups"] = lowerGroups
	minetest.register_node(name .. "_lower", lowerDef)

	upperDef["paramtype"] = "light"
	upperDef["description"] = upperDef["description"] or "".. " (upper)"
	upperDef["drawtype"] = "nodebox"
	upperDef["node_box"] = {
		type = "fixed",
		fixed = {-0.50, 0.00, -0.50, 0.50, 0.50, 0.50 } }
	upperDef["drop"] = {
		max_items = 1,
		items = {
			{ items = { name .. "_lower" } }
		}
	}
	local upperGroups = shallow_copy(definition["groups"])
	upperGroups.not_in_creative_inventory = 1
	upperGroups.orion_slabtype = SLAB_UPPER
	upperDef["groups"] = upperGroups
	minetest.register_node(name .. "_upper", upperDef)



	name = string.gsub(name, ":","_")
	if definition.drawtype ~= "glasslike" and definition.drawtype ~= "allfaces_optional" then
		for secondName, secondDefinition in pairs(opaqueNodes) do
			local secondUpper = shallow_copy(definition)
			local secondLower = shallow_copy(definition)
			local escapedName = string.gsub(secondName, ":", "_")
			local firstTiles = shallow_copy(definition["tiles"])
			if #firstTiles == 1 then
				firstTiles[2] = firstTiles[1]
				firstTiles[3] = firstTiles[1]
				firstTiles[4] = firstTiles[1]
				firstTiles[5] = firstTiles[1]
				firstTiles[6] = firstTiles[1]
			end
			local secondTiles = shallow_copy(secondDefinition["tiles"])
			if #secondTiles == 1 then
				secondTiles[2] = secondTiles[1]
				secondTiles[3] = secondTiles[1]
				secondTiles[4] = secondTiles[1]
				secondTiles[5] = secondTiles[1]
				secondTiles[6] = secondTiles[1]
			end
			secondLower["tiles"] = {
				firstTiles[1],
				secondTiles[2],
				firstTiles[3] .. "^[resize:128x128^[lowpart:50:" .. secondTiles[3] .. "\\^[resize\\:128x128",
				firstTiles[4] .. "^[resize:128x128^[lowpart:50:" .. secondTiles[4] .. "\\^[resize\\:128x128",
				firstTiles[5] .. "^[resize:128x128^[lowpart:50:" .. secondTiles[5] .. "\\^[resize\\:128x128",
				firstTiles[6] .. "^[resize:128x128^[lowpart:50:" .. secondTiles[6] .. "\\^[resize\\:128x128",
			}
			secondUpper["tiles"] = {
				secondTiles[1],
				firstTiles[2],
				secondTiles[3] .. "^[resize:128x128^[lowpart:50:" .. firstTiles[3] .. "\\^[resize\\:128x128",
				secondTiles[4] .. "^[resize:128x128^[lowpart:50:" .. firstTiles[4] .. "\\^[resize\\:128x128",
				secondTiles[5] .. "^[resize:128x128^[lowpart:50:" .. firstTiles[5] .. "\\^[resize\\:128x128",
				secondTiles[6] .. "^[resize:128x128^[lowpart:50:" .. firstTiles[6] .. "\\^[resize\\:128x128",
			}
			local groups = shallow_copy(definition["groups"])
			local secondGroup = shallow_copy(definition["groups"])
			for key, value in pairs(secondGroup) do
				if not groups[key] then groups[key] = value end
			end
			groups.not_in_creative_inventory = 1
			groups.orion_slabtype = SLAB_COMBO
			secondLower.groups = groups
			secondLower.description = definition["description"] .. " (upper)\n" .. secondDefinition["description"] .. " (lower)"
			minetest.register_node(":" ..maskName .. ":" .. name .. "_" .. escapedName, secondLower)
			secondUpper.groups = groups
			secondUpper.description = secondDefinition["description"] .. " (upper)\n" .. definition["description"] .. " (lower)"
			minetest.register_node(":" ..maskName .. ":" .. escapedName .. "_" .. name, secondUpper)
		end
		opaqueNodes[name] = definition
	end
end


local decoslab_enabled = minetest.settings:get("orion_world_gen_decoslabs")
local mapDecoSetting = minetest.get_mapgen_setting("orion_world_gen_decoslabs")
function orion.worldgen_register_decoration_slab(name)
	if mapDecoSetting == nil and decoslab_enabled == false then
		minetest.set_mapgen_setting("orion_world_gen_decoslabs", "false")
		mapDecoSetting = minetest.get_mapgen_setting("orion_world_gen_decoslabs")
	end
	if mapDecoSetting == nil and decoslab_enabled == true then
		minetest.set_mapgen_setting("orion_world_gen_decoslabs", "true")
		mapDecoSetting = minetest.get_mapgen_setting("orion_world_gen_decoslabs")
	end

	if mapDecoSetting == "true" then
		minetest.register_decoration({
			deco_type = "simple",
			check_offset = 0,
			num_spawn_by = 1,
			place_on = NAME .. ":" .. name,
			spawn_by = NAME .. ":" .. name,
			decoration = NAME .. ":".. name .."_lower",
			name = NAME .. ":".. name .."_lower",
			height = 1,
			fill_ratio = 10,
		})
	end
end
