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

local thModName = g_currentModName
local thModPath = g_currentModDirectory
_G.THGameVersion = 22
if g_minModDescVersion ~= nil
    and g_minModDescVersion >= 90
then
    _G.THGameVersion = 25
end
_G.THMessage = {
    INTERNAL_ERROR = "An internal error has occurred",
    ARGUMENT_INVALID = "Invalid argument %q (%s) in function call",
    FILE_NOT_FOUND = "File not found: %s"
}
_G.THSpecType = {
    VEHICLE = 1,
    PLACEABLE = 2
}
source(thModPath .. "scripts/utilities/THUtils.lua")
THPlaceableDesign = {}
local THPlaceableDesign_mt = Class(THPlaceableDesign)
function THPlaceableDesign.new(isServer, isClient, customMt)
    customMt = customMt or THPlaceableDesign_mt
    if THUtils.argIsValid(not isServer or isServer == true, "isServer", isServer)
        and THUtils.argIsValid(not isClient or isClient == true, "isClient", isClient)
        and THUtils.argIsValid(type(customMt) == "table", "customMt", customMt)
    then
        local self = setmetatable({}, customMt)
        self.isServer = isServer == true
        self.isClient = isClient == true
        self.modName = thModName
        self.modPath = thModPath
        self.i18n = g_i18n
        self.specializations = {}
        for _, typeIndex in pairs(THSpecType) do
            self.specializations[typeIndex] = {}
        end
        return self
    end
end
function THPlaceableDesign.getDataTable(self, target, isRaw)
    if target == nil then
        return nil, true
    end
    if THUtils.argIsValid(type(target) == "table", "target", target)
        and THUtils.argIsValid(not isRaw or isRaw == true, "isRaw", isRaw)
    then
        local dataTable = nil
        if isRaw then
            dataTable = rawget(target, self)
        else
            dataTable = target[self]
        end
        return dataTable, true
    end
end
function THPlaceableDesign.createDataTable(self, target, isRaw)
    if target == nil then
        THUtils.errorMsg(true, THMessage.ARGUMENT_INVALID, "target", target)
        return
    end
    local dataTable, success = self:getDataTable(target, isRaw)
    if success then
        if dataTable == nil then
            dataTable = {
                parent = target,
                owner = self
            }
            if isRaw then
                rawset(target, self, dataTable)
            else
                target[self] = dataTable
            end
        end
        return dataTable
    end
end
function THPlaceableDesign.addSpecialization(self, specTypeIndex, specName, className, filename)
    if THUtils.argIsValid(self.specializations[specTypeIndex] ~= nil, "specTypeIndex", specTypeIndex)
        and THUtils.argIsValid(type(specName) == "string" and specName ~= "", "specName", specName)
        and THUtils.argIsValid(type(className) == "string", "className", className)
        and THUtils.argIsValid(type(filename) == "string", "filename", filename)
    then
        local manager = nil
        local typeName, typeNamePlural = "", ""
        if specTypeIndex == THSpecType.VEHICLE then
            manager = g_specializationManager
            typeName = "vehicle"
            typeNamePlural = "vehicles"
        elseif specTypeIndex == THSpecType.PLACEABLE then
            manager = g_placeableSpecializationManager
            typeName = "placeable"
            typeNamePlural = "placeables"
        else
            THUtils.errorMsg(nil, "Could not find suitable specialization manager")
            return false
        end
        if manager ~= nil then
            local absSpecName = self.modName .. "." .. specName
            local absClassName = self.modName .. "." .. className
            local absFilename = Utils.getFilename(filename, self.modPath)
            if absFilename == nil or not fileExists(absFilename) then
                THUtils.errorMsg(nil, THMessage.FILE_NOT_FOUND, absFilename)
                return false
            end
            if manager:getSpecializationByName(specName) then
                THUtils.errorMsg(nil, "Duplicate %s specialization found: %s", typeName, specName)
                return false
            end
            local specData = {
                name = specName,
                absName = absSpecName,
                className = className,
                absClassName = absClassName,
                typeName = typeName,
                typeNamePlural = typeNamePlural,
                filename = filename,
                absFilename = absFilename,
                modName = self.modName
            }
            manager:addSpecialization(specName, className, absFilename, self.modName)
            if manager:getSpecializationByName(specName) then
                local specClass = _G[className]
                if specClass == nil then
                    THUtils.errorMsg(nil, "Cannot find specialization %q class table: %s", specData.name, specData.className)
                    return false
                end
                specData.class = specClass
                table.insert(self.specializations[specTypeIndex], specData)
                THUtils.displayMsg("Added %s specialization: %s", typeName, specName)
                return true
            end
        end
    end
    return false
end
function THPlaceableDesign.inj_finalizeTypes(self, superFunc, manager, ...)
    THUtils.call(function()
        local specTypeIndex = nil
        if manager.typeName == "vehicle" then
            specTypeIndex = THSpecType.VEHICLE
        elseif manager.typeName == "placeable" then
            specTypeIndex = THSpecType.PLACEABLE
        end
        if specTypeIndex ~= nil then
            local specsArray = self.specializations[specTypeIndex]
            local typedObjects = manager:getTypes()
            if specsArray ~= nil and typedObjects ~= nil then
                for specIndex = 1, #specsArray do
                    local specData = specsArray[specIndex]
                    local timesAdded = 0
                    for objectTypeName, objectType in pairs(typedObjects) do
                        if objectType.specializations ~= nil
                            and not SpecializationUtil.hasSpecialization(specData.class, objectType.specializations)
                            and specData.class.prerequisitesPresent(objectType.specializations)
                        then
                            manager:addSpecialization(objectTypeName, specData.absName)
                            if SpecializationUtil.hasSpecialization(specData.class, objectType.specializations) then
                                timesAdded = timesAdded + 1
                            end
                        end
                    end
                    if timesAdded > 0 then
                        THUtils.displayMsg("Specialization %q added to %d %s", specData.name, timesAdded, specData.typeNamePlural)
                    end
                end
            end
        end
    end)
    return superFunc(manager, ...)
end
THUtils.call(function()
    local thMain = THPlaceableDesign.new(g_server ~= nil, g_client ~= nil)
    if thMain ~= nil then
        _G.g_thPlaceableDesign = thMain
        THUtils.setFunctionHook(TypeManager, "finalizeTypes", false, false, thMain, THPlaceableDesign.inj_finalizeTypes)
        thMain:addSpecialization(THSpecType.PLACEABLE, "thPlaceableDesign", "THPlaceableDesignSpec", "scripts/placeables/specializations/THPlaceableDesignSpec.lua")
    end
end)