
local function draw_debug()
    return (CLIENT or game.SinglePlayer()) and TacRP.Developer(2)
end

local function IsPenetrating(ptr, ptrent)
    if ptrent:IsWorld() then
        return ptr.Contents != CONTENTS_EMPTY
    elseif IsValid(ptrent) then

        local withinbounding = false
        local hboxset = ptrent:GetHitboxSet()
        local hitbone = ptrent:GetHitBoxBone(ptr.HitBox, hboxset)
        if hitbone then
            -- If we hit a hitbox, compare against that hitbox only
            local mins, maxs = ptrent:GetHitBoxBounds(ptr.HitBox, hboxset)
            local bonepos, boneang = ptrent:GetBonePosition(hitbone)
            mins = mins * 1.1
            maxs = maxs * 1.1
            local lpos = WorldToLocal(ptr.HitPos, ptr.HitNormal:Angle(), bonepos, boneang)

            withinbounding = lpos:WithinAABox(mins, maxs)
            if draw_debug() then
                debugoverlay.BoxAngles(bonepos, mins, maxs, boneang, 5, Color(255, 255, 255, 10))
            end
        elseif util.PointContents(ptr.HitPos) != CONTENTS_EMPTY then
            -- Otherwise default to rotated OBB
            local mins, maxs = ptrent:OBBMins(), ptrent:OBBMaxs()
            withinbounding = ptrent:WorldToLocal(ptr.HitPos):WithinAABox(mins, maxs)
            if draw_debug() then
                debugoverlay.BoxAngles(ptrent:GetPos(), mins, maxs, ptrent:GetAngles(), 5, Color(255, 255, 255, 10))
            end
        end
        if draw_debug() then
            debugoverlay.Cross(ptr.HitPos, withinbounding and 4 or 6, 5, withinbounding and Color(255, 255, 0) or Color(128, 255, 0), true)
        end

        return withinbounding
    end
    return false
end

function SWEP:Penetrate(tr, range, penleft, alreadypenned)
    if !TacRP.ConVars["penetration"]:GetBool() then return end

    if !IsValid(self:GetOwner()) then return end

    local hitpos, startpos = tr.HitPos, tr.StartPos
    local dir    = (hitpos - startpos):GetNormalized()

    if tr.HitSky then return end

    if penleft <= 0 then return end

    alreadypenned = alreadypenned or {}

    local skip = false

    local trent = tr.Entity

    local penmult     = TacRP.PenTable[tr.MatType] or 1

    local pentracelen = math.max(penleft * penmult / 2, 2)
    local curr_ent    = trent

    if !tr.HitWorld then penmult = penmult * 0.5 end
    if trent.Impenetrable then penmult = 100000 end
    if trent.mmRHAe then penmult = trent.mmRHAe end

    -- penmult = penmult * math.Rand(0.9, 1.1) * math.Rand(0.9, 1.1)

    local endpos = hitpos

    local td  = {}
    td.start  = endpos
    td.endpos = endpos + (dir * pentracelen)
    td.mask   = MASK_SHOT

    local ptr = util.TraceLine(td)

    local ptrent = ptr.Entity

    while !skip and penleft > 0 and IsPenetrating(ptr, ptrent) and ptr.Fraction < 1 and ptrent == curr_ent do
        penleft = penleft - (pentracelen * penmult)

        td.start  = endpos
        td.endpos = endpos + (dir * pentracelen)
        td.mask   = MASK_SHOT

        ptr = util.TraceLine(td)

        if TacRP.Developer() then
            local pdeltap = penleft / self:GetValue("Penetration")
            local colorlr = Lerp(pdeltap, 0, 255)

            debugoverlay.Line(endpos, endpos + (dir * pentracelen), 10, Color(255, colorlr, colorlr), true)
        end

        endpos = endpos + (dir * pentracelen)
        range = range + pentracelen

        ptrent = ptr.Entity
    end

    if penleft > 0 then
        if (dir:Length() == 0) then return end

        self:GetOwner():FireBullets({
            Damage = self:GetValue("Damage_Max"),
            Force = 4,
            Tracer = 0,
            Num = 1,
            Dir = dir,
            Src = endpos,
            Callback = function(att, btr, dmg)
                range = range + (btr.HitPos - btr.StartPos):Length()
                self:AfterShotFunction(btr, dmg, range, penleft, alreadypenned)

                if TacRP.Developer() then
                    if SERVER then
                        debugoverlay.Cross(btr.HitPos, 4, 5, Color(255, 0, 0), false)
                    else
                        debugoverlay.Cross(btr.HitPos, 4, 5, Color(255, 255, 255), false)
                    end
                end
            end
        })

        self:GetOwner():FireBullets({
            Damage = 0,
            Force = 0,
            Tracer = 0,
            Num = 1,
            Distance = 1,
            Dir = -dir,
            Src = endpos,
        })
    end
end
