local S = minetest.get_translator("mcl_banners")
local N = function(s) return s end

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

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

mcl_banners = {}

mcl_banners.colors = {
	-- Format:
	-- [ID] = { banner description, wool, unified dyes color group, overlay color, dye, color name for emblazonings }
	["unicolor_white"] =      {"white",      S("White Banner"),      "mcl_wool:white", "#FFFFFF", "mcl_dye:white", N("White") },
	["unicolor_darkgrey"] =   {"grey",       S("Grey Banner"),       "mcl_wool:grey", "#303030", "mcl_dye:dark_grey", N("Grey") },
	["unicolor_grey"] =       {"silver",     S("Light Grey Banner"), "mcl_wool:silver", "#5B5B5B", "mcl_dye:grey", N("Light Grey") },
	["unicolor_black"] =      {"black",      S("Black Banner"),      "mcl_wool:black", "#000000", "mcl_dye:black", N("Black") },
	["unicolor_red"] =        {"red",        S("Red Banner"),        "mcl_wool:red", "#BC0000", "mcl_dye:red", N("Red") },
	["unicolor_yellow"] =     {"yellow",     S("Yellow Banner"),     "mcl_wool:yellow", "#E6CD00", "mcl_dye:yellow", N("Yellow") },
	["unicolor_dark_green"] = {"green",      S("Green Banner"),      "mcl_wool:green", "#006000", "mcl_dye:dark_green", N("Green") },
	["unicolor_cyan"] =       {"cyan",       S("Cyan Banner"),       "mcl_wool:cyan", "#00ACAC", "mcl_dye:cyan", N("Cyan") },
	["unicolor_blue"] =       {"blue",       S("Blue Banner"),       "mcl_wool:blue", "#0000AC", "mcl_dye:blue", N("Blue") },
	["unicolor_red_violet"] = {"magenta",    S("Magenta Banner"),    "mcl_wool:magenta", "#AC007C", "mcl_dye:magenta", N("Magenta")},
	["unicolor_orange"] =     {"orange",     S("Orange Banner"),     "mcl_wool:orange", "#E67300", "mcl_dye:orange", N("Orange") },
	["unicolor_violet"] =     {"purple",     S("Purple Banner"),     "mcl_wool:purple", "#6400AC", "mcl_dye:violet", N("Violet") },
	["unicolor_brown"] =      {"brown",      S("Brown Banner"),      "mcl_wool:brown", "#603000", "mcl_dye:brown", N("Brown") },
	["unicolor_pink"] =       {"pink",       S("Pink Banner"),       "mcl_wool:pink", "#DE557C", "mcl_dye:pink", N("Pink") },
	["unicolor_lime"] =       {"lime",       S("Lime Banner"),       "mcl_wool:lime", "#30AC00", "mcl_dye:green", N("Lime") },
	["unicolor_light_blue"] = {"light_blue", S("Light Blue Banner"), "mcl_wool:light_blue", "#4040CF", "mcl_dye:lightblue", N("Light Blue") },
}


local pattern_names = {
      "",
      "border",
      "bricks",
      "circle",
      "creeper",
      "cross",
      "curly_border",
      "diagonal_up_left",
      "diagonal_up_right",
      "diagonal_right",
      "diagonal_left",
      "flower",
      "gradient",
      "gradient_up",
      "half_horizontal_bottom",
      "half_horizontal",
      "half_vertical",
      "half_vertical_right",
      "thing",
      "rhombus",
      "skull",
      "small_stripes",
      "square_bottom_left",
      "square_bottom_right",
      "square_top_left",
      "square_top_right",
      "straight_cross",
      "stripe_bottom",
      "stripe_center",
      "stripe_downleft",
      "stripe_downright",
      "stripe_left",
      "stripe_middle",
      "stripe_right",
      "stripe_top",
      "triangle_bottom",
      "triangle_top",
      "triangles_bottom",
      "triangles_top",
}

local colors_reverse = {}
for k,v in pairs(mcl_banners.colors) do
	colors_reverse["mcl_banners:banner_item_"..v[1]] = k
end

-- Add pattern/emblazoning crafting recipes
dofile(minetest.get_modpath("mcl_banners").."/patterncraft.lua")

