-- Copyright ©2024 by Todd Hundersmarck (ThundR)
-- All Rights Reserved

local thModName = g_currentModName
local thModPath = g_currentModDirectory
THUtils = {}
_G.toboolean = function(value)
    local valType = type(value)
    if valType == "boolean" then
        return value
    end
    if valType == "string" then
        local valUpper = value:upper()
        if valUpper == "TRUE" then
            return true
        elseif valUpper == "FALSE" then
            return false
        end
    end
end
function THUtils.displayMsg(text, ...)
    if text == "" then
        print(text)
    else
        local isError = false
        if type(text) == "string" then
            if select("#", ...) > 0 then
                text = string.format(text, ...)
            end
        else
            text = "ERROR: " .. string.format(THMessage.ARGUMENT_INVALID, "text", text)
            isError = true
        end
        local msgText = string.format("** [%s]: %s", thModName, text)
        print(msgText)
        if isError then
            printCallstack()
        end
    end
end
function THUtils.warningMsg(text, ...)
    if type(text) == "string" and text ~= "" then
        text = "WARNING: " .. text
    end
    THUtils.displayMsg(text, ...)
end
function THUtils.errorMsg(showStack, text, ...)
    if showStack == false then
        THUtils.warningMsg(text, ...)
    else
        if type(text) == "string" and text ~= "" then
            text = "ERROR: " .. text
        elseif text == nil then
            text = "ERROR: " .. THMessage.INTERNAL_ERROR
        end
        THUtils.displayMsg(text, ...)
        if showStack == true then
            printCallstack()
        elseif showStack ~= nil then
            text = string.format(THMessage.ARGUMENT_INVALID, "showStack", showStack)
            THUtils.displayMsg("ERROR: " .. text)
            printCallstack()
        end
    end
end
function THUtils.xmlErrorMsg(xmlKey, showStack, text, ...)
    if xmlKey ~= nil and xmlKey ~= "" then
        xmlKey = tostring(xmlKey)
        if showStack == nil or showStack == true then
            THUtils.errorMsg(nil, xmlKey)
        else
            THUtils.errorMsg(false, xmlKey)
        end
        if type(text) == "string" and text ~= "" then
            text = "- " .. text
        end
    end
    return THUtils.errorMsg(showStack, text, ...)
end
function THUtils.msgOnTrue(expression, showStack, text, ...)
    if expression then
        THUtils.displayMsg(text, ...)
        if showStack == true then
            printCallstack()
        elseif showStack ~= nil and showStack ~= false then
            THUtils.errorMsg(true, THMessage.ARGUMENT_INVALID, "showStack", showStack)
        end
        return true
    end
    return false
end
function THUtils.assert(expression, showStack, text, ...)
    if not expression then
        THUtils.displayMsg(text, ...)
        if showStack == true then
            printCallstack()
        elseif showStack ~= nil and showStack ~= false then
            THUtils.errorMsg(true, THMessage.ARGUMENT_INVALID, "showStack", showStack)
        end
    end
    return expression
end
function THUtils.assertValue(expression, yesValue, noValue, showStack, text, ...)
    if THUtils.assert(expression, showStack, text, ...) then
        return yesValue
    end
    return noValue
end
function THUtils.argIsValid(expression, argName, argValue)
    if not expression then
        THUtils.errorMsg(true, THMessage.ARGUMENT_INVALID, argName, argValue)
        return false
    end
    return true
end
function THUtils.verifyArg(expression, argName, argValue, defValue)
    if THUtils.argIsValid(expression, argName, argValue) then
        return argValue
    end
    return defValue
end
function THUtils.printTable(target, ...)
    if g_thDebugTools ~= nil then
        g_thDebugTools:printTable(target, ...)
    end
end
THUtils.dumpTable = THUtils.printTable
function THUtils.traceFunction(...)
    if g_thDebugTools ~= nil then
        g_thDebugTools:traceFunction(...)
    end
end
function THUtils.pack(...)
    return { n = select("#", ...), ... }
end
function THUtils.unpack(target, index, endIndex)
    if target ~= nil then
        if endIndex == nil and target.n ~= nil then
            index = THUtils.getNoNil(index, 1)
            endIndex = target.n
        end
        return unpack(target, index, endIndex)
    end
end
function THUtils.getNoNil(target, replacement, includeEmptyStrings)
    if target == nil or (target == "" and includeEmptyStrings == true) then
        return replacement
    end
    return target
end
function THUtils.evaluate(expression, yesValue, noValue)
    if expression then
        return yesValue
    end
    return noValue
end
function THUtils.pcall(func, pa,pb,pc,pd,pe,pf,pg,ph,pi,pj,pk,pl,pm,pn,po,pp,pq,pr,ps,pt)
    local function errHandler(errMsg)
        errMsg = THUtils.getNoNil(errMsg, THMessage.INTERNAL_ERROR)
        if type(errMsg) == "string" then
            THUtils.errorMsg(true, errMsg)
        else
            printCallstack()
        end
    end
    local function protectedFunc()
        return func(pa,pb,pc,pd,pe,pf,pg,ph,pi,pj,pk,pl,pm,pn,po,pp,pq,pr,ps,pt)
    end
    return xpcall(protectedFunc, errHandler)
