local S = core.get_translator("block_league")

local function cast_entity_ray() end
local function check_for_touchdown() end
local function add_point() end
local function after_point() end



-- entità
local ball = {
  initial_properties = {
    physical = true,
    pointable = false,
    collide_with_objects = false,
    visual = "mesh",
    mesh = "bl_ball.b3d",
    visual_size = {x = 5.0, y = 5.0, z = 5.0},
    collisionbox = {-0.5, -0.5, -0.5, 0.5, 0.8, 0.5},
    use_texture_alpha = true,

    textures = {"bl_ball_unclaimed.png"},
  },

  _p_name = nil,
  _team_id = nil,

  _timer_limit = 10,
  _timer_bool = false,
  _timer = 0,

  _prev_player_blue = {name = "", timestamp = 99999},
  _prev_player_orange = {name = "", timestamp = 99999}
}



function ball:_destroy()
  -- se era attaccata a qualcunə (punto)
  if self._p_name then
    local wielder = core.get_player_by_name(self._p_name)
    local p_meta = wielder:get_meta()

    if p_meta:get_int("wl_is_speed_locked") == 0
       and p_meta:get_int("wl_weapon_state") == 0
       and p_meta:get_int("wl_zooming") == 0 then                   -- non posso controllare wl_slowed_down == 0 perché se ricarico prima di far punto
                                                                    -- mentre non ho stamina, non andrà a 1 dato che can_alter_speed torna false sul reload di WL
      wielder:set_physics_override({speed = block_league.SPEED})
      p_meta:set_int("wl_slowed_down", 0)
    end
  end

  self.object:remove()
end



function ball:get_staticdata()
  if self == nil or self.arena == nil then return end
  return self._p_name
end



function ball:on_activate(staticdata, d_time)
  if staticdata ~= nil then

    local _, arena = arena_lib.get_arena_by_name("block_league", staticdata)

    if arena == nil or not arena.in_game then
      self:_destroy()
      return
    end

    arena_lib.add_spectate_entity("block_league", arena, "Ball", self)

    local ball_obj = self.object

    self._p_name = nil
    self._timer_bool = false
    self._team_id = nil
    self._timer = 0
    self.arena = arena

    ball_obj:set_hp(65535)
    ball_obj:set_animation({x=0,y=40}, 20, 0, true)

    cast_entity_ray(ball_obj)

  else -- se gli staticdata sono nil
    self:_destroy()
    return
  end

end



function ball:on_step(d_time, moveresult)
  local arena = self.arena

  if not arena or not arena.in_game then
    self:_destroy()
    return
  end

  --se nessuno la sta portando a spasso...
  if self._p_name == nil then
    -- se il timer per il reset è attivo, controllo a che punto sta
    if self._timer_bool then
      self._timer = self._timer + d_time
      if self._timer > self._timer_limit then
        self:reset()
      return end
    end

    local pos = self.object:get_pos()
    local objects = core.get_objects_inside_radius(pos, 1.5)

    -- se nel suo raggio trova un giocatore in vita, si attacca
    for _, object in pairs(objects) do
      if object:is_player() and object:get_hp() > 0 and arena.players[object:get_player_name()] then
        self:attach(object)
        return
      end
    end

  -- se ce l'ha qualcuno
  -- NB: se quel qualcuno è appena morto, al posto di controllarlo qui su ogni step, viene controllato sul callback della morte in player_manager.lua
  else
    local p_name = self._p_name
    local wielder = core.get_player_by_name(p_name)

    -- se si è disconnesso
    if not wielder then
      self:detach()
      return
    end

    local p_pos = wielder:get_pos()
    local goal = arena.teams[self._team_id].name == S("orange") and arena.goal_orange or arena.goal_blue

    check_for_touchdown(arena, self, p_name, p_pos, goal)
  end
end



function ball:attach(player)
  local arena = self.arena
  local p_name = player:get_player_name()

  self._p_name = p_name
  self._team_id = arena.players[p_name].teamID

  self:announce_ball_possession_change()
  player:get_meta():set_int("bl_has_ball", 1)

  block_league.remove_immunity(player)
  block_league.stamina_drain(arena, p_name)

  arena.players[p_name].points = arena.players[p_name].points + 2
  block_league.HUD_infopanel_update_points(arena, self._team_id)
  block_league.HUD_spectate_update(arena, p_name, "ball")

  local ball_obj = self.object
  local team_texture = self._team_id == 1 and "bl_ball_orange.png" or "bl_ball_blue.png"

  ball_obj:set_attach(player, "Body", {x=0, y=18, z=0}, {x=0, y=0, z=0})

  self._timer_bool = false
  self._timer = 0

  ball_obj:set_properties({textures={team_texture}})
  ball_obj:set_animation({x=120,y=160}, 20, 0, true)   -- smette di oscillare quando presa

  local ball_spectators = arena_lib.get_target_spectators("block_league", arena.name, "entity", "Ball")

  for sp_name, _ in pairs(ball_spectators) do
    arena_lib.spectate_target("block_league", arena, sp_name, "player", p_name)
  end
