local l={}function SurfaceTool.Equip()ShowUI()EnableSurfaceSelection()SetSurface(SurfaceTool.Surface)end function SurfaceTool.Unequip()HideUI()ClearConnections()end function ClearConnections()for w,Z in pairs(l)do Z:Disconnect()l[w]=nil end end function ShowUI()local l=SurfaceTool if SurfaceTool.UI then SurfaceTool.UI.Visible=true UIUpdater=Support.ScheduleRecurringTask(UpdateUI,.1)return end SurfaceTool.UI=Core.Tool.Interfaces.BTSurfaceToolGUI:Clone()SurfaceTool.UI.Parent=Core.UI SurfaceTool.UI.Visible=true local w={"\065\108\108","\084\111\112","\066\111\116\116\111\109";"\070\114\111\110\116","\066\097\099\107","\076\101\102\116","\082\105\103\104\116"}local function Z()return Roact.createElement(Dropdown,{Position=UDim2.new(0,30,0,0);Size=UDim2.new(0,72,0,25),Options=w;MaxRows=4;CurrentOption=l.Surface;OnOptionSelected=function(l)SetSurface(l)end})end local M=Roact.mount(Z(),l.UI.SideOption,"\068\114\111\112\100\111\119\110")l.OnSurfaceChanged:Connect(function()Roact.update(M,Z())end)local I={"\083\109\111\111\116\104";"\083\116\117\100\115";"\073\110\108\101\116","\087\101\108\100","\072\105\110\103\101","\077\111\116\111\114","\085\110\105\118\101\114\115\097\108";"\071\108\117\101"}local function K()return Roact.createElement(Dropdown,{Position=UDim2.new(0,30,0,0);Size=UDim2.new(0,91,0,25),Options=I;MaxRows=4;CurrentOption=l.CurrentSurfaceType and l.CurrentSurfaceType.Name;OnOptionSelected=function(l)SetSurfaceType(Enum.SurfaceType[l])end})end local G=Roact.mount(K(),l.UI.TypeOption,"\068\114\111\112\100\111\119\110")l.OnSurfaceTypeChanged:Connect(function()Roact.update(G,K())end)local j=(SurfaceTool.UI:WaitForChild("\084\105\116\108\101")):WaitForChild("\083\105\103\110\097\116\117\114\101")ListenForManualWindowTrigger(SurfaceTool.ManualText,SurfaceTool.Color.Color,j)UIUpdater=Support.ScheduleRecurringTask(UpdateUI,.1)end function HideUI()if not SurfaceTool.UI then return end SurfaceTool.UI.Visible=false UIUpdater:Stop()end function GetSurfaceTypeDisplayName(l)if l==Enum.SurfaceType.SteppingMotor then return"\083\116\101\112\112\105\110\103 \077\111\116\111\114"elseif l==Enum.SurfaceType.SmoothNoOutlines then return"\078\111 \079\117\116\108\105\110\101"else return l.Name end end function UpdateUI()local l=SurfaceTool if not SurfaceTool.UI then return end if#Selection.Parts==0 then if l.CurrentSurfaceType~=""then l.CurrentSurfaceType=""l.OnSurfaceTypeChanged:Fire("")end return end local w={}for l,Z in pairs(Selection.Parts)do if SurfaceTool.Surface=="\065\108\108"then table.insert(w,Z.TopSurface)table.insert(w,Z.BottomSurface)table.insert(w,Z.FrontSurface)table.insert(w,Z.BackSurface)table.insert(w,Z.LeftSurface)table.insert(w,Z.RightSurface)else table.insert(w,Z[SurfaceTool.Surface.."\083\117\114\102\097\099\101"])end end local Z=Support.IdentifyCommonItem(w)if l.CurrentSurfaceType~=Z then l.CurrentSurfaceType=Z l.OnSurfaceTypeChanged:Fire(Z)end end function SetSurface(l)SurfaceTool.Surface=l SurfaceTool.OnSurfaceChanged:Fire(l)end function SetSurfaceType(l)if not SurfaceTool.Surface then return end TrackChange()for w,Z in pairs(Selection.Parts)do if SurfaceTool.Surface=="\065\108\108"then Z.TopSurface=l Z.BottomSurface=l Z.FrontSurface=l Z.BackSurface=l Z.LeftSurface=l Z.RightSurface=l else Z[SurfaceTool.Surface.."\083\117\114\102\097\099\101"]=l end end RegisterChange()end function EnableSurfaceSelection()l.SurfaceSelection=Core.Mouse.Button1Down:Connect(function()local l,w=Core.Targeting:UpdateTarget()if Selection.IsSelected(w)then SetSurface(Core.Mouse.TargetSurface.Name)end end)end function TrackChange()HistoryRecord={Parts=Support.CloneTable(Selection.Parts);BeforeSurfaces={},AfterSurfaces={},Selection=Selection.Items;Unapply=function(l)Selection.Replace(l.Selection)local w={}for Z,M in pairs(l.Parts)do table.insert(w,{Part=M;Surfaces=l.BeforeSurfaces[M]})end Core.SyncAPI:Invoke("\083\121\110\099\083\117\114\102\097\099\101",w)end,Apply=function(l)Selection.Replace(l.Selection)local w={}for Z,M in pairs(l.Parts)do table.insert(w,{Part=M;Surfaces=l.AfterSurfaces[M]})end Core.SyncAPI:Invoke("\083\121\110\099\083\117\114\102\097\099\101",w)end}for l,w in pairs(HistoryRecord.Parts)do HistoryRecord.BeforeSurfaces[w]={}local Z=HistoryRecord.BeforeSurfaces[w]if SurfaceTool.Surface=="\065\108\108"then Z.Top=w.TopSurface Z.Bottom=w.BottomSurface Z.Front=w.FrontSurface Z.Back=w.BackSurface Z.Left=w.LeftSurface Z.Right=w.RightSurface else Z[SurfaceTool.Surface]=w[SurfaceTool.Surface.."\083\117\114\102\097\099\101"]end end end function RegisterChange()if not HistoryRecord then return end local l={}for w,Z in pairs(HistoryRecord.Parts)do HistoryRecord.AfterSurfaces[Z]={}local M=HistoryRecord.AfterSurfaces[Z]if SurfaceTool.Surface=="\065\108\108"then M.Top=Z.TopSurface M.Bottom=Z.BottomSurface M.Front=Z.FrontSurface M.Back=Z.BackSurface M.Left=Z.LeftSurface M.Right=Z.RightSurface else M[SurfaceTool.Surface]=Z[SurfaceTool.Surface.."\083\117\114\102\097\099\101"]end table.insert(l,{Part=Z,Surfaces=M})end Core.SyncAPI:Invoke("\083\121\110\099\083\117\114\102\097\099\101",l)Core.History.Add(HistoryRecord)HistoryRecord=nil end

