-- Code based on: sGroupLoot by Shantalya, modified by Alza.
--                Butsu by Haste, heavily modified by Alza
--[[-------------------------------------------------------------------------
  Copyright (c) 2007-2008, Trond A Ekseth
  All rights reserved.

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are
  met:

      * Redistributions of source code must retain the above copyright
        notice, this list of conditions and the following disclaimer.
      * Redistributions in binary form must reproduce the above
        copyright notice, this list of conditions and the following
        disclaimer in the documentation and/or other materials provided
        with the distribution.
      * Neither the name of Butsu nor the names of its contributors may
        be used to endorse or promote products derived from this
        software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------]]
local _, private = ...

-- Lua Globals --
local next = _G.next

-- Libs --
local Aurora = _G.Aurora
local Base = Aurora.Base
local Skin = Aurora.Skin
local Color = Aurora.Color

-- RealUI --
local RealUI = private.RealUI
local db
--if RealUI.isDragonflight then return end
-- TODO: move this to Skins

local MODNAME = "Loot"
local Loot = RealUI:NewModule(MODNAME, "AceEvent-3.0")

-- Runtime toggle: /run RealUI_LootRollDebug = true (or false)
local DEBUG_GROUP_LOOT = false

local function IsGroupLootDebugEnabled()
    return DEBUG_GROUP_LOOT or _G.RealUI_LootRollDebug == true
end

local function GroupLootDebug(...)
    if not IsGroupLootDebugEnabled() then
        return
    end

    local parts = {}
    for i = 1, _G.select("#", ...) do
        parts[i] = _G.tostring(_G.select(i, ...))
    end

    _G.print("|cff33ff99RealUI Loot Debug:|r ".._G.table.concat(parts, " "))
end

--------------------
---- GROUP LOOT ----
--------------------
local GroupLootIconSize = 32
local grouplootlist, grouplootframes = {}, {}
local RealUIGroupLootFrame

