module GameData
  class Evolution
    attr_reader :id
    attr_reader :real_name
    attr_reader :parameter
    attr_reader :minimum_level   # 0 means parameter is the minimum level
    attr_reader :level_up_proc
    attr_reader :level_up_proc_first
    attr_reader :use_item_proc
    attr_reader :on_trade_proc
    attr_reader :after_battle_proc
    attr_reader :event_proc
    attr_reader :after_evolution_proc
    #DATA = {}
    extend ClassMethodsSymbols
    include InstanceMethods
    def self.load; end
    def self.save; end
    def initialize(hash)
      @id                   = hash[:id]
      @real_name            = hash[:id].to_s       || "Unnamed"
      @parameter            = hash[:parameter]
      @minimum_level        = hash[:minimum_level] || 0
      @level_up_proc        = hash[:level_up_proc]
      @level_up_proc_first    = hash[:level_up_proc_first]
      @use_item_proc        = hash[:use_item_proc]
      @on_trade_proc        = hash[:on_trade_proc]
      @after_battle_proc    = hash[:after_battle_proc]
      @event_proc           = hash[:event_proc]
      @after_evolution_proc = hash[:after_evolution_proc]
    end
   
    def call_level_up(*args)
      return (@level_up_proc) ? @level_up_proc.call(*args) : nil
    end
   
    def call_level_up_firsttime(*args)
      return (@level_up_proc_first) ? @level_up_proc_first.call(*args) : nil
    end
    def call_use_item(*args)
      return (@use_item_proc) ? @use_item_proc.call(*args) : nil
    end
    def call_on_trade(*args)
      return (@on_trade_proc) ? @on_trade_proc.call(*args) : nil
    end
    def call_after_battle(*args)
      return (@after_battle_proc) ? @after_battle_proc.call(*args) : nil
    end
    def call_event(*args)
      return (@event_proc) ? @event_proc.call(*args) : nil
    end
    def call_after_evolution(*args)
      @after_evolution_proc&.call(*args)
    end
   
   
   
  end
