--------------------------------------------------------
-- Injury Indicator - Build 42 clean version (15s fixed)
-- HALO-only, silent for zombies
--------------------------------------------------------

-- === MOD OPTIONS ===
InjuryIndicator_Options = {}
InjuryIndicator_Options.hideInjuryNotify = nil  -- Don't show when injured
InjuryIndicator_Options.hideHealNotify   = nil  -- Don't show when healed

-- Fixed cooldown (seconds between same-part messages)
local COOLDOWN = 15

local function InitializeOptions()
    if not PZAPI or not PZAPI.ModOptions then
        InjuryIndicator_Options.hideInjuryNotify = { value = false }
        InjuryIndicator_Options.hideHealNotify   = { value = false }
        return
    end

    local options = PZAPI.ModOptions:create("PainSense", "Pain Sense")

    InjuryIndicator_Options.hideInjuryNotify = options:addTickBox(
        "hideInjuryNotify",
        "Don't show when injured",
        false,
        "If enabled, the floating text when you get injured will not appear."
    )

    InjuryIndicator_Options.hideHealNotify = options:addTickBox(
        "hideHealNotify",
        "Don't show when healed",
        false,
        "If enabled, the floating text when a wound heals will not appear."
    )
end

InitializeOptions()

-- === INTERNALS ===
local timers = {}          -- [partName] = { state, last, hadInjury }
local player, bodyParts = nil, nil

local function T(part)
    return getText("UI_InjuryIndicator_" .. part)
end

local function isCurrentlyInjured(bp)
    if bp:HasInjury() then return true end
    if bp.getBleedingTime   and bp:getBleedingTime()   > 0 then return true end
    if bp.getBiteTime       and bp:getBiteTime()       > 0 then return true end
    if bp.getScratchTime    and bp:getScratchTime()    > 0 then return true end
    if bp.getDeepWoundTime  and bp:getDeepWoundTime()  > 0 then return true end
    if bp.getBurnTime       and bp:getBurnTime()       > 0 then return true end
    if bp.isInfectedWound   and bp:isInfectedWound()       then return true end
    if bp.haveGlass         and bp:haveGlass()             then return true end
    return false
end

local HEAL_VARIANTS, HURT_VARIANTS = 5, 5

local function countVariants(prefix, fallback)
    local n = 0
    while true do
        local key = prefix .. n
        local txt = (getTextOrNull and getTextOrNull(key)) or nil
        if not txt or txt == "" or txt == key then break end
        n = n + 1
    end
    if n == 0 then n = fallback end
    return n
end

local function refreshVariantCounts()
    HEAL_VARIANTS = countVariants("UI_InjuryIndicator_HealSpeech", HEAL_VARIANTS)
    HURT_VARIANTS = countVariants("UI_InjuryIndicator_HurtSpeech", HURT_VARIANTS)
end

local function getColor(isGood)
    if isGood then return 100, 255, 100 else return 255, 100, 100 end
end

local function Notify(partString, isGood)
    local partLabel = T(partString)
    local idx = ZombRand(isGood and HEAL_VARIANTS or HURT_VARIANTS)
    local text = isGood and getText("UI_InjuryIndicator_HealSpeech" .. idx)
                        or  getText("UI_InjuryIndicator_HurtSpeech" .. idx)
    local msg = partLabel .. " - " .. text
    local r, g, b = getColor(isGood)
    HaloTextHelper.addTextWithArrow(player, msg, isGood, r, g, b)
end

-- === PLAYER INIT ===
local function OnCreatePlayer()
    player = getPlayer()
    bodyParts = player and player:getBodyDamage():getBodyParts() or nil
    timers = {}
    refreshVariantCounts()

    if not bodyParts then return end

    for i = 1, bodyParts:size() do
        local bp = bodyParts:get(i - 1)
        local name = BodyPartType.ToString(bp:getType())
        local injured  = isCurrentlyInjured(bp)
        local bandaged = bp:bandaged()

        timers[name] = { state = nil, last = 0, hadInjury = false }

        if injured then
            timers[name].state = bandaged and "injured_bandaged" or "injured_open"
            timers[name].hadInjury = true
        elseif bandaged then
            timers[name].state = "injured_bandaged"
            timers[name].hadInjury = true
        end
    end
