local S = core.get_translator("vg_campfire")

-- Time factor when cooking on a campfire
-- (higher values -> slower cooking)
local PENALTY = 2.4
local TPENALTY = PENALTY * 5

-- The maximum amount of sticks a campfire can additionally consume
local MAX_STICKS = 5

local function find_entity(pos)
	for obj in core.objects_inside_radius(pos, 0.45) do
		local le = obj:get_luaentity()
		if le and le.name == "vg_campfire:item" then
			return le
		end
	end
end

local function find_or_add_entity(pos)
	return find_entity(pos) or core.add_entity(pos, "vg_campfire:item"):get_luaentity()
end

local function remove_entity(pos)
	local le = find_entity(pos)
	if le then
		le.object:remove()
	end
end

local function update_entity(pos)
	local inv = core.get_meta(pos):get_inventory()
	local item = inv:get_stack("src", 1):get_name()
	if not item or item == "" then
		remove_entity(pos)
		return
	end

	local le = find_or_add_entity(pos)
	le._pos = pos
	le:set_item(pos, item)
end

local initial_properties = {
	visual = "wielditem",
	visual_size = {x = 0.3, y = 0.3},
	textures = {"blank.png"},
	physical = false,
	pointable = false,
}

core.register_entity("vg_campfire:item", {
	initial_properties = initial_properties,
	get_staticdata = function(self)
		local data = {
			item = self._item,
			pos = self._pos
		}
		data.props = self.object:get_properties()
		return core.serialize(data)
	end,
	on_activate = function(self, staticdata, dtime_s)
		local data = core.deserialize(staticdata)
		if (type(staticdata) == "string" and dtime_s and dtime_s > 0) then
			local pos = core.find_node_near(self.object:get_pos(), 1, {"group:campfire"})
			self.object:remove()
			if pos then
				update_entity(pos)
			end
		elseif data then
			self._item = data.item
			self._pos = data.pos
			update_entity(self._pos)
		end
	end,
	set_item = function(self, pos, name)
		if not name then
			self.object:remove()
			update_entity(pos)
			return
		end
		if pos then
			self._pos = pos
		else
			pos = self._pos
		end

		if core.get_item_group(core.get_node(pos).name, "campfire") <= 0 then
			self.object:remove()
			update_entity(pos)
			return
		end

		self.object:set_pos(vector.offset(pos, 0, 0.1, 0)) -- just a bit higher to improve item visibility

		local def = core.registered_items[name]
		if not def then return end
		self._item = name

		self.object:set_properties(table.update({}, initial_properties, {
			wield_item = name,
			visual_size = {
				x = initial_properties.visual_size.x / def.wield_scale.x,
				y = initial_properties.visual_size.y / def.wield_scale.y
			},
			glow = def.light_source and math.round(def.light_source / 2),

			-- 1. Use negative rotation to go in the opposite direction of dropped items
			-- 2. Randomly lower the speed for decorative effect
			automatic_rotate = -0.5 + (math.random() * 0.1)
		}))
	end,
})

local function on_construct(pos)
	local meta = core.get_meta(pos)
	local inv = meta:get_inventory()

	-- Campfire can only hold a single item
	inv:set_size("src", 1)
	-- Initial cooking time
	meta:set_float("burntime", TPENALTY)
end

