--camera and character controller
local math = assert( math )
local mat = require( "mat4" )

local maxPitch = math.pi / 2
local speed = 2

local function logistic( x )
  return 1.0 / ( 1.0 + math.exp( x ) )
end

local player = {
  turnRate = 0,
  rate = 0.01,
  x = 0,
  y = 0.5,
  z = 0,
  vx = 0,
  vz = 0,
  yaw = 0,
  pitch = 0,
  desx = 0,
  desz = 0,
  view = mat.id(),
  proj = mat.id(),
}

function player:updateDesiredDirection( forward, left, right, back )
  local x = (right and 1 or 0) -   (left and 1 or 0)
  local z = (forward and 1 or 0) - (back and 1 or 0)
  local c, s = math.cos( self.yaw ), math.sin( self.yaw )
  local n = math.sqrt( x * x + z * z )
  if n < 0.0001 then n = 1.0 end
  x, z = x / n, z / n
  self.desx, self.desz = x * c + z * s, -x * s + z * c
end

--[[function player:getViewDirection()
  local cy, sy, cp, sp = math.cos(self.yaw), math.sin(self.yaw), math.cos( self.pitch ), math.sin( self.yaw )
  return cy, sp, 1
end]]

function player:setFOV( fov )
  self.proj = mat.projection( 0.05, 100, math.rad( fov or 90 ) )
end

function player:setTurnRate( sensitivity )
  self.turnRate = 0.001 * logistic( sensitivity )
end

function player:turn( dx, dy )
  self.yaw = self.yaw + dx * self.turnRate
  self.pitch = math.min( maxPitch, math.max( -maxPitch,
      self.pitch + dy * self.turnRate ))
end

function player:update()
  self.vx = self.desx * speed
  self.vz = self.desz * speed
  
  self.x = self.x + self.vx * self.rate
  self.z = self.z + self.vz * self.rate
  
  self.view = mat.view( self.x, self.y, self.z, self.yaw, self.pitch )
end

function player:tostring()
  return ([[
    x:    %3.2f
    y:    %3.2f
    z:    %3.2f
    vx:   %3.2f
    vz:   %3.2f
    yaw:  %3.2f
    pitch:%3.2f
    desx: %3.2f
    desz: %3.2f
  ]]):format(
  player.x,
  player.y,
  player.z,
  player.vx,
  player.vz,
  player.yaw,
  player.pitch,
  player.desx,
  player.desz  
  )
end

return player