PTGuiScrollFrame = PTGuiComponent:Extend("scroll_frame")
local _G = getfenv(0)
local util = PTUtil

PTGuiScrollFrame.ApplyScrollbarPadding = true

function PTGuiScrollFrame:New(content)
    local obj = setmetatable({}, self)
    local scrollFrame = CreateFrame("ScrollFrame", self:GenerateName(), nil, "UIPanelScrollFrameTemplate")
    if content then
        content:GetHandle():SetParent(scrollFrame)
    else
        content = PTGuiLib.Get("container", scrollFrame)
    end
    scrollFrame:SetScrollChild(content:GetHandle())
    scrollFrame.scrollBarHideable = 1
    obj:SetHandle(scrollFrame)
    obj:AddComponent("content", content)
    obj:SetPrimary()
    obj:SetupScrollbar()
    -- It seems that scroll frames require their child to have a manually set width to function properly,
    -- and I can't think of a better way to ensure the width is always up-to-date other than using OnSizeChanged
    scrollFrame:SetScript("OnSizeChanged", function()
        obj:GetHandle():SetScrollChild(obj:GetContainer())
        obj:GetContainer():SetWidth(math.max(this:GetWidth() - (obj.ApplyScrollbarPadding and 30 or 0), 0))
        obj:GetContainer():SetHeight(this:GetHeight())
    end)
    return obj
end

-- You better be disposing everything contained in this scroll frame before disposing the frame
function PTGuiScrollFrame:OnDispose()
    self.super.OnDispose(self)
end

function PTGuiScrollFrame:SetParent(parent)
    self.super.SetParent(self, parent)
    -- If this isn't done after setting a new parent, the contents go off the scroll frame
    self:GetHandle():SetScrollChild(self:GetContainer())
end

function PTGuiScrollFrame:GetContainer()
    return self:GetComponent("content"):GetHandle()
end

function PTGuiScrollFrame:GetAnchor()
    return self:GetComponent("content"):GetHandle()
end

function PTGuiScrollFrame:SetWidth(width)
    self.super.SetWidth(self, width)
    return self
end

function PTGuiScrollFrame:SetHeight(height)
    self.super.SetHeight(self, height)
    return self
end

function PTGuiScrollFrame:SetApplyScrollbarPadding(applyPadding)
    self.ApplyScrollbarPadding = applyPadding
    return self
end

function PTGuiScrollFrame:UpdateScrollRange()
    util.CallWithThis(self:GetHandle(), ScrollFrame_OnScrollRangeChanged)
    return self
end

function PTGuiScrollFrame:UpdateScrollChildRect()
    self:GetHandle():UpdateScrollChildRect()
    return self
end

-- I'm going insane
function PTGuiScrollFrame:FixNextUpdate()
    PTUtil.RunLater(function()
        self:GetHandle():SetScrollChild(self:GetContainer())
        self:GetContainer():SetWidth(math.max(self:GetWidth() - (self.ApplyScrollbarPadding and 30 or 0), 0))
        self:GetContainer():SetHeight(self:GetHeight())
    end)
end

-- TODO: Reduce the minimum size and make maximum size infinite
function PTGuiScrollFrame:SetupScrollbar()
    local scrollFrame = self:GetHandle()
    local scrollBar = _G[scrollFrame:GetName().."ScrollBar"]
    local scrollUpButton = _G[scrollBar:GetName().."ScrollUpButton"]
    local scrollDownButton = _G[scrollBar:GetName().."ScrollDownButton"]
    
    local defaultPoints = {}
    for i = 1, scrollBar:GetNumPoints() do
        local a,b,c,d,e = scrollBar:GetPoint(i)
        defaultPoints[i] = {a, b, c, d, e}
    end
    self.defaultScrollbarPoints = defaultPoints

    scrollBar:ClearAllPoints()
    scrollBar:SetPoint("TOPRIGHT", scrollFrame, -7, -20)
    scrollBar:SetPoint("BOTTOMRIGHT", scrollFrame, -7, 18)

    local backgroundTop = scrollBar:CreateTexture(scrollBar:GetName().."BgTop", "LOW")
    backgroundTop:SetTexture("Interface\\PaperDollInfoFrame\\UI-Character-ScrollBar")
    backgroundTop:SetWidth(31)
    backgroundTop:SetHeight(128)
    backgroundTop:SetPoint("BOTTOM", scrollUpButton, "TOP", 0, -128 + 4)
    backgroundTop:SetTexCoord(0, 0.484375, 0, 0.5)

    local backgroundBottom = scrollBar:CreateTexture(scrollBar:GetName().."BottomTop", "LOW")
    backgroundBottom:SetTexture("Interface\\PaperDollInfoFrame\\UI-Character-ScrollBar")
    backgroundBottom:SetWidth(31)
    backgroundBottom:SetHeight(106)
    backgroundBottom:SetPoint("TOP", scrollDownButton, "BOTTOM", 0, 106 - 2)
    backgroundBottom:SetTexCoord(0.515625, 1.0, 0, 0.4140625)

    local backgroundMiddle = scrollBar:CreateTexture(scrollBar:GetName().."BgMiddle", "LOW")
    backgroundMiddle:SetTexture("Interface\\PaperDollInfoFrame\\UI-Character-ScrollBar")
    backgroundMiddle:SetWidth(31)
    backgroundMiddle:SetPoint("TOP", backgroundTop, "BOTTOM", 0, 0)
    backgroundMiddle:SetTexCoord(0, 0.484375, 0.125, 0.875)
    backgroundMiddle:Hide()

    scrollBar:SetScript("OnSizeChanged", function()
        -- Scrollbar height value is scaled, but the button heights aren't..
        local missingSpace = (scrollBar:GetHeight() / scrollBar:GetEffectiveScale()) + 
            scrollUpButton:GetHeight() + scrollDownButton:GetHeight() + 10 - 
            (backgroundTop:GetHeight() + backgroundBottom:GetHeight())
        if missingSpace > 0 then
            backgroundMiddle:SetHeight(missingSpace)
            backgroundMiddle:SetTexCoord(0, 0.484375, 0.125, 0.125 + (missingSpace / 256))
            backgroundMiddle:Show()
        else
            backgroundMiddle:Hide()
        end
    end)
end

PTGuiLib.RegisterComponent(PTGuiScrollFrame)