local parser = require 'parser'
local core = require 'core'
local buildVM = require 'vm'

local SymbolKind = {
    File = 1,
    Module = 2,
    Namespace = 3,
    Package = 4,
    Class = 5,
    Method = 6,
    Property = 7,
    Field = 8,
    Constructor = 9,
    Enum = 10,
    Interface = 11,
    Function = 12,
    Variable = 13,
    Constant = 14,
    String = 15,
    Number = 16,
    Boolean = 17,
    Array = 18,
    Object = 19,
    Key = 20,
    Null = 21,
    EnumMember = 22,
    Struct = 23,
    Event = 24,
    Operator = 25,
    TypeParameter = 26,
}

rawset(_G, 'TEST', true)

local EXISTS = {}

local function eq(a, b)
    if a == EXISTS and b ~= nil then
        return true
    end
    local tp1, tp2 = type(a), type(b)
    if tp1 ~= tp2 then
        return false
    end
    if tp1 == 'table' then
        local mark = {}
        for k in pairs(a) do
            if not eq(a[k], b[k]) then
                return false
            end
            mark[k] = true
        end
        for k in pairs(b) do
            if not mark[k] then
                return false
            end
        end
        return true
    end
    return a == b
end

local function checkArcoss(symbols)
    local lastFinish = 0
    for _, symbol in ipairs(symbols) do
        assert(symbol.range[1] <= symbol.selectionRange[1])
        assert(symbol.range[2] >= symbol.selectionRange[2])
        assert(symbol.range[2] > lastFinish)
        lastFinish = symbol.range[2]
        if symbol.children then
            checkArcoss(symbol.children)
        end
    end
end

function TEST(script)
    return function (expect)
        local ast = parser:parse(script, 'lua', 'Lua 5.3')
        local vm = buildVM(ast)
        assert(vm)
        local result = core.documentSymbol(vm)
        assert(eq(expect, result))
        checkArcoss(result)
    end
end

TEST [[
local function f()
end
]]
{
    [1] = {
        name = 'f',
        detail = 'function f()',
        kind = SymbolKind.Function,
        range = {1, 22},
        selectionRange = {16, 16},
        valueRange = {1, 22},
    }
}

TEST [[
function f()
end
]]
{
    [1] = {
        name = 'f',
        detail = 'function f()',
        kind = SymbolKind.Function,
        range = {1, 16},
        selectionRange = {10, 10},
        valueRange = {1, 16},
    }
}

TEST [[
return function ()
end
]]
{
    [1] = {
        name = '',
        detail = 'function ()',
        kind = SymbolKind.Function,
        range = {8, 22},
        selectionRange = {8, 8},
        valueRange = {8, 22},
    }
}

TEST [[
f = function ()
end
]]
{
    [1] = {
        name = 'f',
        detail = 'function f()',
        kind = SymbolKind.Function,
        range = {1, 1},
        selectionRange = {1, 1},
        valueRange = {5, 19},
    }
}

TEST [[
local f = function ()
end
]]
{
    [1] = {
        name = 'f',
        detail = 'function f()',
        kind = SymbolKind.Function,
        range = {7, 7},
        selectionRange = {7, 7},
        valueRange = {11, 25},
    }
}

TEST [[
function mt:add()
end
]]
{
    [1] = {
        name = 'mt:add',
        detail = 'function mt:add()',
        kind = SymbolKind.Field,
        range = {1, 21},
        selectionRange = {10, 15},
        valueRange = {1, 21},
    }
}

TEST [[
function A()
    function A1()
    end
    function A2()
    end
end
function B()
end
]]
{
    [1] = {
        name = 'A',
        detail = 'function A()',
        kind = SymbolKind.Function,
        range = {1, 68},
        selectionRange = {10, 10},
        valueRange = {1, 68},
        children = {
            [1] = {
                name = 'A1',
                detail = 'function A1()',
                kind = SymbolKind.Function,
                range = {18, 38},
                selectionRange = {27, 28},
                valueRange = {18, 38},
            },
            [2] = {
                name = 'A2',
                detail = 'function A2()',
                kind = SymbolKind.Function,
                range = {44, 64},
                selectionRange = {53, 54},
                valueRange = {44, 64},
            },
        },
    },
    [2] = {
        name = 'B',
        detail = 'function B()',
        kind = SymbolKind.Function,
        range = {70, 85},
        selectionRange = {79, 79},
        valueRange = {70, 85},
    },
}

