 
   class Player
 
+    # Dependencies + Properties
+    # -------------------------
+
     constructor: (origin, game, gui) ->
       @sprite = game.add.sprite origin.x, origin.y, 'player', 17
       @sprite.anchor = new Phaser.Point 0.5, 0.5

      @_initDebugging gui

      # Readonly.
      @animation = null
      @state = 'still' # still, running, rising, falling
      @nextAction = 'none' # none, start, stop, jump
      @nextState = null

    debug: (label, value, details) ->
      return unless @debugging

      value = parseFloat value.toFixed(2) if _.isNumber(value)

      prettyPoint = (point) ->
        _.chain point
          .pick 'x', 'y'
          .mapObject (n) -> parseFloat n.toFixed(2)
          .value()

      prettyHash = (hash) ->
        JSON.stringify hash
          .replace RegExps.PrettyHashRemove,''
          .replace RegExps.PrettyHashPad, '$& '

      value = prettyHash prettyPoint(value) if value instanceof Phaser.Point

      if details?.position and details.position instanceof Phaser.Point
        details.position = prettyPoint details.position

      if @tracing
        label = "player:#{label}"
        if details? then console.trace label, value, details
        else console.trace label, value
      else
        details = if details? then prettyHash(details) else ''
        @debugTextItems[label] = "#{label}: #{value} #{details}"

    isInMidAir: -> @state is 'rising' or @state is 'falling'

    update: ->
      @_updateBaseMovement()

      @_updateXMovement()
      @_updateYMovement()

      @_updateAnimations()
      @_changeState @nextState

    _initAnimations: ->
      @animations.add 'run', [0..11], 30, on

      @_jumpTimer = @sprite.game.time.create() # This also acts like a flag.
      @_isTurning = no # Because velocity won't be 0 when turning while running.

    # Change
    # ------

    _changeAnimation: ->
      unless @_isInMidAir() or @nextAction is 'none'
        @_playAnimation @nextAction, @animation?.loop
        return

      switch @nextState
        when 'running' then @_playAnimation 'run', no
        when 'still' then @_playAnimation 17, @animation?.loop
        when 'falling' then @_playAnimation 31, no
        when 'landing' then @_playAnimation 'land'

    _changeState: ->
      return if @nextState is @state

      @debug 'jump:peak', @physics.y if @state is 'rising'

      @debug 'state', @nextState
      @debug 'jump:start', @physics.position if @nextState is 'rising'

      @state = @nextState

    # Computed
    # --------

    _isInMidAir: -> @state is 'rising' or @state is 'falling'
    _isLanded: -> @animation?.name is 'land' and @animation.isFinished
    _isTrulyStill: -> @state is 'still' and @velocity.x is 0

    _xDirectionInput: ->
      if @cursors.left.isDown then Direction.Left
      else if @cursors.right.isDown then Direction.Right

    _xOffset: (direction = @direction) -> direction * 10

    # Jump
    # ----

    _canBeginJump: ->
      @cursors.up.isDown and not (@_jumpTimer.running or @_isInMidAir())
    _canBuildJump: ->
      @cursors.up.isDown and @_jumpTimer.running
    _canEndJump: ->
      @nextAction isnt 'jump' and @_jumpTimer.running and
      (@cursors.up.isUp or @_jumpTimer.ms >= @jumpMaxDuration) # Release to cancel early.

    _canFall: ->
      not @_jumpTimer.running and @state is 'rising' and @velocity.y > 0
    _canLand: ->
      @state is 'falling' and @physics.touching.down

    _beginJump: ->
      @_jumpTimer.start()

      @nextAction = 'jump'
      @nextState = 'rising'