end
GameData::Evolution.register({
  :id            => :Level,
  :parameter     => Integer,
  :level_up_proc_first => proc { |pkmn, parameter|
    next pkmn.level == parameter
  },
  :level_up_proc => proc { |pkmn, parameter|
    next pkmn.level >= parameter
  }
})
GameData::Evolution.register({
  :id            => :LevelMale,
  :parameter     => Integer,
  :level_up_proc => proc { |pkmn, parameter|
    next pkmn.level >= parameter && pkmn.male?
  },
  :level_up_proc_first => proc { |pkmn, parameter|
    next pkmn.level == parameter && pkmn.male?
  }
})
GameData::Evolution.register({
  :id            => :LevelFemale,
  :parameter     => Integer,
  :level_up_proc => proc { |pkmn, parameter|
    next pkmn.level >= parameter && pkmn.female?
  },
  :level_up_proc_first => proc { |pkmn, parameter|
    next pkmn.level == parameter && pkmn.female?
  }
})
GameData::Evolution.register({
  :id            => :LevelDay,
  :parameter     => Integer,
  :level_up_proc => proc { |pkmn, parameter|
    next pkmn.level >= parameter && PBDayNight.isDay?
  },
  :level_up_proc_first => proc { |pkmn, parameter|
    next pkmn.level == parameter && PBDayNight.isDay?
  }
})
GameData::Evolution.register({
  :id            => :LevelNight,
  :parameter     => Integer,
  :level_up_proc => proc { |pkmn, parameter|
    next pkmn.level >= parameter && PBDayNight.isNight?
  },
  :level_up_proc_first => proc { |pkmn, parameter|
    next pkmn.level == parameter && PBDayNight.isNight?
  }
})
GameData::Evolution.register({
  :id            => :LevelMorning,
  :parameter     => Integer,
  :level_up_proc => proc { |pkmn, parameter|
    next pkmn.level >= parameter && PBDayNight.isMorning?
  },
  :level_up_proc_first => proc { |pkmn, parameter|
    next pkmn.level == parameter && PBDayNight.isMorning?
  }
})
GameData::Evolution.register({
  :id            => :LevelAfternoon,
  :parameter     => Integer,
  :level_up_proc => proc { |pkmn, parameter|
    next pkmn.level >= parameter && PBDayNight.isAfternoon?
  },
  :level_up_proc_first => proc { |pkmn, parameter|
    next pkmn.level == parameter && PBDayNight.isAfternoon?
  }
})
GameData::Evolution.register({
  :id            => :LevelEvening,
  :parameter     => Integer,
  :level_up_proc => proc { |pkmn, parameter|
    next pkmn.level >= parameter && PBDayNight.isEvening?
  },
  :level_up_proc_first => proc { |pkmn, parameter|
    next pkmn.level == parameter && PBDayNight.isEvening?
  }
})
GameData::Evolution.register({
  :id            => :LevelNoWeather,
  :parameter     => Integer,
  :level_up_proc => proc { |pkmn, parameter|
    next pkmn.level >= parameter && $game_screen && $game_screen.weather_type == :None
  },
  :level_up_proc_first => proc { |pkmn, parameter|
    next pkmn.level == parameter && $game_screen && $game_screen.weather_type == :None
  }
})
GameData::Evolution.register({
  :id            => :LevelSun,
  :parameter     => Integer,
  :level_up_proc => proc { |pkmn, parameter|
    next pkmn.level >= parameter && $game_screen &&
         GameData::Weather.get($game_screen.weather_type).category == :Sun
  },
  :level_up_proc_first => proc { |pkmn, parameter|
    next pkmn.level == parameter && $game_screen &&
         GameData::Weather.get($game_screen.weather_type).category == :Sun
  }
})
GameData::Evolution.register({
  :id            => :LevelRain,
  :parameter     => Integer,
  :level_up_proc => proc { |pkmn, parameter|
    next pkmn.level >= parameter && $game_screen &&
         [:Rain, :Fog].include?(GameData::Weather.get($game_screen.weather_type).category)
  },
  :level_up_proc_first => proc { |pkmn, parameter|
    next pkmn.level == parameter && $game_screen &&
         [:Rain, :Fog].include?(GameData::Weather.get($game_screen.weather_type).category)
  }
})
GameData::Evolution.register({
  :id            => :LevelSnow,
  :parameter     => Integer,
  :level_up_proc => proc { |pkmn, parameter|
    next pkmn.level >= parameter && $game_screen &&
         GameData::Weather.get($game_screen.weather_type).category == :Hail
  },
  :level_up_proc_first => proc { |pkmn, parameter|
    next pkmn.level == parameter && $game_screen &&
         GameData::Weather.get($game_screen.weather_type).category == :Hail
  }
})
GameData::Evolution.register({
  :id            => :LevelSandstorm,
  :parameter     => Integer,
  :level_up_proc => proc { |pkmn, parameter|
    next pkmn.level >= parameter && $game_screen &&
         GameData::Weather.get($game_screen.weather_type).category == :Sandstorm
  },
  :level_up_proc_first => proc { |pkmn, parameter|
    next pkmn.level == parameter && $game_screen &&
         GameData::Weather.get($game_screen.weather_type).category == :Sandstorm
  }
})
GameData::Evolution.register({
  :id            => :LevelCycling,
  :parameter     => Integer,
  :level_up_proc => proc { |pkmn, parameter|
    next pkmn.level >= parameter && $PokemonGlobal && $PokemonGlobal.bicycle
  },
  :level_up_proc_first => proc { |pkmn, parameter|
    next pkmn.level == parameter && $PokemonGlobal && $PokemonGlobal.bicycle
  }
})
GameData::Evolution.register({
  :id            => :LevelSurfing,
  :parameter     => Integer,
  :level_up_proc => proc { |pkmn, parameter|
    next pkmn.level >= parameter && $PokemonGlobal && $PokemonGlobal.surfing
  },
  :level_up_proc_first => proc { |pkmn, parameter|
    next pkmn.level == parameter && $PokemonGlobal && $PokemonGlobal.surfing
  }
})
GameData::Evolution.register({
  :id            => :LevelDiving,
  :parameter     => Integer,
  :level_up_proc => proc { |pkmn, parameter|
    next pkmn.level >= parameter && $PokemonGlobal && $PokemonGlobal.diving
  },
  :level_up_proc_first => proc { |pkmn, parameter|
    next pkmn.level == parameter && $PokemonGlobal && $PokemonGlobal.diving
  }
})
GameData::Evolution.register({
  :id            => :LevelDarkness,
  :parameter     => Integer,
  :level_up_proc => proc { |pkmn, parameter|
    next pkmn.level >= parameter && $game_map.metadata&.dark_map
  },
  :level_up_proc_first => proc { |pkmn, parameter|
    next pkmn.level == parameter && $game_map.metadata&.dark_map
  }
})
GameData::Evolution.register({
  :id            => :LevelDarkInParty,
  :parameter     => Integer,
  :level_up_proc => proc { |pkmn, parameter|
    next pkmn.level >= parameter && $player.has_pokemon_of_type?(:DARK)
  },
  :level_up_proc_first => proc { |pkmn, parameter|
    next pkmn.level == parameter && $player.has_pokemon_of_type?(:DARK)
  }
})
#===============================================================================
# Instances of this class are individual Pokémon.
# The player's party Pokémon are stored in the array $player.party.
#===============================================================================
class Pokemon
 
  #=============================================================================
  # Evolution checks
  #=============================================================================
  # Checks whether this Pokemon can evolve because of levelling up.
  # @return [Symbol, nil] the ID of the species to evolve into
  def check_evolution_on_level_up
    return check_evolution_internal { |pkmn, new_species, method, parameter|
      success = GameData::Evolution.get(method).call_level_up_firsttime(pkmn, parameter)
      next (success) ? new_species : nil
    }
  end
 
  def check_evolution_on_level_up_first_time
    return check_evolution_internal { |pkmn, new_species, method, parameter|
      success = GameData::Evolution.get(method).call_level_up_firsttime(pkmn, parameter)
      next (success) ? new_species : nil
    }
  end
  def trigger_event_evolution(number)
    new_species = check_evolution_by_event(number)
    if new_species
      #pbFadeOutInWithMusic {
      #  evo = PokemonEvolutionScene.new
      #  evo.pbStartScreen(self, new_species)
      #  evo.pbEvolution
      #  evo.pbEndScreen
      #}
      #return true
      msgWindow = pbCreateMessageWindow
    pbSEPlay("Pkmn move learnt")
      pbMessageDisplay(msgWindow, _INTL("\\c[1]{1} can now evolve!", speciesName))
      pbDisposeMessageWindow(msgwindow)
    end
    return false
  end