local function GroupLootOnEvent(self, event, ...)
    if event == "START_LOOT_ROLL" then
        local rollId = ...
        local _, name, _, _, _, canNeed, canGreed, canDisenchant, _, _, _, _, canTransmog = _G.GetLootRollItemInfo(rollId)
        local rollLink = _G.GetLootRollItemLink(rollId)
        GroupLootDebug(
            "START_LOOT_ROLL",
            "rollId="..rollId,
            "name="..(name or "nil"),
            "need=".._G.tostring(canNeed),
            "greed=".._G.tostring(canGreed),
            "de=".._G.tostring(canDisenchant),
            "transmog=".._G.tostring(canTransmog),
            "linkType=".._G.tostring(type(rollLink) == "string" and rollLink:match("|H([^:|]+):") or nil)
        )
        _G.tinsert(grouplootlist, {rollId = rollId})
        Loot:UpdateGroupLoot()
    elseif event == "CONFIRM_LOOT_ROLL" then
        -- Server requires confirmation before the roll registers (e.g. housing decors,
        -- BoP-need items). The user already expressed intent by clicking a button, so
        -- auto-confirm here. Without this the roll silently fails: our frame is already
        -- hidden before the server's async CONFIRM_LOOT_ROLL event arrives, Blizzard's
        -- popup appears orphaned, the user dismisses it, and the server stays
        -- "Waiting on" this player.
        local confirmRollId, confirmRollType = ...
        GroupLootDebug("CONFIRM_LOOT_ROLL", "rollId=".._G.tostring(confirmRollId), "type=".._G.tostring(confirmRollType))
        _G.StaticPopup_Hide("CONFIRM_LOOT_ROLL", confirmRollId)
        _G.ConfirmLootRoll(confirmRollId, confirmRollType)
    elseif event == "CONFIRM_DISENCHANT_ROLL" then
        local confirmRollId, confirmRollType = ...
        GroupLootDebug("CONFIRM_DISENCHANT_ROLL", "rollId=".._G.tostring(confirmRollId), "type=".._G.tostring(confirmRollType))
        _G.StaticPopup_Hide("CONFIRM_LOOT_ROLL", confirmRollId)
        _G.ConfirmLootRoll(confirmRollId, confirmRollType)
    elseif event == "LOOT_ROLLS_COMPLETE" then
        GroupLootDebug("LOOT_ROLLS_COMPLETE", "active=".._G.tostring(#grouplootlist))
        -- Safety net: remove any stale entries whose rollId no longer returns valid data
        for i = #grouplootlist, 1, -1 do
            local texture = _G.GetLootRollItemInfo(grouplootlist[i].rollId)
            if not texture then
                _G.tremove(grouplootlist, i)
            end
        end
        Loot:UpdateGroupLoot()
    end
end

local function GroupLootFrameOnEvent(self, event, ...)
    if event == "CANCEL_LOOT_ROLL" then
        local rollId = ...
        if (self.rollId and rollId==self.rollId) then
            for index, value in next, grouplootlist do
                if(self.rollId==value.rollId) then
                    _G.tremove(grouplootlist, index)
                    break
                end
            end
            _G.StaticPopup_Hide("CONFIRM_LOOT_ROLL", self.rollId)
            self.rollId = nil
            Loot:UpdateGroupLoot()
        end
    elseif event == "CANCEL_ALL_LOOT_ROLLS" then
        _G.wipe(grouplootlist)
        Loot:UpdateGroupLoot()
    elseif event == "MODIFIER_STATE_CHANGED" then
        local key, state = ...
        if (key == "LSHIFT" or key == "RSHIFT") then
            if state == 1 then
                _G.GameTooltip_ShowCompareItem()
            else
                _G.ShoppingTooltip1:Hide()
                _G.ShoppingTooltip2:Hide()
            end
        end
    end
end

local function GroupLootFrameOnClick(self)
    _G.HandleModifiedItemClick(self.rollLink)
end

local function GroupLootFrameOnEnter(self)
    if(not self.rollId) then return end
    _G.GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
    _G.GameTooltip:SetLootRollItem(self.rollId)
    if _G.IsShiftKeyDown() then _G.GameTooltip_ShowCompareItem() end
    _G.CursorUpdate(self)
end

local function GroupLootFrameOnLeave(self)
    _G.GameTooltip:Hide()
    _G.ResetCursor()
end

local function GroupLootButtonOnClick(self, button)
    local frame = self:GetParent()
    local rollId = frame.rollId
    GroupLootDebug("RollOnLoot", "rollId=".._G.tostring(rollId), "type=".._G.tostring(self.type), "roll=".._G.tostring(self.roll))
    _G.RollOnLoot(rollId, self.type)

    -- Remove the roll entry from grouplootlist (mirrors CANCEL_LOOT_ROLL cleanup)
    for index, value in next, grouplootlist do
        if rollId == value.rollId then
            _G.tremove(grouplootlist, index)
            break
        end
    end

    _G.StaticPopup_Hide("CONFIRM_LOOT_ROLL", rollId)
    frame.rollId = nil
    Loot:UpdateGroupLoot()
end

local function GroupLootSortFunc(a, b)
    return a.rollId < b.rollId
end

local function UpdateSecondaryRollButtons(frame, rollId, canGreed, canTransmog)
    local greedEnabled = canGreed or canTransmog
    frame.greed.type = 2

    -- WoW 12 can expose a transmog roll alongside greed-style behavior.
    -- Keep greed clickable when transmog is available so both options are usable.
    if greedEnabled then
        frame.greed:Enable()
    else
        frame.greed:Disable()
    end

    if canTransmog then
        frame.transmog:Show()
        frame.transmog:Enable()
        frame.greed:ClearAllPoints()
        frame.greed:SetPoint("RIGHT", frame.transmog, "LEFT", -1, -4)
    else
        frame.transmog:Hide()
        frame.greed:ClearAllPoints()
        frame.greed:SetPoint("RIGHT", frame.pass, "LEFT", -1, -4)
    end

    frame.greed:GetNormalTexture():SetDesaturated(not frame.greed:IsEnabled())
    GroupLootDebug(
        "SecondaryButtons",
        "rollId=".._G.tostring(rollId),
        "canGreed=".._G.tostring(canGreed),
        "canTransmog=".._G.tostring(canTransmog),
        "greedEnabledByRules=".._G.tostring(greedEnabled),
        "greedShown=".._G.tostring(frame.greed:IsShown()),
        "greedEnabled=".._G.tostring(frame.greed:IsEnabled()),
        "transmogShown=".._G.tostring(frame.transmog:IsShown())
    )
end

function Loot:UpdateGroupLoot()
    _G.sort(grouplootlist, GroupLootSortFunc)
    for index, value in next, grouplootframes do value:Hide() end

    local frame
    for index, value in next, grouplootlist do
        frame = grouplootframes[index]
        if(not frame) then
            frame = _G.CreateFrame("Frame", "RealUI_GroupLootFrame"..index, _G.UIParent)
            frame:EnableMouse(true)
            frame:SetWidth(240)
            frame:SetHeight(24)
            frame:SetPoint("BOTTOM", RealUIGroupLootFrame, 0, ((index-1)*(GroupLootIconSize+3)))
            frame:RegisterEvent("CANCEL_LOOT_ROLL")
            frame:RegisterEvent("CANCEL_ALL_LOOT_ROLLS")
            frame:RegisterEvent("MODIFIER_STATE_CHANGED")
            frame:SetScript("OnEvent", GroupLootFrameOnEvent)
            frame:SetScript("OnMouseUp", GroupLootFrameOnClick)
            frame:SetScript("OnLeave", GroupLootFrameOnLeave)
            frame:SetScript("OnEnter", GroupLootFrameOnEnter)

            Base.SetBackdrop(frame, Color.frame)

            frame.pass = _G.CreateFrame("Button", nil, frame)
            frame.pass.type = 0
            frame.pass.roll = "pass"
            frame.pass:SetWidth(28)
            frame.pass:SetHeight(28)
            frame.pass:SetNormalTexture("Interface\\Buttons\\UI-GroupLoot-Pass-Up")
            frame.pass:SetHighlightTexture("Interface\\Buttons\\UI-GroupLoot-Pass-Down")
            frame.pass:SetPoint("RIGHT", 0, 1)
            frame.pass:SetScript("OnClick", GroupLootButtonOnClick)

            frame.greed = _G.CreateFrame("Button", nil, frame)
            frame.greed.type = 2
            frame.greed.roll = "greed"
            frame.greed:SetWidth(28)
            frame.greed:SetHeight(28)
            frame.greed:SetNormalTexture("Interface\\Buttons\\UI-GroupLoot-Coin-Up")
            frame.greed:SetPushedTexture("Interface\\Buttons\\UI-GroupLoot-Coin-Down")
            frame.greed:SetHighlightTexture("Interface\\Buttons\\UI-GroupLoot-Coin-Highlight")
            frame.greed:SetPoint("RIGHT", frame.pass, "LEFT", -1, -4)
            frame.greed:SetScript("OnClick", GroupLootButtonOnClick)

            frame.transmog = _G.CreateFrame("Button", nil, frame)
            frame.transmog.type = 4
            frame.transmog.roll = "transmog"
            frame.transmog:SetWidth(28)
            frame.transmog:SetHeight(28)
            frame.transmog:SetNormalAtlas("lootroll-toast-icon-transmog-up")
            frame.transmog:SetPushedAtlas("lootroll-toast-icon-transmog-down")
            frame.transmog:SetHighlightAtlas("lootroll-toast-icon-transmog-highlight")
            frame.transmog:SetPoint("RIGHT", frame.pass, "LEFT", -1, -4)
            frame.transmog:SetScript("OnClick", GroupLootButtonOnClick)
            frame.transmog:Hide()

            frame.need = _G.CreateFrame("Button", nil, frame)
            frame.need.type = 1
            frame.need.roll = "need"
            frame.need:SetWidth(28)
            frame.need:SetHeight(28)
            frame.need:SetNormalTexture("Interface\\Buttons\\UI-GroupLoot-Dice-Up")
            frame.need:SetPushedTexture("Interface\\Buttons\\UI-GroupLoot-Dice-Down")
            frame.need:SetHighlightTexture("Interface\\Buttons\\UI-GroupLoot-Dice-Highlight")
            frame.need:SetPoint("RIGHT", frame.greed, "LEFT", -1, 0)
            frame.need:SetScript("OnClick", GroupLootButtonOnClick)

            frame.text = frame:CreateFontString(nil, "OVERLAY", "SystemFont_Shadow_Med1")
            frame.text:SetJustifyH("LEFT")
            frame.text:SetPoint("LEFT", 2, 0)
            frame.text:SetPoint("RIGHT", frame.need, "LEFT")

            local iconFrame = _G.CreateFrame("Frame", nil, frame)
            iconFrame:SetHeight(GroupLootIconSize)
            iconFrame:SetWidth(GroupLootIconSize)
            iconFrame:ClearAllPoints()
            iconFrame:SetPoint("RIGHT", frame, "LEFT", -2, 0)

            local icon = iconFrame:CreateTexture(nil, "OVERLAY")
            icon:SetPoint("TOPLEFT")
            icon:SetPoint("BOTTOMRIGHT")
            Base.CropIcon(icon, iconFrame)
            frame.icon = icon

            _G.tinsert(grouplootframes, frame)
        end

        local texture, name, _, quality, _, Needable, Greedable, _, _, _, _, _, canTransmog = _G.GetLootRollItemInfo(value.rollId)
        local rollLink = _G.GetLootRollItemLink(value.rollId)

        if not name then
            _G.tremove(grouplootlist, index)
            break
        end

        frame.rollId = value.rollId
        frame.rollLink = rollLink

        if Needable then frame.need:Enable() else frame.need:Disable() end

        UpdateSecondaryRollButtons(frame, value.rollId, Greedable, canTransmog)

        frame.need:GetNormalTexture():SetDesaturated(not Needable);

        frame.text:SetText(_G.ITEM_QUALITY_COLORS[quality].hex..name)

        frame.icon:SetTexture(texture)

        frame:Show()
    end
end

function Loot:GroupLootPosition()
    RealUIGroupLootFrame:ClearAllPoints()
    RealUIGroupLootFrame:SetPoint("LEFT", db.roll.horizontal + 41, db.roll.vertical)
end

function Loot:InitializeGroupLoot()
    RealUIGroupLootFrame = _G.CreateFrame("Frame", "RealUI_GroupLoot", _G.UIParent)
    RealUIGroupLootFrame:RegisterEvent("START_LOOT_ROLL")
    RealUIGroupLootFrame:RegisterEvent("LOOT_ROLLS_COMPLETE")
    RealUIGroupLootFrame:RegisterEvent("CONFIRM_LOOT_ROLL")
    RealUIGroupLootFrame:RegisterEvent("CONFIRM_DISENCHANT_ROLL")
    RealUIGroupLootFrame:SetScript("OnEvent", GroupLootOnEvent)
    RealUIGroupLootFrame:SetFrameStrata("HIGH")
    RealUIGroupLootFrame:SetWidth(db.grouplootwidth)
    RealUIGroupLootFrame:SetHeight(24)
    self:GroupLootPosition()

    for i = 1,4 do
        local glf = _G["GroupLootFrame"..i]
        glf:UnregisterAllEvents()
        glf:Hide()
        glf:SetScript("OnShow", function(frame) frame:Hide() end)
    end

    -- Prevent Blizzard's GroupLootContainer from processing rolls,
    -- otherwise it accumulates hidden frames that never get cleaned up
    _G.GroupLootContainer:UnregisterAllEvents()
    _G.GroupLootContainer:Hide()
    _G.GroupLootContainer_AddRoll = function() end

    -- After a /reload, re-populate from any still-active rolls that the
    -- server remembers.  Blizzard's UIParent does this via
    -- PLAYER_ENTERING_WORLD → GetActiveLootRollIDs → GroupLootContainer_AddRoll,
    -- but we replaced that function above.
    local pendingIDs = _G.GetActiveLootRollIDs and _G.GetActiveLootRollIDs()
    if pendingIDs then
        for i = 1, #pendingIDs do
            _G.tinsert(grouplootlist, {rollId = pendingIDs[i]})
        end
        if #pendingIDs > 0 then
            self:UpdateGroupLoot()
        end
    end
end

--------------
---- LOOT ----
--------------
local LootIconSize = 32

local RealUILootFrame = _G.CreateFrame("Button", "RealUI_Loot", _G.UIParent)
RealUILootFrame:SetFrameStrata("HIGH")
RealUILootFrame:SetToplevel(true)
RealUILootFrame:SetHeight(64)

RealUILootFrame.close = _G.CreateFrame("Button", "RealUI_Loot_Close", RealUILootFrame, "UIPanelCloseButton")
Skin.UIPanelCloseButton(RealUILootFrame.close)
RealUILootFrame.close:SetPoint("TOPRIGHT", RealUILootFrame, "TOPRIGHT", 8, 20)
RealUILootFrame.slots = {}

local function LootOnEnter(self)
    local slot = self:GetID()
    local slotType = _G.GetLootSlotType(slot)
    if slotType == _G.LOOT_SLOT_ITEM then
        _G.GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
        _G.GameTooltip:SetLootItem(slot)
        _G.CursorUpdate(self)
    end
    if slotType == _G.LOOT_SLOT_CURRENCY then
        _G.GameTooltip:SetOwner(self, "ANCHOR_RIGHT");
        _G.GameTooltip:SetLootCurrency(slot);
        _G.CursorUpdate(self);
    end
end

local function LootOnLeave(self)
    _G.GameTooltip:Hide()
    _G.ResetCursor()
end

local function LootOnClick(self)
    if _G.IsModifiedClick() then
        _G.HandleModifiedItemClick(_G.GetLootSlotLink(self:GetID()))
    else
        _G.StaticPopup_Hide("CONFIRM_LOOT_DISTRIBUTION")
        _G.LootFrame.selectedLootButton = self
        _G.LootFrame.selectedSlot = self:GetID()
        _G.LootFrame.selectedQuality = self.quality
        _G.LootFrame.selectedItemName = self.name:GetText()
        _G.LootFrame.selectedTexture = self.icon:GetTexture()

        _G.LootSlot(self:GetID())
    end
end

local function LootOnUpdate(self)
    if _G.GameTooltip:IsOwned(self) then
        LootOnEnter(self)
    end
    _G.CursorOnUpdate(self)
end

local function createSlot(id)
    local frame = _G.CreateFrame("Button", "ButsuSlot"..id, RealUILootFrame)
    frame:SetPoint("TOP", RealUILootFrame, 0, -((id-1)*(LootIconSize+1)))
    frame:SetPoint("RIGHT")
    frame:SetPoint("LEFT")
    frame:SetHeight(24)
    --frame:SetFrameStrata("HIGH")
    --frame:SetFrameLevel(20)
    frame:SetID(id)
    RealUILootFrame.slots[id] = frame

    frame:SetScript("OnClick", LootOnClick)
    frame:SetScript("OnEnter", LootOnEnter)
    frame:SetScript("OnLeave", LootOnLeave)
    frame:SetScript("OnUpdate", LootOnUpdate)

    Base.SetBackdrop(frame, Color.button)
    Base.SetHighlight(frame)

    local iconFrame = _G.CreateFrame("Frame", nil, frame)
    iconFrame:SetHeight(LootIconSize)
    iconFrame:SetWidth(LootIconSize)
    iconFrame:SetFrameStrata("HIGH")
    iconFrame:SetFrameLevel(20)
    iconFrame:ClearAllPoints()
    iconFrame:SetPoint("RIGHT", frame, "LEFT", -2, 0)

    local icon = iconFrame:CreateTexture(nil, "ARTWORK")
    icon:SetPoint("TOPLEFT", 1, -1)
    icon:SetPoint("BOTTOMRIGHT", -1, 1)
    Base.CropIcon(icon, iconFrame)
    frame.icon = icon

    local count = iconFrame:CreateFontString(nil, "OVERLAY", "SystemFont_Shadow_Med1")
    count:SetJustifyH("CENTER")
    count:SetPoint("TOP", iconFrame, 1, -2)
    count:SetText(1)
    frame.count = count

    local name = frame:CreateFontString(nil, "OVERLAY", "SystemFont_Shadow_Med1")
    name:SetJustifyH("LEFT")
    name:SetPoint("RIGHT", frame)
    name:SetPoint("LEFT", icon, "RIGHT", 8, 0)
    name:SetNonSpaceWrap(true)
    frame.name = name

    return frame
end

local function anchorSlots(self)
    local shownSlots = 0
    for i=1, #self.slots do
        local frame = self.slots[i]
        if(frame:IsShown()) then
            shownSlots = shownSlots + 1

            -- We don't have to worry about the previous slots as they're already hidden.
            frame:SetPoint("TOP", RealUILootFrame, 4, (-8 + LootIconSize) - (shownSlots * (LootIconSize+1)))
        end
    end

    self:SetHeight(_G.max(shownSlots * LootIconSize + 16, 20))
end

function Loot:UpdateLootPosition()
    local x, y = _G.GetCursorPosition()
    x = x / RealUILootFrame:GetEffectiveScale()
    y = y / RealUILootFrame:GetEffectiveScale()

    RealUILootFrame:ClearAllPoints()
    if db.loot.cursor then
        RealUILootFrame:SetPoint("TOPLEFT", nil, "BOTTOMLEFT", x-40, y+20)
    else
        RealUILootFrame:SetPoint(db.loot.static.anchor, _G.UIParent, db.loot.static.anchor, db.loot.static.x, db.loot.static.y)
    end
end

RealUILootFrame:SetScript("OnEvent", function(dialog, event, ...)
    dialog[event](dialog, event, ...)
end)

RealUILootFrame:SetScript("OnHide", function(dialog)
    _G.StaticPopup_Hide("CONFIRM_LOOT_DISTRIBUTION")
    _G.CloseLoot()
end)

-- Instant loot ---------------------------------------------------------------
-- Can slot `i` actually be looted right now? Returning false leaves the slot
-- for the loot window rather than issuing a call the server will refuse — the
-- historic auto-loot disconnect pattern is re-issuing refused loot calls.
local function SlotIsLootable(i)
    local slotType = _G.GetLootSlotType(i)
    if slotType == _G.Enum.LootSlotType.Money
    or slotType == _G.Enum.LootSlotType.Currency then
        return true
    end

    local link = _G.GetLootSlotLink(i)
    if not link then return false end -- locked or unreadable

    local itemFamily = _G.C_Item.GetItemFamily(link) or 0
    for bag = 0, _G.NUM_TOTAL_EQUIPPED_BAG_SLOTS do
        local free, bagFamily = _G.C_Container.GetContainerNumFreeSlots(bag)
        if free and free > 0 then
            if not bagFamily or bagFamily == 0 or itemFamily == 0
            or _G.bit.band(itemFamily, bagFamily) > 0 then
                return true
            end
        end
    end

    -- No free slot, but a partial stack may still have room. Approximate:
    -- count % maxStack ~= 0 means at least one non-full stack exists. Errs
    -- toward looting; the window + sound path is the safety net if the merge
    -- turns out to be narrower than the loot quantity.
    local itemID = _G.C_Item.GetItemInfoInstant(link)
    local maxStack = itemID and _G.C_Item.GetItemMaxStackSizeByID(itemID)
    if maxStack and maxStack > 1 then
        local count = _G.C_Item.GetItemCount(itemID)
        if count > 0 and (count % maxStack) ~= 0 then
            return true
        end
    end

    return false
end

-- LOOT_READY can fire more than once for a single loot session; this makes the
-- pass and its sound cue single-shot. Cleared in LOOT_CLOSED.
local lootPassDone

function Loot:LOOT_READY(event, autoLoot)
    if self.db.global.instantLoot and autoLoot and not lootPassDone then
        lootPassDone = true

        local blocked = false
        -- Top-down: slot indices collapse as slots clear.
        for i = _G.GetNumLootItems(), 1, -1 do
            local ok, lootable = _G.pcall(SlotIsLootable, i)
            if ok and lootable then
                _G.LootSlot(i) -- one call per slot, never retried
            else
                blocked = true
            end
        end

        if not blocked then
            -- Everything dispatched; the server closes the session and the
            -- window never appears.
            return
        end

        -- Something could not be looted (full bags or a locked slot): cue the
        -- user and fall through to the window so they can see what is left.
        _G.PlaySound(_G.SOUNDKIT.IG_BACKPACK_OPEN)
    end

    --print("Loot:", event, autoLoot)
    RealUILootFrame:Show()
    RealUILootFrame:SetWidth(db.lootwidth)

    --print("Loot:", not RealUILootFrame:IsShown(), autoLoot == 0)
    if (not RealUILootFrame:IsShown()) then
        --print("Loot:", "Close?")
        _G.CloseLoot(autoLoot == 0)
    end

    local items = _G.GetNumLootItems()

    Loot:UpdateLootPosition()
    RealUILootFrame:Raise()

    if(items > 0) then
        for i = 1, items do
            local slot = RealUILootFrame.slots[i] or createSlot(i)
            local icon, name, quantity, currencyID, quality = _G.GetLootSlotInfo(i)
            if currencyID then
                name, icon, quantity, quality = _G.CurrencyContainerUtil.GetCurrencyContainerInfo(currencyID, quantity, name, icon, quality);
            end
            if icon then
                local color = _G.ITEM_QUALITY_COLORS[quality]

                --print("GetLootSlotType OPENED: "..tostring(GetLootSlotType(i)))
                --print("GetLootSlotType(i) == 2 is "..tostring((GetLootSlotType(i) == 2)))
                if (_G.GetLootSlotType(i) == 2) then
                    name = name:gsub("\n", ", ")
                end

                if(quantity > 1) then
                    slot.count:SetText(quantity)
                    slot.count:Show()
                else
                    slot.count:Hide()
                end

                slot.quality = quality
                slot.name:SetText(name)
                slot.name:SetTextColor(color.r, color.g, color.b)
                slot.icon:SetTexture(icon)
                slot.icon:SetDesaturated(false)

                slot:Enable()
                slot:Show()
            end
        end
    else
        local slot = RealUILootFrame.slots[1] or createSlot(1)

        slot.name:SetText(_G.EMPTY)
        slot.icon:SetTexture[[Interface\Icons\Inv_box_01]]
        slot.icon:SetDesaturated(true)

        slot.count:Hide()
        slot:Disable()
        slot:Show()
    end

    anchorSlots(RealUILootFrame)
end

function Loot:LOOT_SLOT_CLEARED(event, slot)
    --print("LOOT_SLOT_CLEARED: ")
    if(not RealUILootFrame:IsShown()) then return end
    RealUILootFrame.slots[slot]:Hide()
    anchorSlots(RealUILootFrame)
end

function Loot:LOOT_CLOSED(...)
    --print("Loot:", ...)
    lootPassDone = nil
    _G.StaticPopup_Hide"LOOT_BIND"
    RealUILootFrame:Hide()

    for _, v in next, RealUILootFrame.slots do
        v:Hide()
    end
end

function Loot:OPEN_MASTER_LOOT_LIST()
    --print("OPEN_MASTER_LOOT_LIST: ")--..tostring(GetLootSlotType(slot)))
    --_G.Lib_ToggleDropDownMenu(1, nil, self.dropdown, RealUILootFrame.slots[_G.LootFrame.selectedSlot], 0, 0)
end

function Loot:UPDATE_MASTER_LOOT_LIST()
    --print("UPDATE_MASTER_LOOT_LIST: ")
    --_G.Lib_UIDropDownMenu_Refresh(self.dropdown)
end

function Loot:InitializeLoot()
    _G.LootFrame:UnregisterAllEvents()
    _G.tinsert(_G.UISpecialFrames, "RealUI_Loot")

    function Loot:GiveMasterLoot()
        _G.GiveMasterLoot(_G.MasterLooterFrame.slot, _G.MasterLooterFrame.candidateId)
        _G.MasterLooterFrame:Hide();
    end

    self:RegisterEvent("LOOT_READY")
    self:RegisterEvent("LOOT_SLOT_CLEARED")
    self:RegisterEvent("LOOT_CLOSED")
    self:RegisterEvent("OPEN_MASTER_LOOT_LIST")
    self:RegisterEvent("UPDATE_MASTER_LOOT_LIST")
end

function Loot:PLAYER_LOGIN()
    self:RefreshMod()
end

-----------------------
function Loot:RefreshMod()
    if not RealUI:GetModuleEnabled(MODNAME) then return end
    db = self.db.profile

    if db.loot.enabled then
        self:InitializeLoot()
    end
    if db.roll.enabled then
        self:InitializeGroupLoot()
    end
end

function Loot:OnInitialize()
    -- Register with ModuleFramework
    if RealUI.ModuleFramework then
        RealUI:RegisterRealUIModule(MODNAME, "enhancement", {}, {
            description = "Loot frame enhancements and positioning",
            version = "1.0.0"
        })
    end

    self.db = RealUI.db:RegisterNamespace(MODNAME)
    self.db:RegisterDefaults({
        global = {
            -- Account-wide: the install wizard already opts users into auto
            -- loot, so fast auto-loot is the expected experience. New key, so
            -- existing profiles are unaffected.
            instantLoot = true,
        },
        profile = {
            ["**"] = {
                enabled = true,
            },
            loot = {
                cursor = true,
                static = {
                    x = 0,
                    y = 0,
                    anchor = "CENTER",
                },
            },
            roll = {
                vertical = -210,
                horizontal = 0,
            },
            lootwidth = 190,
            grouplootwidth = 260,
        },
    })
    db = self.db.profile

    self:SetEnabledState(RealUI:GetModuleEnabled(MODNAME))
end

function Loot:OnEnable()
    self:RegisterEvent("PLAYER_LOGIN")
end
