
if (true) then
    --return
end

local Details = _G.Details
local bit = _G.bit
local DETAILS_ATTRIBUTE_DAMAGE = _G.DETAILS_ATTRIBUTE_DAMAGE
local DF = _G.DetailsFramework
local tonumber = _G.tonumber
local select = _G.select
local strsplit = _G.strsplit
local floor = _G.floor
local tremove = _G.tremove
local UnitName = _G.UnitName
local tinsert = _G.tinsert
local IsInRaid = _G.IsInRaid
local GetNumGroupMembers = _G.GetNumGroupMembers
local GetRaidRosterInfo = _G.GetRaidRosterInfo
local unpack = _G.unpack
local IsInGroup = _G.IsInGroup
local addonName, Details222 = ...

Details.packFunctions = {}

--lookup actor information tables
local actorInformation = {}
local actorInformationIndexes = {}
local actorDamageInfo = {}
local actorHealInfo = {}
local actorUtilityInfo = {}

--flags
local REACTION_HOSTILE	=	0x00000040
local IS_GROUP_OBJECT 	= 	0x00000007
local REACTION_FRIENDLY	=	0x00000010
local OBJECT_TYPE_MASK =	0x0000FC00
local OBJECT_TYPE_OBJECT =	0x00004000
local OBJECT_TYPE_PETGUARDIAN =	0x00003000
local OBJECT_TYPE_GUARDIAN =	0x00002000
local OBJECT_TYPE_PET =		0x00001000
local OBJECT_TYPE_NPC =		0x00000800
local OBJECT_TYPE_PLAYER =	0x00000400

local INDEX_EXPORT_FLAG = 1
local INDEX_COMBAT_START_TIME = 2
local INDEX_COMBAT_END_TIME = 3
local INDEX_COMBAT_START_DATE = 4
local INDEX_COMBAT_END_DATE = 5
local INDEX_COMBAT_NAME = 6

local TOTAL_INDEXES_FOR_COMBAT_INFORMATION = 6

local entitySerialCounter = 0

local isDebugging = false

function Details.packFunctions.GetAllData()
    local combat = Details:GetCurrentCombat()
    local packedData = Details.packFunctions.PackCombatData(combat, 0x1B)
    --local packedData = Details.packFunctions.PackCombatData(combat, 0x13)
    return packedData
end

--debug
function Details.packFunctions.GetAllDataDebug()
    local combat = Details:GetCurrentCombat()
    local packedData = Details.packFunctions.PackCombatData(combat, 0x13)

    --unpack data
    Details.packFunctions.DeployPackedCombatData(packedData)
end

