-- https://github.com/LuaDist-testing/ltdiff

local ltdiff = {}

local function table_diff(A, B)
    local diff = {
        del = {},
        mod = {},
        sub = {},
    }

    for k, v in pairs(A) do
        if type(A[k]) == "function" or type(A[k]) == "userdata" then
            error("table_diff only supports diffs of tables!")
        elseif B[k] ~= nil and type(A[k]) == "table" and type(B[k]) == "table" then
            diff.sub[k] = table_diff(A[k], B[k])

            if next(diff.sub[k]) == nil then
                diff.sub[k] = nil
            end
        elseif B[k] == nil then
            diff.del[#(diff.del) + 1] = k
        elseif B[k] ~= v then
            diff.mod[k] = B[k]
        end
    end

    for k, v in pairs(B) do
        if type(B[k]) == "function" or type(B[k]) == "userdata" then
            error("table_diff only supports diffs of tables!")
        elseif diff.sub[k] ~= nil then
            -- skip
        elseif A[k] ~= nil and type(A[k]) == "table" and type(B[k]) == "table" then
            diff.sub[k] = table_diff(B[k], A[k])

            if next(diff.sub[k]) == nil then
                diff.sub[k] = nil
            end
        elseif B[k] ~= A[k] then
            diff.mod[k] = v
        end
    end

    if next(diff.sub) == nil then
        diff.sub = nil
    end

    if next(diff.mod) == nil then
        diff.mod = nil
    end

    if next(diff.del) == nil then
        diff.del = nil
    end

    return diff
end

local function table_patch(A, diff)
    if diff.sub ~= nil then
        for k, v in pairs(diff.sub) do
            A[k] = table_patch(A[k], v)
        end
    end

    if diff.del ~= nil then
        for _, v in pairs(diff.del) do
            A[v] = nil
        end
    end

    if diff.mod ~= nil then
        for k, v in pairs(diff.mod) do
            A[k] = v
        end
    end

    return A
end

ltdiff.diff = table_diff
ltdiff.patch = table_patch

return ltdiff
