SWEP.Flashlights = {} -- tracks projectedlights
-- {{att = int, light = ProjectedTexture}}

function SWEP:GetHasFlashlights()
    for i, k in pairs(self.Attachments) do
        if !k.Installed then continue end

        local atttbl = TacRP.GetAttTable(k.Installed)

        if atttbl.Flashlight then return true end
    end

    return false
end

function SWEP:CreateFlashlights()
    self:KillFlashlights()
    self.Flashlights = {}

    local total_lights = 0

    for i, k in pairs(self.Attachments) do
        if !k.Installed then continue end

        local atttbl = TacRP.GetAttTable(k.Installed)

        if atttbl.Flashlight then
            local newlight = {
                att = i,
                light = ProjectedTexture(),
                col = Color(255, 255, 255),
                br = 4,
            }
            total_lights = total_lights + 1

            local l = newlight.light
            if !IsValid(l) then continue end

            table.insert(self.Flashlights, newlight)

            l:SetFOV(atttbl.FlashlightFOV or 60)

            l:SetFarZ(atttbl.FlashlightFarZ or 1024)
            l:SetNearZ(4)

            l:SetQuadraticAttenuation(100)

            l:SetColor(atttbl.FlashlightColor or color_white)
            l:SetTexture("effects/flashlight001")
            l:SetBrightness(atttbl.FlashlightBrightness or 1.5)
            l:SetEnableShadows(true)
            l:Update()

            local g_light = {
                Weapon = self,
                ProjectedTexture = l
            }

            table.insert(TacRP.FlashlightPile, g_light)
        end
    end

    if total_lights > 2 then -- you are a madman
        for i, k in pairs(self.Flashlights) do
            if k.light:IsValid() then k.light:SetEnableShadows(false) end
        end
    end
end

function SWEP:KillFlashlights()
    self:KillFlashlightsVM()
    -- self:KillFlashlightsWM()
end

function SWEP:KillFlashlightsVM()
    if !self.Flashlights then return end

    for i, k in pairs(self.Flashlights) do
        if k.light and k.light:IsValid() then
            k.light:Remove()
        end
    end

    self.Flashlights = nil
end

function SWEP:DrawFlashlightsVM()

    if !self:GetTactical() then
        self:KillFlashlights()
        return
    end

    if !self.Flashlights then
        self:CreateFlashlights()
    end

    for i, k in pairs(self.Flashlights) do
        local model = self.Attachments[k.att].VModel

        local pos, ang

        if !IsValid(model) then
            pos = self:GetOwner():EyePos()
            ang = self:GetOwner():EyeAngles()
        else
            pos = model:GetPos()
            ang = model:GetAngles()
        end

        local tr = util.TraceLine({
            start = self:GetOwner():EyePos(),
            endpos = self:GetOwner():EyePos() - -ang:Forward() * 128,
            mask = MASK_OPAQUE,
            filter = LocalPlayer(),
        })
        if tr.Fraction < 1 then -- We need to push the flashlight back
            local tr2 = util.TraceLine({
                start = self:GetOwner():EyePos(),
                endpos = self:GetOwner():EyePos() + -ang:Forward() * 128,
                mask = MASK_OPAQUE,
                filter = LocalPlayer(),
            })
            -- push it as back as the area behind us allows
            pos = pos + -ang:Forward() * 128 * math.min(1 - tr.Fraction, tr2.Fraction)
        end

        -- ang:RotateAroundAxis(ang:Up(), 90)

        k.light:SetPos(pos)
        k.light:SetAngles(ang)
        k.light:Update()

        -- local col = k.col

        -- local dl = DynamicLight(self:EntIndex())

        -- if dl then
        --     dl.pos = pos
        --     dl.r = col.r
        --     dl.g = col.g
        --     dl.b = col.b
        --     dl.brightness = k.br or 2
        --     -- print(z / maxz)
        --     dl.Decay = 1000 / 0.1
        --     dl.dietime = CurTime() + 0.1
        --     dl.size = (k.br or 2) * 64
        -- end
    end
end

function SWEP:DrawFlashlightsWM()
    if self:GetOwner() != LocalPlayer() then return end

    if !self.Flashlights then
        self:CreateFlashlights()
    end

    for i, k in ipairs(self.Flashlights) do
        local model = (k.slottbl or {}).WModel

        if !IsValid(model) then continue end

        local pos, ang

        if !model then
            pos = self:GetOwner():EyePos()
            ang = self:GetOwner():EyeAngles()
        else
            pos = model:GetPos()
            ang = model:GetAngles()
        end

        -- ang:RotateAroundAxis(ang:Up(), 90)

        local tr = util.TraceLine({
            start = pos,
            endpos = pos + ang:Forward() * 16,
            mask = MASK_OPAQUE,
            filter = LocalPlayer(),
        })
        if tr.Fraction < 1 then -- We need to push the flashlight back
            local tr2 = util.TraceLine({
                start = pos,
                endpos = pos - ang:Forward() * 16,
                mask = MASK_OPAQUE,
                filter = LocalPlayer(),
            })
            -- push it as back as the area behind us allows
            pos = pos + -ang:Forward() * 16 * math.min(1 - tr.Fraction, tr2.Fraction)
        else
            pos = tr.HitPos
        end

        k.light:SetPos(pos)
        k.light:SetAngles(ang)
        k.light:Update()
    end
end

