-- Lightweight item metadata channel (rarity underlays, etc.)
-- Transport: compact binary on Extended Opcode 216, version 12
-- Header: u8 type(1), u8 version(1), u8 ctx(0=container,1=inventory)
-- Container payload: u8 cid, u16 firstIndex, u16 capacity, u16 count,
--   then count * [u16 absIndex, itemMeta]
-- Inventory payload: u16 count, then count * [u8 slot, itemMeta]
-- itemMeta v12 (layout):
--   Header:
--     u8 rarityEnum
--     u8 modCount
--     repeat modCount times:
--       u8 modId        -- upgrade modifier id (1..255)
--       u8 level        -- modifier level/value (0..255)
--   Bases:
--     u16 baseAtk, u16 baseDef, u16 baseArm, u16 baseAtkSpd, u8 weaponType, u8 baseElem, u16 baseElemAtk, u16 baseSpd,
--     u8 baseHitChance, i16 baseResistPhysical, i16 baseResistFire, i16 baseResistIce, i16 baseResistEarth, i16 baseResistEnergy,
--     i8 baseSkillOneHanded, i8 baseSkillTwoHanded, i8 baseSkillDist, i8 baseSkillShield,
--     i8 baseMagicLevel, u16 baseHpRegenPer2s100, u16 baseMpRegenPer2s100, u8 baseRange, u8 baseMaxHitChance,
--     u32 weight, u16 basePower, u16 power,
--   ServerId + Ident + Name:
--     u16 serverId, u8 identifiedFlag, u16 nameLen, name

local OPCODE = 216
local DEBUG = false -- toggle at runtime via itemmeta_debug(true)
local uiReady = false

-- Modifier ids (must stay in sync with server and upgrades.yml)
local MOD_FIELDS = {
    -- Elemental damage: derive elemKey/elemLvl and mods list
    [1]  = { field = "elemLvl", elemKey = 1 }, -- fire
    [2]  = { field = "elemLvl", elemKey = 2 }, -- ice
    [3]  = { field = "elemLvl", elemKey = 3 }, -- earth
    [4]  = { field = "elemLvl", elemKey = 4 }, -- energy
    [5]  = { field = "asLvl" },                -- atk_speed
    [6]  = { field = "critLvl" },              -- crit_chance
    [7]  = { field = "leechLvl" },             -- life_leech
    [8]  = { field = "magicLvl" },             -- magic
    [9]  = { field = "hitChanceLvl" },         -- hit_chance
    [10] = { field = "defLvl" },               -- defense
    [11] = { field = "reflectLvl" },           -- reflect
    [12] = { field = "resistPhysicalLvl" },    -- physical_resist
    [13] = { field = "resistFireLvl" },        -- fire_resist
    [14] = { field = "resistIceLvl" },         -- ice_resist
    [15] = { field = "resistEarthLvl" },       -- earth_resist
    [16] = { field = "resistEnergyLvl" },      -- energy_resist
    [17] = { field = "hpRegenLvl" },           -- hp_regen
    [18] = { field = "mpRegenLvl" },           -- mp_regen
    [19] = { field = "magicLevelLvl" },        -- magic_level
    [20] = { field = "skillOneHandedLvl" },    -- skill_one_handed
    [21] = { field = "skillTwoHandedLvl" },    -- skill_two_handed
    [22] = { field = "skillDistLvl" },         -- skill_distance
    [23] = { field = "skillShieldLvl" },       -- skill_shield
    [24] = { field = "cooldownReductionLvl" }, -- cooldown_reduction
    [25] = { field = "manaCostReductionLvl" }, -- mana_cost_reduction
    [26] = { field = "spdLvl" },               -- move_speed
    [27] = { field = "experienceRateLvl" },    -- experience_rate
    [28] = { field = "goldRateLvl" },          -- gold_rate
    [29] = { field = "lootRarityLvl" },        -- loot_rarity
}

local function dbgEnabled()
    return DEBUG or (_G and _G.__ITEMMETA_DEBUG == true)
end

local function dbg(fmt, ...)
    if dbgEnabled() then
        local ok, msg = pcall(string.format, fmt, ...)
        g_logger.info("[itemmeta] " .. (ok and msg or fmt))
    end
end

