local log, fmt = suppl_add_logfn("tools")

gconfig_register("bgicon_size_px", 64)
gconfig_register("bgicon_grid_px", 92)
gconfig_register("bgicon_border_px", 8)
gconfig_register("bgicon_grid_snap", true)
gconfig_register("bgicon_enable", false)
gconfig_register("bgicon_zoom", false)
gconfig_register("bgicon_pan", false)
gconfig_register("bgicon_font_sz", 8)
gconfig_register("bgicon_bias_side", "right")

local wsicons = {}
-- load / unpack keys into wsicons

--
-- [bringup]
--
--   drag and drop receiver to drag icons from browser or files from clients
--   load/store current set
--   controls for settings
--      -> forward into spawn_iconview as opts
--
--   keyboard controls (move selection, delection, trigger) (need input capture)
--   minimize to non-persistent icon
--   hover to show full text if cropped
--
-- [advanced]
--   zooming
--   region select
--   workspace/display controls
--   stateful images
--   non-uniform sizes
--   bind to named window (essentially becomes folders)
--
-- active options:
--  toggle label
--  status-symbol (window, alert)
--
-- 1. load icons from key config
--    [trigger = path:/]
--    [rtrigger = path:/]
--    [source = icon:/name, bitmap_path]
--    [label = ..]
--

function bgicons_build_icon(v)
	local icons = active_display().icons
	if not icons then
		log("kind=warning:display_no_icons")
	end

	if type(v.trigger) == "string" then
		local sym = v.trigger
		v.trigger_path = sym
		v.trigger = function()
			dispatch_symbol(sym)
		end
	end

	if type(v.alt_trigger) == "string" then
		local sym = v.alt_trigger
		v.alt_trigger_path = sym
		v.trigger = dispatch_symbol(sym)
	end

-- use the icon manager to request image source unless factory is defined

	local icon = icons:add(v.name or "missing_name", v)
	return icon
end

local function disable_all()
	local ad = active_display()
	if ad.icons then
		ad.icons:destroy()
	end
end

local function synch_all()
	local ad = active_display()
	local icons = ad.icons
	if not icons then
		return
	end

	icons.icon_w = gconfig_get("bgicon_size_px") * ad.scalef
	icons.icon_h = ad.icons.icon_w
	icons.allow_pan = gconfig_get("bgicon_pan")
	icons.allow_zoom = gconfig_get("bgicon_zoom")
	icons.grid.snap = gconfig_get("bgicon_grid_snap")
	icons.grid.w = gconfig_get("bgicon_grid_px") * ad.scalef
	icons.grid.h = gconfig_get("bgicon_grid_px") * ad.scalef
	icons.border = gconfig_get("bgicon_border_px") * ad.scalef
	icons.grid.bias = gconfig_get("bgicon_bias_side")

-- trigger the resize/realign
	icons:resize(ad.effective_width, ad.effective_height)
end

