/*

*
* AUTHOR: paradox
*
* PURPOSE: Handles detouring the `debug` library. Comes complete with bytecode
* patches and all.
*

*/

//-/~ Get Function Enviroment (getfenv)
function detour.GetFunctionEnviroment( Object )
    if majestic.IsProtected( Object ) then -- It says "table object" on wiki but it only works with functions.
        local setByClient = detour.Function_Enviroments[ Object ]

        if not setByClient then
            return getfenv( 1 ) -- Return base Garry's Mod enviroment.
        end 

        return setByClient
    end

    return debug.getfenv( Object )
end

_G.debug.getfenv = majestic.Detour( _G.debug.getfenv, detour.GetFunctionEnviroment )

//-/~ Set Function Enviroment (setfenv)
function detour.SetFunctionEnviroment( Object, Enviroment )
    if majestic.IsProtected( Object ) then 
        detour.Function_Enviroments[ Object ] = Enviroment

        -- Don't really set the enviroment, just act like we did.
        return Object 
    end

    return debug.setfenv( Object, Enviroment )
end

_G.debug.setfenv = majestic.Detour( _G.debug.setfenv, detour.GetFunctionEnviroment )

//-/~ Get Hook (gethook)
function detour.GetHook( Thread )
    -- Thread doesn't seem to work even with coroutines... For now this will work.
    local Returns = detour.SetHook_Returns
    
    return Returns.Func, Returns.Mask, Returns.Count
end

_G.debug.gethook = majestic.Detour( _G.debug.gethook, detour.GetHook )

//-/~ Set Hook (sethook)
function detour.SetHook( _Thread, _Hook, _Mask, _Count )
    -- Determine if the first argument is the thread or the hook function
    local Thread, Hook, Mask, Count;

    local Func;
    
    if not isfunction( _Thread ) then 
        Thread = _Thread; Hook = _Hook; Mask = _Mask; Count = _Count
        
        Func = _Hook
    else 
        Hook = _Thread; Mask = _Hook; Count = _Mask

        Func = _Thread
    end -- Simple way of doing this since SetHook( nil, ... ) will just unhook everything.


    -- If it's an unhook, or no function provided, let's unhook.
    if not Func then
        detour.SetHook_Returns = {
            Func = nil,
            Mask = "",
            Count = 0,
        }

        return debug.sethook( _Thread, _Hook, _Mask, _Count )
    end

    -- Save our GetHook info for later.
    detour.SetHook_Returns = {
        Func = Func,
        Mask = string.reverse( Mask ),
        Count = Count
    }

    -- Create the hooked function
    local Hooked = function( ... )
        local funcData = debug.getinfo(2, 'f')

        if majestic.IsProtected( funcData.func ) then
            return -- Don't call our hook.
        end

        -- This hook isn't getting protected data, pass our function along... 
        return Func( ... )
    end
    
    -- Setup our `hooked` hook.
    if Thread then 
        return debug.sethook( Thread, Hooked, Mask, Count )
    else 
        return debug.sethook( Hooked, Mask, Count )
    end
end

_G.debug.sethook = majestic.Detour( _G.debug.sethook, detour.SetHook )

//-/~ Get Local (getlocal)
function detour.GetLocal( Thread, Level, Index )
    if majestic.IsProtected( Thread ) then 
        return nil, nil
    end

    local Variable, Value = debug.getlocal( Thread, Level, Index )

    -- I don't know if this is really needed or not.
    if Variable then 
        if istable( Value ) then 
            if majestic.ProtectedTable( Value ) then 
                return nil, nil
            end
        elseif majestic.IsProtected( Value ) then 
            return nil, nil
        end
    end

    return Variable, Value
end

_G.debug.getlocal = majestic.Detour( _G.debug.getlocal, detour.GetLocal )

//-/~ Get Up Value (getupvalue)
function detour.GetUpValue( Func, Index )
    if Func and majestic.IsProtected( Func ) then 
        return 
    end

    return debug.getupvalue( Func, Index )
end

_G.debug.getupvalue = majestic.Detour( _G.debug.getupvalue, detour.GetUpValue )

//-/~ Set Metatable (setmetatable)
function detour.SetMetaTable( Object, Metatable )
    if ( majestic.IsProtected( Object ) or majestic.ProtectedTable( Object ) ) and istable( Metatable ) then
        detour.ObjectMetatables[ Object ] = Metatable

        -- Don't actually override it, just make it look like we did.
        return true 
    end

    return debug.setmetatable( Object, Metatable )
end

_G.debug.setmetatable = majestic.Detour( _G.debug.setmetatable, detour.SetMetaTable )

//-/~ Get Metatable (getmetatable)
function detour.GetMetaTable( Object )
    local ObjectData = detour.ObjectMetatables[ Object ]

    if ObjectData then
        return ObjectData
    elseif majestic.IsProtected( Object ) then 
        return nil
    end

    return debug.getmetatable( Object )
end

_G.debug.getmetatable = majestic.Detour( _G.debug.getmetatable, detour.GetMetaTable )

//-/~ Get Info
function detour.GetInfo( FuncOrStack, Fields )
    local Original = debug.getinfo( FuncOrStack, Fields )

    if isnumber( FuncOrStack ) then 
        -- We're working with a stack.
        local Returns = debug.getinfo( FuncOrStack, 'fnS' ) -- Call with diffrent fields to check protection levels.

        if Returns then
            if majestic.IsProtected( Returns.func ) or majestic.ProtectedDirectory( Returns.short_src ) then 
                return debug.getinfo( FuncOrStack - 1, Fields )
            end 
        end 

        return Original -- Return, we don't need to override this.
    end

    if majestic.IsProtected( FuncOrStack ) then -- Function, we only need to check this.
        return -- Throw error, we can't really help you anymore.
    end 

    return Original -- Return normal.
end

_G.debug.getinfo = majestic.Detour( _G.debug.getinfo, detour.GetInfo )