-- Map inventory slot id -> slot widget id in inventory UI
local invSlotToId = {
    [InventorySlotHead]   = 'helmet',
    [InventorySlotNeck]   = 'amulet',
    [InventorySlotBack]   = 'backpack',
    [InventorySlotBody]   = 'armor',
    [InventorySlotRight]  = 'shield',
    [InventorySlotLeft]   = 'sword',
    [InventorySlotLeg]    = 'legs',
    [InventorySlotFeet]   = 'boots',
    [InventorySlotFinger] = 'ring',
    [InventorySlotAmmo]   = 'tools',
}

-- in-memory caches so UI redraws are idempotent across events
local containerMeta = {} -- [cid][absIndex] = { r=rarityName, ...attributes }
local inventoryMeta = {} -- [slot] = { r=rarityName, ...attributes }

-- Debug globals are published only when debugging is enabled
local function publishDebugGlobals()
    if dbgEnabled() then
        _G.itemmeta_containerMeta = containerMeta
        _G.itemmeta_inventoryMeta = inventoryMeta
    else
        _G.itemmeta_containerMeta = nil
        _G.itemmeta_inventoryMeta = nil
    end
end

-- Centralized rarity color definitions (used across all modules)
local RARITY_COLORS = {
    rare = '#3a9cff',        -- blue
    epic = '#b855ff',        -- brighter purple
    legendary = '#ffc700',   -- orange-gold (warmer)
    masterpiece = '#ff8000', -- orange
    common = '#dfdfdf'       -- white/gray
}

-- Unified rarity -> fill color mapping (8-digit hex, last byte = alpha)
local function rarityToFill(r)
    if r == 'rare'        then return '#3a9cff2e' end -- blue-ish
    if r == 'epic'        then return '#b855ff2e' end -- brighter purple
    if r == 'legendary'   then return '#ffc70033' end -- orange-gold (warmer)
    if r == 'masterpiece' then return '#ff80002e' end -- orange
    return '#00000000'
end
-- integration: called by game_inventory/inventory.lua after it updates a slot
_G.itemmeta_applySlot = function(slotPanel, slot)
    if not slotPanel then return end
    local itemWidget = slotPanel:getChildById('item')
    if not itemWidget or not itemWidget.setUnderlayColor then return end
    local meta = inventoryMeta[slot]
    local rarity = meta and meta.r or ''
    local fill
    if meta and meta.identified == false then
        fill = '#ff3b302e' -- red-ish for unidentified items
    else
        fill = rarityToFill(rarity)
    end
    itemWidget:setUnderlayColor(fill)
end

-- Legacy overlay helpers removed; we now rely solely on UIItem:setUnderlayColor.

local function getContainerPanel(container)
    return container.itemsPanel or (container.window and container.window:getChildById('contentsPanel'))
end

-- no-op legacy helper removed

local function redrawContainer(cid)
    -- repaint currently visible page from cache
    for _, container in pairs(g_game.getContainers()) do
        if container:getId() == cid then
            local panel = getContainerPanel(container)
            if not panel then return end
            local firstIndex = container.getFirstIndex and container:getFirstIndex() or 0
            local cap = container:getCapacity() or 0
            local meta = containerMeta[cid] or {}
            for pageSlot = 0, cap - 1 do
                local w = panel:getChildById('item' .. pageSlot)
                if w and w.setUnderlayColor then
                    local rec = meta[firstIndex + pageSlot]
                    local rarity = rec and rec.r or ''
                    local fill
                    if w.getItem and w:getItem() ~= nil then
                        if rec and rec.identified == false then
                            fill = '#ff3b302e'
                        else
                            fill = rarityToFill(rarity)
                        end
                    else
                        fill = '#00000000'
                    end
                    w:setUnderlayColor(fill)
                end
            end
            return
        end
    end
end

