--[[
    Author: Valana <Team BATTLE> of Mug'thol Alliance-US
]]

local TbArenaPoints = LibStub("AceAddon-3.0"):NewAddon("TbArenaPoints", "AceEvent-3.0", "AceHook-3.0")

local _G = _G
local MAX_ARENA_TEAMS = MAX_ARENA_TEAMS
local min = min
local max = max
local floor = floor
local exp = exp
local GetArenaTeam = GetArenaTeam
local GetArenaTeamRosterInfo = GetArenaTeamRosterInfo
local GetInspectArenaTeamData = GetInspectArenaTeamData

local _player = UnitName("player")
local _playerTeams = { }
local _inspectTeams = { }
local ratios =
{
    [2] = 0.76,
    [3] = 0.88,
    [5] = 1,
}


-------------------------------------------------------------------------------
-- Initialize TB Arena Points.
-------------------------------------------------------------------------------
function TbArenaPoints:OnEnable()
    --
    -- Watch when the InspectUI add-on loads.
    --
    self:RegisterEvent("ADDON_LOADED")
    for i = 1, MAX_ARENA_TEAMS do
        --
        -- Remember where the rating label (reading "Team Rating") was anchored.
        --
        local label = _G["PVPTeam" .. i .. "DataRatingLabel"]
        local originalPoint, originalAnchor, originalAnchorPoint, originalXOffset, originalYOffset = label:GetPoint(1)

        --
        -- Set a maximum width in the rating field so we can measure the maximum width we're going to need.
        --
        local rating = _G["PVPTeam" .. i .. "DataRating"]
        local originalRatingWidth = rating:GetWidth()
        rating:SetWidth(90)
        rating:SetText("7777 (7777)")
        local newRatingWidth = rating:GetStringWidth()

        --
        -- Adjust the arena team frame to accomodate the display of the estimated point gains for each team.
        --
        label:ClearAllPoints()
        label:SetPoint(originalPoint, originalAnchor, originalAnchorPoint, originalXOffset - (newRatingWidth + 10 - originalRatingWidth), originalYOffset)
        rating:SetWidth(newRatingWidth)
    end

    --
    -- Hook the PVPTeam_Update function so we may update our point game calculations every time new team data is received.
    --
    self:SecureHook("PVPTeam_Update")
end

function TbArenaPoints:ADDON_LOADED(e, name)
    --
    -- Adjust the inspect arena team frames once the Inspect UI has loaded.
    --
    if (name == "Blizzard_InspectUI") then
        self:UnregisterEvent("ADDON_LOADED")
        for i = 1, MAX_ARENA_TEAMS do
            local label = _G["InspectPVPTeam" .. i .. "DataRatingLabel"]
            local originalPoint, originalAnchor, originalAnchorPoint, originalXOffset, originalYOffset = label:GetPoint(1)

            local rating = _G["InspectPVPTeam" .. i .. "DataRating"]
            local originalRatingWidth = rating:GetWidth()
            rating:SetWidth(90)
            rating:SetText("7777 (7777)")
            local newRatingWidth = rating:GetStringWidth() + 10

            label:ClearAllPoints()
            label:SetPoint(originalPoint, originalAnchor, originalAnchorPoint, originalXOffset - (newRatingWidth - originalRatingWidth), originalYOffset)
            rating:SetWidth(newRatingWidth)
        end
        self:SecureHook("InspectPVPTeam_Update")
    end
end


-------------------------------------------------------------------------------
-- Display the estimated point gain for each of the player's teams.
-------------------------------------------------------------------------------
function TbArenaPoints:PVPTeam_Update(...)
    --
    -- Append the expected arena point gain from each team to its rating display.
    --
    for i = 1, MAX_ARENA_TEAMS do
        local teamName, teamSize, teamRating, teamWeekPlayed = GetArenaTeam(i)
        if (teamName) then
            --
            -- Sometimes roster information is not available for some reason. In that case, use the team's rating for all calculations.
            --
            local effectiveRating = teamRating
            local playerPersonalRating = teamRating
            if (teamWeekPlayed) > 9 then
                --
                -- Add up the personal ratings of the members who played at least 30% of the team's games this week.
                --
                local neededPlayed = teamWeekPlayed * 0.3
                local sumPersonalRatings = 0
                local numIncludedMembers = 0
                for memberIndex = 1, teamSize * 2 do
                    local name, _, _, _, _, memberWeekPlayed, _, _, _, personalRating = GetArenaTeamRosterInfo(i, memberIndex)
                    if (not name) then break end
                    if (UnitIsUnit(name, _player)) then
                        playerPersonalRating = personalRating
                    end

                    if (memberWeekPlayed >= neededPlayed) then
                        sumPersonalRatings, numIncludedMembers = sumPersonalRatings + personalRating, numIncludedMembers + 1
                    end
                end

                --
                -- Determine the team's effective rating for point calculation purposes. It is the lesser of the team's rating and the
                -- average personal rating of all the members who played the minimum number of games this week.
                --
                if (numIncludedMembers > 0) then
                    effectiveRating = min(effectiveRating, sumPersonalRatings / numIncludedMembers)
                end
            end

            --
            -- Determine the player's effective rating for point calculation purposes. It is either the team's effective rating, or the
            -- player's personal rating if less than the team's effective rating minus 150.
            --
            if (playerPersonalRating < effectiveRating - 150) then
                effectiveRating = playerPersonalRating
            end

            --
            -- Save the text to display for this team. The indices of the arena team panels are not necessarily the same as the order the
            -- panels appear in, so we can't assume that the i-th panel corresponds to the i-th team.
            --
            _playerTeams[teamName] = format("%s (%d)", teamRating, self:CalculatePoints(teamSize, effectiveRating))
        end
    end

    --
    -- Now set the rating and point estimate for each arena team panel. We use team names to match arena teams since those are unique.
    --
    for i = 1, MAX_ARENA_TEAMS do
        _G["PVPTeam" .. i .. "DataRating"]:SetText(_playerTeams[_G["PVPTeam" .. i .. "DataName"]:GetText()])
    end
end


-------------------------------------------------------------------------------
-- Display the estimated point gain for each of the inspected player's teams.
-------------------------------------------------------------------------------
function TbArenaPoints:InspectPVPTeam_Update(...)
    for i = 1, MAX_ARENA_TEAMS do
        --
        -- If the inspected player's personal rating is less than the team's rating minus 150, use the personal rating instead.
        --
        local teamName, teamSize, teamRating, _, _, _, personalRating = GetInspectArenaTeamData(i)
        if (teamName) then
            local effectiveRating = teamRating
            if (personalRating < teamRating - 150) then
                effectiveRating = personalRating
            end
            _inspectTeams[teamName] = format("%s (%d)", teamRating, self:CalculatePoints(teamSize, effectiveRating))
        end
    end

    for i = 1, MAX_ARENA_TEAMS do
        _G["InspectPVPTeam" .. i .. "DataRating"]:SetText(_inspectTeams[_G["InspectPVPTeam" .. i .. "DataName"]:GetText()])
    end
end


-------------------------------------------------------------------------------
-- Calculate the points earned by a team based on its bracket size and effective rating, rounded to the nearest integer.
-- The effective rating is 1500 is constrained to a range of 1500-3000.
-------------------------------------------------------------------------------
function TbArenaPoints:CalculatePoints(teamSize, effectiveRating)
    return floor(0.5 + ratios[teamSize] * 1511.26 / (1 + 1639.28 / exp(0.00412 * max(1500, min(3000, effectiveRating)))))
end
