if engine.ActiveGamemode() ~= "terrortown" then return end

AddCSLuaFile()

ENT.Type                     = "anim"
ENT.Base                     = "base_entity"
ENT.RenderGroup              = RENDERGROUP_BOTH

ENT.PrintName                = "tacrp_ammocrate_name"

ENT.Model                    = "models/weapons/tacint/ammoboxes/ammo_box-2b.mdl"
ENT.CanHavePrints = true
ENT.CanUseKey = true
ENT.MaxHealth = 250

ENT.MaxStored = 100
ENT.RechargeRate = 1
ENT.RechargeFreq = 2

ENT.AutomaticFrameAdvance = true

ENT.CollisionSoundsHard = {
    "physics/metal/metal_box_impact_hard1.wav",
    "physics/metal/metal_box_impact_hard2.wav",
    "physics/metal/metal_box_impact_hard3.wav",
}

ENT.CollisionSoundsSoft = {
    "physics/metal/metal_box_impact_soft1.wav",
    "physics/metal/metal_box_impact_soft2.wav",
    "physics/metal/metal_box_impact_soft3.wav",
}

ENT.ExplodeSounds = {
    "^TacRP/weapons/grenade/frag_explode-1.wav",
    "^TacRP/weapons/grenade/frag_explode-2.wav",
    "^TacRP/weapons/grenade/frag_explode-3.wav",
}

ENT.AmmoInfo = {
    -- ammo per use, max, cost per use
    ["357"] = {10, nil, 12}, -- 2 to fill
    ["smg1"] = {30, nil, 10}, -- 2 to fill
    ["pistol"] = {20, nil, 5}, -- 3 to fill
    ["alyxgun"] = {12, nil, 6}, -- 3 to fill
    ["buckshot"] = {8, nil, 8}, -- 3 to fill

    ["smg1_grenade"] = {1, 3, 34},
    ["rpg_round"] = {1, 2, 50},
    ["ti_sniper"] = {5, 10, 50},
}

if CLIENT then

    ENT.Icon = "entities/tacrp_ammo_crate.png"

    LANG.AddToLanguage("english", "tacrp_ammocrate_hint", "Press {usekey} to get ammo. Remaining charge: {num}")
    LANG.AddToLanguage("english", "tacrp_ammocrate_broken", "Your Ammo Crate has been destroyed!")
    LANG.AddToLanguage("english", "tacrp_ammocrate_subtitle", "Press {usekey} to receive ammo.")
    LANG.AddToLanguage("english", "tacrp_ammocrate_charge", "Remaining charge: {charge}.")
    LANG.AddToLanguage("english", "tacrp_ammocrate_empty", "No charge left")

    LANG.AddToLanguage("english", "tacrp_ammocrate_short_desc", "The ammo crate recharges over time")

    local TryT = LANG.TryTranslation
    local ParT = LANG.GetParamTranslation

    ENT.TargetIDHint = {
        name = "tacrp_ammocrate_name",
        hint = "tacrp_ammocrate_hint",
        fmt = function(ent, txt)
            return ParT(txt, {
                usekey = Key("+use", "USE"),
                num = ent:GetStoredAmmo() or 0
            })
        end
    }

    local key_params = {
        usekey = Key("+use", "USE")
    }

    -- TTT2 does this
    hook.Add("TTTRenderEntityInfo", "tacrp_ammo_crate", function(tData)
        local client = LocalPlayer()
        local ent = tData:GetEntity()

        if not IsValid(client) or not client:IsTerror() or not client:Alive()
        or not IsValid(ent) or tData:GetEntityDistance() > 100 or ent:GetClass() ~= "tacrp_ammo_crate_ttt" then
            return
        end

        -- enable targetID rendering
        tData:EnableText()
        tData:EnableOutline()
        tData:SetOutlineColor(client:GetRoleColor())

        tData:SetTitle(TryT(ent.PrintName))
        tData:SetSubtitle(ParT("tacrp_ammocrate_subtitle", key_params))
        tData:SetKeyBinding("+use")

        local charge = ent:GetStoredAmmo() or 0

        tData:AddDescriptionLine(TryT("tacrp_ammocrate_short_desc"))

        tData:AddDescriptionLine(
            (charge > 0) and ParT("tacrp_ammocrate_charge", {charge = charge}) or TryT("tacrp_ammocrate_empty"),
            (charge > 0) and roles.DETECTIVE.ltcolor or COLOR_ORANGE
        )
    end)
end

AccessorFuncDT(ENT, "StoredAmmo", "StoredAmmo")
AccessorFunc(ENT, "Placer", "Placer")

function ENT:SetupDataTables()
   self:DTVar("Int", 0, "StoredAmmo")
end

function ENT:AddToStorage(amount)
    self:SetStoredAmmo(math.min(self.MaxStored, self:GetStoredAmmo() + amount))
end