local flaremat = Material("tacrp/particle_flare")
function SWEP:DrawFlashlightGlare(pos, ang, strength, dot)
    strength = strength or 1

    local diff = EyePos() - pos
    local wep = LocalPlayer():GetActiveWeapon()
    --local dot = math.Clamp((-ang:Forward():Dot(EyeAngles():Forward()) - 0.707) / (1 - 0.707), 0, 1) ^ 2
    if TacRP.ConVars["flashlight_blind"]:GetBool() then
        dot = dot ^ 4
        local tr = util.QuickTrace(pos, diff, {self:GetOwner(), LocalPlayer()})
        local s = math.Clamp(1 - diff:Length() / 328, 0, 1) ^ 1 * dot * 2000 * math.Rand(0.95, 1.05)
        if IsValid(wep) and wep.ArcticTacRP and wep:IsInScope() and wep:GetValue("ScopeOverlay") then
            s = s + math.Clamp(1 - diff:Length() / 4096, 0, 1) ^ 1.2 * wep:GetSightAmount() * dot * 3000 * math.Rand(0.95, 1.05)
        end
        if tr.Fraction == 1 then
            s = TacRP.SS(s)
            local toscreen = pos:ToScreen()
            cam.Start2D()
                surface.SetMaterial(flaremat)
                surface.SetDrawColor(255, 255, 255, 255)
                surface.DrawTexturedRect(toscreen.x - s / 2, toscreen.y - s / 2, s, s)
            cam.End2D()
        end
    end

    local rad = math.Rand(0.9, 1.1) * 128 * strength
    local a = 50 + strength * 205

    pos = pos + ang:Forward() * 2
    pos = pos + diff:GetNormalized() * (2 + 14 * strength)

    render.SetMaterial(flaremat)
    render.DrawSprite(pos, rad, rad, Color(255, 255, 255, a))
end

function SWEP:DrawFlashlightGlares()
    if !self:GetOwner():IsPlayer() then return end
    for i, k in pairs(self.Attachments) do
        if !k.Installed then continue end

        local atttbl = TacRP.GetAttTable(k.Installed)

        local src, dir
        if atttbl.Blinding and self:GetTactical() then
            if IsValid(k.WModel) then
                src, dir = k.WModel:GetPos(), self:GetShootDir()
            else
                src, dir = self:GetTracerOrigin(), self:GetShootDir()
            end
        else
            continue
        end

        local power = 1
        local dot = -dir:Forward():Dot(EyeAngles():Forward())
        local dot2 = dir:Forward():Dot((EyePos() - src):GetNormalized())
        dot = (dot + dot2) / 2
        if dot < 0 then continue end

        power = power * math.Clamp(dot * 2 - 1, 0, 1)
        local distsqr = (src - EyePos()):LengthSqr()
        power = power * ((1 - math.Clamp(distsqr / 4194304, 0, 1)) ^ 1.25)

        self:DrawFlashlightGlare(src, dir, power, dot)
    end
end

local glintmat = Material("effects/blueflare1")
local glintmat2 = Material("tacrp/scope_flare")
function SWEP:DoScopeGlint()
    --if self:GetOwner() == LocalPlayer() then return end
    if !TacRP.ConVars["glint"]:GetBool() then return end
    if !self:GetValue("ScopeOverlay") then return end
    local src, dir = self:GetTracerOrigin(), self:GetShootDir()

    local diff = EyePos() - src

    local dot = -dir:Forward():Dot(EyeAngles():Forward())
    local dot2 = dir:Forward():Dot(diff:GetNormalized())
    dot = math.max(0, (dot + dot2) / 2) ^ 1.5

    local strength = dot * math.Clamp((diff:Length() - 1024) / 3072, 0, 3) * math.Clamp(90 / self:GetValue("ScopeFOV") / 10, 0, 1)

    local rad = strength * 128 * (self:GetSightAmount() * 0.5 + 0.5)

    src = src + dir:Up() * 4 + diff:GetNormalized() * math.Clamp(diff:Length() / 2048, 0, 1) * 16

    local a = math.min(255, strength * 200 + 100)

    render.SetMaterial(glintmat)
    render.DrawSprite(src, rad, rad, Color(a, a, a))

    -- if self:GetSightAmount() > 0 then
        render.SetMaterial(glintmat2)
        render.DrawSprite(src, rad * 2, rad * 2, color_white)
    -- end
end

function SWEP:DoMuzzleLight()
    if (!IsFirstTimePredicted() and !game.SinglePlayer()) or !TacRP.ConVars["muzzlelight"]:GetBool() then return end

    if IsValid(self.MuzzleLight) then self.MuzzleLight:Remove() end

    local lamp = ProjectedTexture()
    lamp:SetTexture("tacrp/muzzleflash_light")
    local val1, val2
    if self:GetValue("Silencer") then
        val1, val2 = math.Rand(0.2, 0.4), math.Rand(100, 105)
        lamp:SetBrightness(val1)
        lamp:SetFOV(val2)
    else
        val1, val2 = math.Rand(2, 3), math.Rand(115, 120)
        lamp:SetBrightness(val1)
        lamp:SetFOV(val2)
    end

    lamp:SetFarZ(600)
    lamp:SetPos(self:GetMuzzleOrigin() + self:GetShootDir():Forward() * 8)
    lamp:SetAngles(self:GetShootDir() + Angle(math.Rand(-1, 1), math.Rand(-1, 1), math.Rand(0, 360)))
    lamp:Update()

    self.MuzzleLight = lamp
    self.MuzzleLightStart = UnPredictedCurTime()
    self.MuzzleLightEnd = UnPredictedCurTime() + 0.06
    self.MuzzleLightBrightness = val1
    self.MuzzleLightFOV = val2

    -- In multiplayer the timer will last longer than intended - sh_think should kill the light first.
    -- This is a failsafe for when the weapon stops thinking before light is killed (holstered, removed etc.).
    timer.Simple(0.06, function()
        if IsValid(lamp) then lamp:Remove() end
    end)
end
