-- Keyboard controller is really keyboard and mouse controller
local computerInputTypeToModuleMap = {
	[Enum.UserInputType.Keyboard] = Keyboard,
	[Enum.UserInputType.MouseButton1] = Keyboard,
	[Enum.UserInputType.MouseButton2] = Keyboard,
	[Enum.UserInputType.MouseButton3] = Keyboard,
	[Enum.UserInputType.MouseWheel] = Keyboard,
	[Enum.UserInputType.MouseMovement] = Keyboard,
	[Enum.UserInputType.Gamepad1] = Gamepad,
	[Enum.UserInputType.Gamepad2] = Gamepad,
	[Enum.UserInputType.Gamepad3] = Gamepad,
	[Enum.UserInputType.Gamepad4] = Gamepad,
}

function ControlModule.new()
	local self = setmetatable({},ControlModule)
	
	-- The Modules above are used to construct controller instances as-needed, and this
	-- table is a map from Module to the instance created from it
	self.controllers = {}

	self.activeControlModule = nil	-- Used to prevent unnecessarily expensive checks on each input event
	self.activeController = nil
	self.touchJumpController = nil
	self.moveFunction = Players.LocalPlayer.Move
	self.humanoid = nil
	self.lastInputType = Enum.UserInputType.None
	self.cameraRelative = true
	
	-- For Roblox self.vehicleController
	self.humanoidSeatedConn = nil
	self.vehicleController = nil
	
	self.touchControlFrame = nil
	
	self.vehicleController = VehicleController.new(CONTROL_ACTION_PRIORITY)
	
	Players.LocalPlayer.CharacterAdded:Connect(function(char) self:OnCharacterAdded(char) end)
	Players.LocalPlayer.CharacterRemoving:Connect(function(char) self:OnCharacterAdded(char) end)
	if Players.LocalPlayer.Character then
		self:OnCharacterAdded(Players.LocalPlayer.Character)
	end
	
	RunService:BindToRenderStep("ControlScriptRenderstep", Enum.RenderPriority.Input.Value, function(dt) self:OnRenderStepped(dt) end)
	
	UserInputService.LastInputTypeChanged:Connect(function(newLastInputType) self:OnLastInputTypeChanged(newLastInputType) end)
	
	local propertyChangeListeners = {
		UserGameSettings:GetPropertyChangedSignal("TouchMovementMode"):Connect(function() self:OnTouchMovementModeChange() end),
		Players.LocalPlayer:GetPropertyChangedSignal("DevTouchMovementMode"):Connect(function() self:OnTouchMovementModeChange() end),
	
		UserGameSettings:GetPropertyChangedSignal("ComputerMovementMode"):Connect(function() self:OnComputerMovementModeChange() end),
		Players.LocalPlayer:GetPropertyChangedSignal("DevComputerMovementMode"):Connect(function() self:OnComputerMovementModeChange() end),
	}
	
	--[[ Touch Device UI ]]--
	self.playerGui = nil
	self.touchGui = nil
	self.playerGuiAddedConn = nil

	if UserInputService.TouchEnabled then
		self.playerGui = Players.LocalPlayer:FindFirstChildOfClass("PlayerGui")
		if self.playerGui then
			self:CreateTouchGuiContainer()
			self:OnLastInputTypeChanged(UserInputService:GetLastInputType())
		else
			self.playerGuiAddedConn = Players.LocalPlayer.ChildAdded:Connect(function(child)
				if child:IsA("PlayerGui") then
					self.playerGui = child
					self:CreateTouchGuiContainer()
					self.playerGuiAddedConn:Disconnect()
					self.playerGuiAddedConn = nil
					self:OnLastInputTypeChanged(UserInputService:GetLastInputType())
				end
			end)
		end
	else
		self:OnLastInputTypeChanged(UserInputService:GetLastInputType())
	end
	
	return self
end