-- Copyright (c) 2025 Huawei Technologies Co., Ltd.
-- openUBMC is licensed under Mulan PSL v2.
-- You can use this software according to the terms and conditions of the Mulan PSL v2.
-- You may obtain a copy of Mulan PSL v2 at:
--         http://license.coscl.org.cn/MulanPSL2
-- THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
-- EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
-- MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
-- See the Mulan PSL v2 for more details.

--- @class SpanParser
--- @field parse_span_text function
--- @field parse_span_file function
local SpanParser = {}
local complex_parser = {}

---
--- 是否包含在数组内, 如果table为空, 则返回false, 如果value为空, 则返回false
---
--- @param table table
--- @param value string
--- @return boolean
local function contains(table, value)
    for _, v in ipairs(table) do
        if v == value then
            return true
        end
    end
    return false
end

---
--- 解析attributes
---
--- @param context table
--- @param lineno number
--- @param attributes table
--- @return table
local function parse_attributes(context, lineno, attributes)
    while lineno <= #context do
        local content = context[lineno]

        -- attributes end
        if content:match("^}$") then
            return lineno
        end

        -- attributes key-value
        local key, value = content:match("^([^:]+):(.*)$")
        key = key and string.gsub(key, "^%s*(.-)%s*$", "%1") or nil
        value = value and string.gsub(value, "^%s*(.-)%s*$", "%1") or nil
        if not key or complex_parser[key] then
            return lineno
        end
        if key and value then
            attributes[key] = value
        end
        lineno = lineno + 1
    end
    return lineno
end

local function parse_node(context, lineno, node)
    while lineno <= #context do
        local content = context[lineno]

        -- node end
        if content:match("^}$") then
            return lineno
        end

        -- node properties
        local key, value = content:match("^([^:]+):(.*)$")
        key = key and string.gsub(key, "^%s*(.-)%s*$", "%1") or nil
        value = value and string.gsub(value, "^%s*(.-)%s*$", "%1") or nil
        if key == "attributes" then
            node.attributes = {}
            lineno = parse_attributes(context, lineno + 1, node.attributes)
            goto continue
        end
        if key and value then
            node[key] = value
        end
        lineno = lineno + 1
        ::continue::
    end
    return lineno
end

local function parse_nodes(context, lineno, nodes)
    while lineno <= #context do
        local content = context[lineno]
        if content:match("^{$") then
            local node = {}
            lineno = parse_node(context, lineno + 1, node)
            table.insert(nodes, node)
        else
            return lineno
        end
        lineno = lineno + 1
    end
    return lineno
end

complex_parser = {attributes = parse_attributes, events = parse_nodes, links = parse_nodes}

local function parse_span(context, lineno, span)
    while lineno <= #context do
        local content = context[lineno]

        -- span end
        if content:match("^}$") then
            return lineno + 1
        end

        -- span properties
        local key, value = content:match("^([^:]+):(.*)$")
        key = key and string.gsub(key, "^%s*(.-)%s*$", "%1") or nil
        value = value and string.gsub(value, "^%s*(.-)%s*$", "%1") or nil
        if not key then
            return lineno
        end
        if complex_parser[key] then
            span[key] = {}
            lineno = complex_parser[key](context, lineno + 1, span[key])
        elseif key and value then
            span[key] = value
            lineno = lineno + 1
        end
    end
    return lineno
end

---
--- 解析span文本内容
---
--- @param text string
--- @return table
function SpanParser:parse_span_text(text)
    local spans = {}
    
    -- 按行处理文本
    local context = {}
    for line in text:gmatch("[^\r\n]+") do
        local content = string.gsub(line, "^%s*(.-)%s*$", "%1")
        
        -- 跳过空行
        if content == "" then
            goto continue
        end

        table.insert(context, content)
        ::continue::
    end

    local lineno = 1
    while lineno <= #context do
        local content = context[lineno]
        -- span start
        if content:match("^{") then
            local span = {}
            lineno = parse_span(context, lineno + 1, span)
            table.insert(spans, span)
        else
            lineno = lineno + 1
        end
    end
    
    return spans
end

---
--- 解析span文件
---
--- @param text string
--- @return table
function SpanParser:parse_span_file(filename)
    local file = io.open(filename, "r")
    if not file then
        return nil
    end
    local text = file:read("*all")
    file:close()
    return self:parse_span_text(text)
end

return SpanParser
