local LibAnim = LibStub( 'LibAnimation-1.0' )
if ( not LibAnim ) then return end

PSBT_ScrollArea     = ZO_Object:Subclass()
local tinsert       = table.insert
local tremove       = table.remove
local NUM_STICKY    = 4

local PSBT_Fifo     = PSBT_Fifo
local CENTER        = CENTER

function PSBT_ScrollArea:New( ... )
    local result = ZO_Object.New( self )
    result:Initialize( ... )
    return result
end

function PSBT_ScrollArea:Initialize( super, areaName, anchor )
    self.control        = super:GetNamedChild( areaName )
    self._anchor        = anchor
    self._animHeight    = nil
    self._newSticky     = false
    self._sticky        = PSBT_Fifo.New()
    self._pendingSticky = PSBT_Fifo.New()
    self._normal        = {}
    self._pendingNormal = PSBT_Fifo.New()

    if ( anchor == TOP ) then
        self._animHeight = self.control:GetHeight()
    else
        self._animHeight = -1 * self.control:GetHeight()
    end

    self.control:SetHandler( 'OnUpdate', function( event, ... ) self:OnUpdate( ... ) end )
end

function PSBT_ScrollArea:Push( entry, sticky )
    if ( sticky ) then
        entry.control:SetAnchor( CENTER, self.control, CENTER, 0, self.control:GetHeight() )
        self._pendingSticky:Push( entry )
        return
    end

    entry.control:SetAnchor( CENTER, self.control, self._anchor, 0, 0 )
    self._pendingNormal:Push( entry )
end

function PSBT_ScrollArea:OnUpdate( frameTime )
    while ( self._sticky:Size() > NUM_STICKY ) do
        local old = self._sticky:Pop()
        local anim = LibAnim:New( old.control )
        anim:AlphaTo( 0.0, 200 )
        anim:Play()

        old:SetMoving( false )

        old:SetExpire( frameTime + 2 )
        self._newSticky = true
    end

    repeat
        local entry = self._sticky:Peek()
        if ( entry and entry:WillExpire( frameTime + 2 ) ) then
            local anim = LibAnim:New( entry.control )
            anim:AlphaTo( 0.0, 200 )
            anim:TranslateTo( 0, -200, 200 )
            anim:Play()

            entry:SetMoving( false )

            self._sticky:Pop()
            self._newSticky = true
        end
    until( not entry or not entry:WillExpire( frameTime + 2 )  )

    if ( self._pendingNormal:Size() ) then
        local newEntry = self._pendingNormal:Pop()
        if ( newEntry ) then
            newEntry:SetExpire( frameTime + 5 )

            local anim = LibAnim:New( newEntry.control )
            anim:AlphaTo( 1.0, 200 )
            anim:Play()

            tinsert( self._normal, newEntry )
        end
    end

    if ( self._pendingSticky:Size() ) then
        local newEntry = self._pendingSticky:Pop()
        if ( newEntry ) then
            newEntry:SetExpire( frameTime + 5 )

            local anim = LibAnim:New( newEntry.control )
            anim:AlphaTo( 1.0, 200 )
            anim:Play()

            self._sticky:Push( newEntry )
            self._newSticky = true
        end
    end

    local i = 1
    while ( i <= #self._normal ) do
        local entry = self._normal[ i ]

        if ( entry:WillExpire( frameTime + 2 ) ) then
            local anim = LibAnim:New( entry.control )
            anim:AlphaTo( 0.0, 200 )
            anim:Play()

            entry:SetMoving( false )

            tremove( self._normal, i )
        else
            if ( not entry:IsMoving() ) then
                local anim = LibAnim:New( entry.control )
                anim:TranslateTo( 0, self._animHeight, 3000 )
                anim:Play()

                entry:SetMoving( true )
            end
            i = i + 1
        end
    end

    local top = 0
    for i, entry in self._sticky:Iterator() do
        if ( self._newSticky ) then
            local anim = LibAnim:New( entry.control )
            anim:TranslateTo( 0, top, 200 )
            anim:Play()

            entry:SetMoving( true )

            top = top + entry.control:GetHeight()
        end
    end

    self._newSticky = false
end