local S = core.get_translator("block_league")

local function show_damage() end

local ASSIST_TIME = block_league.ASSIST_TIME


local old_calculate_knockback = core.calculate_knockback

function core.calculate_knockback(player, hitter, time_from_last_punch, tool_capabilities, dir, distance, damage)
  if arena_lib.is_player_playing(player:get_player_name(), "block_league") then return 0 end

  return old_calculate_knockback(player, hitter, time_from_last_punch, tool_capabilities, dir, distance, damage)
end



function block_league.util_weapon_use_check(player, action)
  local p_name = player:get_player_name()
  if not arena_lib.is_player_playing(p_name, "block_league") then return true end

  local arena = arena_lib.get_arena_by_player(p_name)

  if arena.weapons_disabled or player:get_hp() <= 0 then return end

  block_league.remove_immunity(player) -- TODO: in futuro spostare immu e abilità su una funzione a parte di w_l, es. on_preuse

  local skill = skills.get_def(block_league.get_equipped_skill(p_name))

  if action and action.type ~= "zoom" and skill._interrupt_by_attacking then
    p_name:stop_skill(skill.internal_name)
  end

  return true
end



function block_league.util_on_can_alter_speed(p_name)
  if not arena_lib.is_player_playing(p_name, "block_league") then return true end

  local arena = arena_lib.get_arena_by_player(p_name)
  return arena.players[p_name].stamina > 0
end



function block_league.util_update_mag_hud(p_name, w_name)
  if not arena_lib.is_player_playing(p_name, "block_league") then return end

  local arena = arena_lib.get_arena_by_player(p_name)
  block_league.HUD_weapons_update(arena, p_name, w_name)
end



function block_league.util_on_hit(hitter, target, weapon, action, hit_point, damage, knockback, proxy_obj)
  local p_name = hitter:get_player_name()
  local is_target_player = target:is_player()
  local t_name = is_target_player and target:get_player_name() or target:get_luaentity().name

  if not arena_lib.is_player_playing(p_name, "block_league")
     or (is_target_player and (not arena_lib.is_player_playing(t_name, "block_league")
     or target:get_meta():get_int("bl_immunity") == 1))
    then return 0 end

  local arena = arena_lib.get_arena_by_player(p_name)

  -- se non c'è fuoco amico, non fare danno allɜ componenti della stessa squadra..
  if not action._friendly_fire then
    local self_hit = p_name == t_name

    if is_target_player then
      if arena_lib.is_player_in_same_team(arena, p_name, t_name) and not self_hit then return 0 end
    else
      if block_league.is_entity_in_player_team(p_name, target:get_luaentity()) then return 0 end
    end
  -- ..sennò riduci a seconda della percentuale specificata
  else
    if is_target_player then
      if arena_lib.is_player_in_same_team(arena, p_name, t_name) then
        damage = damage * action._friendly_dmg_perc
      end
    else
      if block_league.is_entity_in_player_team(p_name, target:get_luaentity()) then
        damage = damage * action._friendly_dmg_perc
      end
    end
  end

  local is_critical

  if action.bullet and action.bullet.explosion then -- TODO: in futuro explosion sarà fuori da bullet
    is_critical = vector.distance(proxy_obj:get_pos(), target:get_pos()) <= 2
  else
    is_critical = hit_point and hit_point.y - target:get_pos().y > 1.275 -- TODO: la spada non lo passa
  end

  if is_critical then
    damage = math.round(damage * 1.5 + 0.5)
    block_league.HUD_critical_show(p_name)
    block_league.play_sound_to_player(p_name, "bl_voice_critical")
  end

  if not is_target_player and target:get_luaentity()._no_knockback then
    knockback = 0
  end

  local dmg_table = is_target_player and arena.players[t_name].dmg_received or target:get_luaentity()._dmg_received

  if dmg_table then -- alcune entità possono non averla, es. ostacoli tutorial
    dmg_table[p_name] = {
      timestamp = arena.current_time,
      dmg = arena.current_time > dmg_table[p_name].timestamp - ASSIST_TIME and dmg_table[p_name].dmg + damage or damage,
      w_name = weapon.name,
      w_icon = weapon.inventory_image
    }
  end

  local dmg_hud_pos = hit_point or vector.add(target:get_pos(), vector.new(0,1.275,0))

  show_damage(hitter, damage, target:get_hp(), dmg_hud_pos, is_critical)

  return damage, knockback
