require '../../helpers'

{_} = require 'underscore'
shared = require '../../shared'
{Conditions} = require '../../../shared/conditions'
{Protocol} = require '../../../shared/protocol'

describe "Battle timer", ->
  describe "without team preview", ->
    beforeEach ->
      @clock.tick(10000)
      shared.create.call this,
        conditions: [ Conditions.TIMED_BATTLE ]
      @battle.TIMER_CAP = Infinity

    it "starts a timer that ends the battle in 5 minutes", ->
      @battle.isOver().should.be.false
      delta = 100
      @clock.tick(@battle.DEFAULT_TIMER - delta)
      @battle.isOver().should.be.false
      @clock.tick(delta)
      @battle.isOver().should.be.true

    it "declares a timer win for the player that didn't run out of time", ->
      @battle.playerTimes[@id1] += 1000
      spy = @sandbox.spy(@battle, 'tell')
      @clock.tick(@battle.DEFAULT_TIMER)
      index1 = @battle.getPlayerIndex(@id1)
      spy.calledWith(Protocol.TIMER_WIN, index1).should.be.true

    it "increases time remaining by 20 seconds for each player each turn", ->
      @battle.timeRemainingFor(@id1).should.equal(@battle.DEFAULT_TIMER)

      @battle.beginTurn()
      delta = @battle.TIMER_PER_TURN_INCREASE
      @battle.timeRemainingFor(@id1).should.equal(@battle.DEFAULT_TIMER + delta)
      @battle.timeRemainingFor(@id2).should.equal(@battle.DEFAULT_TIMER + delta)

      @battle.beginTurn()
      delta *= 2
      @battle.timeRemainingFor(@id1).should.equal(@battle.DEFAULT_TIMER + delta)
      @battle.timeRemainingFor(@id2).should.equal(@battle.DEFAULT_TIMER + delta)

    it "recalculates timer after increasing time remaining", ->
      @battle.beginTurn()
      delta = @battle.TIMER_PER_TURN_INCREASE
      spy = @sandbox.spy(@battle, 'tell')

      @clock.tick(@battle.DEFAULT_TIMER + delta / 2)
      spy.calledWith(Protocol.TIMER_WIN).should.be.false

      @clock.tick(delta / 2)
      spy.calledWith(Protocol.TIMER_WIN).should.be.true

    it "stops timer for players who have moved", ->
      delta = 5000
      @battle.timeRemainingFor(@id1).should.equal(@battle.DEFAULT_TIMER)
      @battle.timeRemainingFor(@id2).should.equal(@battle.DEFAULT_TIMER)

      @clock.tick(delta)
      @battle.timeRemainingFor(@id1).should.equal(@battle.DEFAULT_TIMER - delta)
      @battle.timeRemainingFor(@id2).should.equal(@battle.DEFAULT_TIMER - delta)

      @battle.recordMove(@id1, @battle.getMove("Splash"))

      @clock.tick(delta)
      @battle.timeRemainingFor(@id1).should.equal(@battle.DEFAULT_TIMER - delta)
      @battle.timeRemainingFor(@id2).should.equal(@battle.DEFAULT_TIMER - 2 * delta)

    it "recalculates the timer after a player chooses an action", ->
      delta = 4000
      # give player 2 more time
      @battle.playerTimes[@id2] += delta

      @battle.recordMove(@id1, @battle.getMove("Splash"))

      spy = @sandbox.spy(@battle, 'tell')
      @clock.tick(@battle.DEFAULT_TIMER)
      spy.calledWith(Protocol.TIMER_WIN).should.be.false

      @clock.tick(delta)
      spy.calledWith(Protocol.TIMER_WIN).should.be.true

    it "grants time if player selected a move before the battle continued", ->
      @clock.tick(2500)

      spy = @sandbox.spy(@battle, 'requestActions')
      @controller.makeMove(@id1, "Splash")

      # 5 seconds after the player moves, the battle progresses
      @clock.tick(5000)
      @controller.makeMove(@id2, "Splash")
      (@battle.DEFAULT_TIMER + @battle.TIMER_PER_TURN_INCREASE -
        @battle.timeRemainingFor(@id1)).should.equal(2500)

      # Turn has progressed. Make another move and check the time.
      @clock.tick(2500)
      @controller.makeMove(@id1, "Splash")
      @clock.tick(5000)
      @controller.makeMove(@id2, "Splash")
      (@battle.DEFAULT_TIMER + 2 * @battle.TIMER_PER_TURN_INCREASE -
        @battle.timeRemainingFor(@id1)).should.equal(5000)

    it "ends battle if canceling after which they'd lose to timer", ->
      # So the second player won't trigger the end condition.
      @battle.playerTimes[@id2] += 4000

      @clock.tick(2500)

      @controller.makeMove(@id1, "Splash")
      @clock.tick(@battle.DEFAULT_TIMER)

      mock = @sandbox.mock(@battle).expects('timerWin').once()
      @controller.undoCompletedRequest(@id1)
      @battle.timeRemainingFor(@id1).should.equal(-2500)
      mock.verify()

    it "sends timer updates when battle enters a new turn", ->
      @battle.recordMove(@id1, @battle.getMove("Splash"))
      @battle.recordMove(@id2, @battle.getMove("Splash"))

      spy = @sandbox.spy(@battle, 'send')
      @battle.continueTurn()
      spy.calledWith('updateTimers').should.be.false
      @battle.beginTurn()
      spy.calledWith('updateTimers').should.be.true

    it "gets cleared if the battle ends prematurely", ->
      @battle.endBattle()

      mock = @sandbox.mock(@battle).expects('timerWin').never()
      @clock.tick(@battle.DEFAULT_TIMER)
      mock.verify()

    it "has a cap every time a new turn begins", ->
      @battle.TIMER_CAP = @battle.DEFAULT_TIMER

      @clock.tick(@battle.TIMER_PER_TURN_INCREASE >> 1)
      @battle.beginTurn()

      @battle.timeRemainingFor(@id1).should.equal(@battle.TIMER_CAP)

    it "has a cap every time a player gains time after a new action request", ->
      @battle.TIMER_CAP = @battle.DEFAULT_TIMER
      @battle.recordMove(@id1, @battle.getMove("U-turn"))

      @clock.tick(@battle.TIMER_PER_TURN_INCREASE >> 1)
      @battle.continueTurn()

      @battle.timeRemainingFor(@id1).should.equal(@battle.TIMER_CAP)

    it "recalculates timer every time an action is requested", ->
      # Player 2 has more time.
      delta = 4000
      @battle.playerTimes[@id2] += delta
      @battle.recordMove(@id2, @battle.getMove("U-turn"))
      @battle.recordMove(@id1, @battle.getMove("Splash"))

      @battle.continueTurn()
      # Action requested due to U-turn

      spy = @sandbox.spy(@battle, 'tell')
      @clock.tick(@battle.DEFAULT_TIMER)
      spy.calledWith(Protocol.TIMER_WIN).should.be.false

      @clock.tick(delta)
      spy.calledWith(Protocol.TIMER_WIN).should.be.true

  describe "with team preview", ->
    beforeEach ->
      @clock.tick(10000)
      shared.create.call this,
        conditions: [ Conditions.TIMED_BATTLE, Conditions.TEAM_PREVIEW ]

    it "starts a timer that auto-starts the battle after 1.5 mins", ->
      @battle.arranging.should.be.true
      spy = @sandbox.spy(@battle, 'startBattle')
      @clock.tick(@battle.TEAM_PREVIEW_TIMER)
      spy.calledOnce.should.be.true
      @battle.arranging.should.be.false

    it "arranges teams of those who already submitted arrangements", ->
      @battle.arranging.should.be.true
      arrangement = [0...@team1.size()]
      arrangement.reverse()
      pokemon = _.clone(@team1.pokemon)
      pokemon.reverse()
      @controller.arrangeTeam(@id1, arrangement)
      @clock.tick(@battle.TEAM_PREVIEW_TIMER)
      @team1.pokemon.should.eql(pokemon)