-- Overlay ratios (0-255)
local base_color_ratio = 224
local layer_ratio = 255

local standing_banner_entity_offset = { x=0, y=-0.499, z=0 }
local hanging_banner_entity_offset = { x=0, y=-1.7, z=0 }

local rotation_level_to_yaw = function(rotation_level)
	return (rotation_level * (math.pi/8)) + math.pi
end

local on_dig_banner = function(pos, node, digger)
	-- Check protection
	local name = digger:get_player_name()
	if minetest.is_protected(pos, name) then
		minetest.record_protection_violation(pos, name)
		return
	end
	-- Drop item
	local meta = minetest.get_meta(pos)
	local item = meta:get_inventory():get_stack("banner", 1)
	if not item:is_empty() then
		minetest.handle_node_drops(pos, {item:to_string()}, digger)
	else
		minetest.handle_node_drops(pos, {"mcl_banners:banner_item_white"}, digger)
	end
	-- Remove node
	minetest.remove_node(pos)
end

local on_destruct_banner = function(pos, hanging)
	local offset, nodename
	if hanging then
		offset = hanging_banner_entity_offset
		nodename = "mcl_banners:hanging_banner"
	else
		offset = standing_banner_entity_offset
		nodename = "mcl_banners:standing_banner"
	end
	-- Find this node's banner entity and remove it
	local checkpos = vector.add(pos, offset)
	local objects = minetest.get_objects_inside_radius(checkpos, 0.5)
	for _, v in ipairs(objects) do
		local ent = v:get_luaentity()
		if ent and ent.name == nodename then
			v:remove()
		end
	end
end

local on_destruct_standing_banner = function(pos)
	return on_destruct_banner(pos, false)
end

local on_destruct_hanging_banner = function(pos)
	return on_destruct_banner(pos, true)
end

local make_banner_texture = function(base_color, layers)
	local colorize
	if mcl_banners.colors[base_color] then
		colorize = mcl_banners.colors[base_color][4]
	end
	if colorize then
		-- Base texture with base color
		local base = "(mcl_banners_banner_base.png^[mask:mcl_banners_base_inverted.png)^((mcl_banners_banner_base.png^[colorize:"..colorize..":"..base_color_ratio..")^[mask:mcl_banners_base.png)"

		-- Optional pattern layers
		if layers then
			local finished_banner = base
			for l=1, #layers do
				local layerinfo = layers[l]
				local pattern = "mcl_banners_" .. layerinfo.pattern .. ".png"
				local color = mcl_banners.colors[layerinfo.color][4]

				-- Generate layer texture
				local layer = "(("..pattern.."^[colorize:"..color..":"..layer_ratio..")^[mask:"..pattern..")"

				finished_banner = finished_banner .. "^" .. layer
			end
			return { finished_banner }
		end
		return { base }
	else
		return { "mcl_banners_banner_base.png" }
	end
end

local spawn_banner_entity = function(pos, hanging, itemstack)
	local banner
	if hanging then
		banner = minetest.add_entity(pos, "mcl_banners:hanging_banner")
	else
		banner = minetest.add_entity(pos, "mcl_banners:standing_banner")
	end
	if banner == nil then
		return banner
	end
	local imeta = itemstack:get_meta()
	local layers_raw = imeta:get_string("layers")
	local layers = minetest.deserialize(layers_raw)
	local colorid = colors_reverse[itemstack:get_name()]
	banner:get_luaentity():_set_textures(colorid, layers)
	local mname = imeta:get_string("name")
	if mname ~= nil and mname ~= "" then
		banner:get_luaentity()._item_name = mname
		banner:get_luaentity()._item_description = imeta:get_string("description")
	end

	return banner
end

local respawn_banner_entity = function(pos, node, force)
	local hanging = node.name == "mcl_banners:hanging_banner"
	local offset
	if hanging then
		offset = hanging_banner_entity_offset
	else
		offset = standing_banner_entity_offset
	end
	-- Check if a banner entity already exists
	local bpos = vector.add(pos, offset)
	local objects = minetest.get_objects_inside_radius(bpos, 0.5)
	for _, v in ipairs(objects) do
		local ent = v:get_luaentity()
		if ent and (ent.name == "mcl_banners:standing_banner" or ent.name == "mcl_banners:hanging_banner") then
			if force then
				v:remove()
			else
				return
			end
		end
	end
	-- Spawn new entity
	local meta = minetest.get_meta(pos)
	local banner_item = meta:get_inventory():get_stack("banner", 1)
	local banner_entity = spawn_banner_entity(bpos, hanging, banner_item)

	-- Set rotation
	local rotation_level = meta:get_int("rotation_level")
	local final_yaw = rotation_level_to_yaw(rotation_level)
	banner_entity:set_yaw(final_yaw)
