-- Grab environment
local setmetatable = setmetatable
local string = string
local type = type
local pcall = pcall
local print = print
local math = math

-- local Gdk = require("lgi").Gdk
-- local pixbuf = require("lgi").GdkPixbuf
-- local cairo = require("lgi").cairo
local base = require("wibox.widget.base")
local surface = require("gears.surface")
local color = require("gears.color")

local pixbuf
local function load_pixbuf()
	local lgi = require("lgi")
	lgi.require("Gtk", "3.0")
	pixbuf = lgi.GdkPixbuf
end
local is_pixbuf_loaded = pcall(load_pixbuf)

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

-- Cache table and a counter for the number of cached images
local cache = setmetatable({}, { __mode = "k" })
local cache_count = 0

-- Support functions
-- Check if given argument is SVG file
local function is_svg(args)
	return type(args) == "string" and string.match(args, "%.svg")
end

-- Check if need scale image
local function need_scale(widg, width, height)
	return (widg._image.width ~= width or widg._image.height ~= height) and widg.resize_allowed
end

-- Cache functions
local function get_cache(file, width, height)
	return cache[file .. "-" .. width .. "x" .. height]
end

local function set_cache(file, width, height, surf)
	cache[file .. "-" .. width .. "x" .. height] = surf
	cache_count = cache_count + 1

	-- Clear the cache if it grows too large (arbitrary limit)
	if cache_count > 50 then
		cache = setmetatable({}, { __mode = "k" })
		cache_count = 0
		print("svgbox cache cleared.")
	end
end

-- Get cairo pattern
local function get_current_pattern(cr)
	cr:push_group()
	cr:paint()
	return cr:pop_group()
end

-- Create Gdk PixBuf from SVG file with given sizes
local function pixbuf_from_svg(file, width, height)
	local cached = get_cache(file, width, height)

	if cached then
		return cached
	else
		-- naughty.notify({ text = file })
		local buf = pixbuf.Pixbuf.new_from_file_at_scale(file, width, height, true)
		set_cache(file, width, height, buf)
		return buf
	end
end

-- Returns a new svgbox
function svgbox.new(image, resize_allowed, newcolor)
	-- Create custom widget
	local widg = base.make_widget()

	-- User functions
	function widg:set_image(image_name)
		local loaded_image

		if type(image_name) == "string" then
			local success, result = pcall(surface.load, image_name)
			if not success then
				print("Error while reading '" .. image_name .. "': " .. result)
				return false
			end
			self.image_name = image_name
			loaded_image = result
		else
			loaded_image = surface.load(image_name)
		end

		if loaded_image and (loaded_image.height <= 0 or loaded_image.width <= 0) then
			return false
		end

		self._image = loaded_image
		self.is_svg = is_svg(image_name)

		self:emit_signal("widget::redraw_needed")
		return true
	end

	function widg:set_color(new_color)
		if self.color ~= new_color then
			self.color = new_color
			self:emit_signal("widget::redraw_needed")
		end
	end

	function widg:set_resize(allowed)
		self.resize_allowed = allowed
		self:emit_signal("widget::redraw_needed")
	end

	function widg:set_vector_resize(allowed)
		self.vector_resize_allowed = allowed
		self:emit_signal("widget::redraw_needed")
	end

	-- Fit
	function widg:fit(_, width, height)
		local fw, fh = self:get_forced_width(), self:get_forced_height()
		if fw or fh then
			return fw or width, fh or height
		else
			if not self._image then
				return 0, 0
			end

			local w, h = self._image.width, self._image.height

			if self.resize_allowed or w > width or h > height then
				local aspect = math.min(width / w, height / h)
				return w * aspect, h * aspect
			end

			return w, h
		end
	end

	-- Draw
	function widg:draw(_, cr, width, height)
		if width == 0 or height == 0 or not self._image then
			return
		end

		local w, h = self._image.width, self._image.height
		local aspect = math.min(width / w, height / h)

		cr:save()

		-- Apply scaling for both raster and vector images
		if need_scale(self, width, height) then
			cr:scale(aspect, aspect)
		end

		if self.is_svg and self.vector_resize_allowed and is_pixbuf_loaded then
			-- for vector image
			local pixbuf_ = pixbuf_from_svg(self.image_name, math.floor(w * aspect), math.floor(h * aspect))
			cr:set_source_pixbuf(pixbuf_, 0, 0)
		else
			-- for raster image
			cr:set_source_surface(self._image, 0, 0)
		end

		-- set icon color if need
		if self.color then
			local pattern = get_current_pattern(cr)
			cr:set_source(color(self.color))
			cr:mask(pattern, 0, 0)
		else
			cr:paint()
		end

		cr:restore()
	end

	if resize_allowed ~= nil then
		widg.resize_allowed = resize_allowed
	else
		widg.resize_allowed = true
	end

	widg.color = newcolor
	widg.vector_resize_allowed = true

	if image then
		widg:set_image(image)
	end

	return widg
end

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

return setmetatable(svgbox, svgbox.mt)