local Connections = {};

function SurfaceTool.Equip()
	-- Enables the tool's equipped functionality

	-- Start up our interface
	ShowUI();
	EnableSurfaceSelection();

	-- Set our current surface mode
	SetSurface(SurfaceTool.Surface);

end;

function SurfaceTool.Unequip()
	-- Disables the tool's equipped functionality

	-- Clear unnecessary resources
	HideUI();
	ClearConnections();

end;

function ClearConnections()
	-- Clears out temporary connections

	for ConnectionKey, Connection in pairs(Connections) do
		Connection:Disconnect();
		Connections[ConnectionKey] = nil;
	end;

end;

function ShowUI()
	-- Creates and reveals the UI

	local self = SurfaceTool

	-- Reveal UI if already created
	if SurfaceTool.UI then

		-- Reveal the UI
		SurfaceTool.UI.Visible = true;

		-- Update the UI every 0.1 seconds
		UIUpdater = Support.ScheduleRecurringTask(UpdateUI, 0.1);

		-- Skip UI creation
		return;

	end;

	-- Create the UI
	SurfaceTool.UI = Core.Tool.Interfaces.BTSurfaceToolGUI:Clone();
	SurfaceTool.UI.Parent = Core.UI;
	SurfaceTool.UI.Visible = true;

	-- Create type dropdown
	local Surfaces = {
		'All';
		'Top';
		'Bottom';
		'Front';
		'Back';
		'Left';
		'Right';
	}
	local function BuildSurfaceDropdown()
		return Roact.createElement(Dropdown, {
			Position = UDim2.new(0, 30, 0, 0);
			Size = UDim2.new(0, 72, 0, 25);
			Options = Surfaces;
			MaxRows = 4;
			CurrentOption = self.Surface;
			OnOptionSelected = function (Option)
				SetSurface(Option)
			end;
		})
	end

	-- Mount surface dropdown
	local SurfaceDropdownHandle = Roact.mount(BuildSurfaceDropdown(), self.UI.SideOption, 'Dropdown')
	self.OnSurfaceChanged:Connect(function ()
		Roact.update(SurfaceDropdownHandle, BuildSurfaceDropdown())
	end)

	-- Create type dropdown
	local SurfaceTypes = {
		'Smooth';
		'Studs';
		'Inlet';
		'Weld';
		'Hinge';
		'Motor';
		'Universal';
		'Glue';
	}
	local function BuildSurfaceTypeDropdown()
		return Roact.createElement(Dropdown, {
			Position = UDim2.new(0, 30, 0, 0);
			Size = UDim2.new(0, 91, 0, 25);
			Options = SurfaceTypes;
			MaxRows = 4;
			CurrentOption = self.CurrentSurfaceType and self.CurrentSurfaceType.Name;
			OnOptionSelected = function (Option)
				SetSurfaceType(Enum.SurfaceType[Option])
			end;
		})
	end

	-- Mount type dropdown
	local TypeDropdownHandle = Roact.mount(BuildSurfaceTypeDropdown(), self.UI.TypeOption, 'Dropdown')
	self.OnSurfaceTypeChanged:Connect(function ()
		Roact.update(TypeDropdownHandle, BuildSurfaceTypeDropdown())
	end)

	-- Hook up manual triggering
	local SignatureButton = SurfaceTool.UI:WaitForChild('Title'):WaitForChild('Signature')
	ListenForManualWindowTrigger(SurfaceTool.ManualText, SurfaceTool.Color.Color, SignatureButton)

	-- Update the UI every 0.1 seconds
	UIUpdater = Support.ScheduleRecurringTask(UpdateUI, 0.1);