end
function THUtils.call(func, ...)
    local function appendFunc(rSuccess, ret1, ...)
        if rSuccess == true then
            return ret1, ...
        end
    end
    return appendFunc(THUtils.pcall(func, ...))
end
function THUtils.splitString(value, pattern, isList)
    local splitTable = {}
    if THUtils.argIsValid(type(value) == "string", "value", value)
        and THUtils.argIsValid(type(pattern) == "string", "pattern", pattern)
        and THUtils.argIsValid(not isList or isList == true, "isList", isList)
    then
        local strPos = 1
        local valToAdd, valLength = nil, string.len(value)
        while true do
            if strPos <= 0 or strPos > valLength then
                break
            end
            local strStart, strEnd = string.find(value, pattern, strPos, true)
            if strStart == nil or strEnd == nil then
                valToAdd = string.sub(value, strPos)
                strPos = valLength + 1
            else
                if strEnd <= 1 then
                    valToAdd = nil
                else
                    valToAdd = string.sub(value, strPos, strEnd - 1)
                end
                strPos = strEnd + 1
            end
            if valToAdd ~= nil and valToAdd ~= "" and valToAdd ~= pattern then
                if isList == true then
                    splitTable[valToAdd] = true
                else
                    table.insert(splitTable, valToAdd)
                end
            end
        end
    end
    return splitTable
end
function THUtils.clearTable(target)
    if THUtils.argIsValid(type(target) == "table", "target", target) then
        for key in pairs(target) do
            target[key] = nil
        end
    end
    return target
end
function THUtils.getTableValue(tablePath, target)
    target = target or _G
    if THUtils.argIsValid(type(tablePath) == "string", "tablePath", tablePath)
        and THUtils.argIsValid(type(target) == "table", "target", target)
    then
        if not string.find(tablePath, ".", nil, true) then
            return target[tablePath]
        end
        local tableKeys = THUtils.splitString(tablePath, ".")
        if tableKeys ~= nil then
            local numEntries = #tableKeys
            if numEntries > 0 then
                local finalValue = target
                for entryIndex = 1, numEntries do
                    local entry = tableKeys[entryIndex]
                    if type(finalValue) ~= "table" then
                        THUtils.errorMsg(true, "Invalid table: %s", entry)
                        return
                    end
                    if finalValue[entry] ~= nil then
                        finalValue = finalValue[entry]
                    else
                        local otherEntry = toboolean(entry)
                        if otherEntry ~= nil and finalValue[otherEntry] ~= nil then
                            finalValue = finalValue[otherEntry]
                        else
                            otherEntry = tonumber(entry)
                            if otherEntry ~= nil and finalValue[otherEntry] ~= nil then
                                finalValue = finalValue[otherEntry]
                            end
                        end
                    end
                end
                return finalValue
            end
        end
    end
end
function THUtils.getSpecTable(object, specKey, modName)
    if THUtils.argIsValid(type(object) == "table", "object", object)
        and THUtils.argIsValid(type(specKey) == "string", "specKey", specKey)
        and THUtils.argIsValid(modName == nil or type(modName) == "string", "modName", modName)
    then
        if modName ~= nil then
            specKey = modName .. "." .. specKey
        end
        return object["spec_" .. specKey], object
    end
    return nil, object
end
function THUtils.setFunctionHook(srcTable, srcFuncName, allowCreate, useSelf, extraArg, tgtFunc)
    local srcTableName = srcTable
    if srcTable == nil then
        srcTable = _G
    elseif type(srcTable) == "string" then
        srcTable = THUtils.getTableValue(srcTable)
    end
    if THUtils.argIsValid(type(srcTable) == "table", "srcTable", srcTableName)
        and THUtils.argIsValid(type(srcFuncName) == "string", "srcFuncName", srcFuncName)
        and THUtils.argIsValid(not allowCreate or allowCreate == true, "allowCreate", allowCreate)
        and THUtils.argIsValid(not useSelf or useSelf == true, "useSelf", useSelf)
        and THUtils.argIsValid(type(tgtFunc) == "function", "tgtFunc", tgtFunc)
    then
        local srcFunc = srcTable[srcFuncName]
        if srcFunc == nil and allowCreate == true then
            srcFunc = function() end
        end
        if THUtils.assert(type(srcFunc) == "function", true, "Could not find source function: %s", srcFuncName) then
            local function callbackFunc(p1, ...)
                if useSelf == true then
                    if extraArg ~= nil then
                        return tgtFunc(p1, srcFunc, extraArg, ...)
                    else
                        return tgtFunc(p1, srcFunc, ...)
                    end
                else
                    if extraArg ~= nil then
                        return tgtFunc(extraArg, srcFunc, p1, ...)
                    else
                        return tgtFunc(srcFunc, p1, ...)
                    end
                end
            end
            rawset(srcTable, srcFuncName, callbackFunc)
            return callbackFunc, srcTable
        end
    end
end