local V={[Enum.NormalId.Top]=Vector3.new(0,1,0);[Enum.NormalId.Bottom]=Vector3.new(0,-1,0);[Enum.NormalId.Front]=Vector3.new(0,0,-1),[Enum.NormalId.Back]=Vector3.new(0,0,1),[Enum.NormalId.Left]=Vector3.new(-1,0,0),[Enum.NormalId.Right]=Vector3.new(1,0,0)}function HandleDragging.new(V)local K={Tool=V;IsHandleDragging=false,Handles=nil;InitialExtentsSize=nil;InitialExtentsCFrame=nil;InitialState=nil;InitialFocusCFrame=nil}return setmetatable(K,HandleDragging)end function HandleDragging.AttachHandles(I,K,q)if q and not I.Tool.Maid.AutofocusHandle then I.Tool.Maid.AutofocusHandle=Selection.FocusChanged:Connect(function()I:AttachHandles(Selection.Focus,true)end)elseif not q and I.Tool.Maid.AutofocusHandle then I.Tool.Maid.AutofocusHandle=nil end if I.Handles then I.Handles:BlacklistObstacle(BoundingBox.GetBoundingBox())I.Handles:SetAdornee(K)return end local y local function S()Core.Targeting.CancelSelecting()I.IsHandleDragging=true if BoundingBox.GetBoundingBox()then local V,K=BoundingBox.CalculateExtents(Selection.Parts,BoundingBox.StaticExtents)I.InitialExtentsSize=V I.InitialExtentsCFrame=K BoundingBox.PauseMonitoring()end local V,K,q=I.Tool:PrepareSelectionForDragging()I.InitialPartStates=V I.InitialModelStates=K I.InitialFocusCFrame=q I.Tool:TrackChange()if Core.Mode=="\084\111\111\108"then y=Security.GetPermissions(Security.GetSelectionAreas(Selection.Parts),Core.Player)end end local function w(K,q)if not I.IsHandleDragging then return end q=MoveUtil.GetIncrementMultiple(q,I.Tool.Increment)I.Tool:MovePartsAlongAxesByFace(K,q,I.InitialPartStates,I.InitialModelStates,I.InitialFocusCFrame)if Core.Mode=="\084\111\111\108"and Security.ArePartsViolatingAreas(Selection.Parts,Core.Player,false,y)then local V,K=next(I.InitialPartStates)V.CFrame=K.CFrame MoveUtil.TranslatePartsRelativeToPart(V,I.InitialPartStates,I.InitialModelStates)q=0 end I.Tool.DragChanged:Fire(q)if I.Tool.Axes=="\071\108\111\098\097\108"and BoundingBox.GetBoundingBox()then(BoundingBox.GetBoundingBox()).CFrame=I.InitialExtentsCFrame+V[K]*q end end local function L()if not I.IsHandleDragging then return end I.IsHandleDragging=false for V,K in pairs(I.InitialPartStates)do V:MakeJoints()Core.RestoreJoints(K.Joints)V.CanCollide=K.CanCollide V.Anchored=K.Anchored end I.Tool:RegisterChange()BoundingBox.RecalculateStaticExtents()BoundingBox.ResumeMonitoring()end local J=require(Libraries:WaitForChild("\072\097\110\100\108\101\115"))I.Handles=J.new({Color=I.Tool.Color.Color,Parent=Core.UIContainer;Adornee=K;ObstacleBlacklist={BoundingBox.GetBoundingBox()};OnDragStart=S,OnDrag=w,OnDragEnd=L})end function HandleDragging.HideHandles(V)if not V.Handles then return end V.Handles=V.Handles:Destroy()V.Tool.Maid.AutofocusHandle=nil end return HandleDragging

local AxisMultipliers = {
	[Enum.NormalId.Top] = Vector3.new(0, 1, 0);
	[Enum.NormalId.Bottom] = Vector3.new(0, -1, 0);
	[Enum.NormalId.Front] = Vector3.new(0, 0, -1);
	[Enum.NormalId.Back] = Vector3.new(0, 0, 1);
	[Enum.NormalId.Left] = Vector3.new(-1, 0, 0);
	[Enum.NormalId.Right] = Vector3.new(1, 0, 0);
}

function HandleDragging.new(Tool)
    local self = {
		Tool = Tool;

		-- Handle state
		IsHandleDragging = false;
		Handles = nil;

		-- Selection state
		InitialExtentsSize = nil;
		InitialExtentsCFrame = nil;
		InitialState = nil;
		InitialFocusCFrame = nil;
    }

    return setmetatable(self, HandleDragging)
end