end;

function HideUI()
	-- Hides the tool UI

	-- Make sure there's a UI
	if not SurfaceTool.UI then
		return;
	end;

	-- Hide the UI
	SurfaceTool.UI.Visible = false;

	-- Stop updating the UI
	UIUpdater:Stop();

end;

function GetSurfaceTypeDisplayName(SurfaceType)
	-- Returns a more friendly name for the given `SurfaceType`

	-- For stepping motors, add a space
	if SurfaceType == Enum.SurfaceType.SteppingMotor then
		return 'Stepping Motor';

	-- For no outlines, simplify name
	elseif SurfaceType == Enum.SurfaceType.SmoothNoOutlines then
		return 'No Outline';

	-- For other surface types, return their normal name
	else
		return SurfaceType.Name;

	end;

end;

function UpdateUI()
	-- Updates information on the UI

	local self = SurfaceTool

	-- Make sure the UI's on
	if not SurfaceTool.UI then
		return;
	end;

	-- Only show and identify current surface type if selection is not empty
	if #Selection.Parts == 0 then
		if self.CurrentSurfaceType ~= '' then
			self.CurrentSurfaceType = ''
			self.OnSurfaceTypeChanged:Fire('')
		end
		return;
	end;

	------------------------------------
	-- Update the surface type indicator
	------------------------------------

	-- Collect all different surface types in selection
	local SurfaceTypeVariations = {};
	for _, Part in pairs(Selection.Parts) do

		-- Search for variations on all surfaces if all surfaces are selected
		if SurfaceTool.Surface == 'All' then
			table.insert(SurfaceTypeVariations, Part.TopSurface);
			table.insert(SurfaceTypeVariations, Part.BottomSurface);
			table.insert(SurfaceTypeVariations, Part.FrontSurface);
			table.insert(SurfaceTypeVariations, Part.BackSurface);
			table.insert(SurfaceTypeVariations, Part.LeftSurface);
			table.insert(SurfaceTypeVariations, Part.RightSurface);

		-- Search for variations on single selected surface
		else
			table.insert(SurfaceTypeVariations, Part[SurfaceTool.Surface .. 'Surface']);
		end;

	end;

	-- Identify common surface type in selection
	local CommonSurfaceType = Support.IdentifyCommonItem(SurfaceTypeVariations);

	-- Update the current surface type in the surface type dropdown
	if self.CurrentSurfaceType ~= CommonSurfaceType then
		self.CurrentSurfaceType = CommonSurfaceType
		self.OnSurfaceTypeChanged:Fire(CommonSurfaceType)
	end
end

function SetSurface(SurfaceName)
	-- Changes the surface option to `Surface`

	-- Set the surface option
	SurfaceTool.Surface = SurfaceName;
	SurfaceTool.OnSurfaceChanged:Fire(SurfaceName)

end;