--pack the combat
function Details.packFunctions.PackCombatData(combatObject, flags)

    --0x1 damage
    --0x2 healing
    --0x4 energy
    --0x8 misc
    --0x10 no combat header

    table.wipe(actorInformation)
    table.wipe(actorInformationIndexes)
    table.wipe(actorDamageInfo)
    table.wipe(actorHealInfo)
    table.wipe(actorUtilityInfo)

    --reset the serial counter
    entitySerialCounter = 0

    local isBossEncouter = combatObject.is_boss
    local startDate, endDate = combatObject:GetDate()

    local startCombatTime = combatObject:GetStartTime() or 0
    local endCombatTime = combatObject:GetEndTime() or 0
    local combatInfo = {
        floor(startCombatTime), --1
        floor(endCombatTime), --2
        startDate, --3
        endDate, --4
        isBossEncouter and isBossEncouter.encounter or "Unknown Enemy" --5
    }

    --if there's no combat information, indexes for combat data is zero
    if (bit.band(flags, 0x10) ~= 0) then
        TOTAL_INDEXES_FOR_COMBAT_INFORMATION = 0
    end

    if (bit.band(flags, 0x1) ~= 0) then
        ---print("pack damage")
        Details.packFunctions.PackDamage(combatObject)
    end

    if (bit.band(flags, 0x2) ~= 0) then
        --print("pack heal")
        Details.packFunctions.PackHeal(combatObject)
    end

    if (bit.band(flags, 0x8) ~= 0) then
        --print("pack utility")
        Details.packFunctions.PackUtility(combatObject)
    end

    --prepare data to send over network
        local exportedString = flags .. ","

        --add the combat info
        if (bit.band(flags, 0x10) == 0) then
            for index, data in ipairs(combatInfo) do
                exportedString = exportedString .. data .. ","
            end
        end

        --add the actor references table
        for index, data in ipairs(actorInformation) do
            exportedString = exportedString .. data .. ","
        end

        --add the damage actors data
        if (bit.band(flags, 0x1) ~= 0) then
            exportedString = exportedString .. "!D" .. ","
            for index, data in ipairs(actorDamageInfo) do
                exportedString = exportedString .. data .. ","
            end
        end

        --add the heal actors data
        if (bit.band(flags, 0x2) ~= 0) then
            exportedString = exportedString .. "!H" .. ","
            for index, data in ipairs(actorHealInfo) do
                exportedString = exportedString .. data .. ","
            end
        end

        --add the utility actors data
        if (bit.band(flags, 0x8) ~= 0) then
            exportedString = exportedString .. "!U" .. ","
            for index, data in ipairs(actorUtilityInfo) do
                exportedString = exportedString .. data .. ","
            end
        end

        --main stuff
        --print("finished export (debug):", exportedString) --debug
        --print("uncompressed (debug):", format("%.2f", #exportedString/1024), "KBytes")

--if true then return exportedString end

        if (isDebugging) then
            print(exportedString)
        end

        --Details:Dump({exportedString})

        --print("EXPORTING STRING FINAL:", exportedString)

        --compress
        local LibDeflate = _G.LibStub:GetLibrary("LibDeflate")
        local dataCompressed = LibDeflate:CompressDeflate(exportedString, {level = 9})
        local dataEncoded = LibDeflate:EncodeForWoWAddonChannel(dataCompressed)

        --print("encoded for WowAddonChannel (debug):", format("%.2f", #dataEncoded/1024), "KBytes")
        
        return dataEncoded
end

function Details.packFunctions.GenerateSerialNumber()
    local serialNumber = entitySerialCounter
    entitySerialCounter = entitySerialCounter + 1
    return serialNumber
end

--[[
    actor flag IDs
    1: player friendly
    2: player enemy
    3: enemy npc pet
    4: enemy npc non-pet
    5: friendly npc pet
    6: friendly non-npc
    7: unknown entity type
]]

local packActorFlag = function(actor)
    if (actor.grupo) then
        --it's a player in the group
        return 1
    end

    local flag = actor.flag_original or 0

    --check hostility
    if (bit.band(flag, REACTION_HOSTILE) ~= 0) then
        --is hostile
        if (bit.band(flag, OBJECT_TYPE_PLAYER) == 0) then
            --isn't a player
            if (bit.band(flag, OBJECT_TYPE_PETGUARDIAN) ~= 0) then
                --is pet
                return 3
            else
                --is enemy npc
                return 4
            end
        else
            --is enemy player
            return 2
        end
    else
        --is friendly
        if (bit.band(flag, OBJECT_TYPE_PLAYER) == 0) then
            --is player
            return 1
        else
            --isn't a player
            if (bit.band(flag, OBJECT_TYPE_PETGUARDIAN) ~= 0) then
                --is friendly pet
                return 5
            else
                --is a friendly entity, most likely a npc
                return 6
            end
        end
    end

    return 7
end

local unpackActorFlag = function(flag)
    --convert to integer
    flag = tonumber(flag)

    if (flag == 1) then --player
        return 0x511

    elseif (flag == 2) then --enemy player
        return 0x548

    elseif (flag == 3) then --enemy pet with player or AI controller
        return 0x1A48

    elseif (flag == 4) then --enemy npc
        return 0xA48

    elseif (flag == 5) then --friendly pet
        return 0x1914

    elseif (flag == 6) then --friendly npc
        return 0xA14

    elseif (flag == 7) then --unknown entity
        return 0x4A28
    end
end

local isActorInGroup = function(class, flag)
    if (bit.band(flag, IS_GROUP_OBJECT) ~= 0 and class ~= "UNKNOW" and class ~= "UNGROUPPLAYER" and class ~= "PET") then
        return true
    end
    return false
end

--[[
    actor class IDs
    1-12: player class Id
    20: "PET"
    21: "UNKNOW"
    22: "UNGROUPPLAYER"
    23: "ENEMY"
    24: "MONSTER"
]]

local detailsClassIndexToFileName = {
    [20] = "PET",
    [21] = "UNKNOW",
    [22] = "UNGROUPPLAYER",
    [23] = "ENEMY",
    [24] = "MONSTER",
}

local packActorClass = function(actor)
    local classId = DF.ClassFileNameToIndex[actor.classe]
    if (classId) then
        return classId
    elseif (classId == "PET") then
        return 20
    elseif (classId == "UNKNOW") then
        return 21
    elseif (classId == "UNGROUPPLAYER") then
        return 22
    elseif (classId == "ENEMY") then
        return 23
    elseif (classId == "MONSTER") then
        return 24
    end

    return 21
end

local unpackActorClass = function(classId)
    --convert to integer
    classId = tonumber(classId)

    local classFileName = DF.ClassIndexToFileName[classId]
    if (not classFileName) then
        classFileName = detailsClassIndexToFileName[classId]
    end

    return classFileName
end

--[[
    actor serial
    creature: C12345 (numbers are the npcId)
    player: P
--]]

local packActorSerial = function(actor)
    local serial = actor.serial
    if (serial:match("^C") == "C") then
        local npcId = tonumber(select(6, strsplit("-", serial)) or 0)
        return "C" .. npcId

    elseif (serial:match("^P") == "P") then
        return "P"
    
    elseif (serial == "") then
        return "C12345"
    end
end

local unpackActorSerial = function(serialNumber)
    --player serial
    if (serialNumber:match("^P")) then
        return "Player-1-" .. Details.packFunctions.GenerateSerialNumber()

    elseif (serialNumber:match("^C")) then
        return "Creature-0-0-0-0-" .. serialNumber:gsub("C", "") .."-" .. Details.packFunctions.GenerateSerialNumber()
    end
end

function Details.packFunctions.AddActorInformation(actor)
    --the next index to use on the actor info table
    local currentIndex = #actorInformation + 1

    --calculate where this actor will be placed on the combatData table
    local indexOnCombatDataTable = TOTAL_INDEXES_FOR_COMBAT_INFORMATION + currentIndex + 1

    --add the actor start information index
    actorInformationIndexes[actor.nome] = indexOnCombatDataTable

    --index 1: actor name
    actorInformation[currentIndex] = actor.nome or "unnamed" --[1]

    --index 2: actor flag
    actorInformation[currentIndex + 1] = packActorFlag(actor) --[2]

    --index 3: actor serial
    actorInformation[currentIndex + 2] = packActorSerial(actor) or "" --[3]

    --index 4: actor class
    actorInformation[currentIndex + 3] = packActorClass(actor) --[4]

    --index 5: actor spec
    actorInformation[currentIndex + 4] = actor.spec or 0 --[5]

    return indexOnCombatDataTable
end

function Details.packFunctions.RetriveActorInformation(combatData, index)
    --name [1]
    local actorName = combatData[index]
    if (not actorName) then
        return
    end

    --flag [2]
    local actorFlag = combatData[index + 1]
    actorFlag = unpackActorFlag(actorFlag)

    --serial [3]
    local serialNumber = combatData[index + 2]
    serialNumber = unpackActorSerial(serialNumber)

    --class[4]
    local class = combatData[index + 3]
    class = unpackActorClass(class)

    --spec[5]
    local spec = tonumber(combatData[index + 4])

    --return the values
    return actorName, actorFlag, serialNumber, class, spec
end



--pack damage passes the player damage info + pets the player own
--each player will also send an enemy, the enemy will be in order of raidIndex of the player
function Details.packFunctions.PackDamage(combatObject)

    if (isDebugging) then
        print("PackDamage(): start.")
    end

    --store actorObjects to pack
    local actorsToPack = {}

    --get the player object from the combat > damage container
    local playerName = UnitName("player")
    local playerObject = combatObject:GetActor(DETAILS_ATTRIBUTE_DAMAGE, playerName)
    if (not playerObject) then
        if (isDebugging) then
            print("PackDamage(): return | no player object.")
        end
        return
    end

    tinsert(actorsToPack, playerObject)

    --get the list of pets the player own
    local playerPets = playerObject.pets
    for _, petName in ipairs(playerPets) do
        local petObject = combatObject:GetActor(DETAILS_ATTRIBUTE_DAMAGE, petName)
        if (petObject) then
            tinsert(actorsToPack, petObject)
        end
    end

    local playerIndex = _G.UnitInRaid("player")

    if (not playerIndex) then --no player index
        if (isDebugging) then
            print("PackDamage(): return | no player index found.")
        end
        return
    end

    --get all npc enemies and sort them by their respaw id
    local allActors = combatObject[DETAILS_ATTRIBUTE_DAMAGE]._ActorTable
    local allEnemies = {} --this have subtables, format: {actorObject, spawnId}

    for i = 1, #allActors do
        --get the actor object
        local actor = allActors[i]
        --check if is an enemy or neutral
        if (actor:IsNeutralOrEnemy()) then
            --get the spawnId
            local spawnId = select(7, strsplit("-", actor.serial))
            if (spawnId) then
                --convert hex to number
                spawnId = tonumber(spawnId:sub(1, 10), 16)
                if (spawnId) then
                    --first index is the actorObject, the second index is the spawnId to sort enemies
                    tinsert(allEnemies, {actor, spawnId})
                end
            end
        end
    end
    --sort enemies by their spawnId
    table.sort(allEnemies, Details.Sort2)

    local allPlayerNames = {}
    for i = 1, 20 do
        local name, _, subgroup = GetRaidRosterInfo(i)
        if (name) then --maybe the group has less than 20 players
            name = _G.Ambiguate(name, "none")
            if (name and subgroup <= 4) then
                tinsert(allPlayerNames, name)
            end
        end
    end
    table.sort(allPlayerNames, function(t1, t2) return t1 < t2 end)

    local playerName = UnitName("player")
    for i = 1, #allPlayerNames do
        if (playerName == allPlayerNames[i]) then
            playerIndex = i
            break
        end
    end

    --this is the enemy that this player has to send
    local enemyObjectToSend = allEnemies[playerIndex] and allEnemies[playerIndex][1]
    if (enemyObjectToSend) then
        tinsert(actorsToPack, enemyObjectToSend)
    end

    --add the actors to actor information table
    for _, actorObject in ipairs(actorsToPack) do
        --check if already has the actor information
        local indexToActorInfo = actorInformationIndexes[actorObject.nome] --actor name
        if (not indexToActorInfo) then
            --need to add the actor general information into the actor information table
            indexToActorInfo = Details.packFunctions.AddActorInformation(actorObject)
        end
    end

    for i = 1, #actorsToPack do
        --get the actor object
        local actor = actorsToPack[i]
        local indexToActorInfo = actorInformationIndexes[actor.nome]

        --where the information of this actor starts
        local currentIndex = #actorDamageInfo + 1

        --[1] index where is stored the this actor info like name, class, spec, etc
        actorDamageInfo[currentIndex] = indexToActorInfo  --[1]

        --[2 - 6]
        actorDamageInfo [currentIndex + 1] = floor(actor.total)              --[2]
        actorDamageInfo [currentIndex + 2] = floor(actor.totalabsorbed)      --[3]
        actorDamageInfo [currentIndex + 3] = floor(actor.damage_taken)       --[4]
        actorDamageInfo [currentIndex + 4] = floor(actor.friendlyfire_total) --[5]
        actorDamageInfo [currentIndex + 5] = floor(actor.total_without_pet)  --[6]

        local spellContainer = actor.spells._ActorTable

        --reserve an index to tell the length of spells
        actorDamageInfo [#actorDamageInfo + 1] = 0
        local reservedSpellSizeIndex = #actorDamageInfo
        local totalSpellIndexes = 0

        for spellId, spellInfo in pairs(spellContainer) do
            local spellDamage = spellInfo.total
            local spellHits = spellInfo.counter
            local spellTargets = spellInfo.targets

            actorDamageInfo [#actorDamageInfo + 1] = floor(spellId)
            actorDamageInfo [#actorDamageInfo + 1] = floor(spellDamage)
            actorDamageInfo [#actorDamageInfo + 1] = floor(spellHits)
            totalSpellIndexes = totalSpellIndexes + 3

            --build targets
            local targetsSize = Details.packFunctions.CountTableEntriesValid(spellTargets) * 2
            actorDamageInfo [#actorDamageInfo + 1] = targetsSize
            totalSpellIndexes = totalSpellIndexes + 1

            for actorName, damageDone in pairs(spellTargets) do
                actorDamageInfo [#actorDamageInfo + 1] = actorName
                actorDamageInfo [#actorDamageInfo + 1] = floor(damageDone)
                totalSpellIndexes = totalSpellIndexes + 2
            end
        end

        --amount of indexes spells are using
        actorDamageInfo[reservedSpellSizeIndex] = totalSpellIndexes
    end

    if (isDebugging) then
        print("PackDamage(): done.")
    end
end

--------------------------------------------------------------------------------------------------------------------------------
--unpack

--@currentCombat: details! combat object
--@combatData: array with strings with combat information
--@tablePosition: first index of the first damage actor
function Details.packFunctions.UnPackDamage(currentCombat, combatData, tablePosition)
    if (isDebugging) then
        print("UnPackDamage(): start.")
    end

    --get the damage container
    local damageContainer = currentCombat[DETAILS_ATTRIBUTE_DAMAGE]

    --loop from 1 to 199, the amount of actors store is unknown
    --todo: it's only unpacking the first actor from the table, e.g. theres izimode and eye of corruption, after export it only shows the eye of corruption
    --table position does not move forward
    for i = 1, 199 do
        --actor information index in the combatData table
        --this index gives the position where the actor name, class, spec are stored
        local actorReference = tonumber(combatData[tablePosition]) --[1]
        local actorName, actorFlag, serialNumber, class, spec = Details.packFunctions.RetriveActorInformation(combatData, actorReference)

        if (isDebugging) then
            print("UnPackDamage(): Retrivied Data From " .. (actorReference or "nil") .. ":", actorName, actorFlag, serialNumber, class, spec)
        end

        --check if all damage actors has been processed
        --if there's no actor name it means it reached the end
        if (not actorName) then
            if (isDebugging) then
                print("UnPackDamage(): break damage, end index:", i, (actorReference or "nil"), "tablePosition:", tablePosition, "value:", combatData[tablePosition])
            end
            break
        end

        --get or create the actor object
        local actorObject = damageContainer:GetOrCreateActor(serialNumber, actorName, actorFlag, true)

        --set the actor class, spec and group
        actorObject.classe = class
        actorObject:SetSpecId(spec)
        actorObject.grupo = isActorInGroup(class, actorFlag)
        actorObject.flag_original = actorFlag

        --copy back the base damage
            actorObject.total =               tonumber(combatData[tablePosition+1]) --[2]
            actorObject.totalabsorbed =       tonumber(combatData[tablePosition+2]) --[3]
            actorObject.damage_taken =        tonumber(combatData[tablePosition+3]) --[4]
            actorObject.friendlyfire_total =  tonumber(combatData[tablePosition+4]) --[5]
            actorObject.total_without_pet  =  tonumber(combatData[tablePosition+5]) --[6]

            tablePosition = tablePosition + 6 --increase table position

        --copy back the actor spells
            --amount of indexes used to store spells for this actor
            local spellsSize = tonumber(combatData [tablePosition]) --[7]
            if (isDebugging) then
                print("spell size unpack:", spellsSize)
            end

            tablePosition = tablePosition + 1
            local newTargetsTable = {}

            local spellIndex = tablePosition
            while(spellIndex < tablePosition + spellsSize) do
                local spellId = tonumber(combatData [spellIndex]) --[1]
                local spellDamage = tonumber(combatData [spellIndex+1]) --[2]
                local spellHits = tonumber(combatData [spellIndex+2]) --[3]

                local targetsSize = tonumber(combatData[spellIndex+3]) --[4]

                local targetTable = Details.packFunctions.UnpackTable(combatData, spellIndex+3, true)
                local spellObject = actorObject.spells:GetOrCreateSpell(spellId, true) --this one need some translation
                spellObject.total = spellDamage
                spellObject.counter = spellHits
                spellObject.targets = targetTable

                for targetName, amount in pairs(spellObject.targets) do
                    newTargetsTable[targetName] = (newTargetsTable[targetName] or 0) + amount
                end

                spellIndex = spellIndex + targetsSize + 4
            end

            actorObject.targets = newTargetsTable
            tablePosition = tablePosition + spellsSize --increase table position
    end

    if (isDebugging) then
        print("UnPackDamage(): done.")
    end

    return tablePosition
end

function Details.packFunctions.PackHeal(combatObject)
    if (isDebugging) then
        print("PackHeal(): start.")
    end

    --store actorObjects to pack
    local actorsToPack = {}

    --get the player object from the combat > damage container
    local playerName = UnitName("player")
    local playerObject = combatObject:GetActor(DETAILS_ATTRIBUTE_HEAL, playerName)
    if (not playerObject) then
        if (isDebugging) then
            print("PackHeal(): return | no player object.")
        end
        return
    end

    tinsert(actorsToPack, playerObject)

    --get the list of pets the player own
    local playerPets = playerObject.pets
    for _, petName in ipairs(playerPets) do
        local petObject = combatObject:GetActor(DETAILS_ATTRIBUTE_HEAL, petName)
        if (petObject) then
            tinsert(actorsToPack, petObject)
        end
    end

    local playerIndex = UnitInRaid("player")

    if (not playerIndex) then --no player index
        if (isDebugging) then
            print("PackHeal(): return | no player index found.")
        end
        return
    end

    --get all npc enemies and sort them by their respaw id
    local allActors = combatObject[DETAILS_ATTRIBUTE_HEAL]._ActorTable
    local allEnemies = {} --this have subtables, format: {actorObject, spawnId}

    for i = 1, #allActors do
        --get the actor object
        local actor = allActors[i]
        --check if is an enemy or neutral
        if (actor:IsNeutralOrEnemy()) then
            --get the spawnId
            local spawnId = select(7, strsplit("-", actor.serial))
            if (spawnId) then
                --convert hex to number
                spawnId = tonumber(spawnId:sub(1, 10), 16)
                if (spawnId) then
                    --first index is the actorObject, the second index is the spawnId to sort enemies
                    tinsert(allEnemies, {actor, spawnId})
                end
            end
        end
    end
    --sort enemies by their spawnId
    table.sort(allEnemies, Details.Sort2)

    local allPlayerNames = {}
    for i = 1, 20 do
        local name, _, subgroup = GetRaidRosterInfo(i)
        if (name) then --maybe the group has less than 20 players
            name = Ambiguate(name, "none")
            if (name and subgroup <= 4) then
                tinsert(allPlayerNames, name)
            end
        end
    end
    table.sort(allPlayerNames, function(t1, t2) return t1 < t2 end)

    local playerName = UnitName("player")
    for i = 1, #allPlayerNames do
        if (playerName == allPlayerNames[i]) then
            playerIndex = i
            break
        end
    end

    --this is the enemy that this player has to send
    local enemyObjectToSend = allEnemies[playerIndex] and allEnemies[playerIndex][1]
    if (enemyObjectToSend) then
        tinsert(actorsToPack, enemyObjectToSend)
    end

    --add the actors to actor information table
    for _, actorObject in ipairs(actorsToPack) do
        --check if already has the actor information
        local indexToActorInfo = actorInformationIndexes[actorObject.nome] --actor name
        if (not indexToActorInfo) then
            --need to add the actor general information into the actor information table
            indexToActorInfo = Details.packFunctions.AddActorInformation(actorObject)
        end
    end

    for i = 1, #actorsToPack do
        --get the actor object
        local actor = actorsToPack[i]
        local indexToActorInfo = actorInformationIndexes[actor.nome]

        --where the information of this actor starts
        local currentIndex = #actorHealInfo + 1

        --[1] index where is stored the this actor info like name, class, spec, etc
        actorHealInfo[currentIndex] = indexToActorInfo --[1]

        --[2 - 6]
        actorHealInfo [currentIndex + 1] = floor(actor.total)                    --[2]
        actorHealInfo [currentIndex + 2] = floor(actor.totalabsorb)              --[3]
        actorHealInfo [currentIndex + 3] = floor(actor.totalover)                --[4]
        actorHealInfo [currentIndex + 4] = floor(actor.healing_taken)            --[5]
        actorHealInfo [currentIndex + 5] = floor(actor.totalover_without_pet)    --[6]

        local spellContainer = actor.spells._ActorTable

        --reserve an index to tell the length of spells
        actorHealInfo [#actorHealInfo + 1] = 0
        local reservedSpellSizeIndex = #actorHealInfo
        local totalSpellIndexes = 0

        for spellId, spellInfo in pairs(spellContainer) do
            local spellHealingDone = spellInfo.total
            local spellHits = spellInfo.counter
            local spellTargets = spellInfo.targets

            actorHealInfo [#actorHealInfo + 1] = floor(spellId)
            actorHealInfo [#actorHealInfo + 1] = floor(spellHealingDone)
            actorHealInfo [#actorHealInfo + 1] = floor(spellHits)
            totalSpellIndexes = totalSpellIndexes + 3

            --build targets
            local targetsSize = Details.packFunctions.CountTableEntriesValid(spellTargets) * 2
            actorHealInfo [#actorHealInfo + 1] = targetsSize
            totalSpellIndexes = totalSpellIndexes + 1

            for actorName, healingDone in pairs(spellTargets) do
                actorHealInfo [#actorHealInfo + 1] = actorName
                actorHealInfo [#actorHealInfo + 1] = floor(healingDone)
                totalSpellIndexes = totalSpellIndexes + 2
            end
        end

        --amount of indexes spells are using
        actorHealInfo[reservedSpellSizeIndex] = totalSpellIndexes
    end

    if (isDebugging) then
        print("PackHeal(): done.")
    end
end

--------------------------------------------------------------------------------------------------------------------------------
--unpack

function Details.packFunctions.UnPackHeal(currentCombat, combatData, tablePosition)

    if (isDebugging) then
        print("UnPackHeal(): start.")
    end

    --get the healing container
    local healContainer = currentCombat[DETAILS_ATTRIBUTE_HEAL]

    --loop from 1 to 199, the amount of actors store is unknown
    --todo: it's only unpacking the first actor from the table, e.g. theres izimode and eye of corruption, after export it only shows the eye of corruption
    --table position does not move forward
    for i = 1, 199 do
        --actor information index in the combatData table
        --this index gives the position where the actor name, class, spec are stored
        local actorReference = tonumber(combatData[tablePosition]) --[1]
        local actorName, actorFlag, serialNumber, class, spec = Details.packFunctions.RetriveActorInformation(combatData, actorReference)

        if (isDebugging) then
            print("UnPackHeal(): Retrivied Data From " .. (actorReference or "nil") .. ":", actorName, actorFlag, serialNumber, class, spec)
        end

        --check if all healing actors has been processed
        --if there's no actor name it means it reached the end
        if (not actorName) then
            if (isDebugging) then
                print("UnPackHeal(): break | Heal loop has been stopped", "index:", i, "tablePosition:", tablePosition, "value:", combatData[tablePosition])
            end
            break
        end

        --get or create the actor object
        local actorObject = healContainer:GetOrCreateActor(serialNumber, actorName, actorFlag, true)

        --set the actor class, spec and group
        actorObject.classe = class
        actorObject:SetSpecId(spec)
        actorObject.grupo = isActorInGroup(class, actorFlag)
        actorObject.flag_original = actorFlag

        --copy the base healing
            actorObject.total =                 tonumber(combatData[tablePosition+1]) --[2]
            actorObject.totalabsorb =           tonumber(combatData[tablePosition+2]) --[3]
            actorObject.totalover =             tonumber(combatData[tablePosition+3]) --[4]
            actorObject.healing_taken =         tonumber(combatData[tablePosition+4]) --[5]
            actorObject.totalover_without_pet = tonumber(combatData[tablePosition+5]) --[6]

            tablePosition = tablePosition + 6

        --copy back the actor spells
            --amount of indexes used to store spells for this actor
            local spellsSize = tonumber(combatData [tablePosition]) --[7]
            if (isDebugging) then
                print("spell size unpack:", spellsSize)
            end

            tablePosition = tablePosition + 1
            local newTargetsTable = {}

            local spellIndex = tablePosition
            while(spellIndex < tablePosition + spellsSize) do
                local spellId = tonumber(combatData [spellIndex]) --[1]
                local spellHealingDone = tonumber(combatData [spellIndex+1]) --[2]
                local spellHits = tonumber(combatData [spellIndex+2]) --[3]

                local targetsSize = tonumber(combatData[spellIndex+3]) --[4]

                local targetTable = Details.packFunctions.UnpackTable(combatData, spellIndex+3, true)
                local spellObject = actorObject.spells:GetOrCreateSpell(spellId, true) --this one need some translation
                spellObject.total = spellHealingDone
                spellObject.counter = spellHits
                spellObject.targets = targetTable

                for targetName, amount in pairs(spellObject.targets) do
                    newTargetsTable[targetName] = (newTargetsTable[targetName] or 0) + amount
                end

                spellIndex = spellIndex + targetsSize + 4
            end

            actorObject.targets = newTargetsTable
            tablePosition = tablePosition + spellsSize --increase table position
    end

    if (isDebugging) then
        print("UnPackHeal(): done.")
    end

    return tablePosition
end

--------------------------------------------------------------------------------------------------
--pack utility data

--pack utility passes the player utility info + pets the player own
--each player will also send an enemy, the enemy will be in order of raidIndex of the player
function Details.packFunctions.PackUtility(combatObject)

    if (isDebugging) then
        print("PackUtility(): start.")
    end

    --store actorObjects to pack
    local actorsToPack = {}

    --get the player object from the combat > utility container
    local playerName = UnitName("player")
    local playerObject = combatObject:GetActor(DETAILS_ATTRIBUTE_MISC, playerName)
    if (not playerObject) then
        if (isDebugging) then
            print("PackUtility(): return | no player object.")
        end
        return
    end

    tinsert(actorsToPack, playerObject)

    --get the list of pets the player own
    local playerPets = playerObject.pets
    for _, petName in ipairs(playerPets) do
        local petObject = combatObject:GetActor(DETAILS_ATTRIBUTE_MISC, petName)
        if (petObject) then
            tinsert(actorsToPack, petObject)
        end
    end

    local playerIndex = _G.UnitInRaid("player")

    if (not playerIndex) then --no player index
        if (isDebugging) then
            print("PackUtility(): return | no player index found.")
        end
        return
    end

    --get all npc enemies and sort them by their respaw id
    local allActors = combatObject[DETAILS_ATTRIBUTE_MISC]._ActorTable
    local allEnemies = {} --this have subtables, format: {actorObject, spawnId}

    for i = 1, #allActors do
        --get the actor object
        local actor = allActors[i]
        --check if is an enemy or neutral
        if (actor:IsNeutralOrEnemy()) then
            --get the spawnId
            local spawnId = select(7, strsplit("-", actor.serial))
            if (spawnId) then
                --convert hex to number
                spawnId = tonumber(spawnId:sub(1, 10), 16)
                if (spawnId) then
                    --first index is the actorObject, the second index is the spawnId to sort enemies
                    tinsert(allEnemies, {actor, spawnId})
                end
            end
        end
    end
    --sort enemies by their spawnId
    table.sort(allEnemies, Details.Sort2)

    local allPlayerNames = {}
    for i = 1, 20 do
        local name, _, subgroup = GetRaidRosterInfo(i)
        if (name) then --maybe the group has less than 20 players
            name = _G.Ambiguate(name, "none")
            if (name and subgroup <= 4) then
                tinsert(allPlayerNames, name)
            end
        end
    end
    table.sort(allPlayerNames, function(t1, t2) return t1 < t2 end)

    local playerName = UnitName("player")
    for i = 1, #allPlayerNames do
        if (playerName == allPlayerNames[i]) then
            playerIndex = i
            break
        end
    end

    --this is the enemy that this player has to send
    local enemyObjectToSend = allEnemies[playerIndex] and allEnemies[playerIndex][1]
    if (enemyObjectToSend) then
        tinsert(actorsToPack, enemyObjectToSend)
    end

    --add the actors to actor information table
    for _, actorObject in ipairs(actorsToPack) do
        --check if already has the actor information
        local indexToActorInfo = actorInformationIndexes[actorObject.nome] --actor name
        if (not indexToActorInfo) then
            --need to add the actor general information into the actor information table
            indexToActorInfo = Details.packFunctions.AddActorInformation(actorObject)
        end
    end

    for i = 1, #actorsToPack do
        --get the actor object
        local actor = actorsToPack[i]
        local indexToActorInfo = actorInformationIndexes[actor.nome]

        --where the information of this actor starts
        local currentIndex = #actorUtilityInfo + 1

        --[1] index where is stored the actor info like name, class, spec, etc
        actorUtilityInfo[currentIndex] = indexToActorInfo  --[1]

        --[=[
        interrupt_spells._ActorTable [spellId] = {
            [2139] = {
                ["id"] = 2139,
                ["interrompeu_oque"] = {
                    [337110] = 1,
                },
                ["targets"] = {
                    ["Baroness Frieda"] = 1,
                },
                ["counter"] = 1,
            },
        }

        cooldowns_defensive
        cooldowns_defensive_targets
        ["cooldowns_defensive_spells"] = {
            ["_ActorTable"] = {
                [45438] = {
                    ["id"] = 45438,
                    ["targets"] = {
                        ["Relune-Tichondrius"] = 1,
                    },
                    ["counter"] = 1,
                },
            },
            ["tipo"] = 9,
        },

        cc_break
        cc_break_oque
        cc_break_targets
        ["cc_break_spells"] = {
            ["_ActorTable"] = {
                [42223] = {
                    ["cc_break_oque"] = {
                        [197214] = 2,
                    },
                    ["id"] = 42223,
                    ["targets"] = {
                        ["Castellan Niklaus"] = 1,
                        ["Baroness Frieda"] = 1,
                    },
                    ["cc_break"] = 2,
                    ["counter"] = 0,
                },
            },
        },

        ["cc_done_spells"] = {
            ["_ActorTable"] = {
                [127797] = {
                    ["id"] = 127797,
                    ["targets"] = {
                        ["Castellan Niklaus"] = 1,
                        ["Lord Stavros"] = 1,
                        ["Baroness Frieda"] = 1,
                    },
                    ["counter"] = 3,
                },
            },
            ["tipo"] = 9,
        },

        ["dispell_spells"] = {
            ["_ActorTable"] = {
                [77130] = {
                    ["targets"] = {
                        ["Magicgreenie-BleedingHollow"] = 1,
                    },
                    ["id"] = 77130,
                    ["dispell_oque"] = {
                        [337110] = 1,
                    },
                    ["dispell"] = 1,
                    ["counter"] = 0,
                },
            },
            ["tipo"] = 9,
        },

        ress
        ress_targets
        ["ress_spells"] = {
            ["_ActorTable"] = {
                [212048] = {
                    ["id"] = 212048,
                    ["ress"] = 7,
                    ["targets"] = {
                        ["Freezerbeef-Illidan"] = 1,
                        ["Jilkari-ThoriumBrotherhood"] = 1,
                        ["Ditador"] = 1,
                        ["Caller-Thrall"] = 1,
                        ["Thebappo-Tichondrius"] = 1,
                        ["Superskourge-Area52"] = 1,
                        ["Rorschak-Area52"] = 1,
                    },
                    ["counter"] = 0,
                },
            },
            ["tipo"] = 9,
        },

        --]=]

        --cooldowns, pack player cooldowns used
            actorUtilityInfo [#actorUtilityInfo + 1] = "C"
            local cooldownContainer = actor.cooldowns_defensive_spells and actor.cooldowns_defensive_spells._ActorTable
            local totalSpellIndexes = 0

            if (cooldownContainer) then
                --reserve an index to tell the length of spells
                actorUtilityInfo [#actorUtilityInfo + 1] = 0
                local reservedSpellSizeIndex = #actorUtilityInfo

                for spellId, spellInfo in pairs(cooldownContainer) do
                    local spellCounter = spellInfo.counter
                    local spellTargets = spellInfo.targets

                    actorUtilityInfo [#actorUtilityInfo + 1] = floor(spellId)
                    actorUtilityInfo [#actorUtilityInfo + 1] = floor(spellCounter)
                    totalSpellIndexes = totalSpellIndexes + 2

                    --build targets
                    local targetsSize = Details.packFunctions.CountTableEntriesValid(spellTargets) * 2
                    actorUtilityInfo [#actorUtilityInfo + 1] = targetsSize
                    totalSpellIndexes = totalSpellIndexes + 1

                    for actorName, totalDone in pairs(spellTargets) do
                        actorUtilityInfo [#actorUtilityInfo + 1] = actorName
                        actorUtilityInfo [#actorUtilityInfo + 1] = floor(totalDone)
                        totalSpellIndexes = totalSpellIndexes + 2
                    end
                end

                --amount of indexes spells are using
                actorUtilityInfo[reservedSpellSizeIndex] = totalSpellIndexes
            else
                actorUtilityInfo [#actorUtilityInfo + 1] = 0
            end

        --interrupts, pack player interrupts
            actorUtilityInfo [#actorUtilityInfo + 1] = "I"
            local interruptsContainer = actor.interrupt_spells and actor.interrupt_spells._ActorTable
            local totalSpellIndexes = 0

            if (interruptsContainer) then
                --reserve an index to tell the length of spells
                actorUtilityInfo [#actorUtilityInfo + 1] = 0
                local reservedSpellSizeIndex = #actorUtilityInfo

                for spellId, spellInfo in pairs(interruptsContainer) do
                    local spellCounter = spellInfo.counter
                    local spellTargets = spellInfo.targets
                    local whatGotInterrupted = spellInfo.interrompeu_oque

                    actorUtilityInfo [#actorUtilityInfo + 1] = floor(spellId)
                    actorUtilityInfo [#actorUtilityInfo + 1] = floor(spellCounter) --spellCounter is nil
                    totalSpellIndexes = totalSpellIndexes + 2

                    --build targets
                    local targetsSize = Details.packFunctions.CountTableEntriesValid(spellTargets) * 2
                    actorUtilityInfo [#actorUtilityInfo + 1] = targetsSize
                    totalSpellIndexes = totalSpellIndexes + 1

                    for actorName, totalDone in pairs(spellTargets) do
                        actorUtilityInfo [#actorUtilityInfo + 1] = actorName
                        actorUtilityInfo [#actorUtilityInfo + 1] = floor(totalDone)
                        totalSpellIndexes = totalSpellIndexes + 2
                    end

                    --build what was interrupted by the spell
                    local interruptedSize = Details.packFunctions.CountTableEntriesValid(whatGotInterrupted) * 2
                    actorUtilityInfo [#actorUtilityInfo + 1] = interruptedSize
                    totalSpellIndexes = totalSpellIndexes + 1

                    for spellId, totalDone in pairs(whatGotInterrupted) do
                        actorUtilityInfo [#actorUtilityInfo + 1] = spellId
                        actorUtilityInfo [#actorUtilityInfo + 1] = floor(totalDone)
                        totalSpellIndexes = totalSpellIndexes + 2
                    end
                end

                --amount of indexes spells are using
                actorUtilityInfo[reservedSpellSizeIndex] = totalSpellIndexes
            else
                actorUtilityInfo [#actorUtilityInfo + 1] = 0
            end

        --cc break, pack player crowd control breaks
            actorUtilityInfo [#actorUtilityInfo + 1] = "B"
            local ccBreakContainer = actor.cc_break_spells and actor.cc_break_spells._ActorTable
            local totalSpellIndexes = 0

            if (ccBreakContainer) then
                --reserve an index to tell the length of spells
                actorUtilityInfo [#actorUtilityInfo + 1] = 0
                local reservedSpellSizeIndex = #actorUtilityInfo

                for spellId, spellInfo in pairs(ccBreakContainer) do
                    local spellCounter = spellInfo.cc_break
                    local spellTargets = spellInfo.targets
                    local whatGotCCBroken = spellInfo.cc_break_oque

                    actorUtilityInfo [#actorUtilityInfo + 1] = floor(spellId)
                    actorUtilityInfo [#actorUtilityInfo + 1] = floor(spellCounter)
                    totalSpellIndexes = totalSpellIndexes + 2

                    --build targets
                    local targetsSize = Details.packFunctions.CountTableEntriesValid(spellTargets) * 2
                    actorUtilityInfo [#actorUtilityInfo + 1] = targetsSize
                    totalSpellIndexes = totalSpellIndexes + 1

                    for actorName, totalDone in pairs(spellTargets) do
                        actorUtilityInfo [#actorUtilityInfo + 1] = actorName
                        actorUtilityInfo [#actorUtilityInfo + 1] = floor(totalDone)
                        totalSpellIndexes = totalSpellIndexes + 2
                    end

                    --build what was interrupted by the spell
                    local ccBrokenSize = Details.packFunctions.CountTableEntriesValid(whatGotCCBroken) * 2
                    actorUtilityInfo [#actorUtilityInfo + 1] = ccBrokenSize
                    totalSpellIndexes = totalSpellIndexes + 1

                    for spellId, totalDone in pairs(whatGotCCBroken) do
                        actorUtilityInfo [#actorUtilityInfo + 1] = spellId
                        actorUtilityInfo [#actorUtilityInfo + 1] = floor(totalDone)
                        totalSpellIndexes = totalSpellIndexes + 2
                    end
                end

                --amount of indexes spells are using
                actorUtilityInfo[reservedSpellSizeIndex] = totalSpellIndexes
            else
                actorUtilityInfo [#actorUtilityInfo + 1] = 0
            end

        --dispel, pack player dispels done
            actorUtilityInfo [#actorUtilityInfo + 1] = "D"
            local dispelsContainer = actor.dispell_spells and actor.dispell_spells._ActorTable
            local totalSpellIndexes = 0

            if (dispelsContainer) then
                --reserve an index to tell the length of spells
                actorUtilityInfo [#actorUtilityInfo + 1] = 0
                local reservedSpellSizeIndex = #actorUtilityInfo
            
                for spellId, spellInfo in pairs(dispelsContainer) do
                    local spellTotal = spellInfo.dispell
                    local spellTargets = spellInfo.targets
                    local whatGotDispelled = spellInfo.dispell_oque

                    actorUtilityInfo [#actorUtilityInfo + 1] = floor(spellId)
                    actorUtilityInfo [#actorUtilityInfo + 1] = floor(spellTotal)
                    totalSpellIndexes = totalSpellIndexes + 2

                    --build targets
                    local targetsSize = Details.packFunctions.CountTableEntriesValid(spellTargets) * 2
                    actorUtilityInfo [#actorUtilityInfo + 1] = targetsSize
                    totalSpellIndexes = totalSpellIndexes + 1

                    for actorName, totalDone in pairs(spellTargets) do
                        actorUtilityInfo [#actorUtilityInfo + 1] = actorName
                        actorUtilityInfo [#actorUtilityInfo + 1] = floor(totalDone)
                        totalSpellIndexes = totalSpellIndexes + 2
                    end

                    --build what was dispelled by the spell
                    local dispelsSize = Details.packFunctions.CountTableEntriesValid(whatGotDispelled) * 2
                    actorUtilityInfo [#actorUtilityInfo + 1] = dispelsSize
                    totalSpellIndexes = totalSpellIndexes + 1

                    for spellId, totalDone in pairs(whatGotDispelled) do
                        actorUtilityInfo [#actorUtilityInfo + 1] = spellId
                        actorUtilityInfo [#actorUtilityInfo + 1] = floor(totalDone)
                        totalSpellIndexes = totalSpellIndexes + 2
                    end
                end

                --amount of indexes spells are using
                actorUtilityInfo[reservedSpellSizeIndex] = totalSpellIndexes
            else
                actorUtilityInfo [#actorUtilityInfo + 1] = 0
            end

        --ress, pack player ress performed
            actorUtilityInfo [#actorUtilityInfo + 1] = "R"
            local ressContainer = actor.ress_spells and actor.ress_spells._ActorTable
            local totalSpellIndexes = 0

            if (ressContainer) then
                --reserve an index to tell the length of spells
                actorUtilityInfo [#actorUtilityInfo + 1] = 0
                local reservedSpellSizeIndex = #actorUtilityInfo
            
                for spellId, spellInfo in pairs(ressContainer) do
                    local spellTotal = spellInfo.ress
                    local spellTargets = spellInfo.targets

                    actorUtilityInfo [#actorUtilityInfo + 1] = floor(spellId)
                    actorUtilityInfo [#actorUtilityInfo + 1] = floor(spellTotal)
                    totalSpellIndexes = totalSpellIndexes + 2

                    --build targets
                    local targetsSize = Details.packFunctions.CountTableEntriesValid(spellTargets) * 2
                    actorUtilityInfo [#actorUtilityInfo + 1] = targetsSize
                    totalSpellIndexes = totalSpellIndexes + 1

                    for actorName, totalDone in pairs(spellTargets) do
                        actorUtilityInfo [#actorUtilityInfo + 1] = actorName
                        actorUtilityInfo [#actorUtilityInfo + 1] = floor(totalDone)
                        totalSpellIndexes = totalSpellIndexes + 2
                    end
                end

                --amount of indexes spells are using
                actorUtilityInfo[reservedSpellSizeIndex] = totalSpellIndexes
            else
                actorUtilityInfo [#actorUtilityInfo + 1] = 0
            end
    end

    if (isDebugging) then
        print("PackUtility(): done.")
    end
end

function Details.packFunctions.UnPackUtility(currentCombat, combatData, tablePosition)
        if (isDebugging) then
            print("UnPackUtility(): start.")
        end

        --get the utility container
        local utilityContainer = currentCombat[DETAILS_ATTRIBUTE_MISC]

        --loop from 1 to 199, the amount of actors store is unknown
        --todo: it's only unpacking the first actor from the table, e.g. theres izimode and eye of corruption, after export it only shows the eye of corruption
        --table position does not move forward
        for i = 1, 199 do
            --actor information index in the combatData table
            --this index gives the position where the actor name, class, spec are stored
            local actorReference = tonumber(combatData[tablePosition]) --[1]
            local actorName, actorFlag, serialNumber, class, spec = Details.packFunctions.RetriveActorInformation(combatData, actorReference)

            if (isDebugging) then
                print("UnPackUtility(): Retrivied Data From " .. (actorReference or "nil") .. ":", actorName, actorFlag, serialNumber, class, spec)
            end

            --check if all utility actors has been processed
            --if there's no actor name it means it reached the end
            if (not actorName) then
                if (isDebugging) then
                    print("UnPackUtiliyu(): break | Utility loop has been stopped", "index:", i, "tablePosition:", tablePosition, "value:", combatData[tablePosition])
                end
                break
            end

            --get or create the actor object
            local actorObject = utilityContainer:GetOrCreateActor(serialNumber, actorName, actorFlag, true)

            --set the actor class, spec and group
            actorObject.classe = class
            actorObject:SetSpecId(spec)
            actorObject.grupo = isActorInGroup(class, actorFlag)
            actorObject.flag_original = actorFlag
            --finished utility actor setup
            --C - cooldowns
            --I - interrupts
            --B - cc break
            --D - dispels
            --R - ress

            --copy back the actor cooldowns spells
                tablePosition = tablePosition + 1
                tablePosition = tablePosition + 1

                --amount of indexes used to store cooldowns spells for this actor
                local spellsSize = tonumber(combatData[tablePosition]) --[7]
                if (isDebugging) then
                    print("cooldowns size unpack:", spellsSize)
                end

                tablePosition = tablePosition + 1

                --check if there's cooldown data
                if (spellsSize > 0) then
                    local newTotal = 0
                    local newTargetsTable = {}

                    local spellIndex = tablePosition
                    while(spellIndex < tablePosition + spellsSize) do
                        local spellId =          tonumber(combatData[spellIndex]) --[1]
                        local spellTotal =       tonumber(combatData[spellIndex+1]) --[2]
                        local targetsSize =      tonumber(combatData[spellIndex+2]) --[3]

                        local targetTable = Details.packFunctions.UnpackTable(combatData, spellIndex+2, true)

                        if (not actorObject.cooldowns_defensive_spells) then
                            actorObject.cooldowns_defensive_spells = Details.container_habilidades:NovoContainer(Details.container_type.CONTAINER_MISC_CLASS)
                        end

                        local spellObject = actorObject.cooldowns_defensive_spells:GetOrCreateSpell(spellId, true)

                        spellObject.counter = spellTotal
                        spellObject.targets = targetTable

                        for targetName, amount in pairs(spellObject.targets) do
                            newTargetsTable[targetName] = (newTargetsTable[targetName] or 0) + amount
                            newTotal = newTotal + amount
                        end

                        spellIndex = spellIndex + targetsSize + 3
                    end

                    actorObject.cooldowns_defensive = newTotal
                    actorObject.cooldowns_defensive_targets = newTargetsTable
                    tablePosition = tablePosition + spellsSize --increase table position
                end

            --copy back the actor interrupts spells
                --tablePosition = tablePosition + 1
                local startInterrupt = combatData[tablePosition]
                tablePosition = tablePosition + 1

                --amount of indexes used to store interrupt spells for this actor
                local spellsSize = tonumber(combatData[tablePosition]) --[7]
                if (isDebugging) then
                    print("interrupt size unpack:", spellsSize)
                end
                tablePosition = tablePosition + 1

                if (spellsSize > 0) then
                    local newTotal = 0
                    local newTargetsTable = {}
                    local newTargetWhatTable = {}

                    local spellIndex = tablePosition
                    while(spellIndex < tablePosition + spellsSize) do
                        local spellId =          tonumber(combatData[spellIndex]) --[1]
                        local spellTotal =       tonumber(combatData[spellIndex+1]) --[2]
                        local targetsSize =      tonumber(combatData[spellIndex+2]) --[3]

                        local targetTable = Details.packFunctions.UnpackTable(combatData, spellIndex+2, true)

                        if (not actorObject.interrupt_spells) then
                            actorObject.interrupt_spells = Details.container_habilidades:NovoContainer(Details.container_type.CONTAINER_MISC_CLASS)
                        end
                        local spellObject = actorObject.interrupt_spells:GetOrCreateSpell(spellId, true)

                        spellObject.counter = spellTotal
                        spellObject.targets = targetTable

                        for targetName, amount in pairs(spellObject.targets) do
                            newTargetsTable[targetName] = (newTargetsTable[targetName] or 0) + amount
                            newTotal = newTotal + amount
                        end

                        --interrupt what
                        spellIndex = spellIndex + targetsSize + 3
                        local interruptWhatTableSize = combatData[spellIndex]
                        local interruptWhatTable = Details.packFunctions.UnpackTable(combatData, spellIndex, true) --table[index] is nil
                        spellObject.interrompeu_oque = interruptWhatTable

                        for targetName, amount in pairs(spellObject.targets) do
                            newTargetWhatTable[targetName] = (newTargetWhatTable[targetName] or 0) + amount
                        end

                        spellIndex = spellIndex + interruptWhatTableSize + 1
                    end

                    actorObject.interrupt = newTotal
                    actorObject.interrupt_targets = newTargetsTable
                    actorObject.interrompeu_oque = newTargetWhatTable
                    tablePosition = tablePosition + spellsSize --increase table position
                end

           --copy back the actor cc break spells
                --tablePosition = tablePosition + 1
                local startCCBreak = combatData[tablePosition]
                tablePosition = tablePosition + 1

                --amount of indexes used to store cc break spells for this actor
                local spellsSize = tonumber(combatData[tablePosition]) --[7]
                if (isDebugging) then
                    print("cc break size unpack:", spellsSize)
                end
                tablePosition = tablePosition + 1

                if (spellsSize > 0) then
                    local newTotal = 0
                    local newTargetsTable = {}
                    local newTargetWhatTable = {}

                    local spellIndex = tablePosition
                    while(spellIndex < tablePosition + spellsSize) do
                        local spellId =          tonumber(combatData[spellIndex]) --[1]
                        local spellTotal =       tonumber(combatData[spellIndex+1]) --[2]
                        local targetsSize =      tonumber(combatData[spellIndex+2]) --[3]

                        local targetTable = Details.packFunctions.UnpackTable(combatData, spellIndex+2, true)

                        if (not actorObject.cc_break_spells) then
                            actorObject.cc_break_spells = Details.container_habilidades:NovoContainer(Details.container_type.CONTAINER_MISC_CLASS)
                        end
                        local spellObject = actorObject.cc_break_spells:GetOrCreateSpell(spellId, true)

                        spellObject.cc_break = spellTotal
                        spellObject.targets = targetTable

                        for targetName, amount in pairs(spellObject.targets) do
                            newTargetsTable[targetName] = (newTargetsTable[targetName] or 0) + amount
                            newTotal = newTotal + amount
                        end

                        --cc broke what
                        spellIndex = spellIndex + targetsSize + 3
                        local ccBrokeWhatTableSize = combatData[spellIndex]

                        local ccBrokeWhatTable = Details.packFunctions.UnpackTable(combatData, spellIndex, true)
                        spellObject.cc_break_oque = ccBrokeWhatTable

                        for targetName, amount in pairs(spellObject.cc_break_oque) do
                            newTargetWhatTable[targetName] = (newTargetWhatTable[targetName] or 0) + amount
                        end

                        spellIndex = spellIndex + ccBrokeWhatTableSize + 1
                    end

                    actorObject.cc_break = newTotal
                    actorObject.cc_break_targets = newTargetsTable
                    actorObject.cc_break_oque = newTargetWhatTable
                    tablePosition = tablePosition + spellsSize --increase table position
                end

            --copy back the actor dispel spells
                --tablePosition = tablePosition + 1
                tablePosition = tablePosition + 1

                --amount of indexes used to store dispel spells for this actor
                local spellsSize = tonumber(combatData[tablePosition]) --[7]
                if (isDebugging) then
                    print("dispel size unpack:", spellsSize)
                end
                tablePosition = tablePosition + 1

                if (spellsSize > 0) then
                    local newTotal = 0
                    local newTargetsTable = {}
                    local newTargetWhatTable = {}

                    local spellIndex = tablePosition
                    while(spellIndex < tablePosition + spellsSize) do
                        local spellId =          tonumber(combatData[spellIndex]) --[1]
                        local spellTotal =       tonumber(combatData[spellIndex+1]) --[2]
                        local targetsSize =      tonumber(combatData[spellIndex+2]) --[3]

                        local targetTable = Details.packFunctions.UnpackTable(combatData, spellIndex+2, true)

                        if (not actorObject.dispell_spells) then
                            actorObject.dispell_spells = Details.container_habilidades:NovoContainer(Details.container_type.CONTAINER_MISC_CLASS)
                        end
                        local spellObject = actorObject.dispell_spells:GetOrCreateSpell(spellId, true)

                        spellObject.dispell = spellTotal
                        spellObject.targets = targetTable

                        for targetName, amount in pairs(spellObject.targets) do
                            newTargetsTable[targetName] = (newTargetsTable[targetName] or 0) + amount
                            newTotal = newTotal + amount
                        end
                        spellIndex = spellIndex + targetsSize + 3

                        --dispel what
                        local dispelWhatTableSize = combatData[spellIndex]
                        local dispelWhatTable = Details.packFunctions.UnpackTable(combatData, spellIndex, true)
                        spellObject.dispell_oque = dispelWhatTable

                        for spellId, amount in pairs(spellObject.dispell_oque) do
                            newTargetWhatTable[spellId] = (newTargetWhatTable[spellId] or 0) + amount
                        end

                        spellIndex = spellIndex + dispelWhatTableSize + 1
                    end

                    actorObject.dispell = newTotal
                    actorObject.dispell_targets = newTargetsTable
                    actorObject.dispell_oque = newTargetWhatTable
                    tablePosition = tablePosition + spellsSize --increase table position
                end

            --ress
            --ress_targets
            --ress_spells
            --copy back the actor ress spells
                --tablePosition = tablePosition + 1
                tablePosition = tablePosition + 1

                --amount of indexes used to store ress spells for this actor
                local spellsSize = tonumber(combatData[tablePosition]) --[7]
                if (isDebugging) then
                    print("ress size unpack:", spellsSize)
                end
                tablePosition = tablePosition + 1

                if (spellsSize > 0) then
                    local newTotal = 0
                    local newTargetsTable = {}

                    local spellIndex = tablePosition
                    while(spellIndex < tablePosition + spellsSize) do
                        local spellId =          tonumber(combatData[spellIndex]) --[1]
                        local spellTotal =       tonumber(combatData[spellIndex+1]) --[2]
                        local targetsSize =      tonumber(combatData[spellIndex+2]) --[3]

                        local targetTable = Details.packFunctions.UnpackTable(combatData, spellIndex+2, true)

                        if (not actorObject.ress_spells) then
                            actorObject.ress_spells = Details.container_habilidades:NovoContainer(Details.container_type.CONTAINER_MISC_CLASS)
                        end
                        local spellObject = actorObject.ress_spells:GetOrCreateSpell(spellId, true)

                        spellObject.ress = spellTotal
                        spellObject.targets = targetTable

                        for targetName, amount in pairs(spellObject.targets) do
                            newTargetsTable[targetName] = (newTargetsTable[targetName] or 0) + amount
                            newTotal = newTotal + amount
                        end

                        spellIndex = spellIndex + targetsSize + 3
                    end

                    actorObject.ress = newTotal
                    actorObject.ress_targets = newTargetsTable
                    tablePosition = tablePosition + spellsSize --increase table position
                end
        end

        if (isDebugging) then
            print("UnPackUtility(): done.")
        end

        return tablePosition
end


--this function does the same as the function above but does not create a new combat, it just add new information
function Details.packFunctions.DeployPackedCombatData(packedCombatData)
    if (isDebugging) then
        print("DeployPackedCombatData(): start.")
    end

    local LibDeflate = _G.LibStub:GetLibrary("LibDeflate")
    local dataCompressed = LibDeflate:DecodeForWoWAddonChannel(packedCombatData)
    local combatDataString = LibDeflate:DecompressDeflate(dataCompressed)

    if (isDebugging) then
        print(combatDataString)
    end

    local function count(text, pattern)
        return select(2, text:gsub(pattern, ""))
    end

    local combatData = {}
    local amountOfIndexes = count(combatDataString, ",") + 1

    while (amountOfIndexes > 0) do
        local splitPart = {strsplit(",", combatDataString, 4000)}
        if (#splitPart == 4000 and amountOfIndexes > 4000) then
            for i = 1, 3999 do
                combatData[#combatData+1] = splitPart[i]
            end
            --get get part that couldn't be read this loop
            combatDataString = splitPart[4000]
            amountOfIndexes = amountOfIndexes - 3999

        else
            for i = 1, #splitPart do
                combatData[#combatData+1] = splitPart[i]
            end
            amountOfIndexes = amountOfIndexes - #splitPart
        end
    end

    local flags = tonumber(combatData[INDEX_EXPORT_FLAG])
    local tablePosition

    if (bit.band(flags, 0x10) ~= 0) then
        tablePosition = 2
        TOTAL_INDEXES_FOR_COMBAT_INFORMATION = 0 --there's no combat info, data starts after the dataFlag on position [1]
    else
        tablePosition = TOTAL_INDEXES_FOR_COMBAT_INFORMATION + 1
    end

    --get the current combat
    local currentCombat = Details:GetCurrentCombat()

    --check if this export has included damage info
    if (bit.band(flags, 0x1) ~= 0) then
        --find the index where the damage information start
        for i = tablePosition, #combatData do
            if (combatData[i] == "!D") then
                tablePosition = i + 1
                break
            end
        end

        if (isDebugging) then
            print("DeployPackedCombatData(): data has damage info, Damage Index:", tablePosition)
        end

        --unpack damage
        tablePosition = Details.packFunctions.UnPackDamage(currentCombat, combatData, tablePosition)
    end

    if (bit.band(flags, 0x2) ~= 0) then
        --find the index where the heal information start
        for i = tablePosition, #combatData do
            if (combatData[i] == "!H") then
                tablePosition = i + 1
                break
            end
        end

        if (isDebugging) then
            print("DeployPackedCombatData(): data has healing info, Heal Index:", tablePosition)
        end

        --unpack heal
        Details.packFunctions.UnPackHeal(currentCombat, combatData, tablePosition)
    end

    if (bit.band(flags, 0x8) ~= 0) then
        --find the index where the utility information start
        for i = tablePosition, #combatData do
            if (combatData[i] == "!U") then
                tablePosition = i + 1
                break
            end
        end

        if (isDebugging) then
            print("DeployPackedCombatData(): data has utility info, Utility Index:", tablePosition)
        end

        --unpack utility
        Details.packFunctions.UnPackUtility(currentCombat, combatData, tablePosition)
    end

    if (bit.band(flags, 0x10) == 0) then
        --set the start and end of combat time and date
        currentCombat:SetStartTime(combatData[INDEX_COMBAT_START_TIME])
        currentCombat:SetEndTime(combatData[INDEX_COMBAT_END_TIME])
        currentCombat:SetDate(combatData[INDEX_COMBAT_START_DATE], combatData[INDEX_COMBAT_END_DATE])
        currentCombat.enemy = combatData[INDEX_COMBAT_NAME]
    end

    --refresh container
    currentCombat[DETAILS_ATTRIBUTE_DAMAGE]:Remap()
    currentCombat[DETAILS_ATTRIBUTE_HEAL]:Remap()

    --refresh damage taken
    do
        local damageContainer = currentCombat[DETAILS_ATTRIBUTE_DAMAGE]
        local allActors = damageContainer._ActorTable

        for i = 1, #allActors do --reset damage taken table
            local actor = allActors[i]
            actor.damage_taken = 0
            actor.damage_from = {}
        end

        for i = 1, #allActors do
            local actor = allActors[i]
            for targetName, amount in pairs(actor.targets) do
                local actorIndex = damageContainer._NameIndexTable[targetName]
                if (actorIndex) then
                    local targetActor = allActors[actorIndex]
                    if (targetActor) then
                        targetActor.damage_taken = targetActor.damage_taken + amount
                        targetActor.damage_from[actor.nome] = (targetActor.damage_from[actor.nome] or 0) + amount
                    end
                end
            end
        end
    end

    --refresh healing taken
    do
        local healingContainer = currentCombat[DETAILS_ATTRIBUTE_HEAL]
        local allActors = healingContainer._ActorTable

        for i = 1, #allActors do --reset healing taken table
            local actor = allActors[i]
            actor.healing_taken = 0
            actor.healing_from = {}
        end

        for i = 1, #allActors do
            local actor = allActors[i]
            for targetName, amount in pairs(actor.targets) do
                local actorIndex = healingContainer._NameIndexTable[targetName]
                if (actorIndex) then
                    local targetActor = allActors[actorIndex]
                    if (targetActor) then
                        targetActor.healing_taken = targetActor.healing_taken + amount
                        targetActor.healing_from[actor.nome] = (targetActor.healing_from[actor.nome] or 0) + amount
                    end
                end
            end
        end        
    end

    --refresh windows
    currentCombat[DETAILS_ATTRIBUTE_DAMAGE].need_refresh = true
    currentCombat[DETAILS_ATTRIBUTE_HEAL].need_refresh = true
end

--get the amount of entries of a hash table
function Details.packFunctions.CountTableEntries(hasTable)
    local amount = 0
    for _ in pairs(hasTable) do
        amount = amount + 1
    end
    return amount
end

--get the amount of entries and check for validation
function Details.packFunctions.CountTableEntriesValid(hasTable)
    local amount = 0
    for actorName, _ in pairs(hasTable) do
        --if (actorInformationIndexes[actorName]) then
            amount = amount + 1
        --end
    end
    return amount
end

--stract some indexes of a table
local selectIndexes = function(table, startIndex, amountIndexes)
    local values = {}
    for i = startIndex, amountIndexes do
        values[#values+1] = tonumber(table[i]) or 0
    end
    return unpack(values)
end

function Details.packFunctions.UnpackTable(table, index, isPair, valueAsTable, amountOfValues)
    local result = {}
    local reservedIndexes = tonumber(table[index])
    local indexStart = index+1
    local indexEnd = reservedIndexes+index

    if (isPair) then
        amountOfValues = amountOfValues or 2
        for i = indexStart, indexStart + reservedIndexes - 1, amountOfValues do
            if (valueAsTable) then
                local key = tonumber(table[i])
                result[key] = {selectIndexes(table, i+1, amountOfValues-1)}
            else
                local key = table[i]
                local value = tonumber(table[i+1])
                result[key] = value
            end
        end
    else
        for i = indexStart, indexEnd do
            local value = tonumber(table[i])
            result[#result+1] = value
        end
    end

    return result
end