local ZO_ObjectiveCaptureMeter = ZO_Object:Subclass()

-- Capture Meter constants
-----------------

local MIN_NUM_PLAYERS_ONE_ARROW     = 1
local MIN_NUM_PLAYERS_TWO_ARROWS    = 3
local MIN_NUM_PLAYERS_THREE_ARROWS  = 5

local CAPTURE_BAR_BEGIN_OFFSET  = .125  -- Begins 12.5% of the way through the circle
local CAPTURE_BAR_LENGTH        = .76   -- Is 75% of a circle long. We add an extra 1% to allow the semitransparent texture to overlap the bar
local ARROW_LENGTH              = .04   -- Arrow's about 4% of the circle long
local ENDCAP_LENGTH             = .02   -- About 2% of the circle long.

local CAPTURE_BAR_ARCLENGTH = CAPTURE_BAR_LENGTH * ZO_TWO_PI
local ARROW_ARCLENGTH = ARROW_LENGTH * ZO_TWO_PI
local ENDCAP_ARCLENGTH = ENDCAP_LENGTH * ZO_TWO_PI

local FADE_DURATION_MS          = 150
local FULL_BAR_TIMEOUT_MS       = 4000

-- Capture Meter textures
-----------------

-- badge textures
local ALLIANCE_BADGE_TEXTURES =
{
    [ALLIANCE_ALDMERI_DOMINION] = "EsoUI/Art/AvA/avaCaptureBar_allianceBadge_aldmeri.dds",
    [ALLIANCE_EBONHEART_PACT] = "EsoUI/Art/AvA/avaCaptureBar_allianceBadge_ebonheart.dds",
    [ALLIANCE_DAGGERFALL_COVENANT] = "EsoUI/Art/AvA/avaCaptureBar_allianceBadge_daggerfall.dds",
}

local BATTLEGROUND_TEAM_BADGE_TEXTURES =
{
    [BATTLEGROUND_TEAM_FIRE_DRAKES] = "EsoUI/Art/Battlegrounds/battlegroundsCaptureBar_teamBadge_orange.dds",
    [BATTLEGROUND_TEAM_STORM_LORDS] = "EsoUI/Art/Battlegrounds/battlegroundsCaptureBar_teamBadge_purple.dds",
    [BATTLEGROUND_TEAM_PIT_DAEMONS] = "EsoUI/Art/Battlegrounds/battlegroundsCaptureBar_teamBadge_green.dds",
}

-- bar textures
local ALLIANCE_BAR_TEXTURES =
{
    [ALLIANCE_ALDMERI_DOMINION] = "EsoUI/Art/AvA/avaCaptureBar_fill_aldmeri.dds",
    [ALLIANCE_EBONHEART_PACT] = "EsoUI/Art/AvA/avaCaptureBar_fill_ebonheart.dds",
    [ALLIANCE_DAGGERFALL_COVENANT] = "EsoUI/Art/AvA/avaCaptureBar_fill_daggerfall.dds",
}

local BATTLEGROUND_TEAM_BAR_TEXTURES =
{
    [BATTLEGROUND_TEAM_FIRE_DRAKES] = "EsoUI/Art/Battlegrounds/battlegroundsCaptureBar_fill_orange.dds",
    [BATTLEGROUND_TEAM_STORM_LORDS] = "EsoUI/Art/Battlegrounds/battlegroundsCaptureBar_fill_purple.dds",
    [BATTLEGROUND_TEAM_PIT_DAEMONS] = "EsoUI/Art/Battlegrounds/battlegroundsCaptureBar_fill_green.dds",
}

-- point textures
local ALLIANCE_POINT_TEXTURES =
{
    [ALLIANCE_ALDMERI_DOMINION] = "EsoUI/Art/AvA/avaCaptureBar_point_aldmeri.dds",
    [ALLIANCE_EBONHEART_PACT] = "EsoUI/Art/AvA/avaCaptureBar_point_ebonheart.dds",
    [ALLIANCE_DAGGERFALL_COVENANT] = "EsoUI/Art/AvA/avaCaptureBar_point_daggerfall.dds",
}

