
#   00000000   000       0000000   000   000  00000000  00000000 
#   000   000  000      000   000   000 000   000       000   000
#   00000000   000      000000000    00000    0000000   0000000  
#   000        000      000   000     000     000       000   000
#   000        0000000  000   000     000     00000000  000   000
{
clamp
}           = require '/Users/kodi/s/ko/js/tools/tools'
log         = require '/Users/kodi/s/ko/js/tools/log'
Bot         = require './bot'
Action      = require './action'
Timer       = require './timer'
Vector      = require './lib/vector'
Camera      = require './camera'
Quaternion  = require './lib/quaternion'
 
class Player extends Bot
    
    constructor: ->
        
        super
        @name = 'player'
        @key =
            forward:  'w'
            backward: 's'
            left:     'a'
            right:    'd'
            lookUp:   'up'
            lookDown: 'down'
            shoot:    'enter'
            jump:     'space'
            view:     'c'
            push:     'shift'

        @camera = new Camera @,
            aspect: world.view.offsetWidth / world.view.offsetHeight

        @look_action = null
        @look_angle  = 0.0
        @new_dir_sgn = 1.0
        @rotate      = 0
        
        @recorder    = null
        @playback    = null
        
        @addAction new Action @, Action.LOOK_UP,    "look up",    220
        @addAction new Action @, Action.LOOK_DOWN,  "look down",  220
        @addAction new Action @, Action.LOOK_RESET, "look reset", 60
    
        @addEventWithName "landed"
    
    bulletHitSound: -> 'BULLET_HIT_PLAYER'
            
    #    0000000    0000000  000000000  000   0000000   000   000
    #   000   000  000          000     000  000   000  0000  000
    #   000000000  000          000     000  000   000  000 0 000
    #   000   000  000          000     000  000   000  000  0000
    #   000   000   0000000     000     000   0000000   000   000
    
    initAction: (action) ->
        # log "initAction #{action.id} #{action.name}"
        switch action.id
            when Action.CLIMB_DOWN
                # @addMoves 1 
                world.playSound 'BOT_CLIMB'
            when Action.FORWARD
                # @addMoves 1 
                world.playSound 'BOT_MOVE'
            when Action.TURN_LEFT, Action.TURN_RIGHT
                world.playSound 'BOT_TURN'
            when Action.JUMP
                # @addMoves actionId == Action.JUMP and 1 or 2
                world.playSound 'BOT_JUMP'
        
        super action
        
    #   00000000   00000000  00000000   00000000   0000000   00000000   00     00
    #   000   000  000       000   000  000       000   000  000   000  000   000
    #   00000000   0000000   0000000    000000    000   000  0000000    000000000
    #   000        000       000   000  000       000   000  000   000  000 0 000
    #   000        00000000  000   000  000        0000000   000   000  000   000

    performAction: (action) ->
        relTime = action.getRelativeTime()
        switch action.id
            when Action.NOOP      then return
            when Action.LOOK_UP   then @look_angle = relTime * 90.0
            when Action.LOOK_DOWN then @look_angle = relTime * -90.0
            when Action.LOOK_RESET
                if @look_angle > 0 then @look_angle = Math.min @look_angle, (1.0-relTime) * 90.0
                else                    @look_angle = Math.max @look_angle, (1.0-relTime) * -90.0
            else
                super action 
    
    #   00000000  000  000   000  000   0000000  000   000
    #   000       000  0000  000  000  000       000   000
    #   000000    000  000 0 000  000  0000000   000000000
    #   000       000  000  0000  000       000  000   000
    #   000       000  000   000  000  0000000   000   000
    
    finishAction: (action) ->
    
        if action.id == Action.LOOK_RESET
            @look_action = null
            @look_angle  = 0.0
        else
            if action.id == @move_action?.id # move finished, update direction
                @dir_sgn = @new_dir_sgn
            
            if action.id != Action.LOOK_UP and action.id != Action.LOOK_DOWN
                super action
            
            if action.id == Action.TURN_LEFT or action.id == Action.TURN_RIGHT
                if @rotate
                    @rotate_action = @getActionWithId @rotate
                    @rotate_action.reset()
                    Timer.addAction @rotate_action
    
    die: () ->
        super
        # Controller.displayText "game over" 
        world.playSound 'BOT_DEATH'
        world.setCameraMode world.CAMERA_FOLLOW
                
    #   000   000  00000000  000   000
    #   000  000   000        000 000 
    #   0000000    0000000     00000  
    #   000  000   000          000   
    #   000   000  00000000     000   
        
    modKeyComboEventDown: (mod, key, combo, event) ->
                    
        # log "player.modKeyComboEventDown mod:#{mod} key:#{key} combo:#{combo}"
        
        switch key
            when @key.forward, @key.backward
                @push = mod == @key.push
                @move = true # try to move as long as the key is not released
                if not @move_action?
                    @new_dir_sgn = @dir_sgn = (key == @key.backward) and -1 or 1 
                    @moveBot() # perform new move action (depending on environment)
                else
                    if @move_action.id == Action.JUMP and @move_action.getRelativeTime() < 1                        
                        if world.isUnoccupiedPos(@position.plus(@getUp()).plus(@getDir())) and
                            world.isUnoccupiedPos(@position.plus(@getDir())) # forward and above forward also empty
                                action = @getActionWithId Action.JUMP_FORWARD
                                action.takeOver @move_action                                
                                Timer.removeAction @move_action
                                @move_action = action
                                Timer.addAction @move_action                          
                    @new_dir_sgn = (key == @key.backward) and -1 or 1
                return true
        
            when @key.left, @key.right
                @rotate = (key == @key.left) and Action.TURN_LEFT or Action.TURN_RIGHT
                if not @rotate_action? # player is not performing a rotation
                    @rotate_action = @getActionWithId @rotate
                    Timer.addAction @rotate_action
                return true
            
            when @key.jump
                @jump = true # switch to jump mode until jump_key released
                @jump_once = true
                if not @move_action? 
                    @moveBot() # perform jump action (depending on environment)
                    @jump_once = false
                else
                    # log 'jump:moving'
                    if @move_action.id == Action.FORWARD and @move_action.getRelativeTime() < 0.6 or 
                        @move_action.id == Action.CLIMB_DOWN and @move_action.getRelativeTime() < 0.4
                            # abort current move and jump instead
                            # log 'jump:move or climb down'
                            if world.isUnoccupiedPos @position.plus @getUp()
                                # log 'jump:can do'
                                if world.isUnoccupiedPos @position.plus @getUp().plus @getDir()  
                                    action = @getActionWithId Action.JUMP_FORWARD
                                else 
                                    action = @getActionWithId Action.JUMP
                                action.takeOver @move_action                                
                                Timer.removeAction @move_action
                                @move_action = action
                                @jump_once = false
                                Timer.addAction @move_action 
                    else if @move_action.id in [Action.JUMP, Action.JUMP_FORWARD]
                        @jump_once = false
                return true
            
            when @key.push
                @push = true
                return true
            
            when @key.shoot
                if not @shoot
                    @shoot = true
                    Timer.addAction @getActionWithId Action.SHOOT
                return true
            
            when @key.lookUp, @key.lookDown
                if not @look_action
                    @look_action = @getActionWithId (key == @key.lookUp) and Action.LOOK_UP or Action.LOOK_DOWN
                    @look_action.reset()
                    Timer.addAction @look_action
                return true
            
            when @key.view
                world.changeCameraMode()
                return true
        
        false
    
    #   00000000   00000000  000      00000000   0000000    0000000  00000000
    #   000   000  000       000      000       000   000  000       000     
    #   0000000    0000000   000      0000000   000000000  0000000   0000000 
    #   000   000  000       000      000       000   000       000  000     
    #   000   000  00000000  0000000  00000000  000   000  0000000   00000000
    
    modKeyComboEventUp: (mod, key, combo, event) ->
        @push = false if @key.push == 'shift'
        # log "player.modKeyComboEventUp mod:#{mod} key:#{key} combo:#{combo}"
        switch key    
            when @key.shoot
                Timer.removeAction @getActionWithId Action.SHOOT
                @shoot = false
                return true
            
            when @key.forward, @key.backward
                @move = false
                return true
            
            when @key.jump
                @jump = false
                return true
            
            when @key.left, @key.right
                @rotate = 0
                return true
            
            when @key.push
                @push = false
                return true
            
            when @key.lookDown, @key.lookUp
                if @look_action and @look_action.id != Action.LOOK_RESET
                    Timer.removeAction @look_action
                @look_action = @getActionWithId Action.LOOK_RESET
                Timer.addAction @look_action
                return true
            
            when @key.view 
                return true
            
        false
    
module.exports = Player
