-- returns where the floor should be placed given the camera subject, nil if anything is invalid
function VRCamera:GetAvatarFeetWorldYValue(): number?
	local camera = workspace.CurrentCamera
	local cameraSubject = camera.CameraSubject
	if not cameraSubject then
		return nil
	end

	if cameraSubject:IsA("Humanoid") and cameraSubject.RootPart then
		local rootPart = cameraSubject.RootPart
		return rootPart.Position.Y - rootPart.Size.Y / 2 - cameraSubject.HipHeight
	end

	return nil
end

function VRCamera:UpdateFirstPersonTransform(timeDelta, newCameraCFrame, newCameraFocus, lastSubjPos, subjectPosition)
	-- transition from TP to FP
	if self.needsReset then
		self:StartFadeFromBlack()
		self.needsReset = false
		self.stepRotateTimeout = 0.25
		self.VRCameraFocusFrozen = true
		self.cameraOffsetRotation = 0
		self.cameraOffsetRotationDiscrete = 0
	end

	-- blur screen edge during movement
	local player = PlayersService.LocalPlayer
	local subjectDelta = lastSubjPos - subjectPosition
	if subjectDelta.magnitude > 0.01 then
		self:StartVREdgeBlur(player)
	end
	-- straight view, not angled down
	local cameraFocusP = newCameraFocus.p
	local cameraLookVector = self:GetCameraLookVector()
	cameraLookVector = Vector3.new(cameraLookVector.X, 0, cameraLookVector.Z).Unit

	if self.stepRotateTimeout > 0 then
		self.stepRotateTimeout -= timeDelta
	end

	-- step rotate in 1st person
	local rotateInput = CameraInput.getRotation()
	local yawDelta = 0
	if UserGameSettings.VRSmoothRotationEnabled then
		yawDelta = rotateInput.X
	else
		if self.stepRotateTimeout <= 0.0 and math.abs(rotateInput.X) > 0.03 then
			yawDelta = 0.5
			if rotateInput.X < 0 then
				yawDelta = -0.5
			end
			self.needsReset = true
		end
	end

	local newLookVector = self:CalculateNewLookVectorFromArg(cameraLookVector, Vector2.new(yawDelta, 0))
	newCameraCFrame = CFrame.new(cameraFocusP - (FP_ZOOM * newLookVector), cameraFocusP)

	return newCameraCFrame, newCameraFocus
end

