Quantcast
local VarrenDevToolLinkedList = { size = 0; first = nil, last = nil }

function VarrenDevToolLinkedList:GetInfoAtPosition(position)
    if self.size < position or self.first == nil then
        return nil
    end

    local node = self.first
    while position > 1 do
        node = node.next
        position = position - 1
    end

    return node
end

function VarrenDevToolLinkedList:AddNodeAfter(node, prevNode)
    local tempNext = node.next
    node.next = prevNode
    prevNode.next = tempNext
    self.size = self.size + 1;
end

function VarrenDevToolLinkedList:AddNodesAfter(nodeList, parentNode)
    local tempNext = parentNode.next
    local currNode = parentNode;

    for _, node in pairs(nodeList) do
        currNode.next = node
        currNode = node
        self.size = self.size + 1;
    end

    currNode.next = tempNext

    if tempNext == nil then
        self.last = currNode
    end
end

function VarrenDevToolLinkedList:AddNode(data, dataName)
    local node = self:NewNode(data, dataName)

    if self.first == nil then
        self.first = node
        self.last = node
    else
        if self.last ~= nil then
            self.last.next = node
        end
        self.last = node
    end

    self.size = self.size + 1;
end

function VarrenDevToolLinkedList:NewNode(data, dataName, padding, parent)
    return {
        name = dataName,
        value = data,
        next = nil,
        padding = padding == nil and 0 or padding,
        parent = parent
    }
end

function VarrenDevToolLinkedList:RemoveChildNodes(node)
    local currNode = node

    while true do

        currNode = currNode.next

        if currNode == nil then
            node.next = nil
            self.last = node
            break
        end

        if currNode.padding <= node.padding then
            node.next = currNode
            break
        end

        self.size = self.size - 1
    end
end

function VarrenDevToolLinkedList:Clear()
    self.size = 0
    self.first = nil
    self.last = nil
end

local pairs, tostring, type, print, string, getmetatable, table,pcall =  pairs, tostring, type, print, string, getmetatable, table,pcall
local HybridScrollFrame_CreateButtons, HybridScrollFrame_GetOffset, HybridScrollFrame_Update = HybridScrollFrame_CreateButtons,HybridScrollFrame_GetOffset, HybridScrollFrame_Update

function VarrenDevTool_ExpandCell(info)

    local nodeList = {}
    local padding = info.padding + 1
    local couner = 0
    for k, v in pairs(info.value) do
        if type(v) ~= "userdata" then

            nodeList[couner] = VarrenDevToolLinkedList:NewNode(v, tostring(k), padding, info)
        else
            local mt = getmetatable(info.value)
            if mt then
                nodeList[couner] = VarrenDevToolLinkedList:NewNode(mt.__index, "$metatable", padding, info)
            end
        end
        couner = couner + 1
    end

    table.sort(nodeList, function(a, b)
        return a.name < b.name
    end)

    VarrenDevToolLinkedList:AddNodesAfter(nodeList, info)
    info.expanded = true
    VarrenDevTool_ScrollBar_Update()
end

function VarrenDevTool_ColapseCell(info)
    VarrenDevToolLinkedList:RemoveChildNodes(info)
    info.expanded = nil
    print("size: " .. VarrenDevToolLinkedList.size)
    VarrenDevTool_ScrollBar_Update()
end

function VarrenDevTool_AddData(data, dataName)
    VarrenDevToolLinkedList:AddNode(data, dataName)
    VarrenDevTool_ScrollBar_Update()
end

function VarrenDevTool_ClearData()
    VarrenDevToolLinkedList:Clear()
    VarrenDevTool_ScrollBar_Update()
end