local function applyContainer(cid, firstIndex, capacity, items)
    -- Always update cache, even if the UI panel is not mounted yet.
    containerMeta[cid] = containerMeta[cid] or {}
    for _, entry in ipairs(items) do
        local absIndex = tonumber(entry.i) or 0
        containerMeta[cid][absIndex] = entry
    end

    -- Try to find the open container UI created by game_containers and repaint.
    for _, container in pairs(g_game.getContainers()) do
        if container:getId() == cid then
            local panel = getContainerPanel(container)
            if not panel then return end
            -- Prefer the server-provided firstIndex/capacity to avoid local timing races.
            local panelFirst = (firstIndex ~= nil) and firstIndex
                              or (container.getFirstIndex and container:getFirstIndex()) or 0
            local cap = capacity or (container.getCapacity and container:getCapacity()) or 0
            for pageSlot = 0, cap - 1 do
                local w = panel:getChildById('item' .. pageSlot)
                if w and w.setUnderlayColor then
                    local rec = containerMeta[cid][panelFirst + pageSlot]
                    local rarity = rec and rec.r or ''
                    local hasItem = w.getItem and (w:getItem() ~= nil)
                    local fill
                    if hasItem then
                        if rec and rec.identified == false then
                            fill = '#ff3b302e'
                        else
                            fill = rarityToFill(rarity)
                        end
                    else
                        fill = '#00000000'
                    end
                    w:setUnderlayColor(fill)
                end
            end
            return
        end
    end
end

local function getInvPanel(panelName)
    -- Prefer controller if available
    if inventoryController and inventoryController.ui then
        return inventoryController.ui[panelName]
    end
    -- Fallback: traverse from rootWidget (works on clients without g_ui.findWidget)
    local root = _G.rootWidget or (g_ui.getRootWidget and g_ui.getRootWidget())
    if not root or not root.recursiveGetChildById then return nil end
    local main = root:recursiveGetChildById('maininventorypanel')
    if not main then return nil end
    return main:getChildById(panelName)
end

-- Cache of inventory slot item widgets for fast painting
local slotWidgets = { onPanel = {}, offPanel = {} }

local function rebuildSlotWidgetCache()
    slotWidgets = { onPanel = {}, offPanel = {} }
    local onRoot = getInvPanel('onPanel')
    local offRoot = getInvPanel('offPanel')
    for slot, id in pairs(invSlotToId) do
        if id ~= 'soulPanel' and id ~= 'capacityPanel' and id ~= 'soulAndCapacity' then
            if onRoot then
                local sp = onRoot:getChildById(id)
                slotWidgets.onPanel[slot] = sp and sp:getChildById('item') or nil
            end
            if offRoot then
                local sp = offRoot:getChildById(id)
                slotWidgets.offPanel[slot] = sp and sp:getChildById('item') or nil
            end
        end
    end

end

-- Paint one slot on both panels; returns true if any itemWidget found
local function paintSlot(slot)
    local iwOn  = slotWidgets.onPanel[slot]
    local iwOff = slotWidgets.offPanel[slot]
    local meta = inventoryMeta[slot]
    local rarity = meta and meta.r or ''
    local function paint(iw)
        if not iw or not iw.setUnderlayColor then return false end
        local hasItem = iw.getItem and (iw:getItem() ~= nil)
        local fill
        if hasItem then
            if meta and meta.identified == false then
                fill = '#ff3b302e'
            else
                fill = rarityToFill(rarity)
            end
        else
            fill = '#00000000'
        end
        iw:setUnderlayColor(fill)
        return true
    end
    local f1 = paint(iwOn)
    local f2 = paint(iwOff)

    return f1 or f2
end

-- Paint equipment slots from cached inventoryMeta
local function redrawInventory()
    for slot, _ in pairs(invSlotToId) do paintSlot(slot) end
end

-- (applyInventory removed; parsing path updates cache directly)

