-- Grab environment
local setmetatable = setmetatable
local ipairs = ipairs
local wibox = require("wibox")
local awful = require("awful")
local beautiful = require("beautiful")
local timer = require("gears.timer")

local modutil = require("flex.util")

-- Initialize tables for module
local tooltip = { mt = {} }

-- Generate default theme vars
local function default_style()
	local style = {
		padding = { vertical = 3, horizontal = 5 },
		margin = 0,
		timeout = 1,
		font = "Fira Code 12",
		border_width = 2,
		set_position = nil,
		color = { border = "#404040", text = "#aaaaaa", wibox = "#202020" },
		shape = nil,
	}
	return modutil.table.merge(style, modutil.table.check(beautiful, "float.tooltip") or {})
end

-- Create a new tooltip
function tooltip.new(args, style)
	-- Initialize vars
	args = args or {}
	local objects = args.objects or {}
	style = modutil.table.merge(default_style(), style or {})

	-- Construct tooltip window with wibox and textbox
	local ttp = { wibox = wibox({ type = "tooltip" }), tip = nil }
	local tb = wibox.widget.textbox()
	tb:set_align("center")

	ttp.widget = tb
	ttp.wibox:set_widget(tb)
	tb:set_font(style.font)

	-- configure wibox properties
	ttp.wibox.visible = false
	ttp.wibox.ontop = true
	ttp.wibox.border_width = style.border_width
	ttp.wibox.border_color = style.color.border
	ttp.wibox.shape = style.shape
	ttp.wibox:set_bg(style.color.wibox)
	ttp.wibox:set_fg(style.color.text)

	-- Tooltip size configurator
	function ttp:set_geometry()
		local wibox_sizes = self.wibox:geometry()
		local w, h = self.widget:get_preferred_size()
		local requsted_width = w + 2 * style.padding.horizontal
		local requsted_height = h + 2 * style.padding.vertical

		if wibox_sizes.width ~= requsted_width or wibox_sizes.height ~= requsted_height then
			self.wibox:geometry({
				width = requsted_width,
				height = requsted_height,
			})
		end
	end

	-- Set timer to make delay before tooltip show
	local show_timer = timer({ timeout = style.timeout })
	show_timer:connect_signal("timeout", function()
		ttp:set_geometry()
		if style.set_position then
			style.set_position(ttp.wibox)
		else
			awful.placement.under_mouse(ttp.wibox)
		end
		awful.placement.no_offscreen(ttp.wibox, { margins = style.margin })
		ttp.wibox.visible = true
		show_timer:stop()
	end)

	-- Tooltip metods
	function ttp.show()
		if not show_timer.started then
			show_timer:start()
		end
	end

	function ttp.hide()
		if show_timer.started then
			show_timer:stop()
		end
		if ttp.wibox.visible then
			ttp.wibox.visible = false
		end
	end

	function ttp:set_text(text)
		if self.tip ~= text then
			self.widget:set_text(text)
			self.tip = text

			if self.wibox.visible then
				self:set_geometry()
				self.wibox.x = mouse.coords().x - self.wibox.width / 2
				awful.placement.no_offscreen(self.wibox, { margins = style.margin })
			end
		end
	end

	function ttp:add_to_object(object)
		object:connect_signal("mouse::enter", self.show)
		object:connect_signal("mouse::leave", self.hide)
	end

	function ttp:remove_from_object(object)
		object:disconnect_signal("mouse::enter", self.show)
		object:disconnect_signal("mouse::leave", self.hide)
	end

	-- Add tooltip to objects
	if objects then
		for _, object in ipairs(objects) do
			ttp:add_to_object(object)
		end
	end

	return ttp
end

-- Config metatable to call tooltip module as function
function tooltip.mt:__call(...)
	return tooltip.new(...)
end

return setmetatable(tooltip, tooltip.mt)
