detours.protected_files = {}
function detours:ProtectFileOrDir(strPath)
    self.protected_files[strPath] = true
end
function detours:IsProtectingFile(strPath)
    strPath = string.PatternSafe(string.lower(strPath))

    for protected_file in next, self.protected_files do
        if string.find(strPath, protected_file) then
            return true end
    end

    return false
end

-- this script
detours:ProtectFileOrDir("garrysmod/lua/spectre%.lua")
detours:ProtectFileOrDir("garrysmod/lua/spectre")

-- dlls
detours:ProtectFileOrDir("garrysmod/lua/bin/.*proxi.*")
detours:ProtectFileOrDir("garrysmod/lua/bin/.*hninterstate.*")
detours:ProtectFileOrDir("garrysmod/lua/bin/.*bsendpacket.*")
detours:ProtectFileOrDir("garrysmod/lua/bin/.*dickwrap.*")
detours:ProtectFileOrDir("garrysmod/lua/bin/.*roc.*")
detours:ProtectFileOrDir("garrysmod/lua/bin/.*context.*")

-- hnc
detours:ProtectFileOrDir("garrysmod/lua/honocheat%.lua")
detours:ProtectFileOrDir("garrysmod/lua/hnc.*")
detours:ProtectFileOrDir("garrysmod/data/hnc")
-- detours:ProtectFileOrDir("garrysmod/lua/oxide")

-- interstate
detours:ProtectFileOrDir("garrysmod/data/interscripts")

--[[
    not done:

    ["CreateDir"] =	function: 0x0245cd04ca38,
]]
--[[
    current oversights:
    file.CreateDir = not done, doesn't take into account what happens when someone creates dir with one our names
]]

local function GetPathFromFilename(path)
    return string.match(path, "^(.*[/\\])[^/\\]-$") or ""
end
local function RemoveLeadingSlashes(str)
    while string.sub(str, -string.len("/")) == "/" do
        str = string.sub(str, 1, -string.len("/") - 1)
    end

    return str
end

local _SWITCH_UNIVERSE = {
    ["game"] = "garrysmod/",
    ["lua"] = "garrysmod/lua/",
    ["lcl"] = "garrysmod/lua/",
    ["lsv"] = "garrysmod/lua/",
    ["luamenu"] = "garrysmod/lua/",
    ["data"] = "garrysmod/data",
    ["download"] = "garrysmod/download",
    ["mod"] = "garrysmod/",
    ["base_path"] = "garrysmod/",
    ["executable_path"] = "garrysmod/",
    ["thirdparty"] = "garrysmod/",
    ["garrysmod"] = "garrysmod/"
}
local function GetUniversePath(universe)
    universe = string.lower(universe)

    -- if not _SWITCH_UNIVERSE[universe] then
    -- 	print("unk uni???", universe)
    -- end

    return _SWITCH_UNIVERSE[universe] or universe or ""
end

local function GetFileGamePath(path, universe)
    path = RemoveLeadingSlashes(path)

    return GetUniversePath(universe) .. (path or "")
end

_G.file.Exists = detours:Add(file.Exists, function(path, universe)
    local strFilePath = GetFileGamePath(path, universe)

    local bShouldExist = true
    if detours:IsProtectingFile(strFilePath) then
        bShouldExist = false
        detours:Notify(Format("blocked bad file op on %s", path))
    end

    if bShouldExist then
        return fnOriginal(path, universe)
    else
        return false end
end)
_G.file.Time = detours:Add(file.Time, function(path, universe)
    local strFilePath = GetFileGamePath(path, universe)

    local bShouldExist = true
    if detours:IsProtectingFile(strFilePath) then
        bShouldExist = false
        detours:Notify(Format("blocked bad file op on %s", path))
    end

    if bShouldExist then
        return fnOriginal(path, universe)
    else
        return 0 end
end)
_G.file.Size = detours:Add(file.Size, function(path, universe)
    local strFilePath = GetFileGamePath(path, universe)

    local bShouldExist = true
    if detours:IsProtectingFile(strFilePath) then
        bShouldExist = false
        detours:Notify(Format("blocked bad file op on %s", path))
    end

    if bShouldExist then
        return fnOriginal(path, universe)
    else
        return 0 end
end)
_G.file.IsDir = detours:Add(file.IsDir, function(path, universe)
    local strFilePath = GetFileGamePath(path, universe)

    local bShouldExist = true
    if detours:IsProtectingFile(strFilePath) then
        bShouldExist = false
        detours:Notify(Format("blocked bad file op on %s", path))
    end

    if bShouldExist then
        return fnOriginal(path, universe)
    else
        return false end
end)
_G.file.Open = detours:Add(file.Open, function(path, stats, universe)
    local strFilePath = GetFileGamePath(path, universe)

    local bShouldExist = true
    if detours:IsProtectingFile(strFilePath) then
        bShouldExist = false
        detours:Notify(Format("blocked bad file op on %s", path))
    end

    if bShouldExist then
        return fnOriginal(path, stats, universe)
    else
        return end
end)
_G.file.Find = detours:Add(file.Find, function(path, universe, sort)
    local tRet = {fnOriginal(path, universe, sort)}

    local dir = GetPathFromFilename(path)

    local function remove_hidden(tab)
        local i = 1
        repeat
            local v = tab[i]
            if not v then break end

            local strFilePath = GetFileGamePath(dir .. v, universe)
            if detours:IsProtectingFile(strFilePath) then
                G.table.remove(tab, i)
                detours:Notify(Format("blocked bad file op on %s", dir .. v))
                continue
            end

            i = i + 1
        until i > #tab
        --[[local i,v = 1
        while i < #tab do
            v = tab[i]
            if not v then break end

            local strFilePath = GetFileGamePath(dir .. v, universe)
            if detours:IsProtectingFile(strFilePath) then
                G.table.remove(tab, i)
                detours:Notify(Format("blocked bad file op on %s", dir .. v))
                continue
            end

            i = i + 1
        end]]
    end
    remove_hidden(tRet[1])
    remove_hidden(tRet[2])

    return tRet[1], tRet[2]
end)
_G.file.AsyncRead = detours:Add(file.AsyncRead, function(path, universe, callback, sync)
    local strFilePath = GetFileGamePath(path, universe)

    local bShouldExist = true
    if detours:IsProtectingFile(strFilePath) then
        bShouldExist = false
        detours:Notify(Format("blocked bad file op on %s", path))
    end

    if bShouldExist then
        return fnOriginal(path, universe, callback, sync)
    else
        fnOriginal(path, universe, function(a,b)
            callback(a, b, -1, "")
        end, sync)
        return FSASYNC_OK
    end
end)
_G.file.Delete = detours:Add(file.Delete, function(path)
    local strFilePath = GetFileGamePath(path, "data")

    local bShouldExist = true
    if detours:IsProtectingFile(strFilePath) then
        bShouldExist = false
        detours:Notify(Format("blocked bad file op on %s", path))
    end

    if not bShouldExist then
        return end

    return fnOriginal(path)
end)
_G.file.Rename = detours:Add(file.Rename, function(old, new)
    local strFilePathOld = GetFileGamePath(old, "data")
    local strFilePathNew = GetFileGamePath(new, "data")

    local bShouldExist = true
    if detours:IsProtectingFile(strFilePathOld) then
        bShouldExist = false
    end
    if detours:IsProtectingFile(strFilePathNew) then
        bShouldExist = false
    end

    if bShouldExist then
        return fnOriginal(old, new)
    else
        detours:Notify(Format("blocked bad file op on %s", old))
        return false
    end
end)
