-- DopingControl scan/resist.lua
-- Pure resistance-reconstruction math: turn item TOOLTIP lines into a
-- per-school sum, and look up a race's innate bonus. Mirrors scan/hit.lua's
-- pure half (ParseHitLine/SumGear) one level down -- the constants live in
-- data/resist.lua, this file only turns them into behavior.
--
-- NO WoW-glue function lives here on purpose: unlike scan/hit.lua/scan/
-- gear.lua/scan/aura.lua, this module never calls SetInventoryItem itself.
-- The RESIST tab's numbers are reconstructed from tooltip lines
-- scan/hit.lua's item scan ALREADY fetched (see that file's itemHit()) --
-- one more table walk over data already in hand, not a second tooltip
-- operation per item. That is the entire point of hanging resistance
-- reconstruction off the HIT tab's existing per-item cache instead of
-- building an independent RESIST scan pass: 26 players x 19 slots would be
-- ~500 extra SetInventoryItem calls per scan, and scan/hit.lua already pays
-- that cost once per unique item.
--
-- Pure Lua 5.0, no WoW API, dofile-loadable offline: everything here is
-- offline-tested (test_resist.lua).

DopingControl = DopingControl or {}
local DC = DopingControl

DC_Resist = DC_Resist or {}
local R = DC_Resist

-- ParseResistLine(text) -> kind, value | nil
--   kind  : a DC.RESIST_SCHOOLS name ("Fire", "Nature", ...) or "all"
--   value : the flat resistance amount as a number
-- Returns nil (a single value) when no pattern matches -- an unrecognized
-- phrasing is ignored, never guessed at (same contract as
-- DC_Hit.ParseHitLine).
function R.ParseResistLine(text)
    if type(text) ~= "string" then
        return nil
    end
    local pats = DC.RESIST_PATTERNS
    if type(pats) ~= "table" then
        return nil
    end
    local n = table.getn(pats)
    for i = 1, n do
        local e = pats[i]
        local _, _, cap = string.find(text, e.pattern)
        if cap then
            local v = tonumber(cap)
            if v then
                return e.kind, v
            end
        end
    end
    return nil
end

-- SumLines(lines) -> sum
--   lines : the FULL tooltip line array of ONE item, element 1 = item name
--           (same convention as scan/hit.lua's itemHit -- line 1 is never
--           a stat line, so the scan starts at 2; a caller that already
--           stripped the name may safely pass an array starting at the
--           real first stat line too, since no genuine item name matches
--           "^%+%d+ <School> Resistance" or "^Equip: %+%d+ All
--           Resistances" in the first place).
--   sum   : { [UnitResistance school index] = total } -- ONLY schools
--           that actually appeared; a plain item with no resistance stat
--           returns {} (empty, not nil) -- an empty sum is a real "this
--           item carries no resistance", exactly like SumGear's zero.
-- The "All Resistances" trap this table exists to close: a naive parser
-- keyed only on the five single-school patterns would silently skip
-- "Equip: +8 All Resistances." and undercount every one of the five
-- columns by that amount. Handled here by fanning ONE matched line out
-- over every entry of DC.RESIST_SCHOOLS.
function R.SumLines(lines)
    local sum = {}
    lines = lines or {}
    local n = table.getn(lines)
    for i = 2, n do
        local kind, v = R.ParseResistLine(lines[i])
        if kind then
            if kind == "all" then
                local schools = DC.RESIST_SCHOOLS or {}
                for s = 1, table.getn(schools) do
                    local school = schools[s].school
                    sum[school] = (sum[school] or 0) + v
                end
            else
                local num = DC.RESIST_SCHOOL_NUM and DC.RESIST_SCHOOL_NUM[kind]
                if num then
                    sum[num] = (sum[num] or 0) + v
                end
            end
        end
    end
    return sum
end

-- RaceBonus(race) -> { [UnitResistance school index] = bonus }
-- ALWAYS returns a table (empty for "no bonus", an unrecognized race
-- token/display name, or nil input) so callers can fold it onto a gear sum
-- without a nil check. The returned table is a FRESH COPY, never the
-- shared DC.RACE_RESIST entry itself -- DC.RACE_RESIST aliases two
-- spellings of the same race to ONE table object, so handing that
-- reference out would let a caller's in-place addition (E.AssembleResists
-- folds this straight onto the gear sum) corrupt the shared constant for
-- every future lookup of that race.
function R.RaceBonus(race)
    local out = {}
    local tbl = race and DC.RACE_RESIST and DC.RACE_RESIST[race]
    if tbl then
        for school, v in pairs(tbl) do
            out[school] = v
        end
    end
    return out
end