function VRCamera:UpdateThirdPersonTransform(timeDelta, newCameraCFrame, newCameraFocus, lastSubjPos, subjectPosition)
	local zoom = self:GetCameraToSubjectDistance()
	if zoom < 0.5 then
		zoom = 0.5
	end

	if lastSubjPos ~= nil and self.lastCameraFocus ~= nil then
		-- compute delta of subject since last update
		local player = PlayersService.LocalPlayer
		local subjectDelta = lastSubjPos - subjectPosition
		local moveVector = require(player:WaitForChild("PlayerScripts").PlayerModule:WaitForChild("ControlModule")):GetMoveVector()

		-- is the subject still moving?
		local isMoving = subjectDelta.magnitude > 0.01 or moveVector.magnitude > 0.01
		if isMoving then
			self.motionDetTime = 0.1
		end

		self.motionDetTime = self.motionDetTime - timeDelta
		if self.motionDetTime > 0 then
			isMoving = true
		end

		if isMoving and not self.needsReset then
			-- if subject moves keep old camera focus
			newCameraFocus = self.lastCameraFocus

			-- if the focus subject stopped, time to reset the camera
			self.VRCameraFocusFrozen = true
		else
			local subjectMoved = self.lastCameraResetPosition == nil or (subjectPosition - self.lastCameraResetPosition).Magnitude > 1

			-- compute offset for 3rd person camera rotation
			local rotateInput = CameraInput.getRotation()
			local userCameraPan = rotateInput ~= Vector2.new()
			local panUpdate = false
			if userCameraPan then
				if rotateInput.X ~= 0 then
					local tempRotation = self.cameraOffsetRotation + rotateInput.X;
					if(tempRotation < -math.pi) then
						tempRotation = math.pi - (tempRotation + math.pi) 
					else
						if (tempRotation > math.pi) then
							tempRotation = -math.pi + (tempRotation - math.pi) 
						end
					end
					self.cameraOffsetRotation = math.clamp(tempRotation, -math.pi, math.pi)
					if UserGameSettings.VRSmoothRotationEnabled then
						self.cameraOffsetRotationDiscrete = self.cameraOffsetRotation

						-- get player facing direction
						local humanoid = self:GetHumanoid()
						local forwardVector = humanoid.Torso and humanoid.Torso.CFrame.lookVector or Vector3.new(1,0,0)
						-- adjust camera height
						local vecToCameraAtHeight = Vector3.new(forwardVector.X, 0, forwardVector.Z)
						local newCameraPos = newCameraFocus.Position - vecToCameraAtHeight * zoom
						-- compute new cframe at height level to subject
						local lookAtPos = Vector3.new(newCameraFocus.Position.X, newCameraPos.Y, newCameraFocus.Position.Z)

						local tempCF = CFrame.new(newCameraPos, lookAtPos)
						tempCF = tempCF * CFrame.fromAxisAngle(Vector3.new(0,1,0), self.cameraOffsetRotationDiscrete)
						newCameraPos = lookAtPos - (tempCF.LookVector * (lookAtPos - newCameraPos).Magnitude)

						newCameraCFrame = CFrame.new(newCameraPos, lookAtPos)
					else
						local tempRotDisc = math.floor(self.cameraOffsetRotation * 12 / 12)
						if tempRotDisc ~= self.cameraOffsetRotationDiscrete then
							self.cameraOffsetRotationDiscrete = tempRotDisc
							panUpdate = true
						end
					end
				end
			end

			-- recenter the camera on teleport
			if (self.VRCameraFocusFrozen and subjectMoved) or self.needsReset or panUpdate then
				if not panUpdate then
					self.cameraOffsetRotationDiscrete = 0
					self.cameraOffsetRotation = 0
				end

				VRService:RecenterUserHeadCFrame()

				self.VRCameraFocusFrozen = false
				self.needsReset = false
				self.lastCameraResetPosition = subjectPosition

				self:ResetZoom()
				self:StartFadeFromBlack()

				-- get player facing direction
				local humanoid = self:GetHumanoid()
				local forwardVector = humanoid.Torso and humanoid.Torso.CFrame.lookVector or Vector3.new(1,0,0)
				-- adjust camera height
				local vecToCameraAtHeight = Vector3.new(forwardVector.X, 0, forwardVector.Z)
				local newCameraPos = newCameraFocus.Position - vecToCameraAtHeight * zoom
				-- compute new cframe at height level to subject
				local lookAtPos = Vector3.new(newCameraFocus.Position.X, newCameraPos.Y, newCameraFocus.Position.Z)

				if self.cameraOffsetRotation ~= 0 then
					local tempCF = CFrame.new(newCameraPos, lookAtPos)
					tempCF = tempCF * CFrame.fromAxisAngle(Vector3.new(0,1,0), self.cameraOffsetRotationDiscrete)
					newCameraPos = lookAtPos - (tempCF.LookVector * (lookAtPos - newCameraPos).Magnitude)
				end

				newCameraCFrame = CFrame.new(newCameraPos, lookAtPos)
			end
		end
	end

	return newCameraCFrame, newCameraFocus
end

function VRCamera:EnterFirstPerson()
	self.inFirstPerson = true
	
	self:UpdateMouseBehavior()
end

function VRCamera:LeaveFirstPerson()
	self.inFirstPerson = false
	self.needsReset = true
	
	self:UpdateMouseBehavior()
	
	if self.VRBlur then
		self.VRBlur.Visible = false
	end
end

return VRCamera