local Srendarr = _G['Srendarr']
local LMP = LibStub('LibMediaProvider-1.0')
local L = Srendarr:GetLocale()

local Aura                      = {}

-- CONSTANTS --
local ICON_SIZE                 = Srendarr.ICON_SIZE
local BAR_HEIGHT                = Srendarr.BAR_HEIGHT
local AURA_FADE_TIME            = Srendarr.AURA_FADE_TIME

-- UPVALUES --
local WM                        = WINDOW_MANAGER
local GetGameTimeMilliseconds   = GetGameTimeMilliseconds
local strformat                 = string.format
local math_floor                = math.floor
local tremove                   = table.remove
local tinsert                   = table.insert
local zo_strformat              = zo_strformat

local tSec                      = L.Time_Seconds
local tMin                      = L.Time_Minutes
local tHour                     = L.Time_Hours
local tToggle                   = L.Time_Toggle
local tPassive                  = L.Time_Passive
local tCast                     = L.Time_Cast
local currentTime, colours, remFrame, remName

local function AddControl(parent, cType, level)
    local c = WM:CreateControl(nil, parent, cType)
    c:SetDrawLayer(DL_CONTROLS)
    c:SetDrawLevel(level)
    return c, c
end

local function BuildAura(frame)
    local aura, ctrl

    aura, ctrl = AddControl(frame, CT_TEXTURE, 2)
    ctrl:SetDimensions(ICON_SIZE, ICON_SIZE)
    ctrl:SetTexture([[/esoui/art/actionbar/abilityframe64_up.dds]])
    -- ICON
    aura.icon, ctrl = AddControl(aura, CT_TEXTURE, 1)
    ctrl:SetDimensions(ICON_SIZE, ICON_SIZE)
    ctrl:SetAnchor(CENTER)
    -- LABELS
    aura.name, ctrl = AddControl(aura, CT_LABEL, 3)
    ctrl:SetVerticalAlignment(1)
    aura.timer, ctrl = AddControl(aura, CT_LABEL, 3)
    ctrl:SetVerticalAlignment(1)
    ctrl:SetHorizontalAlignment(1)
    -- BAR
    aura.bar, ctrl = AddControl(aura, CT_STATUSBAR, 2)
    ctrl:SetHeight(BAR_HEIGHT)
    ctrl:SetTexture([[/esoui/art/miscellaneous/progressbar_genericfill.dds]])
    ctrl:SetTextureCoords(0, 1, 0, 0.625)
    ctrl:SetLeadingEdge([[/esoui/art/miscellaneous/progressbar_genericfill_leadingedge.dds]], 8, 32)
    ctrl:SetLeadingEdgeTextureCoords(0, 1, 0, 0.625)
    ctrl:EnableLeadingEdge(true)
    ctrl:SetMinMax(0, 1)
    ctrl:SetHandler('OnValueChanged', function(bar, value) bar.gloss:SetValue(value) end) -- change gloss value as main bar changes
    -- BAR GLOSS
    aura.bar.gloss, ctrl = AddControl(aura.bar, CT_STATUSBAR, 3)
    ctrl:SetHeight(BAR_HEIGHT)
    ctrl:SetAnchor(TOPLEFT)
    ctrl:SetTexture([[/esoui/art/miscellaneous/progressbar_genericfill_gloss.dds]])
    ctrl:SetTextureCoords(0, 1, 0, 0.625)
    ctrl:SetLeadingEdge([[/esoui/art/miscellaneous/progressbar_genericfill_leadingedge_gloss.dds]], 8, 32)
    ctrl:SetLeadingEdgeTextureCoords(0, 1, 0, 0.625)
    ctrl:EnableLeadingEdge(true)
    ctrl:SetMinMax(0, 1)
    -- BAR FRAME
    aura.barBorderL, ctrl = AddControl(aura.bar, CT_TEXTURE, 4)
    ctrl:SetDimensions(10, BAR_HEIGHT)
    ctrl:SetAnchor(TOPLEFT)
    ctrl:SetTexture([[/esoui/art/miscellaneous/progressbar_frame.dds]])
    aura.barBorderR, ctrl = AddControl(aura.bar, CT_TEXTURE, 4)
    ctrl:SetDimensions(10, BAR_HEIGHT)
    ctrl:SetAnchor(TOPRIGHT)
    ctrl:SetTexture([[/esoui/art/miscellaneous/progressbar_frame.dds]])
    aura.barBorderM, ctrl = AddControl(aura.bar, CT_TEXTURE, 4)
    ctrl:SetHeight(BAR_HEIGHT)
    ctrl:SetAnchor(TOPLEFT, aura.bar, TOPLEFT, 10, 0)
    ctrl:SetTexture([[/esoui/art/miscellaneous/progressbar_frame.dds]])
    ctrl:SetTextureCoords(0.019500000402331, 0.58980000019073, 0, 0.625)
    -- BAR BACKDROP
    aura.barBackdropEnd, ctrl = AddControl(aura.bar, CT_TEXTURE, 1)
    ctrl:SetDimensions(10, BAR_HEIGHT)
    ctrl:SetTexture([[/esoui/art/miscellaneous/progressbar_genericfill_leadingedge.dds]])
    ctrl:SetColor(0,0,0,0.4)
    aura.barBackdrop, ctrl = AddControl(aura.bar, CT_TEXTURE, 1)
    ctrl:SetHeight(BAR_HEIGHT)
    ctrl:SetTexture('')
    ctrl:SetColor(0,0,0,0.4)

    aura.k_frame        = frame

    aura['Configure']   = Aura.Configure
    aura['Ghost']       = Aura.Ghost
    aura['Release']     = Aura.Release

    aura:Configure()

    return aura
