{Factory} = require './factory'

shouldDoNoDamage = (moveName, battleOptions) ->
  it 'does no damage', ->
    create.call(this, battleOptions)
    move = @battle.getMove(moveName)
    @battle.performMove(@p1, move)
    @p2.currentHP.should.equal @p2.stat('hp')

shouldFailIfUsedTwice = (moveName, battleOptions) ->
  it 'should fail if used twice', ->
    create.call(this, battleOptions)
    move = @battle.getMove(moveName)
    mock = @sandbox.mock(move).expects('fail').once()

    @battle.performMove(@p1, move)
    @battle.performMove(@p1, move)

    mock.verify()

build = (context, opts={}) ->
  generation = opts.gen ? 'bw'
  {Battle} = require("../server/#{generation}/battle")
  {BattleController} = require("../server/#{generation}/battle_controller")
  context.id1 = 'abcde'
  context.id2 = 'fghij'
  team1   = opts.team1 || [Factory('Magikarp'), Factory('Magikarp')]
  team2   = opts.team2 || [Factory('Magikarp'), Factory('Magikarp')]
  conditions = opts.conditions
  players = [
    {id: context.id1, name: context.id1, team: team1, ratingKey: context.id1}
    {id: context.id2, name: context.id2, team: team2, ratingKey: context.id2}
  ]
  numActive = opts.numActive || 1
  format = opts.format
  context.battle = new Battle(createId(), players, {numActive, conditions, format})
  context.controller = new BattleController(context.battle)
  context.team1  = context.battle.getTeam(context.id1)
  context.team2  = context.battle.getTeam(context.id2)
  context.p1 = context.team1.first()
  context.p2 = context.team2.first()

  createTestRNG.call(context)
  biasRNG.call(context, 'next', 'ch', 1)
  biasRNG.call(context, 'randInt', 'damage roll', 0)
  biasRNG.call(context, 'randInt', 'miss', 0)  # Can be overridden, of course.
  biasRNG.call(context, 'next', 'secondary effect', 1)  # No effect unless 100%
  biasRNG.call(context, 'next', 'secondary boost', 0)  # Always happens
  biasRNG.call(context, 'randInt', 'flinch', 99)  # No flinch (unless fake out)
  # moves that call other moves also get new targets
  biasRNG.call(context, 'randInt', 'selected pokemon target', 0)

createId = ->
  Math.random().toString(16).substr(2)

create = (opts={}) ->
  build(this, opts)
  @controller.beginBattle()

createTestRNG = ->
  @biasedRNGFuncs = {}
  for funcName in ['next', 'randInt']
    do (funcName) =>
      oldFunc = @battle.rng[funcName].bind(@battle.rng)
      @battle.rng[funcName] = (args...) =>
        id = args[args.length - 1]
        func = @biasedRNGFuncs[funcName]
        return (if id of func then func[id] else oldFunc(args...))

testEveryMove = (allMoves, gen) ->
  for move in allMoves
    if move.hasPrimaryEffect()
      do (move) ->
        describe move.name, ->
          # Test primary boost moves
          if move.primaryBoostStats?
            it "boosts properly", ->
              create.call(this, gen: gen)
              target = (if move.primaryBoostTarget == 'self' then @p1 else @p2)
              target.stages.should.not.containEql(move.primaryBoostStats)
              @battle.performMove(@p1, move)
              target.stages.should.containEql(move.primaryBoostStats)

            if move.primaryBoostTarget == 'self'
              it "can never miss self", ->
                create.call(this, gen: gen)
                move.chanceToHit(@battle, @p1, @p1).should.equal(0)

biasRNG = (funcName, id, returns) ->
  @biasedRNGFuncs[funcName] ||= {}
  @biasedRNGFuncs[funcName][id] = returns

module.exports = {
  shouldDoNoDamage, shouldFailIfUsedTwice,
  build, create, biasRNG, testEveryMove
}
