--  ________  _________  ___       ___  ________     
-- |\   __  \|\___   ___\\  \     |\  \|\   __  \    
-- \ \  \|\ /\|___ \  \_\ \  \    \ \  \ \  \|\ /_   
--  \ \   __  \   \ \  \ \ \  \    \ \  \ \   __  \  
--   \ \  \|\  \   \ \  \ \ \  \____\ \  \ \  \|\  \ 
--    \ \_______\   \ \__\ \ \_______\ \__\ \_______\
--     \|_______|    \|__|  \|_______|\|__|\|_______|


local MAJOR, MINOR = "BTlib", 12
local BTlib, oldminor = LibStub:NewLibrary(MAJOR, MINOR)		

-- Cache
local UnitExists, UnitName, UnitIsPlayer, UnitClass, UnitSelectionColor = UnitExists, UnitName, UnitIsPlayer, UnitClass, UnitSelectionColor
local GetSpellCooldown = GetSpellCooldown
local GetNumGroupMembers = GetNumGroupMembers
local IsItemInRange = IsItemInRange
local IsInInstance, IsInGroup, IsInRaid = IsInInstance, IsInGroup, IsInRaid
local format, find, strrep, len, sub = string.format, string.find, strrep, string.len, string.sub
local tonumber, pairs, ipairs, error, unpack, select, tostring = tonumber, pairs, ipairs, error, unpack, select, tostring

BTlib.BTMediaPath = "Interface\\Addons\\BTMediaPack\\media\\textures\\"

-- UnitColoredName
BTlib.UnitColoredName = function(unit)
    if UnitExists(unit) then
        if UnitIsPlayer(unit) then
            return WrapTextInColorCode(UnitName(unit), select(4, GetClassColor(select(2,UnitClass(unit)))))
        else
            local r, g, b = UnitSelectionColor(unit)
            return WrapTextInColorCode(UnitName(unit), string.format("ff%02x%02x%02x", r*255, g*255, b*255))
        end
    end
end

-- GroupMembers
--[[
Usage:
for unit in GroupMembers() do
  <block>
end

Parameters:
reversed: set to true to reverse indexing
forceParty: set to true to only return party members
indexOnly: set to true to only  return raid index and not unitId ('5' instead of 'raid5')
]]
BTlib.GroupMembers = function(reversed, forceParty, indexOnly)
    local unit  = (not forceParty and IsInRaid()) and 'raid' or 'party'
    local numGroupMembers = forceParty and GetNumSubgroupMembers()  or GetNumGroupMembers()
    local i = reversed and numGroupMembers or (unit == 'party' and 0 or 1)
    return function()
        local ret 
        if i == 0 and unit == 'party' then 
            ret = indexOnly and i or 'player'
        elseif i <= numGroupMembers and i > 0 then
            ret = indexOnly and i or (unit .. i)
        end
        i = i + (reversed and -1 or 1)
        return ret
    end
end

-- IsEligible
-- Returns if the player is in the 4 first groups (mythic), online and not dead
BTlib.IsEligible = function(raidId)
    local raidIndex = sub(raidId, (find(raidId, '%d+')))
    local _, _, subgroup, _, _, _, _, isOnline, isDead = GetRaidRosterInfo(raidIndex)
    if subgroup >= 1 and subgroup <= 4 and not isDead and isOnline then
        return true
    else
        return false
    end
end

-- GetSpellCooldown
BTlib.GetSpellCooldown = function(spell, getDuration)
    if not spell then return end local start, duration, enabled = GetSpellCooldown(spell)
    if not start then return start end if getDuration then return duration end if start == 0 then return start end
    return (start + duration - GetTime())
end

-- ShortenNumber
BTlib.ShortenNumber = function(value, decimal, pattern)
    if not value then return end
    pattern = pattern or '%s%s'
    function round(val, decimal)
        if (decimal) then
            return math.floor( (val * 10^decimal) + 0.5) / (10^decimal)
        else
            return math.floor(val+0.5)
        end
    end
    if(value > 999999999) then return (pattern):format(round(value/1000000000, decimal), 'b') end           
    if(value > 999999) then return (pattern):format(round(value/1000000, decimal), 'm') end        
    if(value > 999) then return (pattern):format(round(value/1000, decimal), 'k') end        
    return (pattern):format(round(value, decimal), '')
