PmCa = PmCa or {}
base64 = base64 or {}

PmCa.name		   = 'Panic Moce Combat Analyzer'
PmCa.version	   = '1.1.1'
PmCa.versionDB	   = 4
PmCa.loaded	       = false
PmCa.author        = 'silentgecko, deevilius'
PmCa.savedVarsName = 'PMCAVars'

PmCa.variables = {
    data = {},
    metadata = {
        server = 'EU',
        language = 'de',
    }
}
PmCa.tempVars = {
    lastSave     = 0,
    lastSaveTimeStamp = 0,
    inFight      = false,
    lastDps      = 0,
    lastDpsCount = 0,
    trialRunning = false,
    trialId      = 0,
    timeStampOffsetToLead = 0,
}

---------Passing saved variables to the labels at initialize-------
function PmCa.Initialize(_, addonName)
    local self = PmCa

    if addonName ~= self.name then return end

    EVENT_MANAGER:UnregisterForEvent(self.name, EVENT_ADD_ON_LOADED)

    self.savedVariables = ZO_SavedVars:New(self.savedVarsName, self.versionDB, nil, self.variables)

    --save current server and language
    self.savedVariables.metadata = {
        server = GetWorldName(),
        language = GetCVar('language.2')
    }

    self.cleanUp()

    -- death/alive/rezz
    EVENT_MANAGER:RegisterForEvent(self.name, EVENT_PLAYER_DEAD, self.onDeath)
    EVENT_MANAGER:RegisterForEvent(self.name, EVENT_PLAYER_ALIVE, self.onAlive)
    EVENT_MANAGER:RegisterForEvent(self.name, EVENT_RESURRECT_REQUEST, self.onRezz)

    --combat state
    EVENT_MANAGER:RegisterForEvent(self.name, EVENT_PLAYER_COMBAT_STATE, self.OnPlayerCombatState)

    CALLBACK_MANAGER:RegisterCallback("FTC_NewDamage", self.addDamage)

end

function PmCa:getOffset(offset, finished)
    local self = PmCa
    self.debug('PmCa Synced Offset from lead:', offset)
    self.tempVars.timeStampOffsetToLead = offset
end

-- cleanup
function PmCa.cleanUp()
    local self      = PmCa
    local savedVars = self.savedVariables.data
    local currentTs = GetTimeStamp()
    local limitTs   = currentTs - 604800 -- one week in seconds

    for ts, data in pairs(savedVars) do
        if ts <= limitTs then
            self.savedVariables.data[ts] = nil
        end
    end
end

-- fight status
function PmCa.OnPlayerCombatState(_, inCombat)
    local self       = PmCa
    local timeStamp  = GetTimeStamp() + self.tempVars.timeStampOffsetToLead
    local data       = self.savedVariables.data or {}

    data[timeStamp]          = data[timeStamp] or {}
    data[timeStamp]['event'] = data[timeStamp]['event'] or {}
    table.insert(data[timeStamp]['event'], {combat = inCombat})

    -- add group and trial to event
    data[timeStamp]['trial'] = self.getTrial()
    data[timeStamp]['group'] = self.getGroupEnc()
    self.savedVariables.data[timeStamp] = data[timeStamp]
end

-- get current trial
function PmCa.getTrial()
    local self         = PmCa
    local currentRaid  = GetCurrentParticipatingRaidId() or 0
    return currentRaid
end

-- get dmg from FTC
function PmCa.addDamage(damage)
    local self = PmCa

    --only log outgoing stuff and greater than zero
    if damage['out'] and damage['value'] > 0 and damage['target'] ~= '' then
        local data         = self.savedVariables.data or {}
        local lastSave     = self.tempVars.lastSave
        local lastSaveTS   = self.tempVars.lastSaveTimeStamp
        local currentTime  = GetGameTimeMilliseconds()
        local timeStamp    = GetTimeStamp() + self.tempVars.timeStampOffsetToLead
        local lastSaveDiff = currentTime - lastSave

        -- if the last saving data is 1 sek ago, make a new table
        if lastSaveDiff >= 1000 then
            lastSaveTS = timeStamp
            self.tempVars.lastSaveTimeStamp = timeStamp
            self.tempVars.lastSave          = currentTime
        end
        data[lastSaveTS]            = data[lastSaveTS] or {}
        data[lastSaveTS]['damage']  = data[lastSaveTS]['damage'] or {}
        data[lastSaveTS]['healing'] = data[lastSaveTS]['healing'] or {}

        local damageData = {
            abilityId = damage['abilityId'],
            value     = damage['value'],
            crit      = damage['crit'],
            ms        = damage['ms'],
        }

        if damage['heal'] then
            --filter unwanted stuff
            if damage['result'] == ACTION_RESULT_HEAL or damage['result'] == ACTION_RESULT_CRITICAL_HEAL or damage['result'] == ACTION_RESULT_HOT_TICK or damage['result'] == ACTION_RESULT_HOT_TICK_CRITICAL then
                local currentData = data[lastSaveTS]['healing'][damage['target']] or {}
                table.insert(currentData, damageData)
                if #currentData ~= 0 then
                    data[lastSaveTS]['healing'][damage['target']] = currentData
                end
            end
        else
            --filter unwanted stuff
            if damage['result'] == ACTION_RESULT_DAMAGE or damage['result'] == ACTION_RESULT_CRITICAL_DAMAGE or damage['result'] == ACTION_RESULT_DOT_TICK or damage['result'] == ACTION_RESULT_DOT_TICK_CRITICAL then
                local currentData = data[lastSaveTS]['damage'][damage['target']] or {}
                table.insert(currentData, damageData)
                if #currentData ~= 0 then
                    data[lastSaveTS]['damage'][damage['target']] = currentData
                end
            end
        end

        -- only store, when we have damage or healing
        if #data[lastSaveTS]['damage'] ~= 0 or #data[lastSaveTS]['healing'] ~= 0 then
            data[lastSaveTS]['trial'] = self.getTrial()
            data[lastSaveTS]['group'] = self.getGroupEnc()

            self.savedVariables.data[lastSaveTS] = data[lastSaveTS]
        end
    end
