--[[
  Some helping utilities for dealing with car physics. Examples:

  • Get value for selected tyres from “tyres.ini”:

    local carsUtils = require('shared/sim/cars')
    carsUtils.getTyreConfigValue(0, true, 'ANGULAR_INERTIA', 1.05)

  • Get thermal value for selected tyres from “tyres.ini”:

    local carsUtils = require('shared/sim/cars')
    carsUtils.getTyreThermalConfigValue(0, true, 'SURFACE_TRANSFER', 0.01)
]]

local tyresCache = {}

local function getTyreConfigSection(car, prefix)
  local i = car and car.compoundIndex or 0
  return i == 0 and prefix or prefix..'_'..i
end

local function initCarEntry(carIndex)
  return {ac.getCar(carIndex), ac.INIConfig.carData(carIndex, 'tyres.ini')}
end

-- Actual library:
local carsUtils = {}

---Get value from “tyres.ini” for a certain car for currently selected set of tyres.
---@generic T
---@param carIndex integer @0-based car index.
---@param frontTyres boolean @Set to `true` for front tyres or `false` for rear tyres.
---@param key string @Parameter key.
---@param defaultValue T @Default parameter value in case config is damaged or unavailable.
---@return T
function carsUtils.getTyreConfigValue(carIndex, frontTyres, key, defaultValue)
  local entry = table.getOrCreate(tyresCache, carIndex, initCarEntry, carIndex)
  return entry[2]:get(getTyreConfigSection(entry[1], frontTyres and 'FRONT' or 'REAR'), key, defaultValue)
end

---Get thermal value from “tyres.ini” for a certain car for currently selected set of tyres.
---@generic T
---@param carIndex integer @0-based car index.
---@param frontTyres boolean @Set to `true` for front tyres or `false` for rear tyres.
---@param key string @Parameter key.
---@param defaultValue T @Default parameter value in case config is damaged or unavailable.
---@return T
function carsUtils.getTyreThermalConfigValue(carIndex, frontTyres, key, defaultValue)
  local entry = table.getOrCreate(tyresCache, carIndex, initCarEntry, carIndex)
  return entry[2]:get(getTyreConfigSection(entry[1], frontTyres and 'THERMAL_FRONT' or 'THERMAL_REAR'), key, defaultValue)
end

---Get car position and direction from a binary blob generated by `ac.saveCarStateAsync()`.
---@param data binary
---@return mat4x4? @If data is damaged, returns `nil`.
function carsUtils.getCarStateTransform(data)
  return __util.native('car_state_transform', 1, data)
end

---Alter car position and velocity in a binary blob generated by `ac.saveCarStateAsync()`. Car physics should be available
---for car state to be editable.
---
---For example, to rotate car state around vertical axis you can use this transform:
---```
---local tr = mat4x4.translation(-ac.getCar(0).position)
---  :mulSelf(mat4x4.rotation(math.pi / 10, vec3(0, 1, 0)))
---  :mulSelf(mat4x4.translation(ac.getCar(0).position))
---```
---@param data binary
---@param transform mat4x4 @Transformation matrix (please avoid using skewing or scaling here, or the car might break).
---@return binary? @If data is damaged, returns `nil`.
function carsUtils.alterCarStateTransform(data, transform)
  return __util.native('car_state_transform', 2, data, transform)
end

---Alter car velocity and angular velocity in a binary blob generated by `ac.saveCarStateAsync()`. New velocities will be added
---on top. Car physics should be available
---for car state to be editable.
---@param data binary
---@param velocity vec3
---@param angularVelocity vec3? @Default value: `vec3()`
---@return binary? @If data is damaged, returns `nil`.
function carsUtils.alterCarStateVelocity(data, velocity, angularVelocity)
  return __util.native('car_state_transform', 3, data, velocity, angularVelocity or vec3())
end

return carsUtils