function init()
    -- auto-enable debug in dev mode
    if isLocalEnv and isLocalEnv() then
        DEBUG = true
        g_logger.info('[itemmeta] debug enabled (RELENCIA_ENV=local)')
    end
    dbg('module init, registering opcode %d', OPCODE)
    publishDebugGlobals()

    -- verbose UI dump removed to reduce console noise
    ProtocolGame.registerExtendedOpcode(OPCODE, function(_, _, buffer)
        -- binary format:
        -- u8 type(1), u8 version(1), u8 ctx(0=container,1=inventory)
        -- if ctx==container: u8 cid, u16 firstIndex, u16 cap, u16 count, then count*[u16 absIndex, itemMeta]
        -- if ctx==inventory: u16 count, then count*[u8 slot, itemMeta]
        local idx = 1
        local function getU8()
            local b = string.byte(buffer, idx) or 0; idx = idx + 1; return b
        end
        local function getU16()
            local lo = string.byte(buffer, idx) or 0
            local hi = string.byte(buffer, idx + 1) or 0
            idx = idx + 2
            return lo + hi * 256
        end
        local function getU32()
            local b1 = string.byte(buffer, idx) or 0
            local b2 = string.byte(buffer, idx + 1) or 0
            local b3 = string.byte(buffer, idx + 2) or 0
            local b4 = string.byte(buffer, idx + 3) or 0
            idx = idx + 4
            return b1 + b2*256 + b3*65536 + b4*16777216
        end

        local t = getU8(); if t ~= 1 then return end
        local version = getU8(); if version ~= 12 then
            g_logger.warning(string.format('[itemmeta] unsupported version %d (expect 12); ignoring packet', version))
            return
        end
        local ctx = getU8()

        local function rarityNameFromEnum(r)
            if r == 1 then return 'rare' end
            if r == 2 then return 'epic' end
            if r == 3 then return 'legendary' end
            if r == 4 then return 'masterpiece' end
            return '' -- none/common
        end
        local function getString()
            local lo = string.byte(buffer, idx) or 0
            local hi = string.byte(buffer, idx + 1) or 0
            local len = lo + hi * 256
            idx = idx + 2
            if len <= 0 then return '' end
            local s = string.sub(buffer, idx, idx + len - 1)
            idx = idx + len
            return s
        end

        local function getI16()
            local v = getU16()
            if v >= 0x8000 then return v - 0x10000 else return v end
        end
        local function getI8()
            local v = getU8()
            if v >= 0x80 then return v - 0x100 else return v end
        end

        local function readItemMeta()
            local m = {}
            local rarityEnum = getU8()
            m.r = rarityNameFromEnum(rarityEnum)

            -- Dynamic modifiers list: modCount * (id, level)
            local modCount = getU8()
            local mods = {}
            local modsById = {}
            for i = 1, modCount do
                local id = getU8()
                local level = getU8()
                mods[#mods + 1] = { id = id, level = level }
                modsById[id] = level
            end
            m.mods = mods
            m.modsById = modsById

            -- Derive legacy per-mod fields from modsById for existing callers
            local elemKey, elemLvl = 0, 0
            for id, cfg in pairs(MOD_FIELDS) do
                local lvl = modsById[id] or 0
                if cfg.field then
                    m[cfg.field] = lvl
                end
                if cfg.elemKey and lvl > 0 and elemKey == 0 then
                    elemKey = cfg.elemKey
                    elemLvl = lvl
                end
            end
            m.elemKey = elemKey
            m.elemLvl = elemLvl

            m.baseAtk    = getU16()
            m.baseDef    = getU16()
            m.baseArm    = getU16()
            m.baseAtkSpd = getU16()
            m.weaponType = getU8()
            m.baseElem   = getU8()
            m.baseElemAtk = getU16()
            m.baseSpd     = getU16()
            -- v8 additions (new bases)
            m.baseHitChance = getU8()
            m.baseResistPhysical = getI16()
            m.baseResistFire     = getI16()
            m.baseResistIce      = getI16()
            m.baseResistEarth    = getI16()
            m.baseResistEnergy   = getI16()
            m.baseSkillOneHanded = getI8()
            m.baseSkillTwoHanded = getI8()
            m.baseSkillDist   = getI8()
            m.baseSkillShield = getI8()
            m.baseMagicLevel  = getI8()
            m.baseHpRegenPer2s = getU16() / 100.0
            m.baseMpRegenPer2s = getU16() / 100.0
            m.baseRange       = getU8()
            m.baseMaxHitChance = getU8()
            m.weight          = getU32()
            m.basePower       = getU16()
            m.power           = getU16()
            -- server item id for description lookup
            m.serverId        = getU16()
            m.identified = (getU8() ~= 0)
            m.name       = getString()
            if not m.identified then m.r = '' end
            return m
        end

        if ctx == 0 then
            local cid = getU8()
            local firstIndex = getU16()
            local cap = getU16()
            local count = getU16()
            local items = {}
            for i = 1, count do
                local absIndex = getU16()
                local m = readItemMeta()
                m.i = absIndex
                items[#items + 1] = m
            end
            -- If server reports no entries for this container page, clear cached entries for safety.
            if count == 0 then
                containerMeta[cid] = {}
                publishDebugGlobals()
                -- Still repaint current UI if open: clears any stale underlays
                redrawContainer(cid)
            else
                applyContainer(cid, firstIndex, cap, items)
            end
        elseif ctx == 1 then
            local count = getU16()
            local items = {}
            for i = 1, count do
                local slot = getU8()
                local m = readItemMeta()
                m.s = slot
                items[#items + 1] = m
            end
            dbg('inventory packet count=%d', #items)
            if not uiReady then
                for _, m in ipairs(items) do inventoryMeta[m.s] = m end
                publishDebugGlobals()
                return
            end
            for _, m in ipairs(items) do inventoryMeta[m.s] = m end
            publishDebugGlobals()
            for _, m in ipairs(items) do paintSlot(m.s) end

        end
    end)

    -- Keep overlays in sync when container UI mutates locally
    connect(Container, {
        onOpen = function(container)
            redrawContainer(container:getId())
            scheduleEvent(function() redrawContainer(container:getId()) end, 40)
        end,
        onClose = function(container)
            -- Clear cache for this container id so a future container reusing the same cid
            -- does not inherit stale rarity entries.
            local cid = container and container:getId()
            if cid and containerMeta[cid] then
                containerMeta[cid] = {}
                publishDebugGlobals()
            end
        end,
        onAddItem = function(container, slot, item)
            redrawContainer(container:getId())
            scheduleEvent(function() redrawContainer(container:getId()) end, 40)
        end,
        onUpdateItem = function(container, slot, item, oldItem)
            redrawContainer(container:getId())
            scheduleEvent(function() redrawContainer(container:getId()) end, 40)
        end,
        onRemoveItem = function(container, slot, lastItem)
            redrawContainer(container:getId())
            scheduleEvent(function() redrawContainer(container:getId()) end, 40)
        end,
        onSizeChange = function(container, newSize)
            redrawContainer(container:getId())
            scheduleEvent(function() redrawContainer(container:getId()) end, 40)
        end,
    })

    -- When the game starts (UI becomes available), redraw inventory overlays from cache
    connect(g_game, {
        onGameStart = function()
            dbg('onGameStart -> redraw from cache')
            redrawInventory()
            -- redraw all currently open containers from cache
            for _, c in pairs(g_game.getContainers()) do redrawContainer(c:getId()) end
            -- inventory underlays will be applied when UI signals readiness
        end,
        onGameEnd = function()
            -- Reset all caches so no rarity colors leak across sessions.
            containerMeta = {}
            inventoryMeta = {}
            publishDebugGlobals()
        end,
    })

    -- When inventory changes (equip/unequip), re-apply from cache
    connect(LocalPlayer, {
        onInventoryChange = function(player, slot, item, oldItem)
            paintSlot(slot)
            -- also repaint shortly after to catch late item widget creation
            scheduleEvent(function() paintSlot(slot) end, 50)
        end
    })
end

function terminate()
    ProtocolGame.unregisterExtendedOpcode(OPCODE)
    disconnect(Container, {
        onOpen = true,
        onClose = true,
        onAddItem = true,
        onUpdateItem = true,
        onRemoveItem = true,
        onSizeChange = true,
    })
    disconnect(LocalPlayer, { onInventoryChange = true })
    disconnect(g_game, { onGameStart = true, onGameEnd = true })
end

-- Console helper to toggle debug at runtime:  itemmeta_debug(true/false)
function itemmeta_debug(v)
    DEBUG = not not v
    g_logger.info('[itemmeta] debug=' .. tostring(DEBUG))
    publishDebugGlobals()
end

-- Public (minimal) API for other modules
_G.itemmeta = _G.itemmeta or {}

-- Export centralized rarity colors
_G.itemmeta.rarityColors = RARITY_COLORS

-- Get rarity color (returns hex color string)
function _G.itemmeta.getRarityColor(rarity)
    return RARITY_COLORS[rarity] or RARITY_COLORS.common
end

function _G.itemmeta.onInventoryUiReady()
    rebuildSlotWidgetCache()
    uiReady = true
    dbg('onInventoryUiReady -> redraw from cache')
    redrawInventory()
end

-- Upgrade scaling logic (mirrors server-side item_upgrades.lua)
-- These functions compute the actual effects from upgrade levels
    _G.itemmeta.scaling = {
    -- Critical: chance = level, amount fixed at 60%
    crit = function(level)
        if level <= 0 then return { chance = 0, amount = 0 } end
        return { chance = level, amount = 60 }
    end,

    -- Attack speed: level is percent
    atkSpeed = function(level)
        return { pct = level }
    end,

    -- Life leech: amount = level, chance fixed at 100%
    leech = function(level)
        if level <= 0 then return { chance = 0, amount = 0 } end
        return { chance = 100, amount = level }
    end,

    -- Elemental damage: flat = level
    elem = function(level)
        return { flat = level }
    end,

    -- Magic damage (wands): percent = level
        magic = function(level)
            return { pct = level }
        end,
        -- Movement speed: units = level
        moveSpeed = function(level)
            return { units = level }
        end,
        hpRegen = function(level)
            return { perSec = level }
        end,
        mpRegen = function(level)
            return { perSec = level }
        end,
        defense = function(level)
            return { defense = level }
        end,
        reflect = function(level)
            if level <= 0 then
                return { percent = 0, chance = 0 }
            end
            return { percent = level, chance = 100 }
        end,
        -- Skill bonuses: amount = level
        skillOneHanded = function(level) return { amount = level } end,
        skillTwoHanded = function(level) return { amount = level } end,
        skillDistance = function(level) return { amount = level } end,
        skillShield   = function(level) return { amount = level } end,
        magicLevel    = function(level) return { amount = level } end,
    }

-- Public read-only helpers for other modules (tooltips, etc.)
function _G.itemmeta.getInventoryMeta(slot)
    return inventoryMeta and inventoryMeta[slot] or nil
end

function _G.itemmeta.getContainerMeta(cid, absIndex)
    local cm = containerMeta and containerMeta[cid]
    return cm and cm[absIndex] or nil
end

-- Map inventory panel id (e.g. 'helmet') back to slot enum
local panelIdToSlot = {}
for s, id in pairs(invSlotToId) do panelIdToSlot[id] = s end

function _G.itemmeta.slotForPanelId(panelId)
    return panelIdToSlot[panelId]
end

-- Given a UIItem widget, try to resolve the corresponding meta record.
-- Returns { ctx='inventory', slot=slot, meta=table } or { ctx='container', cid=cid, absIndex=i, meta=table }
function _G.itemmeta.findForWidget(widget)
    if not widget or widget.getClassName and widget:getClassName() ~= 'UIItem' then return nil end

    -- Try inventory first: walk up until we find a parent with a slotPosition (y = slot)
    local p = widget:getParent()
    while p do
        if p.slotPosition and type(p.slotPosition) == 'table' and p.slotPosition.y then
            local slot = tonumber(p.slotPosition.y)
            if slot then
                return { ctx = 'inventory', slot = slot, meta = _G.itemmeta.getInventoryMeta(slot) }
            end
        end
        p = p:getParent()
    end

    -- Try container: find container window id 'container<cid>' and local slot id 'item<slot>'
    local function findAncestorByIdPrefix(w, prefix)
        local cur = w
        while cur do
            local id = cur.getId and cur:getId() or ''
            if type(id) == 'string' and id:find('^' .. prefix) then
                return cur, id
            end
            cur = cur:getParent()
        end
        return nil, nil
    end

    -- slot widget might be the UIItem itself or one of its labels; jump to the owner with id 'item%d+'
    local slotWidget = widget
    while slotWidget do
        local sid = slotWidget.getId and slotWidget:getId() or ''
        if type(sid) == 'string' and sid:match('^item%d+$') then break end
        slotWidget = slotWidget:getParent()
    end

    local containerWindow, winId = findAncestorByIdPrefix(widget, 'container')
    if containerWindow and slotWidget then
        local cid = tonumber(winId:match('^container(%d+)$') or '')
        local pageSlot = tonumber((slotWidget:getId() or ''):match('^item(%d+)$') or '') or 0
        if cid then
            -- obtain firstIndex from the live Container object
            for _, c in pairs(g_game.getContainers()) do
                if c:getId() == cid then
                    local firstIndex = c.getFirstIndex and c:getFirstIndex() or 0
                    local absIndex = firstIndex + pageSlot
                    return { ctx = 'container', cid = cid, absIndex = absIndex, meta = _G.itemmeta.getContainerMeta(cid, absIndex) }
                end
            end
        end
    end
    return nil
end