end

-- player death / revive
function PmCa.onDeath(event)
    local self       = PmCa
    local timeStamp  = GetTimeStamp() + self.tempVars.timeStampOffsetToLead
    local data       = self.savedVariables.data or {}
    data[timeStamp] = data[timeStamp] or {}
    if (event == EVENT_PLAYER_DEAD) then -- player died
        data[timeStamp]['event'] = data[timeStamp]['event'] or {}
        table.insert(data[timeStamp]['event'], {death = true})
    else -- player lives again
        data[timeStamp]['event'] = data[timeStamp]['event'] or {}
        table.insert(data[timeStamp]['event'], {revive = true})
    end

    -- add group and trial to event
    data[timeStamp]['trial'] = self.getTrial()
    data[timeStamp]['group'] = self.getGroupEnc()

    self.savedVariables.data[timeStamp] = data[timeStamp]
end

-- player alive
function PmCa.onAlive(event)
    local self       = PmCa
    local timeStamp  = GetTimeStamp() + self.tempVars.timeStampOffsetToLead
    local data       = self.savedVariables.data or {}

    data[timeStamp]          = data[timeStamp] or {}
    data[timeStamp]['event'] = data[timeStamp]['event'] or {}
    table.insert(data[timeStamp]['event'], {alive = true})

    -- add group and trial to event
    data[timeStamp]['trial'] = self.getTrial()
    data[timeStamp]['group'] = self.getGroupEnc()

    self.savedVariables.data[timeStamp] = data[timeStamp]
end

-- player rezz
function PmCa.onRezz(event, requesterCharacterName, timeLeftToAccept)
    local self       = PmCa
    local timeStamp  = GetTimeStamp() + self.tempVars.timeStampOffsetToLead
    local data       = self.savedVariables.data or {}

    data[timeStamp]          = data[timeStamp] or {}
    data[timeStamp]['event'] = data[timeStamp]['event'] or {}

    table.insert(data[timeStamp]['event'], {rezz = zo_strformat("<<!aC:1>>", requesterCharacterName)})

    -- add group and trial to event
    data[timeStamp]['trial'] = self.getTrial()
    data[timeStamp]['group'] = self.getGroupEnc()

    self.savedVariables.data[timeStamp] = data[timeStamp]
end


-- group change
function PmCa.getGroupEnc()
    local self       = PmCa
    local group      = self.getGroup()
    local groupEnc   = ''
    local groupStr   = ''

    --sort group
    local groupSort = function(char1, char2) return char1.name < char2.name end
    table.sort(group, groupSort)

    -- encode group
    for key,charName in pairs(group) do --actualcode
        groupStr = groupStr .. "," .. charName.name
    end
    groupStr = string.sub(groupStr, 2);

    groupEnc = base64.enc(groupStr)

    return groupEnc
end

-- get group
function PmCa.getGroup()
    local self      = PmCa
    local groupSize = GetGroupSize()
    local group     = {}

    -- iterate over group
    if groupSize > 0 then
        for i = 1, groupSize do
            local unitTag = GetGroupUnitTagByIndex(i)
            if (DoesUnitExist(unitTag)) then
                local charName = zo_strformat("<<!aC:1>>", GetUnitName(unitTag))
                local charTable = {
                    name = charName
                }
                table.insert(group, charTable)
            end
        end
    end

    return group
end

-- debug func
function PmCa.debug(message, data)
    d('PM CA Debug:')
    d(message, data)
end

---------Callbacks-----
local LGS = LibStub("LibGroupSocket")
local SyncTimeStampHandler = LGS:GetHandler(LGS.MESSAGE_TYPE_SYNC_TIMESTAMP)
SyncTimeStampHandler:RegisterForFinishedSyncOffset(function(offset, syncFinished)
    PmCa:getOffset(offset, syncFinished)
end)

---------Events-------
EVENT_MANAGER:RegisterForEvent(PmCa.name, EVENT_ADD_ON_LOADED, PmCa.Initialize)