--[[
	Addon: Taos Group Tools
	Author: TProg Taonnor
	Created by @Taonnor
]]--

--[[
	Global callbacks
]]--
TGT_MAP_PING_CHANGED = "TGT-MapPingChanged"

--[[
	Local variables
]]--
local LOG_ACTIVE = false

local LGPS = LibStub("LibGPS2", true)
if(not LGPS) then
	error("Cannot load without LibGPS2")
end

local LMP = LibStub("LibMapPing")
if(not LMP) then
	error("Cannot load without LibMapPing")
end

local _logger = nil

local ABILITY_COEFFICIENT = 100
local ULTIMATE_COEFFICIENT = 1000

--[[
	Table TGT_Communicator
]]--
TGT_Communicator = {}
TGT_Communicator.__index = TGT_Communicator

--[[
	Table Members
]]--
TGT_Communicator.Name = "TGT-Communicator"
TGT_Communicator.IsMocked = false

--[[
	Called on map ping from LibMapPing
]]--
function TGT_Communicator.OnMapPing(pingType, pingTag, offsetX, offsetY, isLocalPlayerOwner)
    if (LOG_ACTIVE) then
        _logger:logTrace("TGT_Communicator.OnMapPing")
        _logger:logDebug("pingTag; offsetX; offsetY", pingTag, offsetX, offsetY)
    end

	if (pingType == MAP_PIN_TYPE_PING) then
        LGPS:PushCurrentMap()
	    SetMapToMapListIndex(GetUnitZoneIndex(pingTag))

	    offsetX, offsetY = LMP:GetMapPing(pingType, pingTag)
        if (LOG_ACTIVE) then _logger:logDebug("MapPing coords; offsetX; offsetY", offsetX, offsetY) end

	    LGPS:PopCurrentMap()

        if (LOG_ACTIVE) then _logger:logDebug("SuppressPing ->", pingType, pingTag) end

        LMP:SuppressPing(pingType, pingTag)

        if (LMP:IsPositionOnMap(offsetX, offsetY) and
		    TGT_Communicator.IsPossiblePing(offsetX, offsetY)) then

            local abilityPing = TGT_Communicator.GetAbilityPing(offsetX)
		    local relativeUltimate = TGT_Communicator.GetRelativeUltimate(offsetY)

            if (LOG_ACTIVE) then
                _logger:logDebug("pingTag; abilityPing; relativeUltimate", pingTag, abilityPing, relativeUltimate)
            end

            if (abilityPing ~= -1 and relativeUltimate ~= -1) then
                CALLBACK_MANAGER:FireCallbacks(TGT_MAP_PING_CHANGED, pingTag, abilityPing, relativeUltimate)
            else
                _logger:logError("TGT_Communicator.OnMapPing, Ping invalid abilityPing: " .. tostring(abilityPing) .. "; relativeUltimate: " .. tostring(relativeUltimate))
            end
        end
    end
end

--[[
	Called on map ping from LibMapPing
]]--
function TGT_Communicator.OnMapPingFinished(pingType, pingTag, offsetX, offsetY, isLocalPlayerOwner)
    offsetX, offsetY = LMP:GetMapPing(pingType, pingTag) -- load from LMP, because offsetX, offsetY from PING_EVENT_REMOVED are 0,0

	if (LOG_ACTIVE) then
        _logger:logTrace("TGT_Communicator.OnMapPingFinished")
		--_logger:logDebug("pingType; pingTag; offsetX; offsetY", pingType, pingTag, offsetX, offsetY)
    end

    if (pingType == MAP_PIN_TYPE_PING and
        LMP:IsPositionOnMap(offsetX, offsetY) and
		TGT_Communicator.IsPossiblePing(offsetX, offsetY)) then

        if (LOG_ACTIVE) then
            _logger:logDebug("UnsuppressPing ->", pingType, pingTag)
        end

        LMP:UnsuppressPing(pingType, pingTag)
    end
end

