--[[
# Element: Runes

Handles the visibility and updating of Death Knight's runes.

## Widget

Runes - An `table` holding `StatusBar`s.

## Sub-Widgets

.bg - A `Texture` used as a background. It will inherit the color of the main StatusBar.

## Notes

A default texture will be applied if the sub-widgets are StatusBars and don't have a texture set.

## Options

.colorSpec - Use `self.colors.runes[specID]` to color the bar based on player's spec. `specID` is defined by the return
             value of [GetSpecialization](https://warcraft.wiki.gg/wiki/API_GetSpecialization) (boolean)
.sortOrder - Sorting order. Sorts by the remaining cooldown time, 'asc' - from the least cooldown time remaining (fully
             charged) to the most (fully depleted), 'desc' - the opposite (string?)['asc', 'desc']

## Sub-Widgets Options

.multiplier - Used to tint the background based on the main widgets R, G and B values. Defaults to 1 (number)[0-1]

## Examples

    local Runes = {}
    for index = 1, 6 do
        -- Position and size of the rune bar indicators
        local Rune = CreateFrame('StatusBar', nil, self)
        Rune:SetSize(120 / 6, 20)
        Rune:SetPoint('TOPLEFT', self, 'BOTTOMLEFT', index * 120 / 6, 0)

        Runes[index] = Rune
    end

    -- Register with oUF
    self.Runes = Runes
--]]

if(select(2, UnitClass('player')) ~= 'DEATHKNIGHT') then return end

local _, ns = ...
local oUF = ns.oUF

local runemap = {1, 2, 3, 4, 5, 6}
local hasSortOrder = false

local function onUpdate(self, elapsed)
    local duration = self.duration + elapsed
    self.duration = duration
    self:SetValue(duration)
end

local function ascSort(runeAID, runeBID)
    local runeAStart, _, runeARuneReady = GetRuneCooldown(runeAID)
    local runeBStart, _, runeBRuneReady = GetRuneCooldown(runeBID)
    if(runeARuneReady ~= runeBRuneReady) then
        return runeARuneReady
    elseif(runeAStart ~= runeBStart) then
        return runeAStart < runeBStart
    else
        return runeAID < runeBID
    end
end

local function descSort(runeAID, runeBID)
    local runeAStart, _, runeARuneReady = GetRuneCooldown(runeAID)
    local runeBStart, _, runeBRuneReady = GetRuneCooldown(runeBID)
    if(runeARuneReady ~= runeBRuneReady) then
        return runeBRuneReady
    elseif(runeAStart ~= runeBStart) then
        return runeAStart > runeBStart
    else
        return runeAID > runeBID
    end
end

local function UpdateColor(self, event)
    local element = self.Runes

    local spec = GetSpecialization() or 0

    local color
    if(spec > 0 and spec < 4 and element.colorSpec) then
        color = self.colors.runes[spec]
    else
        color = self.colors.power.RUNES
    end

    local r, g, b = color[1], color[2], color[3]

    for index = 1, #element do
        element[index]:SetStatusBarColor(r, g, b)

        local bg = element[index].bg
        if(bg) then
            local mu = bg.multiplier or 1
            bg:SetVertexColor(r * mu, g * mu, b * mu)
        end
    end

    --[[ Callback: Runes:PostUpdateColor(r, g, b)
    Called after the element color has been updated.

    * self - the Runes element
    * r    - the red component of the used color (number)[0-1]
    * g    - the green component of the used color (number)[0-1]
    * b    - the blue component of the used color (number)[0-1]
    --]]
    if(element.PostUpdateColor) then
        element:PostUpdateColor(r, g, b)
    end
end

local function ColorPath(self, ...)
    --[[ Override: Runes.UpdateColor(self, event, ...)
    Used to completely override the internal function for updating the widgets' colors.

    * self  - the parent object
    * event - the event triggering the update (string)
    * ...   - the arguments accompanying the event
    --]]
    (self.Runes.UpdateColor or UpdateColor) (self, ...)
end

local function Update(self, event)
    local element = self.Runes

    if(element.sortOrder == 'asc') then
        table.sort(runemap, ascSort)
        hasSortOrder = true
    elseif(element.sortOrder == 'desc') then
        table.sort(runemap, descSort)
        hasSortOrder = true
    elseif(hasSortOrder) then
        table.sort(runemap)
        hasSortOrder = false
    end

    local rune, start, duration, runeReady
    for index, runeID in next, runemap do
        rune = element[index]
        if(not rune) then break end

        if(UnitHasVehicleUI('player')) then
            rune:Hide()
        else
            start, duration, runeReady = GetRuneCooldown(runeID)
            if(runeReady) then
                rune:SetMinMaxValues(0, 1)
                rune:SetValue(1)
                rune:SetScript('OnUpdate', nil)
            elseif(start) then
                rune.duration = GetTime() - start
                rune:SetMinMaxValues(0, duration)
                rune:SetValue(0)
                rune:SetScript('OnUpdate', onUpdate)
            end

            rune:Show()
        end
    end

    --[[ Callback: Runes:PostUpdate(runemap)
    Called after the element has been updated.

    * self    - the Runes element
    * runemap - the ordered list of runes' indices (table)
    --]]
    if(element.PostUpdate) then
        return element:PostUpdate(runemap)
    end
end

local function Path(self, ...)
    --[[ Override: Runes.Override(self, event, ...)
    Used to completely override the internal update function.

    * self  - the parent object
    * event - the event triggering the update (string)
    * ...   - the arguments accompanying the event
    --]]
    (self.Runes.Override or Update) (self, ...)
end

local function AllPath(...)
    Path(...)
    ColorPath(...)
end

local function ForceUpdate(element)
    Path(element.__owner, 'ForceUpdate')
    ColorPath(element.__owner, 'ForceUpdate')
end

local function Enable(self, unit)
    local element = self.Runes
    if(element and UnitIsUnit(unit, 'player')) then
        element.__owner = self
        element.ForceUpdate = ForceUpdate

        for i = 1, #element do
            local rune = element[i]
            if(rune:IsObjectType('StatusBar') and not rune:GetStatusBarTexture()) then
                rune:SetStatusBarTexture([[Interface\TargetingFrame\UI-StatusBar]])
            end
        end

        self:RegisterEvent('PLAYER_SPECIALIZATION_CHANGED', ColorPath)
        self:RegisterEvent('RUNE_POWER_UPDATE', Path, true)

        return true
    end
end

local function Disable(self)
    local element = self.Runes
    if(element) then
        for i = 1, #element do
            element[i]:Hide()
        end

        self:UnregisterEvent('PLAYER_SPECIALIZATION_CHANGED', ColorPath)
        self:UnregisterEvent('RUNE_POWER_UPDATE', Path)
    end
end

oUF:AddElement('Runes', AllPath, Enable, Disable)