end

-- IsClassInParty
BTlib.IsClassInParty = function(class)
    if not class then return nil end
    local name, id, classDisplayName, className, classID
    if type(class) == 'string' then name = string.upper(string.gsub(class, '(%s+)', '')) elseif type(class) == 'number' then id = class end
    local GroupMembers = BTlib.GroupMembers
    for unit in GroupMembers() do
        classDisplayName, className, classID = UnitClass(unit)
        if name then if name == className then return true end elseif id then if id == classID then return true end end       
    end
    return false
end

-- GetUnitPos : 1 = Melee, 2 = Ranged
-- Note: Monks are always considered as Melee
BTlib.GetUnitPos = function(unit)
    if UnitExists(unit) == false or UnitIsPlayer(unit) == false then return nil end
    local _, class = UnitClass(unit)
    if (class == "WARRIOR" or class == "ROGUE" or class == "DEATHKNIGHT" or class == "MONK" or class == "DEMONHUNTER") then
        return 1
    elseif (class == "HUNTER" or class == "PRIEST" or class == "MAGE" or class == "WARLOCK") then
        return 2
    else -- Paladin, Shaman, Druid
        if UnitPowerMax(unit, SPELL_POWER_MANA) == 220000 then
            return 1
        else
            return 2
        end
    end
end

-- PlayerInRange : Returns how many players are in the specified range
BTlib.PlayerInRange = function(rangeToCheck, auraID)
    local numInRange = 0
    local unitsInRange = {}
    local GroupMembers = BTlib.GroupMembers
    
    -- list of items usable on friendly targets
    local rangeItems = {}
    rangeItems[5] = 37727 -- Ruby Acorn
    rangeItems[8] = 34368 -- Attuned Crystal Cores
    rangeItems[13] = 32321 -- Sparrowhawk Net
    rangeItems[15] = 1251 -- Linen Bandage
    rangeItems[20] = 21519 -- Mistletoe
    rangeItems[25] = 31463 -- Zezzak's Shard
    rangeItems[30] = 34191 -- Handful of Snowflakes
    rangeItems[35] = 18904 -- Zorbin's Ultra-Shrinker
    rangeItems[40] = 34471 -- Vial of the Sunwell
    rangeItems[45] = 32698 -- Wrangling Rope
    rangeItems[60] = 32825 -- Soul Cannon
    rangeItems[80] = 35278 -- Reinforced Net
	
	local rangeInteract = {}
	rangeInteract[10] = 3
	rangeInteract[28] = 4
    
    -- adjust range to the nearest possible range
    local possibleRange = rangeToCheck
    while (rangeItems[possibleRange] == nil and rangeInteract[possibleRange] == nil) and possibleRange > 0 do 
        possibleRange = possibleRange - 1
    end
    if possibleRange == 0 then possibleRange = 5 end
    
    -- check each group members
    for unit in GroupMembers() do
        if UnitExists(unit) and not UnitIsUnit(unit, "player") and not UnitIsDeadOrGhost(unit) and 
        UnitIsConnected(unit) and UnitInPhase(unit) then
            local isInRange = false
            if rangeItems[possibleRange] ~= nil then
                isInRange = IsItemInRange(rangeItems[possibleRange], unit)
            elseif rangeInteract[possibleRange] ~= nil then
                isInRange = CheckInteractDistance(unit,rangeInteract[possibleRange])
            end
            if isInRange then
                -- if auraID is specified
                if auraID ~= nil then
                    local auraName = GetSpellInfo(auraID)
                    if UnitAura(unit, auraName) ~= nil or UnitAura(unit, auraName, nil, "HARMFUL") ~= nil then
                        numInRange = numInRange + 1
                        tinsert(unitsInRange, unit)
                    end
                else
                    numInRange = numInRange + 1
                    tinsert(unitsInRange, unit)
                end
            end
        end
    end
    return numInRange, unitsInRange, possibleRange