local BATTLEGROUND_TEAM_POINT_TEXTURES =
{
    [BATTLEGROUND_TEAM_FIRE_DRAKES] = "EsoUI/Art/Battlegrounds/battlegroundsCaptureBar_point_orange.dds",
    [BATTLEGROUND_TEAM_STORM_LORDS] = "EsoUI/Art/Battlegrounds/battlegroundsCaptureBar_point_purple.dds",
    [BATTLEGROUND_TEAM_PIT_DAEMONS] = "EsoUI/Art/Battlegrounds/battlegroundsCaptureBar_point_green.dds",
}

-- tail textures
local ALLIANCE_TAIL_TEXTURES =
{
    [ALLIANCE_ALDMERI_DOMINION] = "EsoUI/Art/AvA/avaCaptureBar_point-inverse_aldmeri.dds",
    [ALLIANCE_EBONHEART_PACT] = "EsoUI/Art/AvA/avaCaptureBar_point-inverse_ebonheart.dds",
    [ALLIANCE_DAGGERFALL_COVENANT] = "EsoUI/Art/AvA/avaCaptureBar_point-inverse_daggerfall.dds",
}

local BATTLEGROUND_TEAM_TAIL_TEXTURES =
{
    [BATTLEGROUND_TEAM_FIRE_DRAKES] = "EsoUI/Art/Battlegrounds/battlegroundsCaptureBar_point-inverse_orange.dds",
    [BATTLEGROUND_TEAM_STORM_LORDS] = "EsoUI/Art/Battlegrounds/battlegroundsCaptureBar_point-inverse_purple.dds",
    [BATTLEGROUND_TEAM_PIT_DAEMONS] = "EsoUI/Art/Battlegrounds/battlegroundsCaptureBar_point-inverse_green.dds",
}

-------------------
-- Object functions
-------------------

function ZO_ObjectiveCaptureMeter:New(...)
    local object = ZO_Object.New(self)
    object:Initialize(...)
    return object
end

function ZO_ObjectiveCaptureMeter:Initialize(control)
    self.control = control
    SHARED_INFORMATION_AREA:AddFlagCapture(self.control)
    self.hiddenReasons = ZO_HiddenReasons:New()
    self:SetHiddenForReason("inCaptureArea", true)

    -- Store references to controls
    self.meter = self.control:GetNamedChild("Meter")
    self.ownerBadge = self.control:GetNamedChild("MeterOwnerBadge")
    self.progressBar = self.control:GetNamedChild("MeterProgressBar")
    self.endCap = self.control:GetNamedChild("MeterProgressBarEndCap")
    self.capturingArrow1 = self.control:GetNamedChild("MeterProgressBarCapturingArrow1")
    self.capturingArrow2 = self.control:GetNamedChild("MeterProgressBarCapturingArrow2")
    self.capturingArrow3 = self.control:GetNamedChild("MeterProgressBarCapturingArrow3")
    self.contestingArrow1 = self.control:GetNamedChild("MeterProgressBarContestingArrow1")
    self.contestingArrow2 = self.control:GetNamedChild("MeterProgressBarContestingArrow2")
    self.contestingArrow3 = self.control:GetNamedChild("MeterProgressBarContestingArrow3")

    self.lastOwningAlliance = nil

    --Set up fade in/out anim
    self.meter.fadeAnim = ZO_AlphaAnimation:New(self.meter)
    self.meter.fadeAnim:SetMinMaxAlpha(0.0, 1.0)
    self.meter:SetHidden(true)

    self.capTimeline = ANIMATION_MANAGER:CreateTimelineFromVirtual("CaptureBarEasing")
    self.capTimeline.startPct = 0

    --Register for updates
    EVENT_MANAGER:RegisterForEvent("GameScore", EVENT_CAPTURE_AREA_STATUS, function(...) self:ServerUpdate(...) end)
    EVENT_MANAGER:RegisterForEvent("GameScore", EVENT_HIDE_OBJECTIVE_STATUS, function(...) self:Hide(...) end)

    local KEYBOARD_STYLE = { meter = "ZO_ObjectiveCapture_Keyboard_Template" }
    local GAMEPAD_STYLE = { meter = "ZO_ObjectiveCapture_Gamepad_Template" }
    ZO_PlatformStyle:New(function(style) self:ApplyPlatformStyle(style) end, KEYBOARD_STYLE, GAMEPAD_STYLE)