end
#put here by Gardenette - for evolving after battle
def pbEvolutionCheck
  $player.party.each_with_index do |pkmn, i|
    next if !pkmn || pkmn.egg?
    next if pkmn.fainted? && !Settings::CHECK_EVOLUTION_FOR_FAINTED_POKEMON
    # Find an evolution
    new_species = nil
    if new_species.nil? && $game_temp.party_levels_before_battle &&
       $game_temp.party_levels_before_battle[i] &&
       $game_temp.party_levels_before_battle[i] < pkmn.level
      new_species = pkmn.check_evolution_on_level_up
    end
    new_species = pkmn.check_evolution_after_battle(i) if new_species.nil?
    next if new_species.nil?
    # Evolve Pokémon if possible
    #evo = PokemonEvolutionScene.new
    #evo.pbStartScreen(pkmn, new_species)
    #evo.pbEvolution
    #evo.pbEndScreen
    pbSEPlay("Pkmn move learnt")
    pbMessage(_INTL("\\c[1]{1} can now evolve!", pkmn.name))
  end
end
#put here by Gardenette
def pbChangeExp(pkmn, new_exp, scene)
  new_exp = new_exp.clamp(0, pkmn.growth_rate.maximum_exp)
  if pkmn.exp == new_exp
    if scene.is_a?(PokemonPartyScreen)
      scene.pbDisplay(_INTL("{1}'s Exp. Points remained unchanged.", pkmn.name))
    else
      pbMessage(_INTL("{1}'s Exp. Points remained unchanged.", pkmn.name))
    end
    return
  end
  old_level           = pkmn.level
  old_total_hp        = pkmn.totalhp
  old_attack          = pkmn.attack
  old_defense         = pkmn.defense
  old_special_attack  = pkmn.spatk
  old_special_defense = pkmn.spdef
  old_speed           = pkmn.speed
  if pkmn.exp > new_exp   # Loses Exp
    difference = pkmn.exp - new_exp
    if scene.is_a?(PokemonPartyScreen)
      scene.pbDisplay(_INTL("{1} lost {2} Exp. Points!", pkmn.name, difference))
    else
      pbMessage(_INTL("{1} lost {2} Exp. Points!", pkmn.name, difference))
    end
    pkmn.exp = new_exp
    pkmn.calc_stats
    scene.pbRefresh
    return if pkmn.level == old_level
    # Level changed
    if scene.is_a?(PokemonPartyScreen)
      scene.pbDisplay(_INTL("{1} dropped to Lv. {2}!", pkmn.name, pkmn.level))
    else
      pbMessage(_INTL("{1} dropped to Lv. {2}!", pkmn.name, pkmn.level))
    end
    total_hp_diff        = pkmn.totalhp - old_total_hp
    attack_diff          = pkmn.attack - old_attack
    defense_diff         = pkmn.defense - old_defense
    special_attack_diff  = pkmn.spatk - old_special_attack
    special_defense_diff = pkmn.spdef - old_special_defense
    speed_diff           = pkmn.speed - old_speed
    pbTopRightWindow(_INTL("Max. HP<r>{1}\r\nAttack<r>{2}\r\nDefense<r>{3}\r\nSp. Atk<r>{4}\r\nSp. Def<r>{5}\r\nSpeed<r>{6}",
                           total_hp_diff, attack_diff, defense_diff, special_attack_diff, special_defense_diff, speed_diff), scene)
    pbTopRightWindow(_INTL("Max. HP<r>{1}\r\nAttack<r>{2}\r\nDefense<r>{3}\r\nSp. Atk<r>{4}\r\nSp. Def<r>{5}\r\nSpeed<r>{6}",
                           pkmn.totalhp, pkmn.attack, pkmn.defense, pkmn.spatk, pkmn.spdef, pkmn.speed), scene)
  else   # Gains Exp
    difference = new_exp - pkmn.exp
    if scene.is_a?(PokemonPartyScreen)
      scene.pbDisplay(_INTL("{1} gained {2} Exp. Points!", pkmn.name, difference))
    else
      pbMessage(_INTL("{1} gained {2} Exp. Points!", pkmn.name, difference))
    end
    pkmn.exp = new_exp
    pkmn.changeHappiness("vitamin")
    pkmn.calc_stats
    scene.pbRefresh
    return if pkmn.level == old_level
    # Level changed
    if scene.is_a?(PokemonPartyScreen)
      scene.pbDisplay(_INTL("{1} grew to Lv. {2}!", pkmn.name, pkmn.level))
    else
      pbMessage(_INTL("{1} grew to Lv. {2}!", pkmn.name, pkmn.level))
    end
    total_hp_diff        = pkmn.totalhp - old_total_hp
    attack_diff          = pkmn.attack - old_attack
    defense_diff         = pkmn.defense - old_defense
    special_attack_diff  = pkmn.spatk - old_special_attack
    special_defense_diff = pkmn.spdef - old_special_defense
    speed_diff           = pkmn.speed - old_speed
    pbTopRightWindow(_INTL("Max. HP<r>+{1}\r\nAttack<r>+{2}\r\nDefense<r>+{3}\r\nSp. Atk<r>+{4}\r\nSp. Def<r>+{5}\r\nSpeed<r>+{6}",
                           total_hp_diff, attack_diff, defense_diff, special_attack_diff, special_defense_diff, speed_diff), scene)
    pbTopRightWindow(_INTL("Max. HP<r>{1}\r\nAttack<r>{2}\r\nDefense<r>{3}\r\nSp. Atk<r>{4}\r\nSp. Def<r>{5}\r\nSpeed<r>{6}",
                           pkmn.totalhp, pkmn.attack, pkmn.defense, pkmn.spatk, pkmn.spdef, pkmn.speed), scene)
    # Learn new moves upon level up
    movelist = pkmn.getMoveList
    movelist.each do |i|
      next if i[0] <= old_level || i[0] > pkmn.level
      pbLearnMove(pkmn, i[1], true) { scene.pbUpdate }
    end
    # Check for evolution
    new_species = pkmn.check_evolution_on_level_up
    if new_species
      #pbFadeOutInWithMusic {
      #  evo = PokemonEvolutionScene.new
      #  evo.pbStartScreen(pkmn, new_species)
      #  evo.pbEvolution
      #  evo.pbEndScreen
      #  scene.pbRefresh if scene.is_a?(PokemonPartyScreen)
      #}
      pbSEPlay("Pkmn move learnt")
      pbMessage(_INTL("\\c[1]{1} can now evolve!", pkmn.name))
    end
  end
