local vm = require("lib.vornmath")


---calculate real desire forward after weight
---@param forward number gas_input
---@param weight number? magic weight constant
---@return number forward actual desire
local function add_forward(forward, weight)
  return forward * (weight or 1.0)
end

---calculate real desire lateral steer after weight
---@param steer number steer_input
---@param weight number? magic weight constant
---@return number lateral
local function add_lateral(steer, weight)
  return steer * (weight or 1.0)
end

---calculate friction impact based on grip
---@param friction number outside friction
---@param grip number car's grip
---@return number friction
local function grip_force(friction, grip)
  return friction * grip
end

---add friction
---@param f vec2 vec2 input
---@param grip number car's grip
---@param friction number floor's friction
---@param weight_tire number? magic weight tire
---@param weight_floor number? magic weight floor
---@return vec2 f vec2 output
local function add_friction(f, grip, friction, weight_tire, weight_floor)
  local f_grip = grip_force(grip, friction)
  -- tire - floor
  return f + f_grip * ((weight_tire or 1.0) - (weight_floor or 0.0))
end

---calculate drag's impact based on streamline
---@param drag number|vec2 drag
---@param streamline number car's streamline
---@return number drag
local function drag_force(drag, streamline)
  return streamline * drag
end

---add drag into f
---@param f vec2 input
---@param drag number|vec2 drag
---@param streamline number car's streamline
---@param weight_drag number? magic weight
---@return vec2 f
local function add_drag(v, drag, streamline, weight_drag)
  local f_drag = drag_force(drag, streamline)
  return v + f_drag * (weight_drag or 1.0)
end

---add inertia into f
---@param f vec2 input
---@param inertia_force vec2 inertia
---@param inertia_weight number? magic weight
---@return vec2 f
local function add_inertia(f, inertia_force, inertia_weight)
  return f + inertia_force * (inertia_weight or 1.0)
end

---centrifugal_force
---@param mass number car's mass
---@param angular_v number velocity on angular
---@param perpendicular number r
---@return number f
local function centrifugal_force(mass, angular_v, perpendicular)
  return mass * angular_v ^ 2 * perpendicular
end

---add centrifugal
---@param f vec2
---@param mass number
---@param inertia_force vec2
---@param weight_centrifugal number?
---@return vec2 f
local function add_centrifugal(v, mass, inertia_force, weight_centrifugal)
  local centrifugal = centrifugal_force(mass, inertia_force[1], inertia_force[2])

  local new_v
  if (v[2] > 0) then
    new_v = v - vm.vec2(0.0, 1.0) * centrifugal * (weight_centrifugal or 1.0)
  elseif (v[2] < 0) then
    new_v = v + vm.vec2(0.0, 1.0) * centrifugal  * (weight_centrifugal or 1.0)
  end
  return new_v
end

---create desire f
---@param desire_forward number forward
---@param desire_steer number lateral
---@param weight_forward number? weight forward
---@param weight_steer number? weight lateral
---@return vec2 desire f
local function add_desire(desire_forward, desire_steer, weight_forward, weight_steer)
  local forward = add_forward(desire_forward)
  local lateral = add_lateral(desire_steer)
  local val = vm.vec2(forward, lateral)
  return val
end

---@param desire_forward number engine's force
---@param desire_steer number axle's steer
---@param grip number car's grip
---@param friction number outside's friction
---@param drag number|vec2 outside's drag
---@param drag_movement number drag from moving
---@param streamline number car's streamline
---@param inertia_force vec2 last's force
---@param mass number car's mass
---@param weight_forward number? magic weight
---@param weight_steer number? magic weight
---@param weight_tire number? magic weight
---@param weight_floor number? magic weight
---@param weight_drag_boost number? magic weight
---@param weight_drag_halt number? magic weight
---@param weight_inertia number? magic weight
---@param weight_centrifugal number? magic weight
local function force_workflow(
  desire_forward, desire_steer,
  -- friction
  grip, friction,
  -- drag
  drag, drag_movement, streamline,
  -- inertia
  inertia_force,
  -- centrifugal
  mass,
  weight_forward, weight_steer,
  weight_tire, weight_floor,
  weight_drag_boost, weight_drag_halt,
  weight_inertia,
  weight_centrifugal
)
  local val = add_desire(desire_forward, desire_steer, weight_forward, weight_steer)
  if (val[1] ~= 0 and val[2] ~= 0) then
    val = add_friction(val, grip, friction, weight_tire, weight_floor)
    val = add_drag(val, drag, streamline, weight_drag_boost)
    val = add_drag(val, drag_movement, streamline, weight_drag_halt)
    val = add_centrifugal(val, mass, inertia_force, weight_centrifugal)
  end
  val = add_inertia(val, inertia_force, weight_inertia)
  return val
end

return force_workflow