end

function ZO_ObjectiveCaptureMeter:GetBadgeTextureFromAlliance(alliance, isBattleground)
    if isBattleground then
        return BATTLEGROUND_TEAM_BADGE_TEXTURES[alliance]
    else
        return ALLIANCE_BADGE_TEXTURES[alliance]
    end
end

function ZO_ObjectiveCaptureMeter:GetBarTextureFromAlliance(alliance, isBattleground)
    if isBattleground then
        return BATTLEGROUND_TEAM_BAR_TEXTURES[alliance]
    else
        return ALLIANCE_BAR_TEXTURES[alliance]
    end
end

function ZO_ObjectiveCaptureMeter:GetPointTextureFromAlliance(alliance, isBattleground)
    if isBattleground then
        return BATTLEGROUND_TEAM_POINT_TEXTURES[alliance]
    else
        return ALLIANCE_POINT_TEXTURES[alliance]
    end
end

function ZO_ObjectiveCaptureMeter:GetTailTextureFromAlliance(alliance, isBattleground)
    if isBattleground then
        return BATTLEGROUND_TEAM_TAIL_TEXTURES[alliance]
    else
        return ALLIANCE_TAIL_TEXTURES[alliance]
    end
end


function ZO_ObjectiveCaptureMeter:ServerUpdate(eventCode, keepId, objectiveId, battlegroundContext, capturePoolValue, capturePoolSize, capturingPlayers, contestingPlayers, owningAlliance)
    -- Short-circuit if we're walking in to an already-owned flag.
    if capturePoolValue / capturePoolSize == 1 and (self.capturePercentage == nil or self.capturePercentage == 1) then
        return
    end

    -- Show the bar frame if we're within range
    self.meter:SetHidden(false)
    self.meter.fadeAnim:FadeIn(0, FADE_DURATION_MS)
    self:SetHiddenForReason("inCaptureArea", false)

    -- Calculate percentage complete
    self.capturePercentage = capturePoolValue / capturePoolSize;

    -- Turn on appropriate arrows
    local numMoreContestingPlayers = contestingPlayers - capturingPlayers
    self.contestingArrow1:SetHidden(numMoreContestingPlayers < MIN_NUM_PLAYERS_ONE_ARROW)
    self.contestingArrow2:SetHidden(numMoreContestingPlayers < MIN_NUM_PLAYERS_TWO_ARROWS)
    self.contestingArrow3:SetHidden(numMoreContestingPlayers < MIN_NUM_PLAYERS_THREE_ARROWS)

    local numMoreCapturingPlayers = capturingPlayers - contestingPlayers
    self.capturingArrow1:SetHidden(numMoreCapturingPlayers < MIN_NUM_PLAYERS_ONE_ARROW)
    self.capturingArrow2:SetHidden(numMoreCapturingPlayers < MIN_NUM_PLAYERS_TWO_ARROWS)
    self.capturingArrow3:SetHidden(numMoreCapturingPlayers < MIN_NUM_PLAYERS_THREE_ARROWS)

    -- Determine visibility of UI
    if owningAlliance ~= 0 then
        local isBattleground = IsActiveWorldBattleground()

        -- Show alliance-specific UI elements
        self.ownerBadge:SetTexture(self:GetBadgeTextureFromAlliance(owningAlliance, isBattleground))
        self.progressBar:SetTexture(self:GetBarTextureFromAlliance(owningAlliance, isBattleground))
        self.ownerBadge:SetHidden(false)
        self.progressBar:SetHidden(false)
        self.lastOwningAlliance = owningAlliance

        -- Show appropriate endcap and arrows
        if self.capturePercentage == 1 then
            self.endCap:SetHidden(true)
            self:HideShowArrows(0)

            -- Flag is fully captured, start the timer to fade out this UI
            zo_callLater(function() self:Hide() end, FULL_BAR_TIMEOUT_MS)
        else
            if capturingPlayers > contestingPlayers then
                -- Flag is being captured; make the endcap a point
                self.endCap:SetTexture(self:GetPointTextureFromAlliance(owningAlliance, isBattleground))
                self.endCap:SetHidden(false)
                -- if we are the team that "owns" the flag (i.e. we are capturing the point for our team)
                -- then play a sound indicating that the point is being captured
                if isBattleground and owningAlliance == GetUnitAlliance("player") then
                    PlaySound(SOUNDS.BATTLEGROUND_CAPTURE_METER_CAPTURING)
                end
            elseif capturingPlayers < contestingPlayers then
                -- Flag is being contested; make the endcap a tail
                self.endCap:SetTexture(self:GetTailTextureFromAlliance(owningAlliance, isBattleground))
                self.endCap:SetHidden(false)
                -- if we aren't the team that owns the flag (i.e. we are taking the point from another team)
                -- then play a sound indicating that the point is being contested
                if isBattleground and owningAlliance ~= GetUnitAlliance("player") then
                    PlaySound(SOUNDS.BATTLEGROUND_CAPTURE_METER_CONTESTING)
                end
            else
                -- Stalemate
                self.endCap:SetHidden(true)
                self:HideShowArrows(0)
            end
        end
    elseif not self.lastOwningAlliance then -- Checks lastOwning alliance against both nil and 0
        -- Hide alliance-specific UI elements
        self.ownerBadge:SetHidden(true)
        self.progressBar:SetHidden(true)
        self.endCap:SetHidden(true)
        self:HideShowArrows(0)
    else
        -- In this case, the capture bar is going from nonzero to zero, which means we need to tween it down to zero and then
        -- hide the alliance-specific textures. This is handled in the AnimationTimeline's OnStop event.
    end

    if self.capTimeline.endPct == nil then
        self.capTimeline.endPct = self.capturePercentage
    end

    self.capTimeline.startPct = self.capTimeline.endPct
    self.capTimeline.endPct = self.capturePercentage
    self:UpdateCaptureBar(self.capTimeline.startPct)
    self.capTimeline:PlayFromStart()