end
#put here by Gardenette - for evolving after trade
class PokemonTrade_Scene
  def pbEndScreen(need_fade_out = true)
    pbDisposeMessageWindow(@sprites["msgwindow"]) if @sprites["msgwindow"]
    pbFadeOutAndHide(@sprites) if need_fade_out
    pbDisposeSpriteHash(@sprites)
    @viewport.dispose
    newspecies = @pokemon2.check_evolution_on_trade(@pokemon)
    if newspecies
      #evo = PokemonEvolutionScene.new
      #evo.pbStartScreen(@pokemon2, newspecies)
      #evo.pbEvolution(false)
      #evo.pbEndScreen
      pbSEPlay("Pkmn move learnt")
      pbMessage(_INTL("\\c[1]{1} can now evolve!", @pokemon2.name))
    end
  end
end
ItemHandlers::UseOnPokemon.add(:RARECANDY, proc { |item, qty, pkmn, scene|
  if pkmn.shadowPokemon?
    scene.pbDisplay(_INTL("It won't have any effect."))
    next false
  end
  if pkmn.level >= GameData::GrowthRate.max_level
    new_species = pkmn.check_evolution_on_level_up_first_time
    if !Settings::RARE_CANDY_USABLE_AT_MAX_LEVEL || !new_species
      scene.pbDisplay(_INTL("It won't have any effect."))
      next false
    end
    # Check for evolution
    pbSEPlay("Pkmn move learnt")
    pbMessage(_INTL("\\c[1]{1} can now evolve!", pkmn.name))
    next true
  end
  # Level up
  pbChangeLevelNoAutoEvolve(pkmn, pkmn.level + qty, scene)
  scene.pbHardRefresh
  next true
})
#renamed by Gardenette so it does not conflict with Misc Bug Fixes from Hotfixes
def pbChangeLevelNoAutoEvolve(pkmn, new_level, scene)
  new_level = new_level.clamp(1, GameData::GrowthRate.max_level)
  if pkmn.level == new_level
    if scene.is_a?(PokemonPartyScreen)
      scene.pbDisplay(_INTL("{1}'s level remained unchanged.", pkmn.name))
    else
      pbMessage(_INTL("{1}'s level remained unchanged.", pkmn.name))
    end
    return
  end
  old_level           = pkmn.level
  old_total_hp        = pkmn.totalhp
  old_attack          = pkmn.attack
  old_defense         = pkmn.defense
  old_special_attack  = pkmn.spatk
  old_special_defense = pkmn.spdef
  old_speed           = pkmn.speed
  pkmn.level = new_level
  pkmn.calc_stats
  scene.pbRefresh
  if old_level > new_level
    if scene.is_a?(PokemonPartyScreen)
      scene.pbDisplay(_INTL("{1} dropped to Lv. {2}!", pkmn.name, pkmn.level))
    else
      pbMessage(_INTL("{1} dropped to Lv. {2}!", pkmn.name, pkmn.level))
    end
    total_hp_diff        = pkmn.totalhp - old_total_hp
    attack_diff          = pkmn.attack - old_attack
    defense_diff         = pkmn.defense - old_defense
    special_attack_diff  = pkmn.spatk - old_special_attack
    special_defense_diff = pkmn.spdef - old_special_defense
    speed_diff           = pkmn.speed - old_speed
    pbTopRightWindow(_INTL("Max. HP<r>{1}\r\nAttack<r>{2}\r\nDefense<r>{3}\r\nSp. Atk<r>{4}\r\nSp. Def<r>{5}\r\nSpeed<r>{6}",
                           total_hp_diff, attack_diff, defense_diff, special_attack_diff, special_defense_diff, speed_diff), scene)
    pbTopRightWindow(_INTL("Max. HP<r>{1}\r\nAttack<r>{2}\r\nDefense<r>{3}\r\nSp. Atk<r>{4}\r\nSp. Def<r>{5}\r\nSpeed<r>{6}",
                           pkmn.totalhp, pkmn.attack, pkmn.defense, pkmn.spatk, pkmn.spdef, pkmn.speed), scene)
  else
    pkmn.changeHappiness("vitamin")
    if scene.is_a?(PokemonPartyScreen)
      scene.pbDisplay(_INTL("{1} grew to Lv. {2}!", pkmn.name, pkmn.level))
    else
      pbMessage(_INTL("{1} grew to Lv. {2}!", pkmn.name, pkmn.level))
    end
    total_hp_diff        = pkmn.totalhp - old_total_hp
    attack_diff          = pkmn.attack - old_attack
    defense_diff         = pkmn.defense - old_defense
    special_attack_diff  = pkmn.spatk - old_special_attack
    special_defense_diff = pkmn.spdef - old_special_defense
    speed_diff           = pkmn.speed - old_speed
    pbTopRightWindow(_INTL("Max. HP<r>+{1}\r\nAttack<r>+{2}\r\nDefense<r>+{3}\r\nSp. Atk<r>+{4}\r\nSp. Def<r>+{5}\r\nSpeed<r>+{6}",
                           total_hp_diff, attack_diff, defense_diff, special_attack_diff, special_defense_diff, speed_diff), scene)
    pbTopRightWindow(_INTL("Max. HP<r>{1}\r\nAttack<r>{2}\r\nDefense<r>{3}\r\nSp. Atk<r>{4}\r\nSp. Def<r>{5}\r\nSpeed<r>{6}",
                           pkmn.totalhp, pkmn.attack, pkmn.defense, pkmn.spatk, pkmn.spdef, pkmn.speed), scene)
    # Learn new moves upon level up
    movelist = pkmn.getMoveList
    movelist.each do |i|
      next if i[0] <= old_level || i[0] > pkmn.level
      pbLearnMove(pkmn, i[1], true) { scene.pbUpdate }
    end
    # Check for evolution
    new_species = pkmn.check_evolution_on_level_up_first_time
    if new_species
      #msgWindow = pbCreateMessageWindow
    pbSEPlay("Pkmn move learnt")
      pbMessage(_INTL("\\c[1]{1} can now evolve!", pkmn.name))
      #pbDisposeMessageWindow(msgwindow)
      scene.pbRefresh if scene.is_a?(PokemonPartyScreen)
    end
  end
end