end

local function OnPlayerDeath()
    timers = {}
end

-- === MAIN LOOP ===
local function OnPlayerUpdate()
    if not player or not bodyParts then return end
    local now = getTimestampMs() / 1000

    for i = 1, bodyParts:size() do
        local bp = bodyParts:get(i - 1)
        local name = BodyPartType.ToString(bp:getType())
        local injured  = isCurrentlyInjured(bp)
        local bandaged = bp:bandaged()

        local t = timers[name]
        if not t then t = { state = nil, last = 0, hadInjury = false } timers[name] = t end

        if injured and not bandaged then
            if (t.state ~= "injured_open") or (now - t.last >= COOLDOWN) then
                if not InjuryIndicator_Options.hideInjuryNotify.value then
                    Notify(name, false)
                end
                t.state = "injured_open"
                t.hadInjury = true
                t.last = now
            end

        elseif injured and bandaged then
            if t.state ~= "injured_bandaged" then
                t.state = "injured_bandaged"
                t.hadInjury = true
                t.last = now
            end

        elseif (not injured) and bandaged then
            if t.hadInjury and ((t.state ~= "healed_bandaged") or (now - t.last >= COOLDOWN)) then
                if not InjuryIndicator_Options.hideHealNotify.value then
                    Notify(name, true)
                end
                t.state = "healed_bandaged"
                t.last = now
            end

        else
            if t.state ~= nil or t.hadInjury then
                t.state = nil
                t.hadInjury = false
                t.last = 0
            end
        end
    end
end

-- === EVENT HOOKS ===
Events.OnCreatePlayer.Add(OnCreatePlayer)
Events.OnPlayerUpdate.Add(OnPlayerUpdate)
Events.OnPlayerDeath.Add(OnPlayerDeath)

-- === STITCHES ADDON v2 (only "can remove", stricter) ===
-- Shows ONLY "can remove stitches" when:
--  1) the body part has stitches, AND
--  2) game API says stitches can be removed (or stitch time >= 40), AND
--  3) there are NO other active wounds on this part (no scratch/bleed/bite/burn/etc).
-- Uses same COOLDOWN and color scheme as core Notify().

local timers_stitches = {} -- [partName] = lastShownTs

local function hasStitches(bp)
    if bp.stitched and bp:stitched() then return true end
    if bp.hasStitches and bp:hasStitches() then return true end
    if bp.getStitchTime and bp:getStitchTime() > 0 then return true end
    return false
end

local function canRemoveStitches_Strict(bp)
    local ok = false
    if bp.canRemoveStitches and bp:canRemoveStitches() then ok = true end
    if not ok and bp.getStitchTime and bp:getStitchTime() >= 40 then ok = true end
    -- block if any other wound is active (scratch/bleed/bite/burn/etc)
    if ok and isCurrentlyInjured and isCurrentlyInjured(bp) then
        return false
    end
    return ok
end

local function NotifyStitchesReady(partString)
    local r, g, b = getColor(true)
    HaloTextHelper.addTextWithArrow(player,
        T(partString) .. " - " .. getText("UI_InjuryIndicator_StitchesReady"),
        true, r, g, b)
end

local function OnPlayerUpdate_Stitches_v2()
    if not player or not bodyParts then return end
    local now = getTimestampMs() / 1000

    for i = 1, bodyParts:size() do
        local bp = bodyParts:get(i - 1)
        local name = BodyPartType.ToString(bp:getType())

        if hasStitches(bp) and canRemoveStitches_Strict(bp) then
            local last = timers_stitches[name] or 0
            if (now - last) >= COOLDOWN then
                NotifyStitchesReady(name)
                timers_stitches[name] = now
            end
        end
    end
end

Events.OnPlayerUpdate.Add(OnPlayerUpdate_Stitches_v2)
-- === END STITCHES ADDON v2 ===
