--   This file is part of Reno desktop.
--
--  Reno desktop is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
--
--  Reno desktop is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
--
--  You should have received a copy of the GNU General Public License along with Reno desktop. If not, see <https://www.gnu.org/licenses/>. 
-- Asynchronous XDG data aggregator
local start_human = os.time()
local start_computer = os.clock()
local menu_utils = require("menubar.utils")
local menu_gen = require("menubar.menu_gen")
local awful = require("awful")
local gears = require("gears")
local json = require("dkjson")
menu_utils.wm_name = ""

-- Directories to scan for .desktop files
local desktop_dirs = {}
local desktop_dirs_complete = 0
local _ = ((table.concat(gears.filesystem.get_xdg_data_dirs(),":") or
"/usr/share:/usr/local/share")..":"..os.getenv("HOME").."/.local/share"):gsub("[^:]*",function(path)
    if gears.filesystem.dir_readable(path.."/applications") then
        table.insert(desktop_dirs, path.."/applications")
    end
end)


-- Global xdg data struct
_G.xdg = {
    directory_integrity = {},
    directory_listings = {},
    apps = {},
    categories = {
        Other = {
            icon = "applications-other",
            apps = {}
        },
        Wine = {
            icon = "wine",
            apps = {}
        }
    }
}

-- Load cached applications
local cache_file = io.open(gears.filesystem.get_xdg_cache_home()..".reno_xdg_cache.json","r")
local cache
if cache_file then
    cache = json.decode(cache_file:read("*a"))
    cache_file:close()
end

-- Add missing category entries as defined by awesome
for _,v in pairs(menu_gen.all_categories) do
    xdg.categories[v.app_type] = {
        name = v.name,
        icon = v.icon_name,
        apps = {}
    }
end

-- Asynchronous scanning process
for _,v in pairs(desktop_dirs) do
        xdg.directory_listings[v] = {}
        awful.spawn.with_line_callback("find "..tostring(v).." -maxdepth 1 -name *.desktop",{
            stdout = function(line)
                -- Assume the cache is valid for a listed file
                if cache and cache.directory_listings[v][line] then
                    xdg.directory_listings[v][line] = true
                    xdg.apps[line] = cache.apps[line]
                    xdg.categories[cache.apps[line].category].apps[line] = cache.apps[line]
                    return
                end
                local data = menu_utils.parse_desktop_file(line)
                if data.NoDisplay then
                    return
                end
                local appdata = {
                    name = data.Name,
                    category = "Other",
                    exec = data.Exec,
                    icon = (data.Icon and menu_utils.lookup_icon(data.Icon)),
                    description = data.Comment
                }
                -- Match first available cateogry for sorting
                for _,vv in pairs(data.Categories or {"Other"}) do
                    if xdg.categories[vv] then
                        appdata.category = vv
                        break
                    end
                    -- Oh how do I love those Wine applications and their categories
                    if vv:match("^Wine") then
                        appdata.category = "Wine"
                        break
                    end
                end
                -- Open terminal apps in the terminal (duh)
                if data.Terminal then
                    appdata.exec = global.terminal.." -e "..appdata.exec
                end
                -- Just for you, Wine - special case because you're being a shit
                if (appdata.exec and appdata.exec:find("%W?wine ")) then
                    appdata.category = "Wine"
                end
                xdg.apps[line] = appdata
                xdg.categories[appdata.category].apps[line] = appdata
                -- Add the file to the listing of cached ones
                xdg.directory_listings[v][line] = true
            end,
            output_done = function()
                -- Save directory listing hash
                desktop_dirs_complete = desktop_dirs_complete + 1
                -- Call a global signal
                awesome.emit_signal("xdg::dir_finished",v)
            end
        })
end

local count = function(t)
    local n = 0
    for _,_ in pairs(t) do
        n = n + 1
    end
    return n
end

awesome.connect_signal("xdg::dir_finished",function(dir)
    -- We only send the all_finished signal when all directories finished processing
    if desktop_dirs_complete == #desktop_dirs then
        -- Clean up empty categories
        for k,v in pairs(xdg.categories) do
            if count(v.apps) == 0 then
                xdg.categories[k] = nil
            end
        end
        -- Save the cache if it doesn't exist yet
        io.open(gears.filesystem.get_xdg_cache_home()..".reno_xdg_cache.json","w"):write(json.encode(xdg)):close()
        -- Finally, call the all_finished signal
        awesome.emit_signal("xdg::all_finished")
    end
end)

-- Before exiting, save all xdg cache
awesome.connect_signal("exit",function()
    io.open(gears.filesystem.get_xdg_cache_home()..".reno_xdg_cache.json","w"):write(json.encode(xdg)):close()
end)
