local R=game:GetService("\082\101\112\108\105\099\097\116\101\100\083\116\111\114\097\103\101")local U=game:GetService("\080\108\097\121\101\114\115")local m=R.AvatarEditor local A=require(m.Client.GuiLib.LazyLoader)local H=require(m.Shared.Util.Maid)local P=require(m.Shared.Util.Signal)local J=require(m.Shared.Util.Promise)local S=require(m.Client.Theme)local i=U.LocalPlayer local s={}s.__index=s local O={}O.__index=O local function C(R,U,m,A,H)return A+(H-A)*((R-U)/(m-U))end local function x(R,U)local m=i.Character local A=m and m:FindFirstChildWhichIsA("\072\117\109\097\110\111\105\100")local H=(A and A.Health>0)and A:FindFirstChild(R)if H then H.Value=U else warn(R,"\110\111\116 \102\111\117\110\100 \111\114 \100\101\097\100")end end function O.new(R,U,m,J,S)local i=setmetatable({},O)i.Maid=H.new()i.Changed=P.new()i.Min=m i.Max=J i.Frame=R i.Callback=S i.Slider=A.Classes.Slider.new(R.SliderFrame)i.Slider.Interval=.01 i.Slider.TweenClick=false i.Slider:Set(C(U,m,J,0,1))i:Update(C(U,m,J,0,1))i.Maid:GiveTask(i.Slider.Changed:Connect(function(R)R=C(R,0,1,m,J)i:Update(R)end))i.Maid:GiveTask(i.Slider.DragStop:Connect(function()local R=C(i.Slider:Get(),0,1,m,J)i.Changed:Fire(R)end))i.Mask=A.Classes.TextMask.new(R.TextFrame.TextBox)i.Mask:SetMaskType("\073\110\116\101\103\101\114")i.Mask:SetMaxLength(3)i.Maid:GiveTask(i.Mask.Frame.FocusLost:Connect(function()local R=math.clamp(i.Mask:GetValue()/100,m,J)i.Slider:Set(C(R,m,J,0,1))i:Update(R)i.Changed:Fire(R)end))return i end function O.Set(U,R)U.Slider:Set(C(R,U.Min,U.Max,0,1))U.Mask.Frame.Text=string.format("\037\100",C(R,0,1,0,100))end function O.Update(U,R)local m=U.Frame local A=U.Callback m.TextFrame.TextBox.Text=string.format("\037\100",C(R,0,1,0,100))A(R)end function O.Destroy(R)R.Maid:DoCleaning()R.Maid=nil R.Changed:Destroy()R.Changed=nil R.Min=nil R.Max=nil R.Frame=nil R.Callback=nil R.Slider:Destroy()R.Slider=nil R.Mask:Destroy()R.Mask=nil setmetatable(R,nil)end function s.new(R,U)local m=setmetatable({},s)m.Maid=H.new()m.ScaleUpdated=P.new()m.Maid:GiveTask(S:Bind(R,"\083\099\114\111\108\108\066\097\114\073\109\097\103\101\067\111\108\111\114\051","\083\099\114\111\108\108\098\097\114"))for R,U in ipairs(R:GetChildren())do if not U:IsA("\070\114\097\109\101")then continue end m.Maid:GiveTask(S:Bind(U.SliderFrame.Background.Bar,"\066\097\099\107\103\114\111\117\110\100\067\111\108\111\114\051","\068\114\097\103\103\101\114\066\097\114"))m.Maid:GiveTask(S:Bind(U.SliderFrame.Dragger,"\066\097\099\107\103\114\111\117\110\100\067\111\108\111\114\051","\068\114\097\103\103\101\114"))m.Maid:GiveTask(S:Bind(U.TextFrame,"\066\097\099\107\103\114\111\117\110\100\067\111\108\111\114\051","\084\101\114\116\105\097\114\121"))m.Maid:GiveTask(S:Bind(U.TextFrame.TextBox,"\080\108\097\099\101\104\111\108\100\101\114\067\111\108\111\114\051","\080\108\097\099\101\104\111\108\100\101\114\084\101\120\116"))m.Maid:GiveTask(S:Bind(U.TextFrame.TextBox,"\084\101\120\116\067\111\108\111\114\051","\084\101\120\116"))m.Maid:GiveTask(S:Bind(U.TextLabel,"\084\101\120\116\067\111\108\111\114\051","\084\101\120\116"))end local A=O.new(R.BodyType,U.BodyTypeScale,0,1,function(R)x("\066\111\100\121\084\121\112\101\083\099\097\108\101",R)end)m.Maid.BodyTypeScale=A m.Maid:GiveTask(A.Changed:Connect(function(R)m.ScaleUpdated:Fire("\066\111\100\121\084\121\112\101\083\099\097\108\101",R)end))local J=O.new(R.Height,U.HeightScale,.9,1.05,function(R)x("\066\111\100\121\072\101\105\103\104\116\083\099\097\108\101",R)end)m.Maid.HeightScale=J m.Maid:GiveTask(J.Changed:Connect(function(R)m.ScaleUpdated:Fire("\072\101\105\103\104\116\083\099\097\108\101",R)end))local i=O.new(R.Width,U.WidthScale,.7,1,function(R)x("\066\111\100\121\087\105\100\116\104\083\099\097\108\101",R)end)m.Maid.WidthScale=i m.Maid:GiveTask(i.Changed:Connect(function(R)m.ScaleUpdated:Fire("\087\105\100\116\104\083\099\097\108\101",R)end))local C=O.new(R.Head,U.HeadScale,.95,1,function(R)x("\072\101\097\100\083\099\097\108\101",R)end)m.Maid.HeadScale=C m.Maid:GiveTask(C.Changed:Connect(function(R)m.ScaleUpdated:Fire("\072\101\097\100\083\099\097\108\101",R)end))local L=O.new(R.Proportion,U.ProportionScale,0,1,function(R)x("\066\111\100\121\080\114\111\112\111\114\116\105\111\110\083\099\097\108\101",R)end)m.Maid.ProportionScale=L m.Maid:GiveTask(L.Changed:Connect(function(R)m.ScaleUpdated:Fire("\080\114\111\112\111\114\116\105\111\110\083\099\097\108\101",R)end))return m end function s.UpdateWearing(m,R,U)local A=m.Maid[R]A:Set(U)end function s.Destroy(R)R.Maid:DoCleaning()R.Maid=nil R.ScaleUpdated:Destroy()R.ScaleUpdated=nil setmetatable(R,nil)end return s