end



function ball:detach(skip_announcement)
  local p_name = self._p_name
  local player = core.get_player_by_name(p_name)
  local arena = self.arena

  if not skip_announcement then
    self:announce_ball_possession_change(true)
  end

  if arena.players[p_name] then
    player:get_meta():set_int("bl_has_ball", 0)
    block_league.HUD_spectate_update(arena, p_name, "ball")
  end

  local ball_obj = self.object

  ball_obj:set_detach()

  self._p_name = nil
  self._timer_bool = true
  self._timer = 0

  if self._team_id == 1 then
    self._prev_player_orange = {name = p_name, timestamp = arena.current_time}
  else
    self._prev_player_blue = {name = p_name, timestamp = arena.current_time}
  end

  ball_obj:set_properties({textures={"bl_ball_unclaimed.png"}})
  ball_obj:set_animation({x=0,y=40}, 20, 0, true)
end



function ball:reset()
  local arena = self.arena

  -- annuncio
  arena_lib.sound_play_all(arena, "bl_voice_ball_reset")
  block_league.HUD_ball_update_all(arena, S("Ball reset"))

  -- se è agganciata a qualcunə...
  if self._p_name then
    local wielder = core.get_player_by_name(self._p_name)
    local p_meta = wielder:get_meta()

    -- ripristino per sicurezza wl_slowed_down; in caso di giocante che deve esser
    -- rallentatə, lo rimetto a 1 sotto
    p_meta:set_int("wl_slowed_down", 0) -- TODO: in teoria non è necessario e posso mettere questa riga dento l'elseif

    local curr_weap = weapons_lib.get_action_in_progress_weapon(self._p_name)

    if p_meta:get_int("wl_zooming") == 1
      or (curr_weap and core.registered_nodes[curr_weap.w_name].slow_down_user
        and p_meta:get_int("wl_weapon_state") ~= 0
        and p_meta:get_int("wl_is_speed_locked") == 0) then
      p_meta:set_int("wl_slowed_down", 1)

    elseif wielder:get_hp() > 0 and p_meta:get_int("wl_is_speed_locked") == 0 then
      wielder:set_physics_override({speed = block_league.SPEED})
    end

    self:detach(true)
  end

  self._p_name = nil
  self._team_id = nil
  self._timer_bool = false
  self._timer = 0

  self.object:set_pos(arena.ball_spawn)
end



function ball:announce_ball_possession_change(is_ball_lost)
  local arena = self.arena
  local teamID = self._team_id
  local enemy_teamID = teamID == 1 and 2 or 1

  if is_ball_lost then
    arena_lib.sound_play_team(arena, teamID, "bl_crowd_ohno")
    arena_lib.sound_play_team(arena, enemy_teamID, "bl_crowd_cheer")

    for sp_name, _ in pairs(arena_lib.get_target_spectators("block_league", arena.name, "entity", "Ball")) do
      audio_lib.play_sound("bl_crowd_ohno", {to_player = sp_name})
    end

    block_league.HUD_ball_update_team(arena, teamID, S("Your team has lost the ball!"), "0xff8e8e")
    block_league.HUD_ball_update_team(arena, enemy_teamID, S("Enemy team has lost the ball!"), "0xabf877")

  else
    local p_name = self._p_name
    block_league.HUD_log_update(arena, "bl_log_ball.png", p_name, "")

    arena_lib.sound_play_team(arena, teamID, "bl_crowd_cheer")
    arena_lib.sound_play_team(arena, enemy_teamID, "bl_crowd_ohno")

    for sp_name, _ in pairs(arena_lib.get_target_spectators("block_league", arena.name, "entity", "Ball")) do
      audio_lib.play_sound("bl_crowd_cheer", {to_player = sp_name})
    end

    block_league.HUD_ball_update_team(arena, teamID, S("Your team has got the ball!"), "0xabf877")
    block_league.HUD_ball_update_team(arena, enemy_teamID, S("Enemy team has got the ball!"), "0xff8e8e")

    block_league.HUD_ball_update(p_name, S("You've got the ball!"), "0xabf877")
  end