function HandleDragging:AttachHandles(Part, Autofocus)
	-- Creates and attaches handles to `Part`, and optionally automatically attaches to the focused part

	-- Enable autofocus if requested and not already on
	if Autofocus and not self.Tool.Maid.AutofocusHandle then
		self.Tool.Maid.AutofocusHandle = Selection.FocusChanged:Connect(function ()
			self:AttachHandles(Selection.Focus, true)
		end)

	-- Disable autofocus if not requested and on
	elseif not Autofocus and self.Tool.Maid.AutofocusHandle then
		self.Tool.Maid.AutofocusHandle = nil
	end

	-- Just attach and show the handles if they already exist
	if self.Handles then
		self.Handles:BlacklistObstacle(BoundingBox.GetBoundingBox())
		self.Handles:SetAdornee(Part)
		return
	end

	local AreaPermissions
	local function OnHandleDragStart()
		-- Prepare for moving parts when the handle is clicked

		-- Prevent selection
		Core.Targeting.CancelSelecting()

		-- Indicate dragging via handles
		self.IsHandleDragging = true

		-- Freeze bounding box extents while dragging
		if BoundingBox.GetBoundingBox() then
			local InitialExtentsSize, InitialExtentsCFrame =
				BoundingBox.CalculateExtents(Selection.Parts, BoundingBox.StaticExtents)
			self.InitialExtentsSize = InitialExtentsSize
			self.InitialExtentsCFrame = InitialExtentsCFrame
			BoundingBox.PauseMonitoring()
		end

		-- Stop parts from moving, and capture the initial state of the parts
		local InitialPartStates, InitialModelStates, InitialFocusCFrame = self.Tool:PrepareSelectionForDragging()
		self.InitialPartStates = InitialPartStates
		self.InitialModelStates = InitialModelStates
		self.InitialFocusCFrame = InitialFocusCFrame

		-- Track the change
		self.Tool:TrackChange()

		-- Cache area permissions information
		if Core.Mode == 'Tool' then
			AreaPermissions = Security.GetPermissions(Security.GetSelectionAreas(Selection.Parts), Core.Player)
		end

	end

	local function OnHandleDrag(Face, Distance)
		-- Update parts when the handles are moved

		-- Only drag if handle is enabled
		if not self.IsHandleDragging then
			return
		end

		-- Calculate the increment-aligned drag distance
		Distance = MoveUtil.GetIncrementMultiple(Distance, self.Tool.Increment)

		-- Move the parts along the selected axes by the calculated distance
		self.Tool:MovePartsAlongAxesByFace(Face, Distance, self.InitialPartStates, self.InitialModelStates, self.InitialFocusCFrame)

		-- Make sure we're not entering any unauthorized private areas
		if Core.Mode == 'Tool' and Security.ArePartsViolatingAreas(Selection.Parts, Core.Player, false, AreaPermissions) then
			local Part, InitialPartState = next(self.InitialPartStates)
			Part.CFrame = InitialPartState.CFrame
			MoveUtil.TranslatePartsRelativeToPart(Part, self.InitialPartStates, self.InitialModelStates)
			Distance = 0
		end

		-- Signal out change in dragged distance
		self.Tool.DragChanged:Fire(Distance)

		-- Update bounding box if enabled in global axes movements
		if self.Tool.Axes == 'Global' and BoundingBox.GetBoundingBox() then
			BoundingBox.GetBoundingBox().CFrame = self.InitialExtentsCFrame + (AxisMultipliers[Face] * Distance)
		end

	end

	local function OnHandleDragEnd()
		if not self.IsHandleDragging then
			return
		end

		-- Disable dragging
		self.IsHandleDragging = false

		-- Make joints, restore original anchor and collision states
		for Part, State in pairs(self.InitialPartStates) do
			Part:MakeJoints()
			Core.RestoreJoints(State.Joints)
			Part.CanCollide = State.CanCollide
			Part.Anchored = State.Anchored
		end

		-- Register change
		self.Tool:RegisterChange()

		-- Resume bounding box updates
		BoundingBox.RecalculateStaticExtents()
		BoundingBox.ResumeMonitoring()
	end

	-- Create the handles
	local Handles = require(Libraries:WaitForChild 'Handles')
	self.Handles = Handles.new({
		Color = self.Tool.Color.Color,
		Parent = Core.UIContainer,
		Adornee = Part,
		ObstacleBlacklist = { BoundingBox.GetBoundingBox() },
		OnDragStart = OnHandleDragStart,
		OnDrag = OnHandleDrag,
		OnDragEnd = OnHandleDragEnd
	})

end

function HandleDragging:HideHandles()
	-- Hides the resizing handles

	-- Make sure handles exist and are visible
	if not self.Handles then
		return
	end

	-- Hide the handles
	self.Handles = self.Handles:Destroy()

	-- Disable handle autofocus
	self.Tool.Maid.AutofocusHandle = nil

end

return HandleDragging
