--Main

local CacheManager: any = {}
CacheManager.__index = CacheManager

function CacheManager.new()
    local self = setmetatable({}, CacheManager)
    self.Cache = {}
    self.CacheLimit = math.huge
    self.Locked = false
    self.CacheParent = workspace

    return self
end

function CacheManager:SetCacheLimit(limit: number)
    self.CacheLimit = math.max(limit, 1)

    for _, cacheEntry: {[string]: any} in pairs(self.Cache) do
        local cacheToPurge: number = math.max(0, self.CacheLimit - #cacheEntry.Cache)

        if cacheToPurge > 0 then
            for _ = 1, cacheToPurge do
                cacheEntry.Cache[#cacheEntry.Cache]:Destroy()
                cacheEntry.Cache[#cacheEntry.Cache] = nil
            end
        end
    end
end

function CacheManager:Return(id: string, obj: Instance)
    if self.Locked then
        obj:Destroy()
        return    
    end

    if self.Cache[id] then
        if #self.Cache[id].Cache < self.CacheLimit then
            if obj:IsA("BasePart") then
                obj.Anchored = true
            elseif obj:IsA("Model") and obj.PrimaryPart then
                obj.PrimaryPart.Anchored = true
            end

            --? Maybe i should use bulkmoveto instead of this
            if obj:IsA("PVInstance") then
                obj:PivotTo(farawayCFrame)
            end
        
            table.insert(self.Cache[id].Cache, obj)
        else
            obj:Destroy()
        end
    end
end

function CacheManager:Take(id: string)
    if self.Locked then return end 

    if self.Cache[id] then
        local storedNum: number = #self.Cache[id].Cache

        if storedNum == 0 then
            local obj: Instance = self.Cache[id].Original:Clone()
            obj.Parent = self.CacheParent

            return obj
        else
            local obj: Instance = self.Cache[id].Cache[storedNum]
            
            self.Cache[id].Cache[storedNum] = nil

            if obj:IsA("BasePart") then
                obj.Anchored = false
            elseif obj:IsA("Model") and obj.PrimaryPart then
                obj.PrimaryPart.Anchored = false
            end

            return obj
        end
    end
end

function CacheManager:Register(id: string, obj: Instance)
    if self.Locked then 
        obj:Destroy()
        return 
    end

    if not self.Cache[id] then
        self.Cache[id] = {
            Cache = {},
            Original = obj
        }
    end
end

function CacheManager:Destroy(): ()
    self.Locked = true
    
    local _, cacheEntry: {[string]: Instance | {Instance}} = next(self.Cache)

    repeat
        cacheEntry.Original:Destroy()

        repeat
            cacheEntry.Cache[#cacheEntry.Cache]:Destroy()
            cacheEntry.Cache[#cacheEntry.Cache] = nil
        until #cacheEntry.Cache == 0

        _, cacheEntry = next(self.Cache)
    until cacheEntry == nil

    self.Cache = nil
    self = nil
end

return CacheManager