end



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

function cast_entity_ray(ent)
  core.add_particlespawner({
    attached = ent, -- If defined, particle positions, velocities and accelerations are relative to this object's position and yaw
    amount = 10,
    time = 0,
    minpos = {x=0, y=3, z=0},
    maxpos = {x=0, y=3, z=0},
    minvel = vector.multiply({x= 0, y = 1, z = 0}, 30),
    maxvel = vector.multiply({x= 0, y = 1, z = 0}, 30),
    minsize = 20,
    maxsize = 20,
    vertical = true,
    texture = "bl_ball_ray.png"
  })
end



function check_for_touchdown(arena, ball_lua, p_name, p_pos, goal)
  if math.abs(p_pos.x - goal.x) <= 1.5 and
     math.abs(p_pos.z - goal.z) <= 1.5 and
     p_pos.y >= goal.y - 1 and
     p_pos.y <= goal.y + 3 and
     not arena.in_celebration then
    local wielder = core.get_player_by_name(p_name)

    wielder:get_meta():set_int("bl_has_ball", 0)

    local teamID = arena.players[p_name].teamID
    local assister

    if teamID == 1 then
      if arena.current_time > ball_lua._prev_player_orange.timestamp - 10 and p_name ~= ball_lua._prev_player_orange.name then
        assister = ball_lua._prev_player_orange.name
      end
    else
      if arena.current_time > ball_lua._prev_player_blue.timestamp - 10 and p_name ~= ball_lua._prev_player_blue.name then
        assister = ball_lua._prev_player_blue.name
      end
    end

    block_league.HUD_log_update(arena, "bl_log_TD.png", p_name, "", assister)
    block_league.HUD_spectate_update(arena, p_name, "ball")

    add_point(p_name, teamID, arena, assister)
    after_point(teamID, arena)

    ball_lua:_destroy()
  end

end



function add_point(p_name, teamID, arena, assister)
  local enemy_teamID = teamID == 1 and 2 or 1

  arena_lib.sound_play_team(arena, teamID, "bl_crowd_cheer")
  arena_lib.sound_play_team(arena, enemy_teamID, "bl_crowd_ohno")

  for sp_name, _ in pairs(arena_lib.get_target_spectators("block_league", arena.name, "entity", "Ball")) do
    audio_lib.play_sound("bl_crowd_cheer", {to_player = sp_name})
  end

  block_league.HUD_ball_update_team(arena, teamID, S("NICE POINT!"), "0xabf877")
  block_league.HUD_ball_update_team(arena, enemy_teamID, S("ENEMY TEAM HAS SCORED..."), "0xff8e8e")

  local scoring_team_color = teamID == 1 and "0xf2a05b" or "0x55aef1"

  for sp_name, _ in pairs(arena.spectators) do
    block_league.HUD_ball_update(sp_name, "TOUCHDOWN!", scoring_team_color)
  end

  arena.teams[teamID].TDs = arena.teams[teamID].TDs + 1
  arena.players[p_name].TDs = arena.players[p_name].TDs + 1
  arena.players[p_name].points = arena.players[p_name].points + 10

  -- eventuale servizio
  if assister and arena.players[assister] then
    arena.players[assister].points = arena.players[assister].points + 5
  end

  block_league.HUD_scoreboard_update_score(arena)
  block_league.HUD_infopanel_update_points(arena, teamID)
  block_league.HUD_spectate_update(arena, p_name, "TD")
end



function after_point(teamID, arena)
  -- disabilita le armi e interrompi spari vari
  arena.weapons_disabled = true

  -- se rimane troppo poco tempo, aspetta la fine della partita
  if arena.current_time <= 6 then return end

  -- se i TD della squadra raggiungono il tetto, vince
  if arena.teams[teamID].TDs == arena.score_cap then
    arena_lib.load_celebration("block_league", arena, teamID)

  -- sennò inizia un nuovo round
  else
    block_league.countdown_and_start(arena, 3)
  end
end



core.register_entity("block_league:ball", ball)



function block_league.get_ball(player)
  for _, child in pairs (player:get_children()) do
    if child:get_luaentity() and child:get_luaentity()._timer then
      return child:get_luaentity()
    end
  end
end
