#===============================================================================
#
#===============================================================================
class PokemonGlobalMetadata
  attr_accessor :pokeradarBattery
end

#===============================================================================
#
#===============================================================================
class Game_Temp
  attr_accessor :poke_radar_data   # [species, level, chain count, grasses (x,y,ring,rarity)]
end

#===============================================================================
# Using the Poke Radar
#===============================================================================
def pbCanUsePokeRadar?
  # Can't use Radar if not in tall grass
  terrain = $game_map.terrain_tag($game_player.x, $game_player.y)
  if !terrain.land_wild_encounters || !terrain.shows_grass_rustle
    pbMessage(_INTL("Can't use that here."))
    return false
  end
  # Can't use Radar if map has no grass-based encounters (ignoring Bug Contest)
  if !$PokemonEncounters.has_normal_land_encounters?
    pbMessage(_INTL("Can't use that here."))
    return false
  end
  # Can't use Radar while cycling
  if $PokemonGlobal.bicycle
    pbMessage(_INTL("Can't use that while on a bicycle."))
    return false
  end
  # Debug
  return true if $DEBUG && Input.press?(Input::CTRL)
  # Can't use Radar if it isn't fully charged
  if $PokemonGlobal.pokeradarBattery && $PokemonGlobal.pokeradarBattery > 0
    pbMessage(_INTL("The battery has run dry!\nFor it to recharge, you need to walk another {1} steps.",
                    $PokemonGlobal.pokeradarBattery))
    return false
  end
  return true
end

def pbUsePokeRadar
  return false if !pbCanUsePokeRadar?
  $stats.poke_radar_count += 1
  $game_temp.poke_radar_data = [nil, 0, 0, [], false] if !$game_temp.poke_radar_data
  $game_temp.poke_radar_data[4] = false
  $PokemonGlobal.pokeradarBattery = 50
  pbPokeRadarHighlightGrass
  return true
end

def pbPokeRadarCancel
  $game_temp.poke_radar_data = nil
end

def pbPokeRadarHighlightGrass(showmessage = true)
  grasses = []   # x, y, ring (0-3 inner to outer), rarity
  # Choose 1 random tile from each ring around the player
  4.times do |i|
    r = rand((i + 1) * 8)
    # Get coordinates of randomly chosen tile
    x = $game_player.x
    y = $game_player.y
    if r <= (i + 1) * 2
      x = $game_player.x - i - 1 + r
      y = $game_player.y - i - 1
    elsif r <= ((i + 1) * 6) - 2
      x = [$game_player.x + i + 1, $game_player.x - i - 1][r % 2]
      y = $game_player.y - i + ((r - 1 - ((i + 1) * 2)) / 2).floor
    else
      x = $game_player.x - i + r - ((i + 1) * 6)
      y = $game_player.y + i + 1
    end
    # Add tile to grasses array if it's a valid grass tile
    next if x < 0 || x >= $game_map.width ||
            y < 0 || y >= $game_map.height
    terrain = $game_map.terrain_tag(x, y)
    next if !terrain.land_wild_encounters || !terrain.shows_grass_rustle
    # Choose a rarity for the grass (0=normal, 1=rare, 2=shiny)
    s = (rand(100) < 25) ? 1 : 0
    if $game_temp.poke_radar_data && $game_temp.poke_radar_data[2] > 0
      v = [(65_536 / Settings::SHINY_POKEMON_CHANCE) - ([$game_temp.poke_radar_data[2], 40].min * 200), 200].max
      v = (65_536 / v.to_f).ceil
      s = 2 if rand(65_536) < v
    end
    grasses.push([x, y, i, s])
  end
  if grasses.length == 0
    # No shaking grass found, break the chain
    pbMessage(_INTL("The grassy patch remained quiet...")) if showmessage
    pbPokeRadarCancel
  else
    # Show grass rustling animations
    grasses.each do |grass|
      case grass[3]
      when 0   # Normal rustle
        $scene.spriteset.addUserAnimation(Settings::RUSTLE_NORMAL_ANIMATION_ID, grass[0], grass[1], true, 1)
      when 1   # Vigorous rustle
        $scene.spriteset.addUserAnimation(Settings::RUSTLE_VIGOROUS_ANIMATION_ID, grass[0], grass[1], true, 1)
      when 2   # Shiny rustle
        $scene.spriteset.addUserAnimation(Settings::RUSTLE_SHINY_ANIMATION_ID, grass[0], grass[1], true, 1)
      end
    end
    $game_temp.poke_radar_data[3] = grasses if $game_temp.poke_radar_data
    pbWait(0.5)
  end
end

def pbPokeRadarGetShakingGrass
  return -1 if !$game_temp.poke_radar_data
  grasses = $game_temp.poke_radar_data[3]
  return -1 if grasses.length == 0
  grasses.each do |i|
    return i[2] if $game_player.x == i[0] && $game_player.y == i[1]
  end
  return -1
end

def pbPokeRadarOnShakingGrass
  return pbPokeRadarGetShakingGrass >= 0
end