end

function ZO_ObjectiveCaptureMeter:VelocityArrowUpdate(captureBarFillPercentage, visibleArrows)
    local arrowAnchorPointPct -- The percentage of the way around the circle that the arrows should anchor to
                               -- Goes from 0.0-1.0, where 0.0 is 12.5% around the circle (starting from the top center)
                               -- and 1.0 is 87.5% of the way around.

    if visibleArrows > 0 then
        -- We're displaying capturing arrows
        if ARROW_ARCLENGTH * zo_abs(visibleArrows) > captureBarFillPercentage * CAPTURE_BAR_ARCLENGTH then
            -- Our arrows are longer than our capture bar, anchor to the end
            arrowAnchorPointPct = ARROW_LENGTH * zo_abs(visibleArrows)
        else
            -- Our capture bar is longer than our arrows, position arrows to follow capture bar
            arrowAnchorPointPct = captureBarFillPercentage
        end
    elseif visibleArrows < 0 then
        -- We're displaying contesting arrows
        if ARROW_ARCLENGTH * zo_abs(visibleArrows) + ENDCAP_ARCLENGTH > (1 - captureBarFillPercentage) * CAPTURE_BAR_ARCLENGTH then
            -- Our arrows are longer than our capture bar, anchor to the end
            arrowAnchorPointPct = 1 - (ARROW_LENGTH * zo_abs(visibleArrows) + ENDCAP_LENGTH)
        else
            -- Our capture bar is longer than our arrows, position arrows to follow capture bar
            arrowAnchorPointPct = captureBarFillPercentage
        end
    else
        -- No arrows to display, but keep updating the position for the next time we unhide them
        arrowAnchorPointPct = captureBarFillPercentage
    end

    local textureRotation = -arrowAnchorPointPct * CAPTURE_BAR_ARCLENGTH
    self.endCap:SetTextureRotation(textureRotation)
    self.capturingArrow1:SetTextureRotation(textureRotation)
    self.capturingArrow2:SetTextureRotation(textureRotation + ARROW_ARCLENGTH)
    self.capturingArrow3:SetTextureRotation(textureRotation + (2 * ARROW_ARCLENGTH))
    self.contestingArrow1:SetTextureRotation(textureRotation)
    self.contestingArrow2:SetTextureRotation(textureRotation - ARROW_ARCLENGTH)
    self.contestingArrow3:SetTextureRotation(textureRotation - (2 * ARROW_ARCLENGTH))
