local PLUGIN = PLUGIN

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 not 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 not 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 not 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