def pbPokeRadarGetEncounter(rarity = 0)
  # Poké Radar-exclusive encounters can only be found in vigorously-shaking grass
  if rarity > 0
    # Get all Poké Radar-exclusive encounters for this map
    map_id = $game_map.map_id
    enc_list = nil
    encounter_data = GameData::Encounter.get(map_id, $PokemonGlobal.encounter_version)
    if encounter_data && encounter_data.types[:PokeRadar] &&
       rand(100) < encounter_data.step_chances[:PokeRadar]
      enc_list = encounter_data.types[:PokeRadar]
    end
    # If there are any exclusives, first have a chance of encountering those
    if enc_list && enc_list.length > 0
      chance_total = 0
      enc_list.each { |a| chance_total += a[0] }
      rnd = rand(chance_total)
      encounter = nil
      enc_list.each do |enc|
        rnd -= enc[0]
        next if rnd >= 0
        encounter = enc
        break
      end
      level = rand(encounter[2]..encounter[3])
      return [encounter[1], level]
    end
  end
  # Didn't choose a Poké Radar-exclusive species, choose a regular encounter instead
  return $PokemonEncounters.choose_wild_pokemon($PokemonEncounters.encounter_type, rarity + 1)
end

#===============================================================================
# Event handlers
#===============================================================================
EventHandlers.add(:on_wild_species_chosen, :poke_radar_chain,
  proc { |encounter|
    if GameData::EncounterType.get($game_temp.encounter_type).type != :land ||
       $PokemonGlobal.bicycle || $PokemonGlobal.partner
      pbPokeRadarCancel
      next
    end
    ring = pbPokeRadarGetShakingGrass
    if ring >= 0   # Encounter triggered by stepping into rustling grass
      # Get rarity of shaking grass
      rarity = 0   # 0 = rustle, 1 = vigorous rustle, 2 = shiny rustle
      $game_temp.poke_radar_data[3].each { |g| rarity = g[3] if g[2] == ring }
      if $game_temp.poke_radar_data[2] > 0   # Chain count, i.e. is chaining
        chain_chance = 58 + (ring * 10)
        chain_chance += [$game_temp.poke_radar_data[2], 40].min / 4   # Chain length
        chain_chance += 10 if $game_temp.poke_radar_data[4]   # Previous in chain was caught
        if rarity == 2 || rand(100) < chain_chance
          # Continue the chain
          encounter[0] = $game_temp.poke_radar_data[0]   # Species
          encounter[1] = $game_temp.poke_radar_data[1]   # Level
          $game_temp.force_single_battle = true
        else
          # Break the chain, force an encounter with a different species
          100.times do
            break if encounter && encounter[0] != $game_temp.poke_radar_data[0]
            new_encounter = $PokemonEncounters.choose_wild_pokemon($PokemonEncounters.encounter_type)
            encounter[0] = new_encounter[0]
            encounter[1] = new_encounter[1]
          end
          if encounter[0] == $game_temp.poke_radar_data[0] && encounter[1] == $game_temp.poke_radar_data[1]
            # Chain couldn't be broken somehow; continue it after all
            $game_temp.force_single_battle = true
          else
            pbPokeRadarCancel
          end
        end
      else   # Not chaining; will start one
        # Force random wild encounter, vigorous shaking means rarer species
        new_encounter = pbPokeRadarGetEncounter(rarity)
        encounter[0] = new_encounter[0]
        encounter[1] = new_encounter[1]
        $game_temp.force_single_battle = true
      end
    elsif encounter   # Encounter triggered by stepping in non-rustling grass
      pbPokeRadarCancel
    end
  }
)

EventHandlers.add(:on_wild_pokemon_created, :poke_radar_shiny,
  proc { |pkmn|
    next if !$game_temp.poke_radar_data
    grasses = $game_temp.poke_radar_data[3]
    next if !grasses
    grasses.each do |grass|
      next if $game_player.x != grass[0] || $game_player.y != grass[1]
      pkmn.shiny = true if grass[3] == 2
      break
    end
  }
)

EventHandlers.add(:on_wild_battle_end, :poke_radar_continue_chain,
  proc { |species, level, decision|
    if $game_temp.poke_radar_data && [1, 4].include?(decision)   # Defeated/caught
      $game_temp.poke_radar_data[0] = species
      $game_temp.poke_radar_data[1] = level
      $game_temp.poke_radar_data[2] += 1
      $stats.poke_radar_longest_chain = [$game_temp.poke_radar_data[2], $stats.poke_radar_longest_chain].max
      # Catching makes the next Radar encounter more likely to continue the chain
      $game_temp.poke_radar_data[4] = (decision == 4)
      pbPokeRadarHighlightGrass(false)
    else
      pbPokeRadarCancel
    end
  }
)

EventHandlers.add(:on_player_step_taken, :poke_radar,
  proc {
    if $PokemonGlobal.pokeradarBattery && $PokemonGlobal.pokeradarBattery > 0 &&
       !$game_temp.poke_radar_data
      $PokemonGlobal.pokeradarBattery -= 1
    end
    terrain = $game_map.terrain_tag($game_player.x, $game_player.y)
    if !terrain.land_wild_encounters || !terrain.shows_grass_rustle
      pbPokeRadarCancel
    end
  }
)

EventHandlers.add(:on_enter_map, :cancel_poke_radar,
  proc { |_old_map_id|
    pbPokeRadarCancel
  }
)

#===============================================================================
# Item handlers
#===============================================================================
ItemHandlers::UseInField.add(:POKERADAR, proc { |item|
  next pbUsePokeRadar
})

ItemHandlers::UseFromBag.add(:POKERADAR, proc { |item|
  next (pbCanUsePokeRadar?) ? 2 : 0
})