end

function ZO_ObjectiveCaptureMeter:UpdateCaptureBar(progress)
    local captureBarFillPercentage = (progress * (self.capTimeline.endPct - self.capTimeline.startPct)) + self.capTimeline.startPct;
    local arrows = self:GetVisibleArrows()
    local NO_LEADING_EDGE = false
    self.progressBar:StartFixedCooldown((captureBarFillPercentage * CAPTURE_BAR_LENGTH) + CAPTURE_BAR_BEGIN_OFFSET, CD_TYPE_RADIAL, CD_TIME_TYPE_TIME_REMAINING, NO_LEADING_EDGE) -- CD_TIME_TYPE_TIME_REMAINING causes clockwise scroll
    self:VelocityArrowUpdate(captureBarFillPercentage, arrows)
end

function ZO_ObjectiveCaptureMeter:EasingAnimationStopped()
    if self.capturePercentage == 0 then
        -- Hide alliance-specific UI elements
        self.ownerBadge:SetHidden(true)
        self.progressBar:SetHidden(true)
        self.endCap:SetHidden(true)
        self:HideShowArrows(0)
    end
end

function ZO_ObjectiveCaptureMeter:Hide(eventCode)
    self.meter.fadeAnim:FadeOut(0, FADE_DURATION_MS)

    -- Give the fade-out animation time to play before hiding the control
    zo_callLater(function()
        self:SetHiddenForReason("inCaptureArea", true)
    end, FADE_DURATION_MS)
end

function ZO_ObjectiveCaptureMeter:SetHiddenForReason(reason, hidden)
    self.hiddenReasons:SetHiddenForReason(reason, hidden)
    SHARED_INFORMATION_AREA:SetHidden(self.control, self.hiddenReasons:IsHidden())
end

-- Param numArrows: A number between -3 and 3, determining which arrows to show and which to hide
--                  -3 corresponds to 3 contesting arrows, and
--                  +3 corresponds to 3 capturing arrows.
--                  If the number is <-3 or >3, 3 arrows will be shown in the corresponding direction
function ZO_ObjectiveCaptureMeter:HideShowArrows(numArrows)
    -- Hide and show arrows
    self.capturingArrow1:SetHidden(numArrows < 1)
    self.capturingArrow2:SetHidden(numArrows < 2)
    self.capturingArrow3:SetHidden(numArrows < 3)
    self.contestingArrow1:SetHidden(numArrows > -1)
    self.contestingArrow2:SetHidden(numArrows > -2)
    self.contestingArrow3:SetHidden(numArrows > -3)
end

function ZO_ObjectiveCaptureMeter:GetVisibleArrows()
    local numArrows = 0
    if not self.capturingArrow1:IsHidden() then
        numArrows = 1
        if not self.capturingArrow2:IsHidden() then
            numArrows = 2
            if not self.capturingArrow3:IsHidden() then
                numArrows = 3
            end
        end
    elseif not self.contestingArrow1:IsHidden() then
        numArrows = -1
        if not self.contestingArrow2:IsHidden() then
            numArrows = -2
            if not self.contestingArrow3:IsHidden() then
                numArrows = -3
            end
        end
    end
    return numArrows
end

function ZO_ObjectiveCaptureMeter:ApplyPlatformStyle(style)
    ApplyTemplateToControl(self.control, style.meter)
end

--------------
-- Global XML
--------------

function ZO_ObjectiveCapture_Initialize(self)
    OBJECTIVE_CAPTURE_METER = ZO_ObjectiveCaptureMeter:New(self)
end

function ZO_ObjectiveCapture_UpdateCaptureBar(progress)
    OBJECTIVE_CAPTURE_METER:UpdateCaptureBar(progress)
end

function ZO_ObjectiveCapture_EasingAnimationStopped()
    OBJECTIVE_CAPTURE_METER:EasingAnimationStopped()
end