--[[
	Called on refresh of timer
]]--
function TGT_Communicator.SendData(abilityGroup)
    if (LOG_ACTIVE) then
		_logger:logTrace("TGT_Communicator.SendData")
		--_logger:logDebug("abilityGroup", abilityGroup)
	end

    if (abilityGroup ~= nil) then
        local current, max, effective_max = GetUnitPower("player", POWERTYPE_ULTIMATE)
        local abilityCost = math.max(1, GetAbilityCost(abilityGroup.GroupAbilityId))

        -- Mocked
        if (TGT_Communicator.IsMocked) then
            TGT_Communicator.SendFakePings()
        -- Standard communication
        else
            local relativeUltimate = math.floor((current / abilityCost) * 100)

	        if (relativeUltimate > 100) then
		        relativeUltimate = 100
	        end

	        local abilityPing = abilityGroup.GroupAbilityPing / ABILITY_COEFFICIENT
            local ultimatePing = 0.0001 -- Zero, if you send "0", the map ping will be invalid

            if (relativeUltimate > 0) then
	            ultimatePing = relativeUltimate / ULTIMATE_COEFFICIENT
            end

            if (LOG_ACTIVE) then
		        --_logger:logDebug("abilityGroup.GroupName", abilityGroup.GroupName)
                --_logger:logDebug("relativeUltimate", relativeUltimate)
                --_logger:logDebug("abilityPing", abilityPing)
                --_logger:logDebug("ultimatePing", ultimatePing)
            end

            LGPS:PushCurrentMap()
            SetMapToMapListIndex(GetUnitZoneIndex("player"))

            LMP:SetMapPing(MAP_PIN_TYPE_PING, MAP_TYPE_LOCATION_CENTERED, abilityPing, ultimatePing)

            LGPS:PopCurrentMap()
        end
    else
        _logger:logError("TGT_Communicator.SendData, abilityGroup is nil.")
    end
end

--[[
	Check if map ping is in possible range
]]--
function TGT_Communicator.IsPossiblePing(offsetX, offsetY)
    if (LOG_ACTIVE) then
        --_logger:logTrace("TGT_Communicator.IsPossiblePing")
        --_logger:logDebug("offsetX; offsetY", offsetX, offsetY)
    end

	local isValidPing = (offsetX ~= 0 or offsetY ~= 0)
	local isCorrectOffsetX = (offsetX >= 0.009 and offsetX <= 0.30)
	local isCorrectOffsetY = (offsetY >= 0.000 and offsetY <= 0.11)

    if (LOG_ACTIVE) then
        --_logger:logDebug("isValidPing; isCorrectOffsetX; isCorrectOffsetY", isValidPing, isCorrectOffsetX, isCorrectOffsetY)
    end

	return isValidPing and (isCorrectOffsetX and isCorrectOffsetY)
end

--[[
	Gets ability ID
]]--
function TGT_Communicator.GetAbilityPing(offset)
    if (LOG_ACTIVE) then
        --_logger:logTrace("TGT_Communicator.GetAbilityPing")
        --_logger:logDebug("offset", offset)
    end

    if (offset > 0) then
        local abilityPing = math.floor((offset * ABILITY_COEFFICIENT) + 0.5)
        if (abilityPing >= 1 and abilityPing <= 29) then
            return abilityPing
        else
            _logger:logError("offset is incorrect: " .. tostring(abilityPing) .. "; offset: " .. tostring(offset))
            return -1
        end
    else
        _logger:logError("offset is incorrect: " .. tostring(offset))
        return -1
    end
end

--[[
	Gets relative ultimate
]]--
function TGT_Communicator.GetRelativeUltimate(offset)
    if (LOG_ACTIVE) then
        --_logger:logTrace("TGT_Communicator.GetRelativeUltimate")
        --_logger:logDebug("offset", offset)
    end

    if (offset >= 0) then
        local relativeUltimate = math.floor((offset * ULTIMATE_COEFFICIENT) + 0.5)
        if (relativeUltimate >= 0 and relativeUltimate <= 100) then
            return relativeUltimate
        elseif (relativeUltimate >= 100 and relativeUltimate <= 110) then
            if (LOG_ACTIVE) then _logger:logDebug("offset; relativeUltimate", offset, relativeUltimate) end
            return 100
        else
            _logger:logError("relativeUltimate is incorrect: " .. tostring(relativeUltimate) .. "; offset: " .. tostring(offset))
            return -1
        end
    else
        _logger:logError("offset is incorrect: " .. tostring(offset))
        return -1
    end
end

