
-- Trace.
Trace = {};

function Trace:Line( pos, ang, dist, ignore, decal, decalindex )
    local id = ignore;
    if( type( ignore ) == "table" ) then
        id = ignore.index;
    end
    
    local vpos = pos;
    if( type(pos) == "table" ) then
        vpos = pos:GetVector3();
    end
    
    local vang = ang;
    if( type(ang) == "table" ) then
        vang = ang:GetVector3();
    end
    
    local mdecal = decal or false;
    
    _TraceLine( vpos, vang, dist, id );
    
    local tr = {};
    tr.endpos = Vector:new( _TraceEndPos() );
    tr.hitent = Entity:new( _TraceGetEnt() );
    tr.hitworld = _TraceHitWorld();
    tr.hitnonworld = _TraceHitNonWorld();
    tr.hit = _TraceHit();
    tr.normal = Vector:new( _TraceGetSurfaceNormal() );
    tr.hitsky = _TraceDidHitSky();
    tr.hithitbox = _TraceDidHitHitbox();
    tr.texture = _TraceGetTexture();
    tr.hitwater = _TraceDidHitWater();
    
    if( _TraceHit() and _TraceDidHitSky() == 0 ) then
        if( mdecal ) then
            _MakeDecal( decalindex );
        end
    end
    
    return tr;
end

function Trace:PenetratingLine( pos, ang, dist, ignore, max, depth, decal, decalindex )
    local id = ignore;
    if( type( ignore ) == "table" ) then
        id = ignore.index;
    end
    
    local vpos = pos;
    if( type(pos) == "table" ) then
        vpos = pos:GetVector3();
    end
    
    local vang = ang;
    if( type(ang) == "table" ) then
        vang = ang:GetVector3();
    end
    
    local mdecal = decal or false;
    
    _TraceLine( vpos, vang, dist, id );
    
    local tr = {};
    tr.endpos = Vector:new( _TraceEndPos() );
    tr.hitent = Entity:new( _TraceGetEnt() );
    tr.hitworld = _TraceHitWorld();
    tr.hitnonworld = _TraceHitNonWorld();
    tr.hit = _TraceHit();
    tr.normal = Vector:new( _TraceGetSurfaceNormal() );
    tr.hitsky = _TraceDidHitSky();
    tr.hithitbox = _TraceDidHitHitbox();
    tr.texture = _TraceGetTexture();
    tr.hitwater = _TraceDidHitWater();
    
    local results = { tr };
    
    -- If we hit something continue through possibly.
    if( not _TraceHit() or _TraceDidHitSky() ~= 0 ) then
        return results;
    end
    
    -- Decal?
    if( mdecal ) then
        _MakeDecal( decalindex );
    end
    
    local penetrations = 0;
    local curdepth = 0;
    local currentpos = _TraceEndPos();
    local inwall = true;
    local itr = 0;
    local fracdist = dist - ( dist * _TraceFraction() );
    
    -- Penetrate up to max times.
    while( penetrations <= max ) do
    
        -- Figure out max iterations.
        itr = itr + 1;
        if( itr >= ( max * depth ) + 100 ) then
            break;
        end
    
        -- If we are inside a wall from the last trace continue forward.
        if( inwall ) then
            currentpos = vecAdd( currentpos, vecMul( vang, 4 ) );
            _TraceLine( currentpos, vang, 4, nil );
            if( _TraceHit() ) then
                curdepth = curdepth + 4;
                if( curdepth > depth ) then
                    break;  -- Too deep!
                end
            else
                -- Trace backward.
                _TraceLine( currentpos, vecMul( vang, -1 ), 4, nil );
                
                -- Build result.
                tr = {};
                tr.endpos = Vector:new( _TraceEndPos() );
                tr.hitent = Entity:new( _TraceGetEnt() );
                tr.hitworld = _TraceHitWorld();
                tr.hitnonworld = _TraceHitNonWorld();
                tr.hit = _TraceHit();
                tr.normal = Vector:new( _TraceGetSurfaceNormal() );
                tr.hitsky = _TraceDidHitSky();
                tr.hithitbox = _TraceDidHitHitbox();
                tr.texture = _TraceGetTexture();
                tr.hitwater = _TraceDidHitWater();
                
                -- Decal?
                if( _TraceHit() and _TraceDidHitSky() == 0 ) then
                    if( mdecal ) then
                        _MakeDecal( decalindex );
                    end
                end
                
                -- Add to list.
                table.insert( results, tr );
                
                -- No longer in wall.
                inwall = false;
                
                -- Up penetration count.
                penetrations = penetrations + 1;
                
                -- Trace forward.
                _TraceLine( currentpos, vang, fracdist, nil );
                if( _TraceHit() and _TraceDidHitSky() == 0 ) then
                    inwall = true;
                    currentpos = _TraceEndPos();
                    
                    fracdist = fracdist - ( fracdist * _TraceFraction() );
                    
                    -- Decal?
                    if( mdecal ) then
                        _MakeDecal( decalindex );
                    end
                end
                
                -- Build result.
                tr = {};
                tr.endpos = Vector:new( _TraceEndPos() );
                tr.hitent = Entity:new( _TraceGetEnt() );
                tr.hitworld = _TraceHitWorld();
                tr.hitnonworld = _TraceHitNonWorld();
                tr.hit = _TraceHit();
                tr.normal = Vector:new( _TraceGetSurfaceNormal() );
                tr.hitsky = _TraceDidHitSky();
                tr.hithitbox = _TraceDidHitHitbox();
                tr.texture = _TraceGetTexture();
                tr.hitwater = _TraceDidHitWater();
                
                -- Add to list.
                table.insert( results, tr );
            end
        else
            break;
        end
    end
    
    return results;
end

function Trace:SetMask( mask )
    _TraceSetMask( mask );
end

function Trace:SetCollisionGroup( group )
    _TraceSetCollisionGroup( group );
end

function Trace:Decal( decal )
    _MakeDecal( decal );
end

function Trace:Attack( victim, inflictor, attacker, dmg )
    local iVictim = victim;
    if( type( victim ) == "table" ) then
        iVictim = victim.index;
    end
    
    local iInflictor = inflictor;
    if( type( inflictor ) == "table" ) then
        iInflictor = inflictor.index;
    end
    
    local iAttacker = attacker;
    if( type( attacker ) == "table" ) then
        iAttacker = attacker.index;
    end
    
    _TraceAttack( iVictim, iInflictor, iAttacker, dmg );
end

