local CallStackTrace = {}

--------------------------------------------------------------------------------

local function describeFrame(desc)

    if desc.what == "Lua" then

        if desc.name then
            return string.format("    %s:%d: in function %s",
                                desc.short_src,
                                desc.currentline,
                                desc.name)
        else
            return string.format("    %s:%d: in function <%s:%d>",
                                desc.short_src,
                                desc.currentline,
                                desc.short_src,
                                desc.linedefined)
        end

    elseif desc.what == "C" then

        return "    native code"

    elseif desc.what == "main" then

        return string.format("    %s:%d: in main chunk", desc.short_src, desc.currentline)

    end

end

--------------------------------------------------------------------------------

local mt = {

    __tostring = function(self)

        local chunks = self._skipPatterns or {}

        local frames = {}

        local skipped = 0
        local skipping = false
        local prevWasNative = false

        for level, desc in ipairs(self._stack) do

            local shortSrc = desc.short_src or ''

            local skipThis = shortSrc:match("[/\\]Rx[/\\]")

            if not skipThis then
                for _, pattern in ipairs(chunks) do
                    if shortSrc:match(pattern) then
                        skipThis = true
                        break
                    end
                end
            end

            if (desc.what == "C" or desc.what == "tail") and skipping then
                skipThis = true
            end

            if skipThis then

                if skipping then

                    skipped = skipped + 1

                else

                    if prevWasNative then
                        frames[#frames] = nil
                    end

                    skipped = 1
                    skipping = true

                    frames[#frames + 1] = "x"

                end

                frames[#frames] = string.format("    (skipped %d frames)", skipped)

            else

                skipping = false
                frames[#frames + 1] = describeFrame(desc)

            end

            prevWasNative = desc.what == "C"

        end

        return table.concat(frames, "\n")

    end,

}

--------------------------------------------------------------------------------

function CallStackTrace.capture(optSkipPatterns)

    local level = 1
    local stackFrameDescriptions = {}

    while true do

        local desc = debug.getinfo(level + 1, 'nSl')
        if not desc then
            break
        end

        stackFrameDescriptions[level] = desc
        level = level + 1

    end

    return setmetatable({ _stack = stackFrameDescriptions, _skipPatterns = optSkipPatterns }, mt)

end

--------------------------------------------------------------------------------

return CallStackTrace