--[[
	Sends fake pings for all group members
]]--
function TGT_Communicator.SendFakePings()
    if (LOG_ACTIVE) then  _logger:logTrace("TGT_Communicator.SendFakePings") end

    local ultimateGroups = TGT_UltimateGroupHandler.GetUltimateGroups()

    -- Directly send to test only UI
    CALLBACK_MANAGER:FireCallbacks(TGT_MAP_PING_CHANGED, "group1", ultimateGroups[1].GroupAbilityPing, 100)
    CALLBACK_MANAGER:FireCallbacks(TGT_MAP_PING_CHANGED, "group2", ultimateGroups[1].GroupAbilityPing, 100)
    CALLBACK_MANAGER:FireCallbacks(TGT_MAP_PING_CHANGED, "group3", ultimateGroups[1].GroupAbilityPing, math.random(90, 100))
    CALLBACK_MANAGER:FireCallbacks(TGT_MAP_PING_CHANGED, "group4", ultimateGroups[1].GroupAbilityPing, math.random(90, 100))
    CALLBACK_MANAGER:FireCallbacks(TGT_MAP_PING_CHANGED, "group5", ultimateGroups[6].GroupAbilityPing, math.random(90, 100))
    CALLBACK_MANAGER:FireCallbacks(TGT_MAP_PING_CHANGED, "group6", ultimateGroups[6].GroupAbilityPing, math.random(90, 100))
    CALLBACK_MANAGER:FireCallbacks(TGT_MAP_PING_CHANGED, "group7", ultimateGroups[6].GroupAbilityPing, 100)
    CALLBACK_MANAGER:FireCallbacks(TGT_MAP_PING_CHANGED, "group8", ultimateGroups[6].GroupAbilityPing, math.random(90, 100))
    CALLBACK_MANAGER:FireCallbacks(TGT_MAP_PING_CHANGED, "group9", ultimateGroups[16].GroupAbilityPing, math.random(90, 100))
    CALLBACK_MANAGER:FireCallbacks(TGT_MAP_PING_CHANGED, "group10", ultimateGroups[16].GroupAbilityPing, math.random(90, 100))
    CALLBACK_MANAGER:FireCallbacks(TGT_MAP_PING_CHANGED, "group11", ultimateGroups[16].GroupAbilityPing, math.random(90, 100))
    CALLBACK_MANAGER:FireCallbacks(TGT_MAP_PING_CHANGED, "group12", ultimateGroups[16].GroupAbilityPing, 100)
    CALLBACK_MANAGER:FireCallbacks(TGT_MAP_PING_CHANGED, "group13", ultimateGroups[28].GroupAbilityPing, 100)
    CALLBACK_MANAGER:FireCallbacks(TGT_MAP_PING_CHANGED, "group14", ultimateGroups[28].GroupAbilityPing, 100)
    CALLBACK_MANAGER:FireCallbacks(TGT_MAP_PING_CHANGED, "group15", ultimateGroups[28].GroupAbilityPing, math.random(90, 100))
    CALLBACK_MANAGER:FireCallbacks(TGT_MAP_PING_CHANGED, "group16", ultimateGroups[28].GroupAbilityPing, math.random(90, 100))
    CALLBACK_MANAGER:FireCallbacks(TGT_MAP_PING_CHANGED, "group17", ultimateGroups[13].GroupAbilityPing, math.random(90, 100))
    CALLBACK_MANAGER:FireCallbacks(TGT_MAP_PING_CHANGED, "group18", ultimateGroups[13].GroupAbilityPing, math.random(90, 100))
    CALLBACK_MANAGER:FireCallbacks(TGT_MAP_PING_CHANGED, "group19", ultimateGroups[13].GroupAbilityPing, 100)
    CALLBACK_MANAGER:FireCallbacks(TGT_MAP_PING_CHANGED, "group20", ultimateGroups[13].GroupAbilityPing, 100)
    CALLBACK_MANAGER:FireCallbacks(TGT_MAP_PING_CHANGED, "group21", ultimateGroups[29].GroupAbilityPing, math.random(90, 100))
    CALLBACK_MANAGER:FireCallbacks(TGT_MAP_PING_CHANGED, "group22", ultimateGroups[29].GroupAbilityPing, math.random(90, 100))
    CALLBACK_MANAGER:FireCallbacks(TGT_MAP_PING_CHANGED, "group23", ultimateGroups[29].GroupAbilityPing, math.random(90, 100))
    CALLBACK_MANAGER:FireCallbacks(TGT_MAP_PING_CHANGED, "group24", ultimateGroups[29].GroupAbilityPing, math.random(90, 100))
end

--[[
	Initialize initializes TGT_Communicator
]]--
function TGT_Communicator.Initialize(logger, isMocked)
    if (LOG_ACTIVE) then
        logger:logTrace("TGT_Communicator.Initialize")
        logger:logDebug("isMocked", isMocked)
    end

    _logger = logger

    TGT_Communicator.IsMocked = isMocked

    -- Register events
    LMP:RegisterCallback("BeforePingAdded", TGT_Communicator.OnMapPing)
    LMP:RegisterCallback("AfterPingRemoved", TGT_Communicator.OnMapPingFinished)
end