 
   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

      kVelocity = (1 - @jumpVelocityFactor) + @jumpVelocityFactor * ratio
      @acceleration.y = @jumpAcceleration * kVelocity

    _buildJump: ->
      # Speed up by persisting the jump acceleration but with quadratic decay.
      # The longer the hold, the higher the final jump, but power decays quickly.
      # Also note that negative is up, positive is down.
      kEasing = (1000 - @jumpMaxDuration) + (@jumpMaxDuration - @_jumpTimer.ms)
      kEasing = Math.pow kEasing / 1000, 2
      @acceleration.y *= kEasing
      @debug 'jump:build', kEasing

    _endJump: ->
      @debug 'jump:end', @_jumpTimer.ms, { position: @physics.position }
      @acceleration.y = @gravity.y # Reset.
      @_jumpTimer.stop()

    # Run
    # ---

    _canBeginRun: ->
      @nextDirection? and @nextDirection is @direction and
      not @_isInMidAir() and (@_isTrulyStill() or @_isLanded())
    _canBuildRun: ->
      @nextDirection?
    _canEndRun: ->
      not (@nextDirection? or @_isInMidAir())
    _canKeepRunning: ->
      not (@_canLand() or @_isInMidAir())

    _beginRun: ->
      @nextAction = 'start' 
      @nextState = 'running'

    _buildRun: ->
      @velocity.clampX -@maxVelocity.x, @maxVelocity.x
      @acceleration.x =
        # No force, just air friction.
        if @_isInMidAir() then @runAcceleration * @airFrictionRatio * -@nextDirection
        # Otherwise, just step on the pedal.
        else @runAcceleration * @nextDirection

    _endRun: ->
      # Without user input, there's no force, so stop, then stay still.
      if @velocity.x isnt 0
        @nextAction = 'stop'
        return
      @nextState = 'still'
      @_isTurning = no # In case of being blocked.

    # Turn
    # ----

    _canBeginTurn: ->
      @nextDirection? and @nextAction isnt 'start' and
      not (@_isTurning or @nextDirection is @direction)
    _canEndTurn: ->
      @nextDirection? and @animation?.isFinished and
      @_isTurning and @nextDirection is @direction

    _beginTurn: ->
      @nextAction = 'stop'
      @_isTurning = yes
      @direction = @nextDirection
      @debug 'turn:start', @velocity.x

    _endTurn: ->
      # Visualize the turn here, for now.
      @sprite.scale.x = @nextDirection
      @physics.offset = new Phaser.Point @_xOffset(@nextDirection), @_yOffset

      @nextAction = 'start'
      @_isTurning = no
      @debug 'turn:end', @velocity.x

    # Helpers
    # -------

    _playAnimation: (nameOrFrame, interrupt = yes) ->
      return if interrupt is no and @animation?.isPlaying

      if _.isNumber(nameOrFrame)
        frame = nameOrFrame
        @animations.frame = frame
        @animation = null

      else
        name = nameOrFrame
        return if @animation?.name is name
        @animation = @animations.play name

      @debug 'animation', nameOrFrame

    _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, '$& '

  Player
