class Battle::Battler
  #=============================================================================
  # Get move's user
  #=============================================================================
  def pbFindUser(_choice, _move)
    return self
  end

  def pbChangeUser(choice, move, user)
    # Snatch
    move.snatched = false
    if move.statusMove? && move.canSnatch?
      newUser = nil
      strength = 100
      @battle.allBattlers.each do |b|
        next if b.effects[PBEffects::Snatch] == 0 ||
                b.effects[PBEffects::Snatch] >= strength
        next if b.effects[PBEffects::SkyDrop] >= 0
        newUser = b
        strength = b.effects[PBEffects::Snatch]
      end
      if newUser
        user = newUser
        user.effects[PBEffects::Snatch] = 0
        move.snatched = true
        @battle.moldBreaker = user.hasMoldBreaker?
        choice[3] = -1   # Clear pre-chosen target
      end
    end
    return user
  end

  #=============================================================================
  # Get move's default target(s)
  #=============================================================================
  def pbFindTargets(choice, move, user)
    preTarget = choice[3]   # A target that was already chosen
    targets = []
    # Get list of targets
    case move.pbTarget(user).id   # Curse can change its target type
    when :NearAlly
      targetBattler = (preTarget >= 0) ? @battle.battlers[preTarget] : nil
      if !pbAddTarget(targets, user, targetBattler, move)
        pbAddTargetRandomAlly(targets, user, move)
      end
    when :UserOrNearAlly
      targetBattler = (preTarget >= 0) ? @battle.battlers[preTarget] : nil
      if !pbAddTarget(targets, user, targetBattler, move, true, true)
        pbAddTarget(targets, user, user, move, true, true)
      end
    when :AllAllies
      @battle.allSameSideBattlers(user.index).each do |b|
        pbAddTarget(targets, user, b, move, false, true) if b.index != user.index
      end
    when :UserAndAllies
      pbAddTarget(targets, user, user, move, true, true)
      @battle.allSameSideBattlers(user.index).each { |b| pbAddTarget(targets, user, b, move, false, true) }
    when :NearFoe, :NearOther
      targetBattler = (preTarget >= 0) ? @battle.battlers[preTarget] : nil
      if !pbAddTarget(targets, user, targetBattler, move)
        if preTarget >= 0 && !user.opposes?(preTarget)
          pbAddTargetRandomAlly(targets, user, move)
        else
          pbAddTargetRandomFoe(targets, user, move)
        end
      end
    when :RandomNearFoe
      pbAddTargetRandomFoe(targets, user, move)
    when :AllNearFoes
      @battle.allOtherSideBattlers(user.index).each { |b| pbAddTarget(targets, user, b, move) }
    when :Foe, :Other
      targetBattler = (preTarget >= 0) ? @battle.battlers[preTarget] : nil
      if !pbAddTarget(targets, user, targetBattler, move, false)
        if preTarget >= 0 && !user.opposes?(preTarget)
          pbAddTargetRandomAlly(targets, user, move, false)
        else
          pbAddTargetRandomFoe(targets, user, move, false)
        end
      end
    when :AllFoes
      @battle.allOtherSideBattlers(user.index).each { |b| pbAddTarget(targets, user, b, move, false) }
    when :AllNearOthers
      @battle.allBattlers.each { |b| pbAddTarget(targets, user, b, move) }
    when :AllBattlers
      @battle.allBattlers.each { |b| pbAddTarget(targets, user, b, move, false, true) }
    else
      # Used by Counter/Mirror Coat/Metal Burst/Bide
      move.pbAddTarget(targets, user)   # Move-specific pbAddTarget, not the def below
    end
    return targets
  end

  #=============================================================================
  # Redirect attack to another target
  #=============================================================================
  def pbChangeTargets(move, user, targets)
    target_data = move.pbTarget(user)
    return targets if @battle.switching   # For Pursuit interrupting a switch
    return targets if move.cannotRedirect? || move.targetsPosition?
    return targets if !target_data.can_target_one_foe? || targets.length != 1
    move.pbModifyTargets(targets, user)   # For Dragon Darts
    return targets if user.hasActiveAbility?([:PROPELLERTAIL, :STALWART])
    priority = @battle.pbPriority(true)
    nearOnly = !target_data.can_choose_distant_target?
    # Spotlight (takes priority over Follow Me/Rage Powder/Lightning Rod/Storm Drain)
    newTarget = nil
    strength = 100   # Lower strength takes priority
    priority.each do |b|
      next if b.fainted? || b.effects[PBEffects::SkyDrop] >= 0
      next if b.effects[PBEffects::Spotlight] == 0 ||
              b.effects[PBEffects::Spotlight] >= strength
      next if !b.opposes?(user)
      next if nearOnly && !b.near?(user)
      newTarget = b
      strength = b.effects[PBEffects::Spotlight]
    end
    if newTarget
      PBDebug.log("[Move target changed] #{newTarget.pbThis}'s Spotlight made it the target")
      targets = []
      pbAddTarget(targets, user, newTarget, move, nearOnly)
      return targets
    end
    # Follow Me/Rage Powder (takes priority over Lightning Rod/Storm Drain)
    newTarget = nil
    strength = 100   # Lower strength takes priority
    priority.each do |b|
      next if b.fainted? || b.effects[PBEffects::SkyDrop] >= 0
      next if b.effects[PBEffects::RagePowder] && !user.affectedByPowder?
      next if b.effects[PBEffects::FollowMe] == 0 ||
              b.effects[PBEffects::FollowMe] >= strength
      next if !b.opposes?(user)
      next if nearOnly && !b.near?(user)
      newTarget = b
      strength = b.effects[PBEffects::FollowMe]
    end
    if newTarget
      PBDebug.log("[Move target changed] #{newTarget.pbThis}'s Follow Me/Rage Powder made it the target")
      targets = []
      pbAddTarget(targets, user, newTarget, move, nearOnly)
      return targets
    end
    # Lightning Rod
    targets = pbChangeTargetByAbility(:LIGHTNINGROD, :ELECTRIC, move, user, targets, priority, nearOnly)
    # Storm Drain
    targets = pbChangeTargetByAbility(:STORMDRAIN, :WATER, move, user, targets, priority, nearOnly)
    return targets
  end

  def pbChangeTargetByAbility(drawingAbility, drawnType, move, user, targets, priority, nearOnly)
    return targets if move.calcType != drawnType
    return targets if targets[0].hasActiveAbility?(drawingAbility)
    priority.each do |b|
      next if b.index == user.index || b.index == targets[0].index
      next if !b.hasActiveAbility?(drawingAbility)
      next if nearOnly && !b.near?(user)
      @battle.pbShowAbilitySplash(b)
      targets.clear
      pbAddTarget(targets, user, b, move, nearOnly)
      if Battle::Scene::USE_ABILITY_SPLASH
        @battle.pbDisplay(_INTL("{1} took the attack!", b.pbThis))
      else
        @battle.pbDisplay(_INTL("{1} took the attack with its {2}!", b.pbThis, b.abilityName))
      end
      @battle.pbHideAbilitySplash(b)
      break
    end
    return targets
  end

  #=============================================================================
  # Register target
  #=============================================================================
  def pbAddTarget(targets, user, target, move, nearOnly = true, allowUser = false)
    return false if !target || (target.fainted? && !move.targetsPosition?)
    return false if !allowUser && target == user
    return false if nearOnly && !user.near?(target) && target != user
    targets.each { |b| return true if b.index == target.index }   # Already added
    targets.push(target)
    return true
  end

  def pbAddTargetRandomAlly(targets, user, move, nearOnly = true)
    choices = []
    user.allAllies.each do |b|
      next if nearOnly && !user.near?(b)
      pbAddTarget(choices, user, b, move, nearOnly)
    end
    if choices.length > 0
      pbAddTarget(targets, user, choices[@battle.pbRandom(choices.length)], move, nearOnly)
    end
  end

  def pbAddTargetRandomFoe(targets, user, move, nearOnly = true)
    choices = []
    user.allOpposing.each do |b|
      next if nearOnly && !user.near?(b)
      pbAddTarget(choices, user, b, move, nearOnly)
    end
    if choices.length > 0
      pbAddTarget(targets, user, choices[@battle.pbRandom(choices.length)], move, nearOnly)
    end
  end
end
