
--[[
			// PackageData.lua

			// Created by Kip Turner
			// Copyright Roblox 2015
]]

local CoreGui = Game:GetService("CoreGui")
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
local Modules = GuiRoot:FindFirstChild("Modules")
local ShellModules = Modules:FindFirstChild("Shell")


local Utility = require(ShellModules:FindFirstChild('Utility'))

local Http = require(ShellModules:FindFirstChild('Http'))
local UserData = require(ShellModules:FindFirstChild('UserData'))
local EventHub = require(ShellModules:FindFirstChild('EventHub'))


local ContentProvider = game:GetService("ContentProvider")
local MarketplaceService = Game:GetService('MarketplaceService')
local PlatformService = nil
pcall(function() PlatformService = game:GetService('PlatformService') end)


local CurrentlyWearingAssetId = nil

local RequestingWearAsset = false

local function AwaitWearAssetRequest()
	while RequestingWearAsset do wait(0.1) end
end

-- local function PreloadCharacterAppearanceAsync()
-- 	-- Preload content so that when we join a game our appearance is cached
-- 	local myAsset = Http.GetCharactersAssetsAsync(UserData:GetLocalUserIdAsync())
-- 	print("MyAsset:" , myAsset)
-- 	if myAsset then
-- 		local assetList = Utility.SplitString(myAsset, ";")
-- 		print("Parsed:" , Utility.PrettyPrint(assetList))
-- 		ContentProvider:PreloadAsync(assetList)
-- 	end
-- end

local function PreloadCharacterAppearanceAsync()
	local character = nil
	local success, msg = pcall(function()
		character = game.Players:GetCharacterAppearanceAsync(UserData:GetLocalUserIdAsync())
	end)
	if character then
		local assetUrl = Http.BaseUrl .. 'asset/?id='
		local assetList = Utility.FindAssetsInModel(character, assetUrl)
		-- print("Preloading:" , Utility.PrettyPrint(assetList))
		ContentProvider:PreloadAsync(assetList)
	end


	return success
end

local function SetOwnedInternal(packageData, newValue)
	if packageData.Owned ~= newValue then
		packageData.Owned = newValue
		packageData.OwnershipChanged:fire(packageData.Owned)
	end
end


local function CreatePackageItem(data)
	local this = {}

	this.Owned = false
	this.OwnershipChanged = Utility.Signal()
	this.IsWearingChanged = Utility.Signal()

	local productInfo = nil

	-- print("PackageData:" , Utility.PrettyPrint(data))


	function this:GetAssetId()
		local assetId = data and data['AssetId']
		if not assetId then
			assetId = data and data['Item'] and data['Item']['AssetId']
		end
		return assetId
	end

	function this:GetProductIdAsync()
		while not productInfo do wait() end
		return productInfo and productInfo['ProductId']
	end

	function this:BuyAsync()
		print("Do buy" , 'productId' , self:GetProductIdAsync() , 'robuxPrice' , self:GetRobuxPrice())
		local purchaseResult = Http.PurchaseProductAsync(self:GetProductIdAsync(), self:GetRobuxPrice(), self:GetCreatorId(), 1)
		local nowOwns = purchaseResult and purchaseResult['TransactionVerb'] == 'bought'
		if nowOwns then
			SetOwnedInternal(self, nowOwns)
		end
		return purchaseResult
	end

	function this:IsOwned()
		return self.Owned
	end

	local lastIsWearing = nil
	function this:IsWearing()
		local wasWearing = lastIsWearing
		lastIsWearing = CurrentlyWearingAssetId and CurrentlyWearingAssetId == self:GetAssetId() and self:IsOwned()
		if lastIsWearing ~= wasWearing then
			self.IsWearingChanged:fire(lastIsWearing)
		end
		return lastIsWearing
	end

	function this:GetRobuxPrice()
		local robuxPrice = data and data['PriceInRobux']
		if not robuxPrice then
			robuxPrice = data and data['Product'] and data['Product']['PriceInRobux']
		end
		local isPublicDomain = data and data['IsPublicDomain'] == true
		if isPublicDomain == nil then
			isPublicDomain = data and data['Product'] and data['Product']['IsPublicDomain'] == true
		end
		if not robuxPrice and isPublicDomain then
			robuxPrice = 0
		end

		return robuxPrice
	end

	function this:GetCreatorId()
		return data and data['Creator'] and data['Creator']['Id']
	end

	function this:WearAsync()
		local assetId = self:GetAssetId()
		if assetId then
			AwaitWearAssetRequest()

			EventHub:dispatchEvent(EventHub.Notifications["AvatarEquipBegin"], assetId)

			RequestingWearAsset = true
			local result = Http.PostWearAssetAsync(assetId)
			RequestingWearAsset = false

			if result and result['success'] == true then
				EventHub:dispatchEvent(EventHub.Notifications["DonnedDifferentPackage"], assetId)
			end


			-- print("Http result from post wear asset:")
			-- print(Utility.PrettyPrint(result))
			return result
		end
	end

	function this:GetName()
		local resultPackageName = self:GetFullName()

		if resultPackageName then
			local colonPosition = string.find(resultPackageName, ":")
			if colonPosition then
				resultPackageName = string.sub(resultPackageName, 1, colonPosition - 1)
			end
		else
			resultPackageName = "Unknown"
		end

		return resultPackageName
	end

	function this:GetFullName()
		local name = data and data['Name']
		if not name then
			name = data and data['Item'] and data["Item"]['Name']
		end
		return name or "Unknown"
	end

	function this:GetDescriptionAsync()
		while not productInfo do wait() end
		return productInfo and productInfo['Description']
	end

	spawn(function()
		productInfo = MarketplaceService:GetProductInfo(this:GetAssetId())
		if productInfo == nil then productInfo = {} end
	end)

	return this
