-- Written by Kip Turner, Copyright ROBLOX 2015

local TextService = game:GetService('TextService')

local CoreGui = game:GetService("CoreGui")
local GuiService = game:GetService('GuiService')
local RunService = game:GetService('RunService')
local ContextActionService = game:GetService("ContextActionService")

local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
local Modules = GuiRoot:FindFirstChild("Modules")

local PackageData = require(Modules:FindFirstChild('PackageData'))
local OutfitData = require(Modules:FindFirstChild('OutfitData'))
local ScrollingGridModule = require(Modules:FindFirstChild('ScrollingGrid'))
local AvatarTile = require(Modules:FindFirstChild('AvatarTile'))
local OutfitTile = require(Modules:FindFirstChild('OutfitTile'))
local Utility = require(Modules:FindFirstChild('Utility'))
local Http = require(Modules:FindFirstChild('Http'))
local ScreenManager = require(Modules:FindFirstChild('ScreenManager'))
local GlobalSettings = require(Modules:FindFirstChild('GlobalSettings'))
local LoadingWidget = require(Modules:FindFirstChild('LoadingWidget'))
local ThumbnailLoader = require(Modules:FindFirstChild('ThumbnailLoader'))
local UserData = require(Modules:FindFirstChild('UserData'))
local EventHub = require(Modules:FindFirstChild('EventHub'))

local Strings = require(Modules:FindFirstChild('LocalizedStrings'))
local AssetManager = require(Modules:FindFirstChild('AssetManager'))
local SoundManager = require(Modules:FindFirstChild('SoundManager'))


local GLOW_BASE_RPM = 2
local GLOW_TOP_RPM = -0.5
local GLOW_TRANSPARENCY = 0.2
local CATALOG_BELOW_POSITION = 320