local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")

local AvatarEditor = ReplicatedStorage.AvatarEditor
local GuiLib = require(AvatarEditor.Client.GuiLib.LazyLoader)
local Maid = require(AvatarEditor.Shared.Util.Maid)
local Signal = require(AvatarEditor.Shared.Util.Signal)
local Promise = require(AvatarEditor.Shared.Util.Promise)
local Theme = require(AvatarEditor.Client.Theme)

local player = Players.LocalPlayer

local Class = {}
Class.__index = Class

local Slider = {}
Slider.__index = Slider


local function map(n, inMin, inMax, outMin, outMax)
	return (outMin + ((outMax - outMin) * ((n - inMin) / (inMax - inMin))))
end


local function setScale(scale, value) -- TODO disable for R6?
	local character = player.Character
	local humanoid = character and character:FindFirstChildWhichIsA("Humanoid")
	local bodyScale = (humanoid and humanoid.Health > 0) and humanoid:FindFirstChild(scale)
	if bodyScale then
		bodyScale.Value = value
	else
		warn(scale, "not found or dead")
	end
end


function Slider.new(frame, x, min, max, callback)
	local self = setmetatable({}, Slider)
	
	self.Maid = Maid.new()
	self.Changed = Signal.new()
	self.Min = min
	self.Max = max
	self.Frame = frame
	self.Callback = callback
	
	self.Slider = GuiLib.Classes.Slider.new(frame.SliderFrame)

	self.Slider.Interval = 0.01
	self.Slider.TweenClick = false
	self.Slider:Set(map(x, min, max, 0, 1))

	self:Update(map(x, min, max, 0, 1))

	self.Maid:GiveTask(self.Slider.Changed:Connect(function(value)
		value = map(value, 0, 1, min, max)
		self:Update(value)
	end))

	self.Maid:GiveTask(self.Slider.DragStop:Connect(function()
		local value = map(self.Slider:Get(), 0, 1, min, max)
		self.Changed:Fire(value)
	end))

	self.Mask = GuiLib.Classes.TextMask.new(frame.TextFrame.TextBox)

	self.Mask:SetMaskType("Integer")
	self.Mask:SetMaxLength(3)

	self.Maid:GiveTask(self.Mask.Frame.FocusLost:Connect(function()
		local value = math.clamp(self.Mask:GetValue()/100, min, max)
		self.Slider:Set(map(value, min, max, 0, 1))
		self:Update(value)
		self.Changed:Fire(value)
	end))
	
	return self