end



function block_league.util_on_after_hit(hitter, weapon, action, objects_hit, total_damage)
  local p_name = hitter:get_player_name()
  if not arena_lib.is_player_playing(p_name, "block_league") then return end

  local arena = arena_lib.get_arena_by_player(p_name)

  if arena.mode == 0 or arena.weapons_disabled then return end  -- nessun prestigio nel tutorial; niente punti se uccido es. con bomba dopo aver fatto punto

  local killed_players = 0
  local killed_players_enemies = 0
  local killed_players_team = 0
  local enemies_damage = 0

  -- calcolo informazioni
  for _, t_data in pairs(objects_hit) do
    local is_player = t_data.type == "player"
    local obj = t_data.ref
    local entity_lua = obj:get_luaentity()

    -- evita entità rotte
    if is_player or entity_lua then
      local in_same_team = is_player  and arena_lib.is_player_in_same_team(arena, p_name, obj:get_player_name())
                                      or (entity_lua and block_league.is_entity_in_player_team(p_name, entity_lua))

      if not in_same_team then
        local prev_hp = t_data.prev_hp
        local dmg = prev_hp - t_data.damage < 0 and prev_hp or t_data.damage

        enemies_damage = enemies_damage + dmg
      end

      if (is_player and obj:get_hp() <= 0) or (not is_player and entity_lua._is_dying) then
        if is_player then
          block_league.kill_player(arena, weapon.inventory_image, hitter, obj)
          killed_players = killed_players + 1

          if in_same_team then
            killed_players_team = killed_players_team + 1
          else
            killed_players_enemies = killed_players_enemies + 1
          end

        else
          block_league.kill_entity(arena, weapon.inventory_image, hitter, obj)
        end

      else
        -- non riprodurre suono se colpisci elementi della tua squadra, e suono errore se invece l'avversariə è immune
        if not in_same_team then -- idem come sopra
          if is_player and t_data.ref:get_meta():get_int("bl_immunity") == 1 then
            block_league.play_sound_to_player(p_name, "bl_hit_deny")
          else
            block_league.play_sound_to_player(p_name, "bl_hit")
          end
        end
      end
    end
  end

  local w_name = block_league.get_original_weapon(weapon.name)
  local w_dmg_dealt = arena.players[p_name].dmg_dealt[w_name]

  -- aggiorno danno totale inflitto ed eventualmente aumento i punti
  block_league.calc_dmg_dealt_and_increase_points(arena, p_name, w_name, enemies_damage)
  w_dmg_dealt.kills = w_dmg_dealt.kills + killed_players_enemies

  -- medaglie
  --
  -- eventuale medaglia doppia/tripla uccisione
  if killed_players_enemies > 1 then
    if killed_players_enemies == 2 then
      if w_name == "block_league:pixelgun" then
        block_league.show_medal(p_name, "bl_medal_pixelgun_doublekill.png")
      elseif w_name == "block_league:bomb" then
        block_league.show_medal(p_name, "bl_medal_bomb_doublekill.png")
      end
    elseif killed_players_enemies >= 3 then
      if w_name == "block_league:pixelgun" then
        block_league.show_medal(p_name, "bl_medal_pixelgun_triplekill.png")
      elseif w_name == "block_league:bomb" then
        block_league.show_medal(p_name, "bl_medal_bomb_triplekill.png")
      end
    end

    arena_lib.send_message_in_arena(arena, "both",core.colorize("#d7ded7", S("@1 has killed @2 players in one shot!", core.colorize("#eea160", p_name), killed_players_enemies)))
  end

  -- licenze
  --
  block_league.calc_licence(arena.players_amount, p_name, w_name, killed_players_enemies)

  -- prestigi
  --
  -- niente prestigi se non ci sono almeno 6 persone
  if arena.players_amount < 6 then return end

  --TODO
  --[[
    if killed_players >= 2 and weapon.name == "block_league:pixelgun" then
      achvmt_lib.award("block_league:quellochesarà", p_name)
    end
  ]]