end

local function FormatTime(remaining)
    if (remaining < 60) then -- seconds
        return strformat(tSec, remaining)
    elseif (remaining < 3600) then -- minutes
        return strformat(tMin, math_floor(remaining / 60))
    else -- hours
        return strformat(tHour, math_floor(remaining / 3600))
    end
end

local function OnMouseEnter(control)
    local abilityIndex
    if control.abilityId then
        local abilityId = control.abilityId
        for index = 1, GetNumAbilities() do
            if GetAbilityIdByIndex(index) == abilityId then
                abilityIndex = index
                break
            end
        end
        if abilityIndex then
            InitializeTooltip(InformationTooltip, control, BOTTOMLEFT, 0, -2, TOPLEFT)
            InformationTooltip:SetAbility(abilityIndex)
        end
    end
    if not abilityIndex then
        if control.buffId then
            local unitTag = control:GetParent().k_id == 4 and 'reticleover' or 'player'
            InitializeTooltip(InformationTooltip, control, BOTTOMLEFT, 0, -2, TOPLEFT)
            InformationTooltip:SetBuff(control.buffId, unitTag)
        elseif control.tooltipText then
            InitializeTooltip(InformationTooltip, control, BOTTOMLEFT, 0, -2, TOPLEFT)
            SetTooltipText(InformationTooltip, control.tooltipText)
        end
    end
end

local function OnMouseExit(control)
    ClearTooltip(InformationTooltip)
end

function Aura:New(frame, aType, tType, name, icon, start, finish, isShield, abilityType, buffId, abilityId)
    local aura = tremove(frame.k_auraPool, 1) -- grab from inactive pool

    if (not aura) then -- no inactive Aura, create one
        aura = BuildAura(frame)
    end

    -- initialize for display
    aura.k_name         = name
    aura.k_type         = aType
    aura.k_timerType    = tType
    aura.k_isGhost      = false
    aura.k_start        = start
    aura.k_finish       = finish
    aura.k_isShield     = isShield
    aura.k_abilityType  = abilityType
    -- tooltip text if showing
    aura.tooltipText    = zo_strformat(SI_ABILITY_TOOLTIP_NAME, name)
    aura.buffId         = buffId
    aura.abilityId      = abilityId

    aura.name:SetText(aura.tooltipText)
    aura.icon:SetTexture(icon)

    if (aType < 3) then -- timed (short) or timed (long)
        currentTime = GetGameTimeMilliseconds() / 1000
        if (start >= finish) then return end -- abort if 0 (or less) duration

        if (start > currentTime) then -- casting (starts later)
            aura.timer:SetText(tCast)
            aura.bar:SetValue(0.99)
        else
            aura.timer:SetText(FormatTime(finish - currentTime))
            aura.bar:SetValue(1 - ((currentTime - start) / (finish - start)))
        end

        colours = frame.k_db.barColours.timed
    elseif (aType == 3) then -- toggle
        aura.timer:SetText((frame.k_db.timerShowTP) and tToggle or '')
        aura.bar:SetValue(0.99)
        colours = frame.k_db.barColours.toggle
    else -- passive
        aura.timer:SetText((frame.k_db.timerShowTP) and tPassive or '')
        aura.bar:SetValue(0.99)
        colours = frame.k_db.barColours.passive
    end

    aura.bar:SetGradientColors(colours.r1, colours.g1, colours.b1, 1, colours.r2, colours.g2, colours.b2, 1)
    aura:SetHidden(false)

    frame.k_auraActive[name] = aura

    return aura
end

function Aura:Ghost()
    if (self.k_isGhost) then return end -- already ghosting

    self.k_isGhost = true
    self.k_finish = (GetGameTimeMilliseconds() / 1000)
    self.icon:SetDesaturation(1)
end

function Aura:Release()
    self:SetHidden(true)
    -- return to default appearance
    self:SetAlpha(1)
    self.icon:SetDesaturation(0)
    self.k_isGhost = false
    -- remove from auralists and add to inactive pool
    remFrame, remName = self.k_frame, self.k_name
    remFrame.k_auraActive[remName] = nil
    tinsert(remFrame.k_auraPool, self)
end

