{Weather} = require '../../../shared/weather'

# Retcon weather abilities to only last 5 turns.
makeWeatherAbility = (name, weather) ->
  makeAbility name, ->
    this::switchIn = ->
      return  if @battle.hasWeather(weather)
      moveName = switch weather
        when Weather.SUN  then "Sunny Day"
        when Weather.RAIN then "Rain Dance"
        when Weather.SAND then "Sandstorm"
        when Weather.HAIL then "Hail"
        when Weather.MOON then "New Moon"
        else throw new Error("#{weather} ability not supported.")

      @pokemon.activateAbility()
      move = @battle.getMove(moveName)
      move.changeWeather(@battle, @pokemon)

# Import old abilities

coffee = require 'coffee-script'
path = require('path').resolve(__dirname, '../../bw/data/abilities.coffee')
eval(coffee.compile(require('fs').readFileSync(path, 'utf8'), bare: true))

# Retcon old abilities

# Effect Spore now does not affect Grass-type Pokemon,
# Pokemon with Overcoat, or Pokemon holding Safety Goggles
oldEffectSpore = Ability.EffectSpore::afterBeingHit
Ability.EffectSpore::afterBeingHit = (move, user, target, damage) ->
  unless user.hasType("Grass") || user.hasAbility("Overcoat") || user.hasItem("Safety Goggles")
    oldEffectSpore.apply(this, arguments)

# Oblivious now also prevents and cures Taunt
makeAttachmentImmuneAbility("Oblivious", [Attachment.Attract, Attachment.Taunt])

# Overcoat now also prevents powder moves from working.
Ability.Overcoat::shouldBlockExecution = (move, user) ->
    if move.hasFlag("powder")
      @pokemon.activateAbility()
      return true

# New ability interfaces

makeNormalTypeChangeAbility = (name, newType) ->
  makeAbility name, ->
    this::editMoveType = (type, target) ->
      return newType  if type == 'Normal' && @pokemon != target
      return type

    this::modifyBasePower = (move, target) ->
      return 0x14CD  if move.type == 'Normal'
      return 0x1000

makeNormalTypeChangeAbility("Aerilate", "Flying")
makeNormalTypeChangeAbility("Pixilate", "Fairy")
makeNormalTypeChangeAbility("Refrigerate", "Ice")

makeAuraAbility = (name, type) ->
  makeAbility name, ->
    this::modifyBasePower = (move, target) ->
      return 0x1000  if move.getType(@battle, @pokemon, target) != type
      for pokemon in @battle.getActiveAlivePokemon()
        return 0xC00  if pokemon.hasAbility("Aura Break")
      return 0x1547

makeAuraAbility("Dark Aura", "Dark")
makeAuraAbility("Fairy Aura", "Fairy")

# New unique abilities

makeAttachmentImmuneAbility("Aroma Veil", [Attachment.Attract, Attachment.Disable,
  Attachment.Encore, Attachment.Taunt, Attachment.Torment], cure: false) # TODO: Add Heal Block

# Implemented in makeAuraAbility
makeAbility "Aura Break"

makeAbility 'Bulletproof', ->
  this::isImmune = (type, move) ->
    if move?.hasFlag('bullet')
      @pokemon.activateAbility()
      return true

# TODO: Cheek Pouch
makeAbility "Cheek Pouch"

makeAbility "Competitive", ->
  this::afterEachBoost = (boostAmount, source) ->
    return  if source.team == @pokemon.team
    @pokemon.activateAbility()
    @pokemon.boost(specialAttack: 2)  if boostAmount < 0

# TODO: Flower Veil
makeAbility "Flower Veil"

makeAbility "Fur Coat", ->
  this::modifyBasePowerTarget = (move) ->
    if move.isPhysical() then 0x800 else 0x1000

makeAbility 'Gale Wings', ->
  this::editPriority = (priority, move) ->
    # TODO: Test if Gale Wings works with Hidden Power Flying.
    return priority + 1  if move.type == 'Flying'
    return priority

makeAbility "Gooey", ->
  this::isAliveCheck = -> true

  this::afterBeingHit = (move, user) ->
    if move.hasFlag("contact")
      user.boost(speed: -1, @pokemon)
      @pokemon.activateAbility()

# TODO: Grass Pelt
makeAbility "Grass Pelt"

# TODO: Magician
makeAbility "Magician"

makeAbility 'Mega Launcher', ->
  this::modifyBasePower = (move, target) ->
    return 0x1800  if move.hasFlag("pulse")
    return 0x1000

makeAbility 'Parental Bond', ->
  this::calculateNumberOfHits = (move, targets) ->
    # Do nothing if this move is multi-hit, has multiple targets, or is status.
    return  if move.minHits != 1 || targets.length > 1 || move.isNonDamaging()
    return 2

  this::modifyDamage = (move, target, hitNumber) ->
    return 0x800  if hitNumber == 2 && move.maxHits == 1
    return 0x1000