TEST [[
local x = 1
local function f()
    local x = 'x'
    local y = {}
    --= 1
end
local y = true
local z
]]
{
    [1] = {
        name = 'x',
        detail = 'local x: number = 1',
        kind = SymbolKind.Variable,
        range = {7, 7},
        selectionRange = {7, 7},
        valueRange = {11, 11},
    },
    [2] = {
        name = 'f',
        detail = 'function f()',
        kind = SymbolKind.Function,
        range = {13, 79},
        selectionRange = {28, 28},
        valueRange = {13, 79},
        children = {
            [1] = {
                name = 'x',
                detail = 'local x: string = "x"',
                kind = SymbolKind.Variable,
                range = {42, 42},
                selectionRange = {42, 42},
                valueRange = {46, 48},
            },
            [2] = {
                name = 'y',
                detail = 'local y: {}',
                kind = SymbolKind.Variable,
                range = {60, 60},
                selectionRange = {60, 60},
                valueRange = {64, 65},
            },
            --[3] = {
            --    name = 'z',
            --    detail = 'global z: number = 1',
            --    kind = SymbolKind.Object,
            --    range = {71, 71},
            --    selectionRange = {71, 71},
            --    valueRange = {75, 75},
            --},
        },
    },
    [3] = {
        name = 'y',
        detail = 'local y: boolean = true',
        kind = SymbolKind.Variable,
        range = {87, 87},
        selectionRange = {87, 87},
        valueRange = {91, 94},
    },
    [4] = {
        name = 'z',
        detail = '',
        kind = SymbolKind.Variable,
        range = {102, 102},
        selectionRange = {102, 102},
        valueRange = {102, 102},
    },
}

TEST [[
local t = {
    a = 1,
    b = 2,
    c = 3,
}
]]
{
    [1] = {
        name = 't',
        detail = EXISTS,
        kind = SymbolKind.Variable,
        range = {7, 7},
        selectionRange = {7, 7},
        valueRange = {11, 46},
        children = {
            [1] = {
                name = 'a',
                detail = 'field a: number = 1',
                kind = SymbolKind.Class,
                range = {17, 17},
                selectionRange = {17, 17},
                valueRange = {21, 21},
            },
            [2] = {
                name = 'b',
                detail = 'field b: number = 2',
                kind = SymbolKind.Class,
                range = {28, 28},
                selectionRange = {28, 28},
                valueRange = {32, 32},
            },
            [3] = {
                name = 'c',
                detail = 'field c: number = 3',
                kind = SymbolKind.Class,
                range = {39, 39},
                selectionRange = {39, 39},
                valueRange = {43, 43},
            },
        }
    }
}

TEST [[
local t = {
    a = {
        b = 1,
    }
}
]]
{
    [1] = {
        name = 't',
        detail = EXISTS,
        kind = SymbolKind.Variable,
        range = {7, 7},
        selectionRange = {7, 7},
        valueRange = {11, 44},
        children = {
            [1] = {
                name = 'a',
                detail = EXISTS,
                kind = SymbolKind.Class,
                range = {17, 17},
                selectionRange = {17, 17},
                valueRange = {21, 42},
                children = {
                    [1] = {
                        name = 'b',
                        detail = EXISTS,
                        kind = SymbolKind.Class,
                        range = {31, 31},
                        selectionRange = {31, 31},
                        valueRange = {35, 35},
                    }
                }
            },
        }
    }
}

TEST[[
local function g()
end

g = 1
]]{
    [1] = {
        name = 'g',
        detail = 'function g()',
        kind = SymbolKind.Function,
        range = {1, 22},
        selectionRange = {16, 16},
        valueRange = {1, 22},
    }
}

TEST[[
function f(...)
    local x = ...
    print(x.a)
end
]]{
    [1] = {
        name = 'f',
        detail = 'function f(...)',
        kind = SymbolKind.Function,
        range = {1, 52},
        selectionRange = {10, 10},
        valueRange = {1, 52},
        children = {
            [1] = {
                name = 'x',
                detail = EXISTS,
                kind = SymbolKind.Variable,
                range = {27, 27},
                selectionRange = {27, 27},
                valueRange = {31, 33},
            }
        }
    },
}

TEST [[
local t = {
    a = 1,
    b = 2,
}

local v = t
]]{
    [1] = {
        name = 't',
        detail = EXISTS,
        kind = SymbolKind.Variable,
        range = {7, 7},
        selectionRange = {7, 7},
        valueRange = {11, 35},
        children = EXISTS,
    },
    [2] = {
        name = 'v',
        detail = EXISTS,
        kind = SymbolKind.Variable,
        range = {44, 44},
        selectionRange = {44, 44},
        valueRange = {48, 48},
    },
}

TEST [[
local x
local function
]]{
    [1] = {
        name = 'x',
        detail = EXISTS,
        kind = SymbolKind.Variable,
        range = {7, 7},
        selectionRange = {7, 7},
        valueRange = {7, 7},
    },
    [2] = {
        name = '',
        detail = EXISTS,
        kind = SymbolKind.Function,
        range = {9, 22},
        selectionRange = {22, 22},
        valueRange = {9, 22},
    }
}

-- 临时
local fs = require 'bee.filesystem'
local function testIfExit(path)
    local buf = io.load(fs.path(path))
    if buf then
        TEST(buf)(EXISTS)
    end
end
testIfExit[[D:\Github\lua\testes\coroutine.lua]]
testIfExit[[D:\Github\War3-lua-map\scripts\game\init.lua]]