end

-- Banner nodes.
-- These are an invisible nodes which are only used to destroy the banner entity.
-- All the important banner information (such as color) is stored in the entity.
-- It is used only used internally.

-- Standing banner node
-- This one is also used for the help entry to avoid spamming the help with 16 entries.
minetest.register_node("mcl_banners:standing_banner", {
	_doc_items_entry_name = S("Banner"),
	_doc_items_image = "mcl_banners_item_base.png^mcl_banners_item_overlay.png",
	_doc_items_longdesc = S("Banners are tall colorful decorative blocks. They can be placed on the floor and at walls. Banners can be emblazoned with a variety of patterns using a lot of dye in crafting."),
	_doc_items_usagehelp = S("Use crafting to draw a pattern on top of the banner. Emblazoned banners can be emblazoned again to combine various patterns. You can draw up to 12 layers on a banner that way. If the banner includes a gradient, only 3 layers are possible.").."\n"..
S("You can copy the pattern of a banner by placing two banners of the same color in the crafting grid—one needs to be emblazoned, the other one must be clean. Finally, you can use a banner on a cauldron with water to wash off its top-most layer."),
	walkable = false,
	is_ground_content = false,
	paramtype = "light",
	sunlight_propagates = true,
	drawtype = "nodebox",
	-- Nodebox is drawn as fallback when the entity is missing, so that the
	-- banner node is never truly invisible.
	-- If the entity is drawn, the nodebox disappears within the real banner mesh.
	node_box = {
		type = "fixed",
		fixed = { -1/32, -0.49, -1/32, 1/32, 1.49, 1/32 },
	},
	-- This texture is based on the banner base texture
	tiles = { "mcl_banners_fallback_wood.png" },

	inventory_image = "mcl_banners_item_base.png",
	wield_image = "mcl_banners_item_base.png",

	selection_box = {type = "fixed", fixed= {-0.3, -0.5, -0.3, 0.3, 0.5, 0.3} },
	groups = {axey=1,handy=1, attached_node = 1, not_in_creative_inventory = 1, not_in_craft_guide = 1, material_wood=1, dig_by_piston=1, flammable=-1 },
	stack_max = 16,
	sounds = node_sounds,
	drop = "", -- Item drops are handled in entity code

	on_dig = on_dig_banner,
	on_destruct = on_destruct_standing_banner,
	on_punch = function(pos, node)
		respawn_banner_entity(pos, node)
	end,
	_mcl_hardness = 1,
	_mcl_blast_resistance = 1,
	on_rotate = function(pos, node, user, mode, param2)
		if mode == screwdriver.ROTATE_FACE then
			local meta = minetest.get_meta(pos)
			local rot = meta:get_int("rotation_level")
			rot = (rot - 1) % 16
			meta:set_int("rotation_level", rot)
			respawn_banner_entity(pos, node, true)
			return true
		else
			return false
		end
	end,
})

