--[[
	LegacyCamera - Implements legacy controller types: Attach, Fixed, Watch
	2018 Camera Update - AllYourBlox
--]]

local FFlagUserCameraInputRefactor do
	local success, result = pcall(function()
		return UserSettings():IsUserFeatureEnabled("UserCameraInputRefactor3")
	end)
	FFlagUserCameraInputRefactor = success and result
end

local ZERO_VECTOR2 = Vector2.new()
local PITCH_LIMIT = math.rad(80)

local Util = require(script.Parent:WaitForChild("CameraUtils"))
local CameraInput = require(script.Parent:WaitForChild("CameraInput"))

--[[ Services ]]--
local PlayersService = game:GetService('Players')

--[[ The Module ]]--
local BaseCamera = require(script.Parent:WaitForChild("BaseCamera"))
local LegacyCamera = setmetatable({}, BaseCamera)
LegacyCamera.__index = LegacyCamera

function LegacyCamera.new()
	local self = setmetatable(BaseCamera.new(), LegacyCamera)

	self.cameraType = Enum.CameraType.Fixed
	self.lastUpdate = tick()
	self.lastDistanceToSubject = nil

	return self
end

function LegacyCamera:GetModuleName()
	return "LegacyCamera"
end

--[[ Functions overridden from BaseCamera ]]--
function LegacyCamera:SetCameraToSubjectDistance(desiredSubjectDistance)
	return BaseCamera.SetCameraToSubjectDistance(self,desiredSubjectDistance)
end

function LegacyCamera:Update(dt)

	-- Cannot update until cameraType has been set
	if not self.cameraType then return end

	local now = tick()
	local timeDelta = (now - self.lastUpdate)
	local camera = 	workspace.CurrentCamera
	local newCameraCFrame = camera.CFrame
	local newCameraFocus = camera.Focus
	local player = PlayersService.LocalPlayer

	if self.lastUpdate == nil or timeDelta > 1 then
		self.lastDistanceToSubject = nil
	end
	local subjectPosition = self:GetSubjectPosition()

	if self.cameraType == Enum.CameraType.Fixed then
		if self.lastUpdate and not FFlagUserCameraInputRefactor then
			-- Cap out the delta to 0.1 so we don't get some crazy things when we re-resume from
			local delta = math.min(0.1, now - self.lastUpdate)
			local gamepadRotation = self:UpdateGamepad()
			self.rotateInput = self.rotateInput + (gamepadRotation * delta)
		end

		if subjectPosition and player and camera then
			local distanceToSubject = self:GetCameraToSubjectDistance()
			local newLookVector
			if FFlagUserCameraInputRefactor then
				newLookVector = self:CalculateNewLookVectorFromArg(nil, CameraInput.getRotation())
			else
				newLookVector = self:CalculateNewLookVector()
				self.rotateInput = ZERO_VECTOR2
			end

			newCameraFocus = camera.Focus -- Fixed camera does not change focus
			newCameraCFrame = CFrame.new(camera.CFrame.p, camera.CFrame.p + (distanceToSubject * newLookVector))
		end

	elseif self.cameraType == Enum.CameraType.Attach then
		if FFlagUserCameraInputRefactor then
			local subjectCFrame = self:GetSubjectCFrame()
			local cameraPitch = camera.CFrame:ToEulerAnglesYXZ()
			local _, subjectYaw = subjectCFrame:ToEulerAnglesYXZ()
			
			cameraPitch = math.clamp(cameraPitch - CameraInput.getRotation().Y, -PITCH_LIMIT, PITCH_LIMIT)
			
			newCameraFocus = CFrame.new(subjectCFrame.p)*CFrame.fromEulerAnglesYXZ(cameraPitch, subjectYaw, 0)
			newCameraCFrame = newCameraFocus*CFrame.new(0, 0, self:StepZoom())
			
		else
			if subjectPosition and camera then
				local distanceToSubject = self:GetCameraToSubjectDistance()
				local humanoid = self:GetHumanoid()
				if self.lastUpdate and humanoid and humanoid.RootPart then

					-- Cap out the delta to 0.1 so we don't get some crazy things when we re-resume from
					local delta = math.min(0.1, now - self.lastUpdate)
					local gamepadRotation = self:UpdateGamepad()
					self.rotateInput = self.rotateInput + (gamepadRotation * delta)

					local forwardVector = humanoid.RootPart.CFrame.lookVector

					local y = Util.GetAngleBetweenXZVectors(forwardVector, self:GetCameraLookVector())
					if Util.IsFinite(y) then
						-- Preserve vertical rotation from user input
						self.rotateInput = Vector2.new(y, self.rotateInput.Y)
					end
				end

				local newLookVector = self:CalculateNewLookVector()
				self.rotateInput = ZERO_VECTOR2

				newCameraFocus = CFrame.new(subjectPosition)
				newCameraCFrame = CFrame.new(subjectPosition - (distanceToSubject * newLookVector), subjectPosition)
			end
		end

	elseif self.cameraType == Enum.CameraType.Watch then
		if subjectPosition and player and camera then
			local cameraLook = nil

			local humanoid = self:GetHumanoid()
			if humanoid and humanoid.RootPart then
				local diffVector = subjectPosition - camera.CFrame.p
				cameraLook = diffVector.unit

				if self.lastDistanceToSubject and self.lastDistanceToSubject == self:GetCameraToSubjectDistance() then
					-- Don't clobber the zoom if they zoomed the camera
					local newDistanceToSubject = diffVector.magnitude
					self:SetCameraToSubjectDistance(newDistanceToSubject)
				end
			end

			local distanceToSubject = self:GetCameraToSubjectDistance()
			local newLookVector
			if FFlagUserCameraInputRefactor then
				newLookVector = self:CalculateNewLookVectorFromArg(cameraLook, CameraInput.getRotation())
			else
				newLookVector = self:CalculateNewLookVector(cameraLook)
				self.rotateInput = ZERO_VECTOR2
			end

			newCameraFocus = CFrame.new(subjectPosition)
			newCameraCFrame = CFrame.new(subjectPosition - (distanceToSubject * newLookVector), subjectPosition)

			self.lastDistanceToSubject = distanceToSubject
		end
	else
		-- Unsupported type, return current values unchanged
		return camera.CFrame, camera.Focus
	end

	self.lastUpdate = now
	return newCameraCFrame, newCameraFocus
end

return LegacyCamera