end



function block_league.util_on_end_melee(p_name, w_name)
  if not arena_lib.is_player_playing(p_name, "block_league") then return end

  local arena = arena_lib.get_arena_by_player(p_name)
  block_league.HUD_weapons_update(arena, p_name, w_name, true)
end



function block_league.util_on_recovery_end(player, weapon, action)
  local p_name = player:get_player_name()
  if not arena_lib.is_player_playing(p_name, "block_league") then return end

  local arena = arena_lib.get_arena_by_player(p_name)

  if weapon.weapon_type == "melee" then
    block_league.HUD_weapons_update(arena, p_name, weapon.name, false)
  end

  if action.physics_override then
    local p_speed = player:get_physics_override().speed
    local p_meta = player:get_meta()

    -- se l'azione viene chiamata poco prima di finire la stamina
    if p_speed == block_league.SPEED and arena.players[p_name].stamina <= 0 then
      player:set_physics_override({speed = block_league.SPEED_LOW})
      p_meta:set_int("wl_slowed_down", 1)

    -- se finisce mentre la stamina da 0 ha ricominciato a caricarsi e nessun'azione
    -- è in corso (ovvero quando si fa punto facendo uno scatto senza stamina)
    elseif p_speed == block_league.SPEED_LOW
       and arena.players[p_name].stamina > 0
       and p_meta:get_int("wl_weapon_state") == 0
       and p_meta:get_int("wl_zooming") == 0 then
      player:set_physics_override({speed = block_league.SPEED})
      p_meta:set_int("wl_slowed_down", 0)
    end
  end
end



function block_league.util_on_reload(p_name, w_name)
  if not arena_lib.is_player_playing(p_name, "block_league") then return end

  local arena = arena_lib.get_arena_by_player(p_name)
  block_league.HUD_weapons_update(arena, p_name, w_name, true)
end



function block_league.util_on_reload_end(p_name, w_name)
  if not arena_lib.is_player_playing(p_name, "block_league") then return end

  local arena = arena_lib.get_arena_by_player(p_name)
  block_league.HUD_weapons_update(arena, p_name, w_name, false)
end



function block_league.calc_dmg_dealt_and_increase_points(arena, p_name, w_name, dmg)
  local p_data = arena.players[p_name]
  local prev_dmg_dealt_total = p_data.dmg_dealt.total
  local prev_dmg_dealt = p_data.dmg_dealt[w_name].hp
  local dmg_dealt = prev_dmg_dealt + dmg
  local dmg_points = math.floor(dmg_dealt/50) - math.floor(prev_dmg_dealt/50)

  if dmg_points > 0 then
    p_data.points = p_data.points + dmg_points
    block_league.HUD_infopanel_update_points(arena, p_data.teamID)
    block_league.HUD_spectate_update(arena, p_name, "points")
  end

  p_data.dmg_dealt.total = prev_dmg_dealt_total + dmg
  p_data.dmg_dealt[w_name].hp = dmg_dealt
end





----------------------------------------------
---------------FUNZIONI LOCALI----------------
----------------------------------------------

function show_damage(player, dmg, target_hp, pos, is_critical)
  if target_hp - dmg < 0 then
    dmg = target_hp
  end

  local col = is_critical and 0xff8672 or 0xFFFFFF
  local _, decimal = math.modf(dmg)

  dmg = decimal == 0 and dmg or string.format("%.1f", dmg)

  local pos_hud = player:hud_add({
    type      = "waypoint",
    name      = dmg,
    number    = col,
    precision = 0,
    world_pos = pos,
    offset    = {x=20, y=5}
  })

  core.after(1, function()
    if not core.get_player_by_name(player:get_player_name()) then return end
    player:hud_remove(pos_hud)
  end)
end