makeAbility 'Protean', ->
  this::beforeMove = (move, user, targets) ->
    type = move.getType(@battle, user, targets[0])
    return  if user.types.length == 1 && user.types[0] == type
    user.types = [ type ]
    @pokemon.activateAbility()
    @battle.cannedText('TRANSFORM_TYPE', user, type)

makeAbility 'Stance Change', ->
  this::beforeMove = (move, user, targets) ->
    newForme = switch
      when !move.isNonDamaging() then "blade"
      when move == @battle.getMove("King's Shield") then "default"
    if newForme && !@pokemon.isInForme(newForme) && @pokemon.species == 'Aegislash'
      @pokemon.activateAbility()
      @pokemon.changeForme(newForme)
      humanized = (if newForme == "blade" then "Blade" else "Shield")
      @battle.message("Changed to #{humanized} Forme!")
    true

makeAbility "Strong Jaw", ->
  this::modifyBasePower = (move) ->
    return 0x1800  if move.hasFlag("bite")
    return 0x1000

# TODO: Sweet Veil (2v2)
makeAttachmentImmuneAbility("Sweet Veil", [Status.Sleep], cure: false)

# TODO: Symbiosis
makeAbility "Symbiosis"

makeAbility "Tough Claws", ->
  this::modifyBasePower = (move) ->
    return 0x14CD  if move.hasFlag("contact")
    return 0x1000

makeWeatherAbility("Noctem", Weather.MOON)

makeAbility 'Heliophobia', ->
  this::endTurn = ->
    amount = Math.floor(@pokemon.stat('hp') / 8)
    if @battle.hasWeather(Weather.SUN)
      @pokemon.setHP(@pokemon.currentHP - amount)
    else if @battle.hasWeather(Weather.MOON)
      @pokemon.setHP(@pokemon.currentHP + amount)

makeWeatherSpeedAbility("Shadow Dance", Weather.MOON)

makeAbility "Amplifier", ->
  this::modifyBasePower = (move) ->
    return 0x1800  if move.hasFlag("sound")
    return 0x1000

makeAbility "Athenian", ->
  this::modifyAttack = (move) ->
    if move.isSpecial() then 0x2000 else 0x1000

makeTypeImmuneAbility("Castle Moat", "Water", "specialDefense")

makeAbility 'Ethereal Shroud', ->
  this::typeEffectiveness = (move, user) ->
    movetype = move.type
    usertypes = user.types
    effect = util.typeEffectiveness(movetype, usertypes)
    ghosteffect = util.typeEffectiveness(movetype, ["Ghost"])
    if ghosteffect < 1
      effect *= ghosteffect
    return effect

makeAbility 'Foundry', ->
    this::editMoveType = (type, target) ->
      return "Fire"  if type == 'Rock' && @pokemon != target
      return type

    this::modifyBasePower = (move, target) ->
      return 0x14CD  if move.type == 'Rock'
      return 0x1000

makeAbility 'Hubris', ->
  this::afterSuccessfulHit = (move, user, target) ->
    if target.isFainted()
      @pokemon.activateAbility()
      @pokemon.boost(specialAttack: 1)

makeWeatherSpeedAbility("Ice Cleats", Weather.HAIL)

makeAbility 'Irrelephant', ->
  this::shouldIgnoreImmunity = (moveType, target) ->
    return true


makeAbility 'Pendulum', ->
  this::modifyBasePower = (move, target) ->
    attachment = @pokemon.get(Attachment.Pendulum)
    layers = attachment?.layers || 0
    0x1000 + layers * 0x333

  this::afterSuccessfulHit = (move, user, target) ->
    user.attach(Attachment.Pendulum, {move})

makeAbility 'Prism Guard', -> 
    this::isAliveCheck = -> true

    this::afterBeingHit = (move, user, target, damage, isDirect) ->
      return  unless !move.hasFlag('contact')
      return  unless isDirect
      amount = user.stat('hp') >> 3
      @pokemon.activateAbility()
      if user.damage(amount)
        @battle.cannedText('POKEMON_HURT', user)