end



local PackageData = {}
local PackageCache = nil


local function GetAvailableXboxCatalogPackagesAsync()
	local isFinalPage = false

	local packages = {}
	local index = 0
	local count = 100

	repeat
		local result = nil

		Utility.ExponentialRepeat(
			function() return result == nil end,
			function() result = Http.GetXboxProductsAsync(index, count) end,
			2)

		if result then
			local items = result['Products']
			if items then
				if #items < count then
					isFinalPage = true
				end
				for _, itemInfo in pairs(items) do
					table.insert(packages, CreatePackageItem(itemInfo))
				end
			end
		end

		index = index + count
	until result == nil or isFinalPage
	-- print("Xbox products:" , packages , Utility.PrettyPrint(packages))

	if isFinalPage then
		return packages
	end
end

local function GetOwnedCatalogPackageIdsByUserAsync(userId)
	local packages = Http.GetUserOwnedPackagesAsync(userId)
	if packages then
		local data = packages['IsValid'] and packages['Data']
		local items = data and data['Items']
		local result = {}
		-- print("Items"  , Utility.PrettyPrint(items))
		if items then
			for _, itemInfo in pairs(items) do
				local assetId = itemInfo and itemInfo['Item'] and itemInfo['Item']['AssetId']
				result[assetId] = itemInfo
			end
		end
		return result
	end
end


local function getCatalogPackagesAsync()
	local xboxCatalogPackages = GetAvailableXboxCatalogPackagesAsync()
	local myPackages = GetOwnedCatalogPackageIdsByUserAsync(UserData:GetRbxUserId())

	if xboxCatalogPackages and myPackages then
		local result = {}
		for _, xboxPackage in pairs(xboxCatalogPackages) do
			local owned = (myPackages[xboxPackage:GetAssetId()] ~= nil)
			SetOwnedInternal(xboxPackage, owned)
			table.insert(result, xboxPackage)
		end


		-- NOTE: Temporary changed to get items that you bought outside xbox
		-- Create a map of what we already have
		local haveAssets = {}
		for _, package in pairs(result) do
			haveAssets[package:GetAssetId()] = true
		end

		for assetId, packageInfo in pairs(myPackages) do
			if not haveAssets[assetId] then
				local myPackage = CreatePackageItem(packageInfo)
				SetOwnedInternal(myPackage, true)
				table.insert(result, myPackage)
			end
		end
		return result
	end
end

local function SetCurrentlyWearingAssetId(newValue)
	if CurrentlyWearingAssetId ~= newValue then
		CurrentlyWearingAssetId = newValue
		if PackageCache then
			for _, package in pairs(PackageCache) do
				package:IsWearing()
			end
		end
	end
end


local UserChangedCount = 0
local function OnUserAccountChanged()
	UserChangedCount = UserChangedCount + 1

	PackageCache = nil
	SetCurrentlyWearingAssetId(nil)

	local wearingAsset = PackageData:GetCurrentlyWearingPackageAssetIdAsync()
	if not CurrentlyWearingAssetId then
		SetCurrentlyWearingAssetId(wearingAsset)
	end
end

spawn(function()
	local function queryWearingAsset()
		local wearingAsset = PackageData:GetCurrentlyWearingPackageAssetIdAsync()
		SetCurrentlyWearingAssetId(wearingAsset)
	end

	EventHub:addEventListener(EventHub.Notifications["DonnedDifferentPackage"], "PackageData",
	function(assetId)
		SetCurrentlyWearingAssetId(assetId)
		spawn(PreloadCharacterAppearanceAsync)
	end)
	EventHub:addEventListener(EventHub.Notifications["DonnedDifferentOutfit"], "PackageData",
	function(outfitId)
		queryWearingAsset()
		spawn(PreloadCharacterAppearanceAsync)
	end)
	EventHub:addEventListener(EventHub.Notifications["AuthenticationSuccess"], "PackageData", OnUserAccountChanged)
	-- Wait until we are ready to make the call be being signed in
	if UserData:GetRbxUserId() then
		queryWearingAsset()
		spawn(PreloadCharacterAppearanceAsync)
	end
end)

local debounceGetXboxCatalogPackages = false
function PackageData:GetXboxCatalogPackagesAsync()
	if debounceGetXboxCatalogPackages then
		while debounceGetXboxCatalogPackages do wait() end
	end
	debounceGetXboxCatalogPackages = true

	-- Ensure that the catalog data we are getting is applicable to the
	-- currently logged in user
	-- while not PackageCache do
		local startCount = UserChangedCount
		local packageData = nil

		Utility.ExponentialRepeat(
			function() return packageData == nil and startCount == UserChangedCount end,
			function() packageData = getCatalogPackagesAsync() end,
			3)

		if startCount == UserChangedCount then
			PackageCache = packageData
		end
	-- end

	debounceGetXboxCatalogPackages = false

	return PackageCache
end


function PackageData:GetCurrentlyWearingPackageAssetIdAsync()
	local currentWearingData = Http.GetXboxCurrentlyWearingPackageAsync()
	return currentWearingData and currentWearingData['AssetId']
end

function PackageData:GetCachedWearingPackage()
	return CurrentlyWearingAssetId
end

function PackageData:AwaitWearAssetRequest()
	AwaitWearAssetRequest()
end


return PackageData
