nuke = {} -- mod-global table

local all_tnt = {}

local creative_is_enabled_for = 
	rawget(_G, "creative") and creative.is_enabled_for or function(name)
	return minetest.settings:get_bool("creative_mode")
end

nuke.spawn_tnt = function(pos, entname)
	assert(table.indexof(all_tnt, entname) ~= -1, "attempting to spawn non-tnt")
	minetest.sound_play("tnt_ignite", {pos = pos, gain = 1.0, max_hear_distance = 16})
	return minetest.add_entity(pos, entname)
end

local function expand_tiles(tiles)
	if #tiles >= 6 then
		return tiles
	end
	local t = table.copy(tiles)
	repeat
		t[#t + 1] = t[#t]
	until #t == 6
	return t
end

local function calculate_velocity(distance, tntradius, mult)
	-- d is the distance vector
	--            tntradius - | d |
	-- vel = d * ------------------- * mult
	--              tntradius - 1
	local q = (tntradius - vector.length(distance)) / (tntradius - 1)
	return vector.multiply(vector.multiply(distance, q), mult)
end

local function activate_if_tnt(nodename, nodepos, tntpos, tntradius)
	local explodetime_short = 4 -- seconds
	local explodetime_vary = 1.5
	if table.indexof(all_tnt, nodename) == -1 then
		return
	end

	local obj = nuke.spawn_tnt(nodepos, nodename)
	obj:setvelocity(calculate_velocity(vector.subtract(nodepos, tntpos), tntradius, {x=3, y=5, z=3}))
	obj:get_luaentity().timer = explodetime_short + math.random(-explodetime_vary, explodetime_vary)
end

local function apply_tnt_physics(tntpos, tntradius)
	local objs = minetest.get_objects_inside_radius(tntpos, tntradius)
	for _, obj in ipairs(objs) do
		local mult = {x=1.5, y=2.5, z=1.5}
		local vel = calculate_velocity(vector.subtract(obj:getpos(), tntpos), tntradius, mult)

		if obj:is_player() then
			if obj:get_hp() > 0 then
				obj:set_hp(obj:get_hp() - 1)
			end
			if obj.add_player_velocity then
				vel = vector.multiply(vel, 10)
				obj:add_player_velocity(vel)
			end
		else
			if table.indexof(all_tnt, obj:get_entity_name()) ~= -1 then
				vel = vector.multiply(vel, 2) -- apply more knockback to tnt entities
			end
			obj:setvelocity(vector.add(obj:getvelocity(), vel))
		end
	end
end


nuke.register_tnt = function(nodename, def)
	def.explodetime = def.explodetime or 10 -- seconds
	if type(def.tiles) ~= "table" then
		local t = def.tiles
		def.tiles = {t .. "_top.png", t .. "_bottom.png", t .. "_side.png"}
	end

	-- register node
	minetest.register_node(nodename, {
		tiles = def.tiles,
		diggable = false,
		description = def.description,
		mesecons = {
			effector = {
				action_on = function(pos, node)
					minetest.remove_node(pos)
					nuke.spawn_tnt(pos, node.name)
					minetest.check_for_falling(pos)
				end,
				action_off = function(pos, node) end,
				action_change = function(pos, node) end,
			},
		},
		on_punch = function(pos, node, puncher)
			minetest.remove_node(pos)
			nuke.spawn_tnt(pos, node.name)
			minetest.check_for_falling(pos)
		end,
	})

	-- register entity
	local entity = {
		physical = true, -- collision
		collisionbox = {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5},
		visual = "cube",
		textures = expand_tiles(def.tiles),
		health = 1, -- number of punches required to defuse

		timer = 0,
		blinktimer = 0,
		blinkstatus = true,
	}
	function entity:on_activate(staticdata)
		self.object:setvelocity({x=0, y=4, z=0})
		self.object:setacceleration({x=0, y=-10, z=0}) -- gravity
		self.object:settexturemod("^[brighten")
	end
	function entity:on_step(dtime)
		self.timer = self.timer + dtime
		local mult = 1
		if self.timer > def.explodetime * 0.8 then -- blink faster before explosion
			mult = 4
		elseif self.timer > def.explodetime * 0.5 then
			mult = 2
		end
		self.blinktimer = self.blinktimer + mult * dtime

		if self.blinktimer > 0.5 then -- actual blinking
			self.blinktimer = self.blinktimer - 0.5
			self.object:settexturemod(self.blinkstatus and "^[brighten" or "")
			self.blinkstatus = not self.blinkstatus
		end

		if self.timer > def.explodetime then -- boom!
			minetest.sound_play("tnt_explode", {pos = pos, gain = 0.8, max_hear_distance = 32})
			def.on_explode(vector.round(self.object:get_pos()))
			self.object:remove()
		end
	end
	function entity:on_punch(hitter)
		self.health = self.health - 1
		if self.health == 0 then -- give tnt node back if defused
			self.object:remove()
			if not creative_is_enabled_for(hitter:get_player_name()) then
				hitter:get_inventory():add_item("main", nodename)
			end
		end
	end
	minetest.register_entity(nodename, entity)

	-- save tnt name
	all_tnt[#all_tnt + 1] = nodename
end


local function on_explode_normal(pos, range)
	local ndef = minetest.registered_nodes[minetest.get_node(pos).name]
	if ndef ~= nil and ndef.groups.water ~= nil then
		apply_tnt_physics(pos, range)
		return -- cancel explosion
	end

	for x=-range, range do
	for y=-range, range do
	for z=-range, range do
		if x*x+y*y+z*z <= range * range + range then

			local nodepos = {x=pos.x+x, y=pos.y+y, z=pos.z+z}
			local n = minetest.get_node(nodepos)
			if n.name ~= "air" and n.name ~= "ignore" then
				activate_if_tnt(n.name, nodepos, pos, range)
				minetest.remove_node(nodepos)
			end

		end
	end
	end
	end
	apply_tnt_physics(pos, range)
end

local function on_explode_split(pos, range, entname)
	for x=-range, range do
	for z=-range, range do
		if x*x+z*z <= range * range then

			local nodepos = vector.add(pos, {x=x, y=0, z=z})
			minetest.add_entity(nodepos, entname)

		end
	end
	end
end


-- Extrosim TNT

nuke.register_tnt("sbo_explosives:extrosim_tnt", {
	description = "Extrosim TNT",
	tiles = "extrosim_tnt",
	on_explode = function(pos)
		on_explode_normal(pos, 6)
	end,
})

minetest.register_craft({
	output = "sbo_explosives:extrosim_tnt 4",
	recipe = {
		{"sbo_explosives:thermite_stick", "sbo_extrosim:raw_extrosim", "sbo_explosives:thermite_stick"},
		{"sbo_extrosim:raw_extrosim", "sbo_explosives:thermite_stick", "sbo_extrosim:raw_extrosim"},
		{"sbo_explosives:thermite_stick", "sbo_extrosim:raw_extrosim", "sbo_explosives:thermite_stick"},
	}
})


nuke.register_tnt("sbo_explosives:extrosim_tntx", {
	description = "Extreme Extrosim TNT",
	tiles = {"extrosim_tnt_top.png", "extrosim_tnt_bottom.png", "extrosim_tnt_side_x.png"},
	on_explode = function(pos)
		on_explode_split(pos, 3, "sbo_explosives:extrosim_tnt")
	end,
})

minetest.register_craft({
	output = "sbo_explosives:extrosim_tntx 1",
	recipe = {
		{"sbo_explosives:extrosim_tnt", "sbo_extrex:extrex_ingot", "sbo_explosives:extrosim_tnt"},
		{"sbo_extrex:extrex_ingot", "sbo_explosives:extrosim_tnt", "sbo_extrex:extrex_ingot"},
		{"sbo_explosives:extrosim_tnt", "sbo_extrex:extrex_ingot", "sbo_explosives:extrosim_tnt"},
	}
})

-- Uranium TNT

nuke.register_tnt("sbo_explosives:uranium_tnt", {
	description = "Uranium TNT",
	tiles = "uranium_tnt",
	on_explode = function(pos)
	on_explode_normal(pos, 12)
	end,
})

minetest.register_craft({
	output = "sbo_explosives:uranium_tnt 4",
	recipe = {
		{"sbo_explosives:nuclear_stick", "sbz_chem:uranium_crystal", "sbo_explosives:nuclear_stick"},
		{"sbz_chem:uranium_crystal", "sbo_explosives:nuclear_stick", "sbz_chem:uranium_crystal"},
		{"sbo_explosives:nuclear_stick", "sbz_chem:uranium_crystal", "sbo_explosives:nuclear_stick"},
	}
})


nuke.register_tnt("sbo_explosives:uranium_tntx", {
	description = "Extreme Uranium TNT",
	tiles = {"uranium_tnt_top.png", "uranium_tnt_bottom.png", "uranium_tnt_side_x.png"},
	on_explode = function(pos)
		on_explode_split(pos, 3, "sbo_explosives:uranium_tnt")
	end,
})

minetest.register_craft({
	output = "sbo_explosives:uranium_tntx 1",
	recipe = {
		{"sbo_explosives:uranium_tnt", "sbo_resium:crystal", "sbo_explosives:uranium_tnt"},
		{"sbo_resium:crystal", "sbo_explosives:uranium_tnt", "sbo_resium:crystal"},
		{"sbo_explosives:uranium_tnt", "sbo_resium:crystal", "sbo_explosives:uranium_tnt"},
	}
})

-- Instantinium TNT

nuke.register_tnt("sbo_explosives:instantinium_tnt", {
	description = "Instantinium TNT",
	tiles = "instantinium_tnt",
	on_explode = function(pos)
		on_explode_normal(pos, 20)
	end,
})

minetest.register_craft({
	output = "sbo_explosives:instantinium_tnt 4",
	recipe = {
		{"sbo_explosives:uranium_tnt", "sbz_instatube:instantinium", "sbo_explosives:uranium_tnt"},
		{"sbz_instatube:instantinium", "sbo_explosives:uranium_tnt", "sbz_instatube:instantinium"},
		{"sbo_explosives:uranium_tnt", "sbz_instatube:instantinium", "sbo_explosives:uranium_tnt"},
	}
})


nuke.register_tnt("sbo_explosives:instantinium_tntx", {
	description = "Extreme Instantinium TNT",
	tiles = {"instantinium_tnt_top.png", "instantinium_tnt_bottom.png", "instantinium_tnt_side_x.png"},
	on_explode = function(pos)
		on_explode_split(pos, 4, "sbo_explosives:instantinium_tnt")
	end,
})

minetest.register_craft({
	output = "sbo_explosives:instantinium_tntx 1",
	recipe = {
		{"sbo_explosives:uranium_tntx", "sbz_instatube:instantinium", "sbo_explosives:uranium_tntx"},
		{"sbz_instatube:instantinium", "sbo_explosives:uranium_tntx", "sbz_instatube:instantinium"},
		{"sbo_explosives:uranium_tntx", "sbz_instatube:instantinium", "sbo_explosives:uranium_tntx"},
	}
})

if minetest.settings:get_bool("log_mods") then
	print("[Nuke] Loaded")
end