# TODO: Protean Maxima
makeAbility "Protean Maxima", ->
  this::beforeMove = (move, user, targets) ->
    type = move.getType(@battle, user, targets[0])
    newForme = switch
      when type == "Normal" then "mega"
      when type == "Water" then "mega-water"
      when type == "Electric" then "mega-electric"
      when type == "Fire" then "mega-fire"
      when type == "Psychic" then "mega-psychic"
      when type == "Dark" then "mega-dark"
      when type == "Grass" then "mega-grass"
      when type == "Ice" then "mega-ice"
      when type == "Fairy" then "mega-fairy"
      else ""
    if newForme != "" && !@pokemon.isInForme(newForme) && @pokemon.species == 'Eevee'
      @pokemon.activateAbility()
      @pokemon.changeForme(newForme)
      @battle.message("Changed to #{type} Forme!")
      if newForme == 'mega-psychic'
        @pokemon.attach(Attachment.MagicCoat)
        @team.attach(Attachment.MagicCoat)
    true
  
  this::shouldBlockExecution = (move, user) ->
    forme = @pokemon.getForme()
    console.log(forme)
    if @pokemon.isInForme("mega-water") && @pokemon.species == 'Eevee'
      return  if move.getType(@battle, user, @pokemon) != "Water" || user == @pokemon
      @pokemon.activateAbility()
      amount = @pokemon.stat('hp') >> 2
      if @pokemon.heal(amount)
        @battle.cannedText('RECOVER_HP', @pokemon)
      return true
    else if @pokemon.isInForme("mega-electric") && @pokemon.species == 'Eevee'
      return  if move.getType(@battle, user, @pokemon) != "Electric" || user == @pokemon
      @pokemon.activateAbility()
      amount = @pokemon.stat('hp') >> 2
      if @pokemon.heal(amount)
        @battle.cannedText('RECOVER_HP', @pokemon)
      return true
    else if @pokemon.isInForme("mega-fire") and @pokemon.species == 'Eevee'
      return  if move.getType(@battle, user, @pokemon) != 'Fire' || user == @pokemon
      if @pokemon.attach(Attachment.FlashFire)
        @pokemon.activateAbility()
        @battle.cannedText('FLASH_FIRE', @pokemon)
      else
        @battle.cannedText('IMMUNITY', @pokemon)
      return true

  this::beginTurn = this::switchIn = ->
    forme = @pokemon.getForme()
    if @pokemon.isInForme("mega-psychic") and @pokemon.species == 'Eevee'
      @pokemon.attach(Attachment.MagicCoat)
      @team.attach(Attachment.MagicCoat)

  this::switchIn = ->
    forme = @pokemon.getForme()
    @doubleSpeed = @battle.hasWeather(Weather.SUN) if @pokemon.isInForme("mega-grass") and @pokemon.species == 'Eevee'

  this::informWeather = (newWeather) ->
    forme = @pokemon.getForme()
    @doubleSpeed = (Weather.SUN == newWeather) if @pokemon.isInForme("mega-grass") and @pokemon.species == 'Eevee'

  this::editSpeed = (speed) ->
    forme = @pokemon.getForme()
    if @doubleSpeed and @pokemon.isInForme("mega-grass") and @pokemon.species == 'Eevee'
      2 * speed 
    else 
      speed

  this::isWeatherDamageImmune = (currentWeather) ->
    forme = @pokemon.getForme()
    return true  if Weather.SUN == currentWeather and @pokemon.isInForme("mega-grass")
    return true  if Weather.HAIL == currentWeather and forme == @pokemon.isInForme("mega-ice")

  this::editEvasion = (accuracy) ->
    forme = @pokemon.getForme()
    if @battle.hasWeather(Weather.HAIL) and @pokemon.isInForme("mega-ice") and @pokemon.species == 'Eevee'
      Math.floor(.8 * accuracy)
    else
      accuracy
 
   this::afterBeingHit = (move, user, target, damage, isDirect) ->
      forme = @pokemon.getForme()
      return  if @pokemon.isInForme("mega-ice") or @pokemon.species != 'Eevee'
      return  if !move.hasFlag("contact")
      return  if @battle.rng.next("contact status") >= .3
      return  if !isDirect
      @pokemon.activateAbility()
      user.attach(Attachment.Attract, source: @pokemon)


#mega-dark is hardcoded in bw/attachments

makeLowHealthAbility("Psycho Call", "Psychic")
makeLowHealthAbility("Shadow Call", "Dark")
makeLowHealthAbility("Spirit Call", "Ghost")


# TODO: Just kidding, this is done
makeAbility "Regurgitation"

makeAbility 'Spectral Jaws', ->
  this::modifyBasePower = (move) ->
    return 0x14CD  if move.hasFlag("bite")
    return 0x1000

makeAbility 'Speed Swap', ->
   this::switchIn = ->
    if @battle.attach(Attachment.TrickRoom)
      @battle.cannedText('TRICK_ROOM_START', @pokemon)
    else
      @battle.unattach(Attachment.TrickRoom)

makeAbility 'Synthetic Alloy', ->
   this::typeEffectiveness  = (move) ->
     return 1 if move.type = "Fire"

makeAbility 'Venomous', ->
  this::afterSuccessfulHit = (move, user, target) ->
   if move.shouldTriggerSecondary && move.ailmentId  == "poison"
     target.attach(Status.Toxic)

makeTypeImmuneAbility("Wind Force", "Flying", "speed")

makeAbility 'Winter Joy', ->
  this::modifyBasePower = (move) ->
    boostmonths = [1,2,11,12]
    badmonths = [5,6,7,8]
    today = new Date
    mm = today.getMonth() + 1
    if mm in boostmonths
      return 0x1666
    else if mm in badmonths
      return 0xB33
    else
      return 0x1000

makeAbility 'Illusion', ->
  this::initialize = ->
    alivemons = @pokemon.team.getAlivePokemon()
    lastalivemon = alivemons[alivemons.length-1]

    @pokemon.attach(Attachment.Illusion, target: lastalivemon)
      