-- Hanging banner node
minetest.register_node("mcl_banners:hanging_banner", {
	walkable = false,
	is_ground_content = false,
	paramtype = "light",
	paramtype2 = "wallmounted",
	sunlight_propagates = true,
	drawtype = "nodebox",
	inventory_image = "mcl_banners_item_base.png",
	wield_image = "mcl_banners_item_base.png",
	tiles = { "mcl_banners_fallback_wood.png" },
	node_box = {
		type = "wallmounted",
		wall_side = { -0.49, 0.41, -0.49, -0.41, 0.49, 0.49 },
		wall_top = { -0.49, 0.41, -0.49, -0.41, 0.49, 0.49 },
		wall_bottom = { -0.49, -0.49, -0.49, -0.41, -0.41, 0.49 },
	},
	selection_box = {type = "wallmounted", wall_side = {-0.5, -0.5, -0.5, -4/16, 0.5, 0.5} },
	groups = {axey=1,handy=1, attached_node = 1, not_in_creative_inventory = 1, not_in_craft_guide = 1, material_wood=1, flammable=-1 },
	stack_max = 16,
	sounds = node_sounds,
	drop = "", -- Item drops are handled in entity code

	on_dig = on_dig_banner,
	on_destruct = on_destruct_hanging_banner,
	on_punch = function(pos, node)
		respawn_banner_entity(pos, node)
	end,
	_mcl_hardness = 1,
	_mcl_blast_resistance = 1,
	on_rotate = function(pos, node, user, mode, param2)
		if mode == screwdriver.ROTATE_FACE then
			local r = screwdriver.rotate.wallmounted(pos, node, mode)
			node.param2 = r
			minetest.swap_node(pos, node)
			local meta = minetest.get_meta(pos)
			local rot = 0
			if node.param2 == 2 then
				rot = 12
			elseif node.param2 == 3 then
				rot = 4
			elseif node.param2 == 4 then
				rot = 0
			elseif node.param2 == 5 then
				rot = 8
			end
			meta:set_int("rotation_level", rot)
			respawn_banner_entity(pos, node, true)
			return true
		else
			return false
		end
	end,
})

