--end


local function wear(player, description)
	assert(typeof(description) == "Instance" and description:IsA("HumanoidDescription"))
	
	local cache = outfitCache[player]
	if not cache then
		return
	end
	
	local humanoid = getHumanoid(player)
	if not humanoid then
		return
	end
	
	if description:IsDescendantOf(cache.Folder) then
		applyDescriptionToPlayer(player, description)
		:doneCall(descriptionUpdated, player, "reset")
		:catch(warn)
	end
end


local function theme(player, theme)
	assert(typeof(theme) == "string")
	
	local _settings = settingsCache[player]
	if not _settings then
		return
	end
	
	if not AvatarEditor.Themes:FindFirstChild(theme) then
		remoteEvent:Fire(player, "theme")
		return
	end
	
	_settings.Folder:SetAttribute("Theme", theme)
	module.SettingChanged:Fire(player, "Theme", theme)
end


local actions = {
	["accessory"] = accessory,
	["reset"] = reset,
	["tone"] = tone,
	["scale"] = scale,
	["create"] = create,
	["delete"] = delete,
	["wear"] = wear,
	["theme"] = theme,
}
local function onServerEvent(player, key, ...)
	assert(typeof(key) == "string")
	
	local canUse = UserCanUse:CanUse(player)
	if not canUse then
		module.PermissionFailed:Fire(player)
		return
	end
	
	local callback = actions[key]
	if callback then
		callback(player, ...)
	end
end


local function playerRemoving(player)
	local cache = outfitCache[player]
	if cache then
		for description, index in pairs(cache.Map) do
			cache.Map[description] = nil
		end
		cache.Map = nil
		cache.Folder:Destroy()
		cache.Folder = nil
		cache.Costumes = nil
		outfitCache[player] = nil
	end
	
	local _settings = settingsCache[player]
	if _settings then
		_settings.Folder:Destroy()
		_settings.Folder = nil
		_settings.Settings = nil
		settingsCache[player] = nil
	end
end


remoteEvent.OnServerEvent:Connect(onServerEvent)
Players.PlayerRemoving:Connect(playerRemoving)


function module:CacheSettings(player, config)
	-- TODO maybe validate table??
	local folder = Instance.new("Configuration")
	folder.Name = "AE_Settings"
	
	for k, v in pairs(config) do
		folder:SetAttribute(k, v)
	end
	
	settingsCache[player] = {
		Folder = folder,
		Settings = config,
	}
	
	folder.Parent = player
end


function module:CacheOutfits(player, costumes)
	local folder = Instance.new("Folder")
	folder.Name = "AE_Costumes"
	
	local map = {}
	
	for i, v in ipairs(costumes) do
		-- https://developer.roblox.com/en-us/api-reference/function/TextService/FilterStringAsync
		-- apparently you need to filter again to keep it updated
		-- is caching illegal? dont ban me roblox. its not publically displayed for everyone, just the user
		
		local description = module:ToHumanoidDescription(v)
		filterText(v.Name, player)
		:andThen(function(result)
			-- update the saved name to comply with new filter rules, if any.
			v.Name = result
		end)
		:catch(warn)
		description.Parent = folder
		map[description] = i
	end
	
	outfitCache[player] = {
		Folder = folder,
		Costumes = TableUtil.Copy(costumes),
		Map = map,
	}
	
	folder.Parent = player
end


function module:ApplyDescriptionFromInfo(character, info)
	return Promise.new(function(resolve, reject)
		local humanoid = character:WaitForChild("Humanoid")

		if not humanoid:IsDescendantOf(workspace) then
			humanoid.AncestryChanged:Wait()
		end
		
		resolve(humanoid)
	end)
	:andThen(function(humanoid)
		local description = info and module:ToHumanoidDescription(info)
		if description then
			applyDescriptionToHumanoid(humanoid, description)
			:catch(warn)
		end
	end)
end


local function fromHex(hex)
	local r, g, b = string.match(hex, "^#?(%w%w)(%w%w)(%w%w)$") -- owo
	return Color3.fromRGB(tonumber(r, 16), tonumber(g, 16), tonumber(b, 16))
end

local function toHex(color)
	return string.format("#%02X%02X%02X", color.R * 255, color.G * 255, color.B * 255)
end


function module:ToHumanoidDescription(info)
	-- TODO should probably check if _info_ is valid but whatever
	local description = Instance.new("HumanoidDescription")
	for k, v in pairs(info) do
		if string.find(k, "Color") then
			v = fromHex(v)
		end
		description[k] = v
	end
	
	-- TODO not sure if filter is needed here, need to get the player somehow
	--filterText(description.Name, player)
	--:andThen(function(result) --print(result)
	--	info.Name = result
	--end)
	--:catch(warn)
	
	return description
end


function module:ToAppearanceInfo(description)
	assert(typeof(description) == "Instance" and description:IsA("HumanoidDescription"), "ToAppearanceInfo is not a HumanoidDescription")
	
	local info = {}
	
	-- TODO here too
	--filterText(description.Name, player)
	--:andThen(function(result) --print(result)
	--	info.Name = result
	--end)
	--:catch(warn)
	
	info.BackAccessory = description.BackAccessory
	info.FaceAccessory = description.FaceAccessory
	info.FrontAccessory = description.FrontAccessory
	info.HairAccessory = description.HairAccessory
	info.HatAccessory = description.HatAccessory
	info.NeckAccessory = description.NeckAccessory
	info.ShouldersAccessory = description.ShouldersAccessory
	info.WaistAccessory = description.WaistAccessory
	
	info.Face = description.Face
	info.Head = description.Head
	info.LeftArm = description.LeftArm
	info.LeftLeg = description.LeftLeg
	info.RightArm = description.RightArm
	info.RightLeg = description.RightLeg
	info.Torso = description.Torso
	
	info.BodyTypeScale = description.BodyTypeScale
	info.DepthScale = description.DepthScale
	info.HeadScale = description.HeadScale
	info.HeightScale = description.HeightScale
	info.ProportionScale = description.ProportionScale
	info.WidthScale = description.WidthScale
	
	info.Pants = description.Pants
	info.Shirt = description.Shirt
	
	info.HeadColor = toHex(description.HeadColor)
	info.LeftArmColor = toHex(description.LeftArmColor)
	info.LeftLegColor = toHex(description.LeftLegColor)
	info.RightArmColor = toHex(description.RightArmColor)
	info.RightLegColor = toHex(description.RightLegColor)
	info.TorsoColor = toHex(description.TorsoColor)
	
	info.ClimbAnimation = description.ClimbAnimation
	info.FallAnimation = description.FallAnimation
	info.IdleAnimation = description.IdleAnimation
	info.JumpAnimation = description.JumpAnimation
	info.RunAnimation = description.RunAnimation
	info.SwimAnimation = description.SwimAnimation
	info.WalkAnimation = description.WalkAnimation
	
	for k, v in pairs(info) do
		if v == "" or ((v == 0) and not (string.find(k, "Scale"))) then
			info[k] = nil
		end
	end
	
	return info
end


function module:GetSettings(player)
	return settingsCache[player]
end


function module:GetOutfits(player)
	return outfitCache[player]
end


return module