function Aura:Configure()
    local db = self.k_frame.k_db

    self.name:SetFont(strformat('%s|%d|%s', LMP:Fetch('font', db.nameFont), db.nameSize, db.nameOutline))
    self.name:SetHeight(db.nameSize)
    self.name:SetColor(db.nameColour.r, db.nameColour.g, db.nameColour.b)
    self.timer:SetFont(strformat('%s|%d|%s', LMP:Fetch('font', db.timerFont), db.timerSize, db.timerOutline))
    self.timer:SetHeight(db.timerSize)
    self.timer:SetColor(db.timerColour.r, db.timerColour.g, db.timerColour.b)

    -- tooltip control
    if (db.tooltips) then
        self:SetMouseEnabled(true)

        if (not self:GetHandler('OnMouseEnter')) then -- no mouse watchers, add
            self:SetHandler('OnMouseEnter', OnMouseEnter)
            self:SetHandler('OnMouseExit', OnMouseExit)
        end
    else
        self:SetMouseEnabled(false)

        if (self:GetHandler('OnMouseEnter')) then -- mouse watchers, remove
            self:SetHandler('OnMouseEnter', nil)
            self:SetHandler('OnMouseExit', nil)
        end
    end

    if (db.auraGrowth == 'LEFT' or db.auraGrowth == 'RIGHT' or db.auraGrowth == 'LEFTCENTER' or db.auraGrowth == 'RIGHTCENTER') then -- horizontal aura growth
        if (db.timerShowHorz == 'HIDE') then
            self.timer:SetHidden(true)
        else
            self.timer:ClearAnchors()

            if (db.timerShowHorz == 'ABOVE') then
                self.timer:SetAnchor(BOTTOM, self, TOP, 0, -5)
            elseif (db.timerShowHorz == 'BELOW') then
                self.timer:SetAnchor(TOP, self, BOTTOM, 0, 1)
            elseif (db.timerShowHorz == 'OVER') then
                self.timer:SetAnchor(BOTTOM, self, BOTTOM, 0, -4)
            end

            self.timer:SetHidden(false)
        end

        self.name:SetHidden(true)
        self.bar:SetHidden(true)
    else -- vertical aura growth
        local point     = (db.auraIconOnRight) and RIGHT or LEFT
        local pointInv  = (db.auraIconOnRight) and LEFT or RIGHT
        local align     = (db.auraIconOnRight) and 2 or 0
        local xMulti    = (db.auraIconOnRight) and -1 or 1

        if (db.timerShowVert == 'HIDE') then
            self.timer:SetHidden(true)
        else
            self.timer:ClearAnchors()
            self.timer:SetAnchor(BOTTOM, self, BOTTOM, 0, -4)
            self.timer:SetHidden(false)
        end

        -- configure bar display
        self.bar:SetWidth(db.barWidth)
        self.bar.gloss:SetWidth(db.barWidth)
        self.bar.gloss:SetHidden(not db.barGloss)
        self.barBorderM:SetWidth(db.barWidth - 20)
        self.barBackdrop:ClearAnchors()
        self.barBackdrop:SetAnchor(point + TOP, self.bar, point + TOP, 0, 0)
        self.barBackdrop:SetWidth(db.barWidth - 10)
        self.barBackdropEnd:ClearAnchors()
        self.barBackdropEnd:SetAnchor(pointInv + TOP, self.bar, pointInv + TOP, 0, 0)

        if (db.auraIconOnRight) then
            self.bar:SetBarAlignment(BAR_ALIGNMENT_REVERSE)
            self.bar.gloss:SetBarAlignment(BAR_ALIGNMENT_REVERSE)
            self.barBorderL:SetTextureCoords(0.6133000254631, 0.59380000829697, 0, 0.625)
            self.barBorderR:SetTextureCoords(0.019500000402331, 0, 0, 0.625)
            self.barBackdropEnd:SetTextureCoords(1, 0, 0, 0.625)
        else
            self.bar:SetBarAlignment(BAR_ALIGNMENT_NORMAL)
            self.bar.gloss:SetBarAlignment(BAR_ALIGNMENT_NORMAL)
            self.barBorderL:SetTextureCoords(0, 0.019500000402331, 0, 0.625)
            self.barBorderR:SetTextureCoords(0.59380000829697, 0.6133000254631, 0, 0.625)
            self.barBackdropEnd:SetTextureCoords(0, 1, 0, 0.625)
        end

        -- anchor (or hide) 'side' elements
        if (db.showNameBar) then
            self.name:ClearAnchors()
            self.name:SetAnchor(point + TOP, self, pointInv + TOP, xMulti * 4, 0)
            self.name:SetHidden(false)
            self.bar:ClearAnchors()
            self.bar:SetAnchor(point + BOTTOM, self, pointInv + BOTTOM, xMulti * 3, 0)
            self.bar:SetHidden(false)
        else
            self.name:SetHidden(true)
            self.bar:SetHidden(true)
        end
    end
end

Srendarr.Aura = Aura