---------------------------------------------------------------------------
-- @author Emmanuel Lepage-Vallee <elv1313@gmail.com>
-- @copyright 2018-2019 Emmanuel Lepage-Vallee
-- @module root
---------------------------------------------------------------------------

local capi = { root = root }
local gtable = require("gears.table")
local gtimer = require("gears.timer")
local unpack = unpack or table.unpack -- luacheck: globals unpack (compatibility with Lua 5.1)

for _, type_name in ipairs { "button", "key" } do
    local prop_name = type_name.."s"

    -- The largest amount of wall clock time when loading Awesome 3.4 rc.lua
    -- was the awful.util.table.join (now gears.table.join). While the main
    -- bottleneck in the newer releases moved into LGI, doing all these `join`
    -- slow startup down quite a lot. On top of that, with the ability to add
    -- and remove keys and buttons can cause a large overhead of its own. To
    -- mitigate that, only set the actual content once per main loop iteration.
    --
    -- The C code also delay uploading these keys into the X server to prevent
    -- too many keyboard map changes from freezing Awesome.
    local has_delayed, added, removed = false, {}, {}

    local function delay(value)
        if value then
            table.insert(added, value)
        end

        if has_delayed then return end

        has_delayed = true

        gtimer.delayed_call(function()
            local new_values = capi.root["_"..prop_name]()

            -- In theory, because they are inserted ordered, it is safe to assume
            -- the once found, the capi.key/button will be next to each other.
            for _, v in ipairs(removed) do
                local idx = gtable.hasitem(new_values, v[1])

                if idx then
                    for i=1, #v do
                        assert(
                            new_values[idx] == v[i],
                            "The root private "..type_name.." table is corrupted"
                        )

                        table.remove(new_values, idx)
                    end
                end

                idx = gtable.hasitem(added, v)

                if idx then
                    table.remove(added, idx)
                end
            end

            local joined = gtable.join(unpack(added))
            new_values = gtable.merge(new_values, joined)

            capi.root["_"..prop_name](new_values)

            has_delayed, added, removed = false, {}, {}
        end)
    end

    capi.root["_append_"..type_name] = function(value)
        if not value then return end

        local t1 = capi.root._private[prop_name]

        -- Simple case
        if (not t1) or not next(t1) then
            capi.root[prop_name] = {value}
            assert(capi.root._private[prop_name])
            return
        end

        delay(value)
    end

    capi.root["_append_"..prop_name] = function(values)
        -- It's pointless to use gears.table.merge, in the background it has the
        -- same loop anyway. Also, this isn't done very often.
        for _, value in ipairs(values) do
            capi.root["_append_"..type_name](value)
        end
    end

    capi.root["_remove_"..type_name] = function(value)
        if not capi.root._private[prop_name] then return end

        local k = gtable.hasitem(capi.root._private[prop_name], value)

        if k then
            table.remove(capi.root._private[prop_name], k)
        end

        -- Because of the legacy API, it is possible the capi.key/buttons will
        -- be in the formatted table but not of the awful.key/button one.
        assert(value[1])

        table.insert(removed, value)
    end

    capi.root["has_"..type_name] = function(item)
        if not item["_is_capi_"..type_name] then
            item = item[1]
        end

        return gtable.hasitem(capi.root["_"..prop_name](), item) ~= nil
    end

    assert(root[prop_name])

end