-- for pattern_name, pattern in pairs(patterns) do
for colorid, colortab in pairs(mcl_banners.colors) do
    for i, pattern_name in ipairs(pattern_names) do
	local itemid = colortab[1]
	local desc = colortab[2]
	local wool = colortab[3]
	local colorize = colortab[4]

	local itemstring
	if pattern_name == "" then
		itemstring = "mcl_banners:banner_item_" .. itemid
	else
		itemstring = "mcl_banners:banner_preview" .. "_" .. pattern_name .. "_" .. itemid
	end

	local inv
	local base
	local finished_banner
	if pattern_name == "" then
	    if colorize then
		-- Base texture with base color
		base = "mcl_banners_item_base.png^(mcl_banners_item_overlay.png^[colorize:"..colorize..")^[resize:32x32"
	    else
		base = "mcl_banners_item_base.png^mcl_banners_item_overlay.png^[resize:32x32"
	    end
	    finished_banner = base
	else
		-- Banner item preview background
		base = "mcl_banners_item_base.png^(mcl_banners_item_overlay.png^[colorize:#CCCCCC)^[resize:32x32"

		desc = S("Preview Banner")

		local pattern = "mcl_banners_" .. pattern_name .. ".png"
		local color = colorize

		-- Generate layer texture

		-- TODO: The layer texture in the icon is squished
		-- weirdly because the width/height aspect ratio of
		-- the banner icon is 1:1.5, whereas the aspect ratio
		-- of the banner entity is 1:2. A solution would be to
		-- redraw the pattern textures as low-resolution pixel
		-- art and use that instead.

		local layer = "(([combine:20x40:-2,-2="..pattern.."^[resize:16x24^[colorize:"..color..":"..layer_ratio.."))"

		function escape(text)
			 return text:gsub("%^", "\\%^"):gsub(":", "\\:") -- :gsub("%(", "\\%("):gsub("%)", "\\%)")
		end

		finished_banner = "[combine:32x32:0,0=" .. escape(base) .. ":8,4=" .. escape(layer)
	end

	inv = finished_banner

	-- Banner items.
	-- This is the player-visible banner item. It comes in 16 base colors with a lot of patterns.
	-- The multiple items are really only needed for the different item images.
	-- TODO: Combine the items into only 1 item.
	local groups
	if pattern_name == "" then
		groups = { banner = 1, deco_block = 1, flammable = -1 }
	else
		groups = { not_in_creative_inventory = 1 }
	end

	minetest.register_craftitem(itemstring, {
		description = desc,
		_tt_help = S("Paintable decoration"),
		_doc_items_create_entry = false,
		inventory_image = inv,
		wield_image = inv,
		-- Banner group groups together the banner items, but not the nodes.
		-- Used for crafting.
		groups = groups,
		stack_max = 16,

		on_place = function(itemstack, placer, pointed_thing)
			local above = pointed_thing.above
			local under = pointed_thing.under

			local node_under = minetest.get_node(under)
			if placer and not placer:get_player_control().sneak then
				-- Use pointed node's on_rightclick function first, if present
				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

				if minetest.get_modpath("mcl_cauldrons") then
					-- Use banner on cauldron to remove the top-most layer. This reduces the water level by 1.
					local new_node
					if node_under.name == "mcl_cauldrons:cauldron_3" then
						new_node = "mcl_cauldrons:cauldron_2"
					elseif node_under.name == "mcl_cauldrons:cauldron_2" then
						new_node = "mcl_cauldrons:cauldron_1"
					elseif node_under.name == "mcl_cauldrons:cauldron_1" then
						new_node = "mcl_cauldrons:cauldron"
					elseif node_under.name == "mcl_cauldrons:cauldron_3r" then
						new_node = "mcl_cauldrons:cauldron_2r"
					elseif node_under.name == "mcl_cauldrons:cauldron_2r" then
						new_node = "mcl_cauldrons:cauldron_1r"
					elseif node_under.name == "mcl_cauldrons:cauldron_1r" then
						new_node = "mcl_cauldrons:cauldron"
					end
					if new_node then
						local imeta = itemstack:get_meta()
						local layers_raw = imeta:get_string("layers")
						local layers = minetest.deserialize(layers_raw)
						if type(layers) == "table" and #layers > 0 then
							table.remove(layers)
							imeta:set_string("layers", minetest.serialize(layers))
							local newdesc = mcl_banners.make_advanced_banner_description(itemstack:get_definition().description, layers)
							local mname = imeta:get_string("name")
							-- Don't change description if item has a name
							if mname == "" then
								imeta:set_string("description", newdesc)
							end
						end

						-- Washing off reduces the water level by 1.
						-- (It is possible to waste water if the banner had 0 layers.)
						minetest.set_node(pointed_thing.under, {name=new_node})

						-- Play sound (from mcl_potions mod)
						minetest.sound_play("mcl_potions_bottle_pour", {pos=pointed_thing.under, gain=0.5, max_hear_range=16}, true)

						return itemstack
					end
				end
			end

			-- Place the node!
			local hanging = false

			-- Standing or hanging banner. The placement rules are enforced by the node definitions
			local _, success = minetest.item_place_node(ItemStack("mcl_banners:standing_banner"), placer, pointed_thing)
			if not success then
				-- Forbidden on ceiling
				if pointed_thing.under.y ~= pointed_thing.above.y then
					return itemstack
				end
				_, success = minetest.item_place_node(ItemStack("mcl_banners:hanging_banner"), placer, pointed_thing)
				if not success then
					return itemstack
				end
				hanging = true
			end
			local place_pos
			if minetest.registered_nodes[node_under.name].buildable_to then
				place_pos = under
			else
				place_pos = above
			end
			local bnode = minetest.get_node(place_pos)
			if bnode.name ~= "mcl_banners:standing_banner" and bnode.name ~= "mcl_banners:hanging_banner" then
				minetest.log("error", "[mcl_banners] The placed banner node is not what the mod expected!")
				return itemstack
			end
			local meta = minetest.get_meta(place_pos)
			local inv = meta:get_inventory()
			inv:set_size("banner", 1)
			local store_stack = ItemStack(itemstack)
			store_stack:set_count(1)
			inv:set_stack("banner", 1, store_stack)

			-- Spawn entity
			local entity_place_pos
			if hanging then
				entity_place_pos = vector.add(place_pos, hanging_banner_entity_offset)
			else
				entity_place_pos = vector.add(place_pos, standing_banner_entity_offset)
			end
			local banner_entity = spawn_banner_entity(entity_place_pos, hanging, itemstack)
			-- Set rotation
			local final_yaw, rotation_level
			if hanging then
				local pdir = vector.direction(pointed_thing.under, pointed_thing.above)
				final_yaw = minetest.dir_to_yaw(pdir)
				if pdir.x > 0 then
					rotation_level = 4
				elseif pdir.z > 0 then
					rotation_level = 8
				elseif pdir.x < 0 then
					rotation_level = 12
				else
					rotation_level = 0
				end
			else
				-- Determine the rotation based on player's yaw
				local yaw = placer:get_look_horizontal()
				-- Select one of 16 possible rotations (0-15)
				rotation_level = round((yaw / (math.pi*2)) * 16)
				if rotation_level >= 16 then
					rotation_level = 0
				end
				final_yaw = rotation_level_to_yaw(rotation_level)
			end
			meta:set_int("rotation_level", rotation_level)

			if banner_entity ~= nil then
				banner_entity:set_yaw(final_yaw)
			end

			if not minetest.is_creative_enabled(placer:get_player_name()) then
				itemstack:take_item()
			end
			minetest.sound_play({name="default_place_node_hard", gain=1.0}, {pos = place_pos}, true)

			return itemstack
		end,

		_mcl_generate_description = function(itemstack)
			local meta = itemstack:get_meta()
			local layers_raw = meta:get_string("layers")
			if not layers_raw then
				return nil
			end
			local layers = minetest.deserialize(layers_raw)
			local desc = itemstack:get_definition().description
			local newdesc = mcl_banners.make_advanced_banner_description(desc, layers)
			meta:set_string("description", newdesc)
			return newdesc
		end,
	})

	if minetest.get_modpath("mcl_core") and minetest.get_modpath("mcl_wool") and pattern_name == "" then
		minetest.register_craft({
			output = itemstring,
			recipe = {
				{ wool, wool, wool },
				{ wool, wool, wool },
				{ "", "mcl_core:stick", "" },
			}
		})
	end

	if minetest.get_modpath("doc") then
		-- Add item to node alias
		doc.add_entry_alias("nodes", "mcl_banners:standing_banner", "craftitems", itemstring)
	end
    end