function VarrenDevTool_ScrollBar_Update()

    local scrollFrame = VarrenDevToolScrollFrame

    local buttons = scrollFrame.buttons;
    local offset = HybridScrollFrame_GetOffset(scrollFrame)
    local totalRowsCount = VarrenDevToolLinkedList.size
    local lineplusoffset; -- an index into our data calculated from the scroll offset

    local nodeInfo = VarrenDevToolLinkedList:GetInfoAtPosition(offset)
    for k, view in pairs(buttons) do

        lineplusoffset = k + offset;
        -- print("ok: " .. lineplusoffset .. "  " .. offset .. "  " .. k .. " " .. (nodeInfo ~= nil and nodeInfo.name or "nil"))
        if lineplusoffset <= totalRowsCount then
            VarrenDevTool_UpdateListItem(view, nodeInfo, lineplusoffset)
            nodeInfo = nodeInfo.next
            view:Show();
        else
            view:Hide();
        end
    end

    HybridScrollFrame_Update(scrollFrame, totalRowsCount * buttons[1]:GetHeight(), scrollFrame:GetHeight());

end



function VarrenDevTool_UpdateListItem(node, info, id)
    local nameButton = node.nameButton;
    local typeButton = node.typeButton
    local valueButton = node.valueButton
    local rowNumberButton = node.rowNumberButton

    local value = info.value
    local name = info.name
    local padding = info.padding

    nameButton:SetPoint("LEFT", node.typeButton, "RIGHT", 20 * padding, 0)

    local valueType = type(value)

    valueButton:SetText(tostring(value))
    nameButton:SetText(tostring(name))
    typeButton:SetText(valueType)
    rowNumberButton:SetText(tostring(id))

    local color = "VarrenDevToolBaseFont"
    if valueType == "table" then
        if name ~= "$metatable" then
            if value.GetObjectType then
                if value.IsForbidden and value:IsForbidden() then
                else
                    valueButton:SetText(value:GetObjectType() .. "  " .. tostring(value))
                end
            end
            color = "VarrenDevToolTableFont";
        else
            color = "VarrenDevToolMetatableFont";
        end
        local resultStringName = tostring(name)
        local MAX_STRING_SIZE = 60
        if #resultStringName >= MAX_STRING_SIZE then
            resultStringName = string.sub(resultStringName, 0, MAX_STRING_SIZE) .. "..."
        end

        local function tablelength(T)
            local count = 0
            for _ in pairs(T) do count = count + 1 end
            return count
        end

        nameButton:SetText(resultStringName .. "   (" .. tablelength(value) .. ") ");

    elseif valueType == "userdata" then
        color = "VarrenDevToolTableFont";
    elseif valueType == "string" then
        valueButton:SetText(string.gsub(string.gsub(tostring(value), "|n", ""), "\n", ""))
        color = "VarrenDevToolStringFont";
    elseif valueType == "number" then
        color = "VarrenDevToolNumberFont";
    elseif valueType == "function" then
        color = "VarrenDevToolFunctionFont";
    end



    node.nameButton:SetNormalFontObject(color);
    node.typeButton:SetNormalFontObject(color)
    node.valueButton:SetNormalFontObject(color)
    node.rowNumberButton:SetNormalFontObject(color)

    if valueType == "table" then
        nameButton:SetScript("OnMouseUp", function(self, button, down)
            print("click")
            if info.expanded then
                VarrenDevTool_ColapseCell(info)
            else
                VarrenDevTool_ExpandCell(info)
            end
        end)
    elseif valueType == "function" then
        nameButton:SetScript("OnMouseUp", function(self, button, down)
            print("click")
            VarrenDevTool_TryCallFunction(info)
        end)
    else
        nameButton:SetScript("OnMouseUp", nil)
    end
end

function VarrenDevTool_TryCallFunction(info)
    local value = info.value

    local ok, result = pcall(value)
    if ok then
        local resultType = type(result)
        local additionalInfo = ""
        if resultType == "string" or resultType == "number" then
            additionalInfo = tostring(result)
        end

        print("returns:  " .. resultType .. " " .. additionalInfo)
    else
        local parent = info.parent
        if parent then
            if parent.name == "$metatable" then
                parent = parent.parent
                print("found metatable" ..  info.name)
            end

            local ok, result = pcall(parent.value[info.name], parent.value)
            local resultType = type(result)
            local additionalInfo = tostring(result)

            print(parent.name ..":".. info.name .."() returns: " .. additionalInfo.. "  ("..resultType ..")" )
        end
    end
end


function DEBUG(self, text)
    if self.debug then
        print(text);
    end
end