function ENT:TakeFromStorage(amount)
    amount = math.min(amount, self:GetStoredAmmo())
    self:SetStoredAmmo(math.max(0, self:GetStoredAmmo() - amount))
    return amount
end

function ENT:Initialize()
    if SERVER then
        self:SetModel(self.Model)

        self:PhysicsInit(SOLID_VPHYSICS)
        self:SetMoveType(MOVETYPE_VPHYSICS)
        self:SetSolid(SOLID_VPHYSICS)
        self:SetCollisionGroup(COLLISION_GROUP_WEAPON)
        self:SetUseType(CONTINUOUS_USE)

        local phys = self:GetPhysicsObject()
        if IsValid(phys) then
           phys:SetMass(150)
           phys:Wake()
        end

        if self.MaxHealth > 0 then
            self:SetMaxHealth(self.MaxHealth)
            self:SetHealth(self.MaxHealth)
        end

        self:SetStoredAmmo(self.MaxStored)

        self:SetPlacer(nil)

        self.fingerprints = {}
    end
end

local function ClampedGiveAmmo(ply, ammo, amt, clamp)
    local count = ply:GetAmmoCount(ammo)

    if count >= clamp then
        return 0
    elseif count + amt > clamp then
        amt = math.max(clamp - count, 0)
    end

    return amt
end

function ENT:ApplyAmmo(ply)
    if (self.NextUse or 0) > CurTime() then return end

    local wpn = ply:GetActiveWeapon()

    local ammotype = string.lower(game.GetAmmoName(wpn:GetPrimaryAmmoType()) or "")
    if !self.AmmoInfo[ammotype] then return end

    local max = self.AmmoInfo[ammotype][2] or wpn.Primary.ClipMax
    local amt = self.AmmoInfo[ammotype][1]
    amt = ClampedGiveAmmo(ply, ammotype, amt, max) -- amount we need
    local cost = self.AmmoInfo[ammotype][3] * (amt / self.AmmoInfo[ammotype][1])
    local f = math.min(cost, self:GetStoredAmmo()) / cost -- fraction of cost we can afford

    amt = math.floor(amt * f)

    if amt > 0 then
        self:TakeFromStorage(cost * f)
        ply:GiveAmmo(amt, ammotype)

        if !self.Open then
            local seq = self:LookupSequence("open")
            self:ResetSequence(seq)
            self:EmitSound("items/ammocrate_open.wav")

            self.Open = true
        end

        self.NextUse = CurTime() + 1
    end
end

function ENT:PhysicsCollide(data)
    if data.DeltaTime < 0.1 then return end

    if data.Speed > 25 then
        self:EmitSound(self.CollisionSoundsHard[math.random(#self.CollisionSoundsHard)])
    else
        self:EmitSound(self.CollisionSoundsSoft[math.random(#self.CollisionSoundsSoft)])
    end
end

if SERVER then

    function ENT:Use(ply)
        if !ply:IsPlayer() then return end
        self:ApplyAmmo(ply)
    end
    function ENT:Think()
        if self.Open and (self.NextUse + 0.1) < CurTime() then
            local seq = self:LookupSequence("close")
            self:ResetSequence(seq)
            self:EmitSound("items/ammocrate_close.wav")

            self.Open = false
        end

        if (self.NextCharge or 0) < CurTime() then
            self:AddToStorage(self.RechargeRate)
            self.NextCharge = CurTime() + self.RechargeFreq
        end

        self:NextThink(CurTime())
        return true
    end

elseif CLIENT then

    function ENT:DrawTranslucent()
        self:Draw()
    end

    function ENT:Draw()
        self:DrawModel()
    end

end

function ENT:OnTakeDamage(dmginfo)
    if self.BOOM then return end

    self:TakePhysicsDamage(dmginfo)
    self:SetHealth(self:Health() - dmginfo:GetDamage())
    local att = dmginfo:GetAttacker()
    local placer = self:GetPlacer()

    if IsPlayer(att) then
        DamageLog(Format("DMG: \t %s [%s] damaged ammo crate [%s] for %d dmg", att:Nick(), att:GetRoleString(), IsPlayer(placer) and placer:Nick() or "<disconnected>", dmginfo:GetDamage()))
    end

    if self:Health() < 0 then
        self:Remove()
        util.EquipmentDestroyed(self:GetPos())

        if IsValid(self:GetPlacer()) then
            LANG.Msg(self:GetPlacer(), "tacrp_ammocrate_broken")
        end

        self.BOOM = true

        local dmg = self:GetStoredAmmo() * 2.25 + 75

        util.BlastDamage(self, dmginfo:GetAttacker(), self:GetPos(), 400, dmg)

        local fx = EffectData()
        fx:SetOrigin(self:GetPos())

        if self:WaterLevel() > 0 then
            util.Effect("WaterSurfaceExplosion", fx)
        else
            util.Effect("Explosion", fx)
        end

        self:EmitSound(table.Random(self.ExplodeSounds), 125, 90)
    end
end