end

if minetest.get_modpath("doc") then
	-- Add item to node alias
	doc.add_entry_alias("nodes", "mcl_banners:standing_banner", "nodes", "mcl_banners:hanging_banner")
end


-- Banner entities.
local entity_standing = {
	physical = false,
	collide_with_objects = false,
	visual = "mesh",
	mesh = "amc_banner.b3d",
	visual_size = { x=2.499, y=2.499 },
	textures = make_banner_texture(),
	pointable = false,

	_base_color = nil, -- base color of banner
	_layers = nil, -- table of layers painted over the base color.
		-- This is a table of tables with each table having the following fields:
			-- color: layer color ID (see colors table above)
			-- pattern: name of pattern (see list above)

	get_staticdata = function(self)
		local out = { _base_color = self._base_color, _layers = self._layers, _name = self._name }
		return minetest.serialize(out)
	end,
	on_activate = function(self, staticdata)
		if staticdata and staticdata ~= "" then
			local inp = minetest.deserialize(staticdata)
			self._base_color = inp._base_color
			self._layers = inp._layers
			self._name = inp._name
			self.object:set_properties({
				textures = make_banner_texture(self._base_color, self._layers),
			})
		end
		-- Make banner slowly swing
		self.object:set_animation({x=0, y=80}, 25)
		self.object:set_armor_groups({immortal=1})
	end,

	-- Set the banner textures. This function can be used by external mods.
	-- Meaning of parameters:
	-- * self: Lua entity reference to entity.
	-- * other parameters: Same meaning as in make_banner_texture
	_set_textures = function(self, base_color, layers)
		if base_color then
			self._base_color = base_color
		end
		if layers then
			self._layers = layers
		end
		self.object:set_properties({textures = make_banner_texture(self._base_color, self._layers)})
	end,
}
minetest.register_entity("mcl_banners:standing_banner", entity_standing)

local entity_hanging = table.copy(entity_standing)
entity_hanging.mesh = "amc_banner_hanging.b3d"
minetest.register_entity("mcl_banners:hanging_banner", entity_hanging)

-- FIXME: Prevent entity destruction by /clearobjects
minetest.register_lbm({
	label = "Respawn banner entities",
	name = "mcl_banners:respawn_entities",
	run_at_every_load = true,
	nodenames = {"mcl_banners:standing_banner", "mcl_banners:hanging_banner"},
	action = function(pos, node)
		respawn_banner_entity(pos, node)
	end,
})

minetest.register_craft({
	type = "fuel",
	recipe = "group:banner",
	burntime = 15,
})