end


function Slider:Set(value)
	self.Slider:Set(map(value, self.Min, self.Max, 0, 1))
	self.Mask.Frame.Text = string.format("%d", map(value, 0, 1, 0, 100))
end


function Slider:Update(value)
	local frame = self.Frame
	local callback = self.Callback
	
	frame.TextFrame.TextBox.Text = string.format("%d", map(value, 0, 1, 0, 100))
	callback(value)
end


function Slider:Destroy()
	self.Maid:DoCleaning()
	self.Maid = nil
	self.Changed:Destroy()
	self.Changed = nil
	self.Min = nil
	self.Max = nil
	self.Frame = nil
	self.Callback = nil
	self.Slider:Destroy()
	self.Slider = nil
	self.Mask:Destroy()
	self.Mask = nil
	setmetatable(self, nil)
end


function Class.new(frame, wearingScales)
	local self = setmetatable({}, Class)
	
	self.Maid = Maid.new()
	self.ScaleUpdated = Signal.new()
	
	self.Maid:GiveTask(Theme:Bind(frame, "ScrollBarImageColor3", "Scrollbar"))
	for i, v in ipairs(frame:GetChildren()) do
		if not v:IsA("Frame") then
			continue
		end
		
		self.Maid:GiveTask(Theme:Bind(v.SliderFrame.Background.Bar, "BackgroundColor3", "DraggerBar"))
		self.Maid:GiveTask(Theme:Bind(v.SliderFrame.Dragger, "BackgroundColor3", "Dragger"))
		self.Maid:GiveTask(Theme:Bind(v.TextFrame, "BackgroundColor3", "Tertiary"))
		self.Maid:GiveTask(Theme:Bind(v.TextFrame.TextBox, "PlaceholderColor3", "PlaceholderText"))
		self.Maid:GiveTask(Theme:Bind(v.TextFrame.TextBox, "TextColor3", "Text"))
		self.Maid:GiveTask(Theme:Bind(v.TextLabel, "TextColor3", "Text"))
	end
	
	
	local bodyTypeScale = Slider.new(frame.BodyType, wearingScales.BodyTypeScale, 0, 1, function(value)
		setScale("BodyTypeScale", value)
	end)
	self.Maid.BodyTypeScale = bodyTypeScale
	self.Maid:GiveTask(bodyTypeScale.Changed:Connect(function(value)
		self.ScaleUpdated:Fire("BodyTypeScale", value)
	end))
	
	
	local heightScale = Slider.new(frame.Height, wearingScales.HeightScale, 0.9, 1.05, function(value)
		setScale("BodyHeightScale", value)
	end)
	self.Maid.HeightScale = heightScale
	self.Maid:GiveTask(heightScale.Changed:Connect(function(value)
		self.ScaleUpdated:Fire("HeightScale", value)
	end))
	
	
	local widthScale = Slider.new(frame.Width, wearingScales.WidthScale, 0.7, 1, function(value)
		setScale("BodyWidthScale", value)
	end)
	self.Maid.WidthScale = widthScale
	self.Maid:GiveTask(widthScale.Changed:Connect(function(value)
		self.ScaleUpdated:Fire("WidthScale", value)
	end))
	
	
	local headScale = Slider.new(frame.Head, wearingScales.HeadScale, 0.95, 1, function(value)
		setScale("HeadScale", value)
	end)
	self.Maid.HeadScale = headScale
	self.Maid:GiveTask(headScale.Changed:Connect(function(value)
		self.ScaleUpdated:Fire("HeadScale", value)
	end))
	
	
	local proportionScale = Slider.new(frame.Proportion, wearingScales.ProportionScale, 0, 1, function(value)
		setScale("BodyProportionScale", value)
	end)
	self.Maid.ProportionScale = proportionScale
	self.Maid:GiveTask(proportionScale.Changed:Connect(function(value)
		self.ScaleUpdated:Fire("ProportionScale", value)
	end))
	
	return self
end


function Class:UpdateWearing(scale, value)
	local slider = self.Maid[scale]
	slider:Set(value)
end


function Class:Destroy()
	self.Maid:DoCleaning()
	self.Maid = nil
	self.ScaleUpdated:Destroy()
	self.ScaleUpdated = nil
	setmetatable(self, nil)
end


return Class
