local vm = require("lib.vornmath")

-- https://jakesgordon.com/writing/javascript-racer-v1-straight/
---@class lib.choro.projection
local projection = {}

--- World {x, y, z} - Camera {x, y, z} (Relative to Camera Pos)
---@param world table {x, y, z}
---@param cam table {x, y, z}
---@return table {x, y, z}
function projection.translateToRelativeCamera(world, cam)
  return vm.vec3(world) - vm.vec3(cam)
end

--- project relative camera {x, y, z} to z render distance on screen {x, y}
---@param camPos table {x, y, z}
---@param distance number distance to render point
---@return table {x, y}
function projection.projectRelativeCamera(camPos, distance)
  return vm.vec2({
    camPos[1] * distance / camPos[3],
    camPos[2] * distance / camPos[3]
  })
end

--- scale things from camera projection {x, y}
---@param camProj table camera projection {x, y, z}
---@param screenScale number projection scale / distance
---@param resolution table {screen width, screen height}
---@return table {x, y}
function projection.scalePosToRelativeProjectCamera(camProj, screenScale, resolution)
  local w2 = resolution[1]/2
  local h2 = resolution[2]/2
  return vm.vec2({
    vm.round(w2 + ( screenScale * camProj[1] * w2)),
    vm.round(h2 - ( screenScale * camProj[2] * h2)),
  })
end

--- scale things from camera projection {w, h}
---@param size table world size {w, h}
---@param screenScale number projection scale / distance
---@param resolution table {screen width, screen height}
---@return table {w, h}
function projection.scaleSizeToRelativeProjectCamera(size, screenScale, resolution)
  local w2 = resolution[1]/2
  local h2 = resolution[2]/2
  return vm.vec2({
    vm.round(       screenScale * size[1]      * w2),
    vm.round(       screenScale * size[2]      * h2)
  })
end

--- calculate distance from field of view
---@param fieldOfView number field of view
---@return number distance distance from camera to projection plane
function projection.distanceCamToProjection(fieldOfView)
  local d = 1 / math.tan((fieldOfView/2) * math.pi/180);
  return d
end

--- apply world to camera projection
---@param world table world position {x, y, z}
---@param cameraPos table camera position {x, y, z}
---@param cameraDepth number distance from camera to projection plane
---@param resolution table render resolution {width, height}
---@param size table object {width, height}
---@return table,table,table {x, y, z}, {x, y}, {w, h}
function projection.projectWorldToCam(world, cameraPos, cameraDepth, resolution, size)
  local relativetocamera = projection.translateToRelativeCamera(world, cameraPos)
  local scale = cameraDepth / relativetocamera[3];
  return
    relativetocamera,
    projection.scalePosToRelativeProjectCamera(relativetocamera, scale, resolution),
    projection.scaleSizeToRelativeProjectCamera(size, scale, resolution)
end

return projection
