--------
-- Simple camera script, using velocity to rotate camera.
--------

-- Options for two different cameras (all distances are in meters):
local maximumCameraAngle = { 54, 68 }  -- degress

-- This thing will smooth car velocity to reduce wobbling with replays or in online:
local carVelocity = smoothing(vec3(), 40)

-- Alternative for ac.getCarVelocity(), possibly smoother:
local calculateVelocityHere = true
local lastCarPos = vec3()

-- Extra thing for looking around:
local lookDirection = smoothing(0, 10)

-- Will be called each frame:
-- Note: `dt` is time passed since last frame, `cameraIndex` is 1 or 2, depending on which camera is
-- chosen.
function script.update(dt, cameraIndex)

  smoothing.setDT(dt)

  -- Get AC camera parameters with some corrections to be somewhat compatible:
  local cameraParameters = ac.getCameraParameters(cameraIndex)
  local distance = cameraParameters.distance + 1.6
  local height = cameraParameters.height - 0.3
  local pitchAngle = cameraParameters.pitch - 5

  -- Get car position and vectors:
  local carPos = ac.getCarPosition()
  local carDir = ac.getCarDirection()
  local carUp = ac.getCarUp()
  local carRight = math.cross(carDir, carUp):normalize()

  if calculateVelocityHere then
    -- Altenative approach, using coordinates and time delta
    if lastCarPos ~= carPos then
      if lastCarPos ~= vec3() then
        local delta = lastCarPos - carPos
        local deltaLength = #delta
        if deltaLength > 5 then delta = delta / deltaLength * 5 end
        carVelocity:update(-delta / dt)
      end
      lastCarPos = carPos
    end
  else
    -- Update smoothing thing with velocity:
    -- Note: method `updateIfNew` would change value only if parameter is different from the one used last 
    -- time. This way, in replays camera will freeze.
    carVelocity:updateIfNew(ac.getCarVelocity())
  end

  -- ac.debug('carVelocity', carVelocity)

  -- Normalize car velocity:
  local carVelocityDir = math.normalize(carVelocity.val + carDir * 0.01)

  -- Get rotation coefficient, from -1 to 1, based on X-component of local velocity (that’s what dot is for)
  -- and taking absolute speed into account as well:
  local velocityX = math.clamp(math.dot(carRight, carVelocityDir) * math.pow(#carVelocity.val, 0.5) / 10, -1, 1)

  -- Camera angle for given coefficient:
  local cameraAngle = -velocityX * math.radians(maximumCameraAngle[cameraIndex])

  -- Extra thing for joystick support:
  local joystickLook = ac.getJoystickLook()
  lookDirection:update(
    (ac.looksLeft() and ac.looksRight() or ac.looksBehind()) and math.sign(lookDirection.val) or
    ac.looksLeft() and 0.5 or
    ac.looksRight() and -0.5 or
    joystickLook ~= nil and joystickLook.x or 0)
  cameraAngle = cameraAngle + lookDirection.val * math.pi

  -- Sine and cosine for camera angle
  local sin, cos = math.sin(cameraAngle), math.cos(cameraAngle)

  -- Up direction for camera (could be used for horizon lock):
  local cameraUp = (carUp + vec3(0, 3, 0)):normalize()

  -- Set camera position:
  ac.Camera.position = carPos 
    + (carRight * sin - carDir * cos) * distance
    + vec3(0, height, 0)

  -- Find camera look
  local cameraLookPosOffset = carDir + carUp * (1 - math.abs(lookDirection.val ))
  local cameraLook = (carPos + cameraLookPosOffset - ac.Camera.position):normalize()

  -- Use for `pitchAngle`:
  cameraLook:rotate(quat.fromAngleAxis(math.radians(pitchAngle), carRight))

  -- Set camera look:
  ac.Camera.direction = cameraLook

  -- Set other parameters:
  ac.Camera.up = (carUp + vec3(0, 3, 0)):normalize()
  ac.Camera.fov = 60

end