end

--[[BTlib.GetRangeFromUnit = function(unit)
    local friendlyItems = {
        [5] = 37727,
        [8] = 34368,
        [13] = 32321, 
        [15] = 1251,
        [20] = 21519, 
        [25] = 31463,
        [30] = 34191, 
        [35] = 18904, 
        [40] = 34471,
        [45] = 32698,
        [60] = 32825,
        [80] = 35278
    }
    local harmItems = {
        [5] = 37727,
        [6] = 63427, 
        [8] = 34368,
        [10] = 32321,
        [15] = 33069,
        [20] = 10645,
        [25] = 24268,
        [30] = 835,
        [35] = 24269,
        [40] = 28767, 
        [45] = 23836, 
        [50] = 116139,
        [60] = 32825,
        [70] = 41265,
        [80] = 35278,
        [100] = 33119,
    }

    if not UnitExists(unit) or UnitIsUnit(unit, "player") then return 0,0 end

    local minRange = 100
    local maxRange = 100 

    -- enemy
    if UnitIsEnemy("player", unit) then
        for k, v in pairs(harmItems) do
            if IsItemInRange(v, unit) and k < minRange then
                minRange = k
            elseif IsItemInRange(v, unit) and k > maxRange then
                maxRange = k
            end      
        end
    -- friendly
    elseif not UnitIsDeadOrGhost(unit) and UnitIsConnected(unit) and UnitInPhase(unit) then
        for k, v in pairs(friendlyItems) do
            if IsItemInRange(v, unit) then
                if k < minRange then
                    minRange = k  
                end  
                if k < maxRange and k > minRange then
                    maxRange = k
                end           
            end            
        end
    end
    return minRange, maxRange
end]]--

-- Send this function a group/raid member's unitID or GUID and it will return their raid frame.
BTlib.GetFrame = function(target)
    if not UnitExists(target) then
        if type(target) == "string" and target:find("Player") then
            target = select(6,GetPlayerInfoByGUID(target))
        else
            return 
        end
    end    
    -- Healbot
    if IsAddOnLoaded("HealBot") then
        for _,frame in pairs(HealBot_Unit_Button) do
            if UnitIsUnit(frame.unit, target) then
                return frame
            end
        end
    end
    -- Vuhdo
    if IsAddOnLoaded("VuhDo") and VUHDO_CONFIG["SHOW_PANELS"] then
        for _, v in pairs(VUHDO_UNIT_BUTTONS) do
            if v[1].raidid and UnitIsUnit(v[1].raidid, target) then
                return v[1]
            end 
        end
    end
    --Grid2
    if IsAddOnLoaded("Grid2") then
        for _, frame in pairs(Grid2Frame.registeredFrames) do
            if frame:IsVisible() then
                if frame.unit and UnitIsUnit(frame.unit, target) then
                    return frame
                end
            end
        end
    end
    -- ElvUI
    if ElvUF then
        for _,frame in pairs(ElvUF.objects) do
            if (frame.unitframeType == "raid" 
                or frame.unitframeType == "party" 
                or frame.unitframeType == "raid40"
            )
            and frame:IsVisible() and frame.unit and UnitIsUnit(frame.unit, target)
            then
                return frame
            end
        end
    end
    --Lastly, default frames
    if CompactRaidFrameContainer.groupMode == "flush" then
        for _,frame in pairs(CompactRaidFrameContainer.flowFrames) do
            if frame.unit and frame:IsVisible() and UnitIsUnit(frame.unit, target) then
                return frame
            end
        end
    else
        for i = 1,8 do
            for j = 1,5 do
                local frame = _G["CompactRaidGroup"..i.."Member"..j]
                if frame == nil then return end
                if frame:IsVisible() and frame.unit and UnitIsUnit(frame.unit, target)
                then
                    return frame
                end
            end
        end
    end
end	
															 