local function add_fileref_icon(v)
	local name = string.split(v, "/")
	name = name[#name]
	if not name then
		return
	end

-- suppl_valid_vsymbol("icon_file") OR
-- unicode symbol for font with 0x_ OR
-- some vallback
	local icon = {
		name = "fileref_bgicon",
		label = name,
		factory =
		function(w, h)
			return color_surface(w, h, 127, 127, 0)
		end,
		trigger = "/browse/" .. v,
		alt_trigger =
		function()
-- popup context menu
		end
	}

	bgicons_build_icon(icon)
end

local function enable_display(ad)
	if ad.icons then
		return
	end

	log("kind=status:enable_desktop_icons")

	local icons =
	uiprim_icon_spawn(
		ad.width, ad.height,
		{
			allow_pan = false,
			allow_zoom = false,
			animation_speed = gconfig_get("animation_speed"),
			grid_snap = gconfig_get("bgicon_grid_snap"),
			icon_w = gconfig_get("bgicon_size_px") * ad.scalef,
			icon_h = gconfig_get("bgicon_size_px") * ad.scalef,
			grid_w = math.floor(gconfig_get("bgicon_grid_px") * ad.scalef),
			grid_h = math.floor(gconfig_get("bgicon_grid_px") * ad.scalef),
			border = math.floor(gconfig_get("bgicon_border_px") * ad.scalef),
			selection_shader =
			function(vid)
				shader_setup(vid, "ui", "regsel", "active");
			end,
			check_modifier =
			function()
				local m1, m2 = dispatch_meta()
				return m1 or m2
			end,
			click_through =
			function(ind, x, y)
				if ind == MOUSE_RBUTTON then
					local action = gconfig_get("float_bg_rclick")
					if action then
						dispatch_symbol(action)
					end
				end
			end
		}
	)

-- need to anchor to ws.anchor and swap when workspace is supposed to
-- switch
	local om = icons.mouse.motion
	local oc = icons.mouse.click

	icons.mouse.motion =
	function(...)
		local ms = mouse_state()
		if ms.cursortag then
		end
		om(...)
	end

	icons.mouse.click =
	function(...)
		local ms = mouse_state()

-- Handling drag / drop is a bit special, if the origin is the browser we can
-- create a reference, then add context menu options for copying, generating
-- preview or indexing.
--
-- If the source is a window, we will need a backing directory (configure)
-- and possibly popup a type selector if there is announced bchunkstate info.
--
-- This is also limited in the sense that we don't have the _tui bgcopy
-- function exposed. This should be added to arcan as another overloaded form
-- of open_nonblock with the source userdata provided, map to
-- arcan_shmif_bgcopy and set the progress pipe to the returned userdata.
--
-- the cursortag has a handler that takes (wnd:dst, accept:bool, table:src)
-- but we don't want to trigger it but rather handle it specifically.
--
		if ms.cursortag then
			if ms.cursortag.ref == "browser" then
				for i,v in ipairs(ms.cursortag.src) do
					add_fileref_icon("shared" .. v.path)
				end

				active_display():cancellation()
			end
		end

		if ad.selected then
			ad.selected:deselect()
		end
		oc(...)
	end

	mouse_addlistener(icons.mouse)
	link_image(icons.clipping_anchor, ad.anchor)
	order_image(icons.clipping_anchor, 4)

-- get set from key_value store, encode as packed shmif-arg in value
--
--   useful subkeys:
--     label
--     path
--     altpath
--     source (to icon-manager)
--

	ad.icons = icons
	for _, v in ipairs(wsicons) do
		local icn = build_icon(v)
	end
end

local function disable_all()
	active_display().icons:destroy()
end

-- we can't just enumerate all displays for positioning and anchoring
-- right now as we might be in the storm of initial display adding and
-- be called from within the existing iterator, add the enabler as a
-- timer.
local function enable_all()
	for disp in all_tilers_iter() do
		enable_display(disp)
	end
end

if gconfig_get("bgicon_enable") then
	timer_add_periodic("bgicon", 1, true, enable_all)
end

local iconmenu = {
	{
	label = "Enabled",
	kind = "value",
	name = "enabled",
	set = {LBL_YES, LBL_NO, LBL_FLIP},
	initial = function()
		return gconfig_get("bgicon_enable") and LBL_YES or LBL_NO;
	end,
	handler = suppl_flip_handler("bgicon_enable",
	function(state)
		if state == LBL_YES then
			enable_all();
		else
			disable_all();
		end
	end);
	},
	{
		name = "icon_sz",
		label = "Icon Size",
		description = "Set the default unit pixel size for the icons at 1x scale",
		kind = "value",
		initial = function()
			return gconfig_get("bgicon_size_px")
		end,
		validator = gen_valid_num(16, 256),
		hint = "(16..256)",
		handler =
		function(ctx, val)
			local num = tonumber(val)
			gconfig_set("bgicon_size_px", num)
			synch_all()
		end
	},
	{
		name = "bias_side",
		label = "Grow Side",
		description = "Set the bias for positioning new icons and display resize alignment",
		kind = "value",
		set = {"left", "right", "top", "bottom"},
		initial = function()
			return gconfig_get("bgicon_bias_side")
		end,
		handler = function(ctx, val)
			gconfig_set("bgicon_bias_side", val)
			synch_all()
		end
	},
	{
		name = "grid_sz",
		label = "Grid Size",
		description = "Set the default unit pixel size for grid alignment",
		kind = "value",
		initial = function()
			return gconfig_get("bgicon_grid_px")
		end,
		hint = "(16..256)",
		validator = gen_valid_num(16, 256),
		handler =
		function(ctx, val)
			local num = tonumber(val)
			gconfig_set("bgicon_grid_px", num)
			synch_all()
		end
	},
	{
		name = "grid_snap_toggle",
		label = "Grid Snap",
		kind = "value",
		description = "Set if icon drag should force align to grid",
		set = {LBL_YES, LBL_NO, LBL_FLIP},
		initial = function()
			return gconfig_get("bgicon_grid_snap") and LBL_YES or LBL_NO;
		end,
		handler = suppl_flip_handler("bgicon_grid_snap",
			function(state)
				synch_all()
			end
		)
	},
	{
		name = "add_random",
		label = "Add Random",
		kind = "action",
		eval = function()
			return gconfig_get("bgicon_enable") and DEBUGLEVEL > 0;
		end,
		handler = function()
			table.insert(
				wsicons, build_icon({
					label = tostring(CLOCK),
					trigger = "/global",
					rtrigger = "/global/system",
					factory =
					function(size_px)
						color_surface(
							size_px,
							size_px,
							math.random(127, 255),
							math.random(127, 255),
							math.random(127, 255)
						);
					end
				})
			)
		end
	}
}

menus_register("global", "settings/wspaces/float",
{
	kind = "action",
	submenu = true,
	label = "Icons",
	name = "icons",
	description = "Control desktop icon presence and behaviour",
	handler = iconmenu
}
);