local function CreateAvatarPane(parent)
	local this = {}

	local inFocus = false
	local isShown = false

	local AvatarObjects = {}

	local OnGuiServiceChangedConn = nil

	local lastParent = parent

	local CrossfadingProfileImage = false

	local MainContainer = Utility.Create'Frame'
	{
		Name = 'AvatarPane';
		Size = UDim2.new(1, 0, 1, 0);
		BackgroundTransparency = 1;
		Visible = false;
	}



	local MyAvatarContainer = Utility.Create'Frame'
	{
		Name = 'MyAvatarContainer';
		Size = UDim2.new(0.38,0,1,0);
		Position = UDim2.new(0,0,0,0);
		BackgroundTransparency = 1;
		ClipsDescendants = true;
		Parent = MainContainer;
	}
		local MyNameLabel = Utility.Create'TextLabel'
		{
			Name = 'MyNameLabel';
			Text = '';
			Size = UDim2.new(1,0,0,25);
			Position = UDim2.new(0,12,0,0);
			TextXAlignment = 'Left';
			TextColor3 = GlobalSettings.WhiteTextColor;
			Font = GlobalSettings.RegularFont;
			FontSize = GlobalSettings.SubHeaderSize;
			BackgroundTransparency = 1;
			Parent = MyAvatarContainer;
		};
		local ProfileImageContainer = Utility.Create'Frame'
		{
			Name = 'ProfileImageContainer';
			Size = UDim2.new(0.68,0,0.9,-MyNameLabel.Size.Y.Offset);
			Position = UDim2.new(0.16, 0, 0.05, MyNameLabel.Size.Y.Offset);
			BackgroundTransparency = 1;
			Parent = MyAvatarContainer;
		}
			local ProfileImage = Utility.Create'ImageLabel'
			{
				Name = 'ProfileImage';
				Size = UDim2.new(0,780,0,780);
				Position = UDim2.new(0.5, 0, 0.5, 0);
				BackgroundTransparency = 1;
				ZIndex = 3;
				Parent = ProfileImageContainer;
			};
				local CrossfadeProfileImage = Utility.Create'ImageLabel'
				{
					Name = 'CrossfadeProfileImage';
					Size = UDim2.new(1,0,1,0);
					Position = UDim2.new(0, 0, 0, 0);
					BackgroundTransparency = 1;
					ImageTransparency = 1;
					ZIndex = 3;
					Parent = ProfileImage;
				};
					local CharacterGlowBase = Utility.Create'ImageLabel'
					{
						Name = 'CharacterGlowBase';
						Size = UDim2.new(0,1015,0,1002);
						Position = UDim2.new(0, 0, 0, 0);
						BackgroundTransparency = 1;
						Image = 'rbxasset://textures/ui/Shell/Images/CharacterGlow/CharacterGlowBase.png';
						ImageTransparency = GLOW_TRANSPARENCY;
						Parent = CrossfadeProfileImage;
					};
					Utility.CalculateAnchor(CharacterGlowBase, UDim2.new(0.5, 0, 0.5, 0), Utility.Enum.Anchor.Center)
					local CharacterGlowTop = Utility.Create'ImageLabel'
					{
						Name = 'CharacterGlowTop';
						Size = UDim2.new(0,1026,0,1009);
						Position = UDim2.new(0, 0, 0, 0);
						BackgroundTransparency = 1;
						Image = 'rbxasset://textures/ui/Shell/Images/CharacterGlow/CharacterGlowTop.png';
						ImageTransparency = GLOW_TRANSPARENCY;
						ZIndex = 2;
						Parent = CrossfadeProfileImage;
					};
					Utility.CalculateAnchor(CharacterGlowTop, UDim2.new(0.5, 0, 0.5, 0), Utility.Enum.Anchor.Center)
			local function onSizeChanged()
				ProfileImage.Size = Utility.CalculateFill(ProfileImage, Vector2.new(576, 324))
				Utility.CalculateAnchor(ProfileImage, UDim2.new(0.5, 0, 0.5, 0), Utility.Enum.Anchor.Center)
			end

	local EquipButtonImage = Utility.Create'ImageLabel'
	{
		Name = 'EquipButtonImage';
		Size = UDim2.new(0,70,0,70);
		Position = UDim2.new(0.5, 25, 0.75, 0);
		Image = 'rbxasset://textures/ui/Shell/ButtonIcons/XButton.png';
		ImageTransparency = 1;
		BackgroundTransparency = 1;
		Parent = MainContainer;
	};
		local EquipHint = Utility.Create'TextLabel'
		{
			Name = 'EquipHint';
			Text = Strings:LocalizedString('EquipWord'):upper();
			Size = UDim2.new(0,0,1,0);
			Position = UDim2.new(1, 5, 0, -3);
			TextXAlignment = 'Left';
			TextColor3 = GlobalSettings.WhiteTextColor;
			TextTransparency = 1;
			Font = GlobalSettings.RegularFont;
			FontSize = GlobalSettings.TitleSize;
			BackgroundTransparency = 1;
			Parent = EquipButtonImage;
		};
		do
			local equipHintTextSize = TextService:GetTextSize(EquipHint.Text, Utility.ConvertFontSizeEnumToInt(EquipHint.FontSize), EquipHint.Font, Vector2.new())
			Utility.CalculateAnchor(EquipButtonImage, UDim2.new(0.95, -equipHintTextSize.X,1,0), Utility.Enum.Anchor.BottomRight)
		end

	local function TweenEquipButton(newValue, duration)
		duration = duration or 0.25
		Utility.PropertyTweener(EquipButtonImage, 'ImageTransparency', EquipButtonImage.ImageTransparency, newValue, duration, Utility.EaseOutQuad, true)
		Utility.PropertyTweener(EquipHint, 'TextTransparency', EquipHint.TextTransparency, newValue, duration, Utility.EaseOutQuad, true)
	end

	local function UpdateEquipButton(IsOwned, IsWearing)
		TweenEquipButton((IsOwned and not IsWearing) and 0 or 1)
	end

	local SelectableAvatarsContainer = Utility.Create'Frame'
	{
		Name = 'SelectableAvatarsContainer';
		Size = UDim2.new(0.6,0,1,0);
		Position = UDim2.new(0.4,0,0,0);
		BackgroundTransparency = 1;
		Parent = MainContainer;
	}
		local OutfitsTitle = Utility.Create'TextLabel'
		{
			Name = 'OutfitsTitle';
			Text = Strings:LocalizedString('AvatarOutfitsTitle'):upper();
			Size = UDim2.new(1,0,0,40);
			TextXAlignment = 'Left';
			TextColor3 = GlobalSettings.WhiteTextColor;
			Font = GlobalSettings.RegularFont;
			FontSize = GlobalSettings.SubHeaderSize;
			BackgroundTransparency = 1;
			Visible = false;
			Parent = SelectableAvatarsContainer;
		};

			local OutfitsScroller = ScrollingGridModule()
			OutfitsScroller:SetSize(UDim2.new(1,0,1,-OutfitsTitle.Size.Y.Offset - 40))
			OutfitsScroller:SetScrollDirection(OutfitsScroller.Enum.ScrollDirection.Horizontal)
			OutfitsScroller:SetCellSize(Vector2.new(220, 220))
			OutfitsScroller:SetSpacing(Vector2.new(25,25))
			OutfitsScroller:SetPosition(UDim2.new(0,0,0,OutfitsTitle.Size.Y.Offset))
			OutfitsScroller:SetRowColumnConstraint(1)
			-- OutfitsScroller:SetParent(SelectableAvatarsContainer)

		local CatalogTitle = Utility.Create'TextLabel'
		{
			Name = 'CatalogTitle';
			Text = Strings:LocalizedString('AvatarCatalogTitle'):upper();
			Size = UDim2.new(1,0,0,40);
			Position = UDim2.new(0,0,0,0);
			TextXAlignment = 'Left';
			TextColor3 = GlobalSettings.WhiteTextColor;
			Font = GlobalSettings.RegularFont;
			FontSize = GlobalSettings.SubHeaderSize;
			BackgroundTransparency = 1;
			Parent = SelectableAvatarsContainer;
		};

			local AvatarScroller = ScrollingGridModule()
			AvatarScroller:SetSize(UDim2.new(1,0,1,-CatalogTitle.Size.Y.Offset - 40))
			AvatarScroller:SetScrollDirection(AvatarScroller.Enum.ScrollDirection.Horizontal)
			AvatarScroller:SetCellSize(Vector2.new(220, 220))
			AvatarScroller:SetPosition(UDim2.new(0,0,0,40))
			AvatarScroller:SetSpacing(Vector2.new(25,25))
			AvatarScroller:SetRowColumnConstraint(2)
			AvatarScroller:SetParent(SelectableAvatarsContainer)

	local function OnAddToOutfitsScroller()
		CatalogTitle.Position = UDim2.new(0,0,0, CATALOG_BELOW_POSITION)
		AvatarScroller:SetPosition(UDim2.new(0,0,0,CATALOG_BELOW_POSITION + 40))
		AvatarScroller:SetRowColumnConstraint(1)
		OutfitsTitle.Visible = true
		OutfitsScroller:SetParent(SelectableAvatarsContainer)
	end


	local LoaderSpinner = nil
	local ProfileImageThumbnailLoader = nil
	local GlobalFadeCount = 1
	local function CrossfadeImage(frontImage, fadeImage, newImageUrl, duration)
		duration = duration or 0.75
		local fadeoutDuration = duration

		GlobalFadeCount = GlobalFadeCount + 1
		local thisFadeCount = GlobalFadeCount

		spawn(function()
			local gotNewThumbnail = false
			local function waitForWearReady()
				PackageData:AwaitWearAssetRequest()
			end
			local function waitForNewThumbnail()
				while not gotNewThumbnail do wait(0.1) end
			end

			if LoaderSpinner then LoaderSpinner:Cleanup() end
			local newLoaderSpinner = LoadingWidget({Parent = frontImage, ZIndex = 3}, {waitForWearReady, waitForNewThumbnail})
			LoaderSpinner = newLoaderSpinner
			spawn(function()
				newLoaderSpinner:AwaitFinished()
				newLoaderSpinner:Cleanup()
			end)
			waitForWearReady()

			if thisFadeCount ~= GlobalFadeCount then
				gotNewThumbnail = true
				return
			end


			CrossfadingProfileImage = true
			local startTime = tick()

			local noPreviousImage = (frontImage.Image == "" and fadeImage.Image == "")

			if frontImage.Image ~= newImageUrl then
				if noPreviousImage then
					fadeoutDuration = 0
				else
					fadeImage.Image = frontImage.Image
				end

				Utility.PropertyTweener(fadeImage, 'ImageTransparency', noPreviousImage and 0 or frontImage.ImageTransparency, 1, fadeoutDuration, Utility.EaseInOutQuad, true)
				Utility.PropertyTweener(CharacterGlowBase, 'ImageTransparency', CharacterGlowBase.ImageTransparency, 1, fadeoutDuration, Utility.EaseInOutQuad, true)
				Utility.PropertyTweener(CharacterGlowTop, 'ImageTransparency', CharacterGlowTop.ImageTransparency, 1, fadeoutDuration, Utility.EaseInOutQuad, true)
				frontImage.ImageTransparency = 1

				if ProfileImageThumbnailLoader then ProfileImageThumbnailLoader:Cancel() end
				ProfileImageThumbnailLoader = ThumbnailLoader:Create(frontImage, newImageUrl, ThumbnailLoader.Sizes.Large, ThumbnailLoader.AssetType.Avatar, true)
				ProfileImageThumbnailLoader:LoadAsync(false, false)

				wait(math.max(0, duration - (tick() - startTime)))

				gotNewThumbnail = true

				if thisFadeCount == GlobalFadeCount then
					Utility.PropertyTweener(frontImage, 'ImageTransparency', frontImage.ImageTransparency, 0, duration, Utility.EaseInOutQuad, true)
					Utility.PropertyTweener(CharacterGlowBase, 'ImageTransparency', CharacterGlowBase.ImageTransparency, GLOW_TRANSPARENCY, duration, Utility.EaseInOutQuad, true)
					Utility.PropertyTweener(CharacterGlowTop, 'ImageTransparency', CharacterGlowTop.ImageTransparency, GLOW_TRANSPARENCY, duration, Utility.EaseInOutQuad, true)
				end
			end
			CrossfadingProfileImage = false
		end)
	end

	local function UpdateProfileImage(nowWearingAssetId)
		MyNameLabel.Text = UserData:GetDisplayName()
		CrossfadeImage(ProfileImage, CrossfadeProfileImage, UserData.GetLocalUserIdAsync()) -- nowWearingAssetId) -- UserData:GetAvatarUrl(THUMBNAIL_SIZE.X, THUMBNAIL_SIZE.Y)..'&cb='..tostring(tick()))
	end

	UpdateProfileImage()

	local function onOwnershipChanged(tile, nowOwns)
		-- Move purchased packages into my outfits
		if nowOwns then
			local guiObject = tile and tile:GetGuiObject()
			if guiObject and AvatarScroller:ContainsItem(guiObject) then
				AvatarScroller:RemoveItem(guiObject)
				OutfitsScroller:AddItem(guiObject)
				OnAddToOutfitsScroller()
				if inFocus then
					GuiService.SelectedCoreObject = guiObject
				end
			end
		end
	end

	local ownershipChangedCns = {}

	local function listenToOwnershipChanged(tile)
		local packageInfo = tile and tile:GetPackageInfo()
		if packageInfo and not packageInfo:IsOwned() and (packageInfo.OwnershipChanged ~= nil) then
			if not ownershipChangedCns[tile] then
				ownershipChangedCns[tile] = packageInfo.OwnershipChanged:connect(function(nowOwns)
					onOwnershipChanged(tile, nowOwns)
				end)
			end
		end
	end

	local function removeListenToOwnershipChanged(tile)
		if ownershipChangedCns[tile] then
			ownershipChangedCns[tile]:disconnect()
			ownershipChangedCns[tile] = nil
		end
	end


	do
		local function loadCatalogPackages()
			local packages = PackageData:GetXboxCatalogPackagesAsync()

			if packages then
				for _, packageInfo in pairs(packages) do
					local avatarItemContainer = AvatarTile(packageInfo)
					if packageInfo:IsOwned() then
						OutfitsScroller:AddItem(avatarItemContainer:GetGuiObject())
						OnAddToOutfitsScroller()
					else
						AvatarScroller:AddItem(avatarItemContainer:GetGuiObject())
						listenToOwnershipChanged(avatarItemContainer)
					end

					AvatarObjects[avatarItemContainer:GetGuiObject()] = avatarItemContainer
					if isShown then
						avatarItemContainer:Show()
					end
				end
			end

		end

		local function loadOutfits()
			local outfits = OutfitData:GetMyOutfitsAsync()

			if outfits then
				for _, outfitInfo in pairs(outfits) do
					local outfitItemContainer = OutfitTile(outfitInfo)
					OutfitsScroller:AddItem(outfitItemContainer:GetGuiObject())
					OnAddToOutfitsScroller()
					AvatarObjects[outfitItemContainer:GetGuiObject()] = outfitItemContainer
					if isShown then
						outfitItemContainer:Show()
					end
				end
			end

		end

		SelectableAvatarsContainer.Visible = false
		local containerSize = SelectableAvatarsContainer.Size
		local loader = LoadingWidget(
			{Parent = MainContainer, Position = SelectableAvatarsContainer.Position + UDim2.new(containerSize.X.Scale / 2, containerSize.X.Offset / 2, containerSize.Y.Scale / 2, containerSize.Y.Offset / 2)},
			-- {loadCatalogPackages, loadOutfits})
			{loadCatalogPackages})
		spawn(function()
			loader:AwaitFinished()
			loader:Cleanup()
			loader = nil
			SelectableAvatarsContainer.Visible = true

			if inFocus and isShown and GuiService.SelectedCoreObject == nil then
				GuiService.SelectedCoreObject = this:GetDefaultSelectableObject()
			end

			if this.TransitionTweens == nil or #this.TransitionTweens == 0 then
				this.TransitionTweens = ScreenManager:FadeInSitu(SelectableAvatarsContainer)
			end
		end)
	end


	local function onEquipChanged()
		local selectedObject = GuiService.SelectedCoreObject

		if AvatarObjects[selectedObject] then
			local packageInfo = AvatarObjects[selectedObject]:GetPackageInfo()
			if packageInfo then
				UpdateEquipButton(packageInfo:IsOwned(), packageInfo:IsWearing())
			end
		end
	end

	local lastSelectedObject = GuiService.SelectedCoreObject
	local function OnSelectedCoreObjectChanged()
		local selectedObject = GuiService.SelectedCoreObject

		if AvatarObjects[lastSelectedObject] then
			AvatarObjects[lastSelectedObject]:RemoveFocus()
		end
		if AvatarObjects[selectedObject] then
			AvatarObjects[selectedObject]:Focus()

			onEquipChanged()
		end

		lastSelectedObject = selectedObject
	end


	function this:GetDefaultSelectableObject()
		if OutfitsScroller.GridItems[1] then
			return OutfitsScroller.GridItems[1]
		end
		if AvatarScroller.GridItems[1] then
			return AvatarScroller.GridItems[1]
		end
	end


	--[[ Public API ]]--
	function this:GetName()
		return Strings:LocalizedString('AvatarWord')
	end

	function this:IsFocused()
		return inFocus
	end

	function this:OnSelectAction()
		local selectedObject = GuiService.SelectedCoreObject
		if selectedObject and AvatarObjects[selectedObject] then
			SoundManager:Play('ButtonPress')
			AvatarObjects[selectedObject]:Select()
		end
	end

	function this:OpenEquippedPackage()
		if isShown and inFocus then
			for _, avatarItemContainer in pairs(AvatarObjects) do
				local packageInfo = avatarItemContainer:GetPackageInfo()
				if packageInfo then
					if packageInfo:IsWearing() then
						avatarItemContainer:OnClick()
					end
				end
			end
		end
	end
	--[[ End Public API ]]--

	local profileImageChangeCn = nil
	function this:Show()
		isShown = true

		Utility.DisconnectEvent(profileImageChangeCn)
		profileImageChangeCn = ProfileImageContainer.Changed:connect(function(prop)
			if prop == 'AbsoluteSize' then
				onSizeChanged()
			end
		end)
		onSizeChanged()

		local lastUpdate = tick()
		RunService:BindToRenderStep("UpdateAvatarGlow", Enum.RenderPriority.Camera.Value,
			function()
				local now = tick()
				local delta = now - lastUpdate

				CharacterGlowBase.Rotation = CharacterGlowBase.Rotation + delta * GLOW_BASE_RPM * 6 -- 6 = 360 / 60
				CharacterGlowTop.Rotation = CharacterGlowTop.Rotation + delta * GLOW_TOP_RPM * 6

				lastUpdate = now
			end)


		for _, avatarItemContainer in pairs(AvatarObjects) do
			avatarItemContainer:Show()
			listenToOwnershipChanged(avatarItemContainer)
			local packageInfo = tile and tile:GetPackageInfo()
			if packageInfo then
				onOwnershipChanged(avatarItemContainer, packageInfo:IsOwned())
			end
		end

		UpdateProfileImage()
		EventHub:addEventListener(EventHub.Notifications["AvatarEquipBegin"], "AvatarPane",
			function()
				-- spawn(function()
				-- 	EquipButtonImage.Visible = false
				-- 	TweenEquipButton(1, 0)
				-- 	PackageData:AwaitWearAssetRequest()
				-- 	EquipButtonImage.Visible = true
				-- end)
				UpdateProfileImage()
			end)

		EventHub:addEventListener(EventHub.Notifications["DonnedDifferentPackage"], "AvatarPane",
		function()
			onEquipChanged()
		end)
		EventHub:addEventListener(EventHub.Notifications["DonnedDifferentOutfit"], "AvatarPane",
		function()
			onEquipChanged()
			UpdateProfileImage()
		end)


		local seenXButtonPressed = false
		local function onSelectAvatar(actionName, inputState, inputObject)
			if inputState == Enum.UserInputState.Begin then
				seenXButtonPressed = true
			elseif inputState == Enum.UserInputState.End and seenXButtonPressed then
				self:OnSelectAction()
			end
		end

		ContextActionService:BindCoreAction("AvatarPaneSelectAction", onSelectAvatar, false, Enum.KeyCode.ButtonX)


		self.TransitionTweens = ScreenManager:DefaultFadeIn(MainContainer)

		MainContainer.Parent = lastParent
		MainContainer.Visible = true
	end


	function this:Hide()
		isShown = false
		MainContainer.Visible = false

		profileImageChangeCn = Utility.DisconnectEvent(profileImageChangeCn)

		RunService:UnbindFromRenderStep("UpdateAvatarGlow")
		ContextActionService:UnbindCoreAction("AvatarPaneSelectAction")


		for _, avatarItemContainer in pairs(AvatarObjects) do
			avatarItemContainer:Hide()
			removeListenToOwnershipChanged(avatarItemContainer)
		end

		EventHub:removeEventListener(EventHub.Notifications["AvatarEquipBegin"], "AvatarPane")
		EventHub:removeEventListener(EventHub.Notifications["DonnedDifferentPackage"], "AvatarPane")
		EventHub:removeEventListener(EventHub.Notifications["DonnedDifferentOutfit"], "AvatarPane")


		ScreenManager:DefaultCancelFade(self.TransitionTweens)
		self.TransitionTweens = nil
		-- Clean out saved selected object so when we tab back
		-- we will start with the default selection
		self.SavedSelectObject = nil
	end

	function this:Focus()
		inFocus = true

		Utility.DisconnectEvent(OnGuiServiceChangedConn)
		OnGuiServiceChangedConn = GuiService.Changed:connect(function(prop)
			if prop == 'SelectedCoreObject' then
				OnSelectedCoreObjectChanged()
			end
		end)
		OnSelectedCoreObjectChanged()

		if self.SavedSelectObject and self.SavedSelectObject:IsDescendantOf(MainContainer) then
			GuiService.SelectedCoreObject = self.SavedSelectObject
		else
			local defaultSelection = self:GetDefaultSelectableObject()
			if defaultSelection then
				GuiService.SelectedCoreObject = defaultSelection
			end
		end
	end

	function this:RemoveFocus()
		inFocus = false

		OnGuiServiceChangedConn = Utility.DisconnectEvent(OnGuiServiceChangedConn)

		local selectedObject = GuiService.SelectedCoreObject
		if isShown and selectedObject and selectedObject:IsDescendantOf(MainContainer) then
			self.SavedSelectObject = GuiService.SelectedCoreObject
			GuiService.SelectedCoreObject = nil
		end
	end

	function this:SetPosition(newPosition)
		MainContainer.Position = newPosition
	end

	function this:SetParent(newParent)
		lastParent = newParent
		MainContainer.Parent = newParent
	end

	return this
end

return CreateAvatarPane