function SetSurfaceType(SurfaceType)
	-- Changes the selection's surface type on the currently selected surface

	-- Make sure a surface has been selected
	if not SurfaceTool.Surface then
		return;
	end;

	-- Track changes
	TrackChange();

	-- Change the surface of the parts locally
	for _, Part in pairs(Selection.Parts) do

		-- Change all surfaces if all selected
		if SurfaceTool.Surface == 'All' then
			Part.TopSurface = SurfaceType;
			Part.BottomSurface = SurfaceType;
			Part.FrontSurface = SurfaceType;
			Part.BackSurface = SurfaceType;
			Part.LeftSurface = SurfaceType;
			Part.RightSurface = SurfaceType;

		-- Change specific selected surface
		else
			Part[SurfaceTool.Surface .. 'Surface'] = SurfaceType;
		end;

	end;

	-- Register changes
	RegisterChange();

end;

function EnableSurfaceSelection()
	-- Allows the player to select surfaces by clicking on them

	-- Watch out for clicks on selected parts
	Connections.SurfaceSelection = Core.Mouse.Button1Down:Connect(function ()
		local _, ScopeTarget = Core.Targeting:UpdateTarget()
		if Selection.IsSelected(ScopeTarget) then

			-- Set the surface option to the target surface
			SetSurface(Core.Mouse.TargetSurface.Name);

		end;
	end);

end;

function TrackChange()

	-- Start the record
	HistoryRecord = {
		Parts = Support.CloneTable(Selection.Parts);
		BeforeSurfaces = {};
		AfterSurfaces = {};
		Selection = Selection.Items;

		Unapply = function (Record)
			-- Reverts this change

			-- Select the changed parts
			Selection.Replace(Record.Selection)

			-- Put together the change request
			local Changes = {};
			for _, Part in pairs(Record.Parts) do
				table.insert(Changes, { Part = Part, Surfaces = Record.BeforeSurfaces[Part]	});
			end;

			-- Send the change request
			Core.SyncAPI:Invoke('SyncSurface', Changes);

		end;

		Apply = function (Record)
			-- Applies this change

			-- Select the changed parts
			Selection.Replace(Record.Selection)

			-- Put together the change request
			local Changes = {};
			for _, Part in pairs(Record.Parts) do
				table.insert(Changes, { Part = Part, Surfaces = Record.AfterSurfaces[Part] });
			end;

			-- Send the change request
			Core.SyncAPI:Invoke('SyncSurface', Changes);

		end;

	};

	-- Collect the selection's initial state
	for _, Part in pairs(HistoryRecord.Parts) do

		-- Begin to record surfaces
		HistoryRecord.BeforeSurfaces[Part] = {};
		local Surfaces = HistoryRecord.BeforeSurfaces[Part];

		-- Record all surfaces if all selected
		if SurfaceTool.Surface == 'All' then
			Surfaces.Top = Part.TopSurface;
			Surfaces.Bottom = Part.BottomSurface;
			Surfaces.Front = Part.FrontSurface;
			Surfaces.Back = Part.BackSurface;
			Surfaces.Left = Part.LeftSurface;
			Surfaces.Right = Part.RightSurface;

		-- Record specific selected surface
		else
			Surfaces[SurfaceTool.Surface] = Part[SurfaceTool.Surface .. 'Surface'];
		end;

	end;

end;

function RegisterChange()
	-- Finishes creating the history record and registers it

	-- Make sure there's an in-progress history record
	if not HistoryRecord then
		return;
	end;

	-- Collect the selection's final state
	local Changes = {};
	for _, Part in pairs(HistoryRecord.Parts) do

		-- Begin to record surfaces
		HistoryRecord.AfterSurfaces[Part] = {};
		local Surfaces = HistoryRecord.AfterSurfaces[Part];

		-- Record all surfaces if all selected
		if SurfaceTool.Surface == 'All' then
			Surfaces.Top = Part.TopSurface;
			Surfaces.Bottom = Part.BottomSurface;
			Surfaces.Front = Part.FrontSurface;
			Surfaces.Back = Part.BackSurface;
			Surfaces.Left = Part.LeftSurface;
			Surfaces.Right = Part.RightSurface;

		-- Record specific selected surface
		else
			Surfaces[SurfaceTool.Surface] = Part[SurfaceTool.Surface .. 'Surface'];
		end;

		-- Create the change request for this part
		table.insert(Changes, { Part = Part, Surfaces = Surfaces });

	end;

	-- Send the changes to the server
	Core.SyncAPI:Invoke('SyncSurface', Changes);

	-- Register the record and clear the staging
	Core.History.Add(HistoryRecord);
	HistoryRecord = nil;

end;
