-- This work is licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported License.
-- To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/3.0/
-- or send a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA.

local MereHealingFrames, privateVars = ...

-- This file handles the interaction with libunitchange
-- For the needs of MHF we're simply looking to watch for any group membership changes

local BumpEventCounter
local RMUnits = MereHealingFrames.RaidManagement.Units
MereHealingFrames.RaidManagement.LibUnitChange = {}
MereHealingFrames.Events = MereHealingFrames.Events or {}

function MereHealingFrames.Events.GroupJoin (handle, UnitId, groupSpecifier)
    BumpEventCounter("Event.Group.Join")

    local playerInfo = MereHealingFrames.RaidManagement.playerInfo:new(nil, UnitId, groupSpecifier)
    for layoutName, layout in pairs(MereHealingFrames.Layouts) do
        layout:QueueLayoutChange()
    end
end

function MereHealingFrames.Events.GroupLeave (handle, unitId, groupSpecifier)
    BumpEventCounter("Event.Group.Leave")
    if (unitId == MereHealingFrames.PlayerId) then
        -- can't remove the player, just change the specifier
        return MereHealingFrames.Events.GroupChange(handle, unitId, groupSpecifier, "player")
    end

    local playerInfo = RMUnits[unitId]
    if (playerInfo) then
        playerInfo:delete()
    end

    for layoutName, layout in pairs(MereHealingFrames.Layouts) do
        layout:GroupLeave(unitId, groupSpecifier)
    end
end

function MereHealingFrames.Events.GroupChange (handle, UnitId, oldSpec, newSpec)
    BumpEventCounter("Event.Group.Change")
    MereHealingFrames.Debug(4, "GroupChange: %s, %s, %s", UnitId, oldSpec, newSpec)

    local playerInfo = RMUnits[UnitId]
    if (playerInfo) then
        playerInfo:changeSpecifier(newSpec)
    end

    for layoutName, layout in pairs(MereHealingFrames.Layouts) do
        layout:GroupChange(UnitId, oldSpec, newSpec)
    end
end

function MereHealingFrames.Events.PlayerSolo (handle)
    BumpEventCounter("Event.PlayerSolo")

    local playerInfo = RMUnits[MereHealingFrames.PlayerId]

    if (playerInfo ~= nil) then
        playerInfo:Active()
        return
    end

    playerInfo = MereHealingFrames.RaidManagement.playerInfo:new(nil, MereHealingFrames.PlayerId, "player")

    for layoutName, layout in pairs(MereHealingFrames.Layouts) do
        layout:GroupJoin(playerInfo)
    end

    if not Inspect.System.Secure() then
        MereHealingFrames.RaidManagement.CheckForStrays()
    end
end

local specTracking = {}
local pendingSpecChanges = {}
local pendingUnitChanges = {}
local pendingChanges = 0

local function UnitChanged(handle, unitid, spec)
    MereHealingFrames.Debug(2, "UnitChange %s, unitid %s", spec, unitid)
    if (not specTracking[spec]) then
        return
    end

    pendingSpecChanges[spec] = unitid
    if (unitid) then
        pendingUnitChanges[unitid] = spec
    end
    pendingChanges = pendingChanges + 1
end

local function UnitChangesComplete(handle, units)
    if (pendingChanges == 0) then
        return
    end
    MereHealingFrames.Debug(2, "UnitChangeComplete, pending changes: %d", pendingChanges)

    local oldUnitIdBySpec = {}
    local oldSpecByUnitId = {}

    for unitid, playerInfo in pairs(MereHealingFrames.RaidManagement.Units) do
        local spec = playerInfo.specifier
        oldUnitIdBySpec[spec] = unitid
        oldSpecByUnitId[unitid] = spec
    end

    MereHealingFrames.DebugDump(2, oldUnitIdBySpec)
    MereHealingFrames.DebugDump(2, oldSpecByUnitId)
    MereHealingFrames.DebugDump(2, pendingSpecChanges)
    MereHealingFrames.DebugDump(2, pendingUnitChanges)

    for spec, unitid in pairs(pendingSpecChanges) do
        MereHealingFrames.Debug(2, "spec %s is now %s", spec, unitid)

        -- find what kind of update this is
        local oldUnitId = oldUnitIdBySpec[spec]

        if (oldUnitId ~= nil) then
            -- so either a remove or the old unit moved from this spec
            local newSpec = pendingUnitChanges[oldUnitId]
            if (newSpec == nil) then
                -- deleted unit
                MereHealingFrames.Debug(2, "UnitChangeComplete, deleted unit: %s, spec: %s", oldUnitId, spec)
                MereHealingFrames.Events.GroupLeave (nil, oldUnitId, spec)
            else
                -- the old unitid moved elsewhere (should be dealt with by the spec it move to, so just trace)
                MereHealingFrames.Debug(2, "UnitChangeComplete, old unit moved unit: %s, from spec: %s, to spec: %s",
                    oldUnitId, spec, newSpec)
                --MereHealingFrames.Events.GroupChange(nil, oldUnitId, spec, newSpec)
            end
        end

        if (unitid) then
            -- add or move the new unit in
            local oldSpec = oldSpecByUnitId[unitid]
            if (not oldSpec) then
                -- brand new unit
                MereHealingFrames.Debug(2, "UnitChangeComplete, new unit: %s, spec: %s", unitid, spec)
                MereHealingFrames.Events.GroupJoin (nil, unitid, spec)
            else
                -- moved unit
                MereHealingFrames.Debug(2, "UnitChangeComplete, moved unit: %s, from spec: %s, to spec: %s",
                    unitid, oldSpec, spec)
                MereHealingFrames.Events.GroupChange(nil, unitid, oldSpec, spec)
            end
        end
    end

    pendingSpecChanges = {}
    pendingUnitChanges = {}
    pendingChanges = 0

    for layoutName, layout in pairs(MereHealingFrames.Layouts) do
        layout:ProcessPendingLayoutChanges()
    end
end

local function TrackSpec(spec)
    specTracking[spec] = true
    local event = Library.LibUnitChange.Register(spec)
    Command.Event.Attach(event, function (handle, newUnitId) UnitChanged(handle, newUnitId, spec) end, "MereHealingFrames.UnitChange")
end

function MereHealingFrames.RaidManagement.LibUnitChange.PrimeSpecs()
    local unitIds = Inspect.Unit.Lookup(specTracking)
    MereHealingFrames.DebugDump(2, unitIds)
    local unitCount = 0

    for spec in pairs(specTracking) do
        local unitId = unitIds[spec]
        if (unitId ~= nil) then
            UnitChanged(nil, unitId, spec)
            unitCount = unitCount + 1
        else
            UnitChanged(nil, false, spec)
        end
    end

    if (unitCount == 0) then
        MereHealingFrames.Events.PlayerSolo(nil)
    end

    UnitChangesComplete(nil, {})
end

function MereHealingFrames.RaidManagement.LibUnitChange.Register()
    BumpEventCounter = MereHealingFrames.Events.BumpEventCounter
    for i = 1, 20, 1 do
        local spec = string.format("group%02d", i)
        TrackSpec(spec)
    end
    Command.Event.Attach(Event.Unit.Remove, UnitChangesComplete, "MHF.UnitChangeDone", -100)
end