local function on_rightclick(pos, node, clicker, itemstack)
	if not clicker or not clicker:is_player() then
		return
	end

	local meta = core.get_meta(pos)
	local inv = meta:get_inventory()

	-- Check for protection
	local name = clicker:get_player_name()
	if core.is_protected(pos, name) then
		core.record_protection_violation(pos, name)
		return
	end

	local item = itemstack and itemstack:get_name()

	-- Consume sticks for additional burntime
	if core.get_item_group(item, "stick") > 0 and meta:get_int("sticks") < MAX_STICKS then
		meta:set_float("burntime", meta:get_float("burntime") + PENALTY * 1.5)
		meta:set_int("sticks", meta:get_int("sticks") + 1)

		core.sound_play({name = "default_dig_snappy", gain = 0.8}, {pos = pos, max_hear_distance = 8})
		core.add_particlespawner({
			pos = {
				min = vector.offset(pos, -0.3, -0.6, -0.3),
				max = vector.offset(pos,  0.3, -0.2,  0.3),
			},
			size = 0.5,
			amount = 16,
			time = 1.0,
			node = {name = "default:tree", param2 = 0}, -- tree-like particles as stick-like particles
		})

		if not core.is_creative_enabled(name) then
			itemstack:take_item()
		end
		return itemstack
	end

	local out = core.get_craft_result({
		method = "cooking",
		width = 1,
		items = {item},
	})

	-- Check for whether the item is cookable and whether we can add it to the inventory
	if out.time ~= 0 and inv:add_item("src", itemstack):get_count() ~= itemstack:get_count() then
		meta:set_string("dst", out.item:get_name())
		meta:set_string("infotext", S("Cooking @1", itemstack:get_short_description()))

		if not core.is_creative_enabled(name) then
			itemstack:take_item()
		end

		core.get_node_timer(pos):start(out.time * PENALTY)
	end

	update_entity(pos)

	return itemstack
end

local function on_timer(pos, elapsed)
	local meta = core.get_meta(pos)
	local burntime = meta:get_float("burntime")

	-- If the item took too long to cook, drop it as-is
	local item
	if elapsed > burntime then
		item = meta:get_inventory():get_stack("src", 1):get_name()
	else
		item = meta:get_string("dst")
	end

	-- Spawn the item
	core.add_item(vector.offset(pos, 0, 0.5, 0), item)

	core.sound_play("fire_extinguish_flame", {
		pos = pos,
		max_hear_distance = 16,
		gain = 0.1
	}, true)

	remove_entity(pos)

	core.set_node(pos, {name = "vg_campfire:campfire_unlit"})
end

local selbox = {
	type = "fixed",
	fixed = {-0.375, -0.5, -0.375, 0.375, 0, 0.375},
}

core.register_node("vg_campfire:campfire", {
	description = S("Campfire"),
	_tt_help = S("Used to cook a single item") .. "\n" .. S("Add sticks to increase cooking time"),
	drawtype = "plantlike",
	tiles = {
		{
			name = "vg_campfire_campfire_animated.png",
			animation = {
				type = "vertical_frames",
				aspect_w = 16,
				aspect_h = 16,
				length = 2.0
			}
		}
	},
	inventory_image = "vg_campfire_campfire.png",
	wield_image = "vg_campfire_campfire.png",
	paramtype = "light",
	sunlight_propagates = true,
	walkable = false,
	floodable = true,
	drop = "dye:black",
	damage_per_second = 1,
	light_source = 12,
	groups = {snappy = 3, campfire = 1, attached_node = 1, has_flames = 1},
	sounds = default.node_sound_defaults(),
	selection_box = selbox,

	on_construct = on_construct,
	on_rightclick = on_rightclick,
	on_timer = on_timer,
	on_destruct = remove_entity,
})

core.register_node("vg_campfire:campfire_unlit", {
	description = S("Unlit Campfire"),
	drawtype = "plantlike",
	tiles = {"vg_campfire_campfire_unlit.png"},
	paramtype = "light",
	sunlight_propagates = true,
	walkable = false,
	floodable = true,
	drop = "dye:black",
	groups = {snappy = 3, campfire = 1, attached_node = 1, not_in_creative_inventory = 1},
	sounds = default.node_sound_defaults(),
	selection_box = selbox,
})

core.register_craft({
	output = "vg_campfire:campfire",
	recipe = {
		{"",            "group:stick",  ""           },
		{"group:stick", "group:leaves", "group:stick"},
		{"group:stick", "group:stick",  "group:stick"},
	}
})

core.register_alias("default:bonfire", "vg_campfire:campfire")
