--[[
]]
local CoreGui = Game:GetService("CoreGui")
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
local Modules = GuiRoot:FindFirstChild("Modules")
local GuiService = game:GetService("GuiService")
local UserInputService = game:GetService("UserInputService")

local ThumbnailLoader = require(Modules:FindFirstChild('ThumbnailLoader'))
local Utility = require(Modules:FindFirstChild('Utility'))

local function CreateImageSlider(size, position)
	local this = {}

	local items = {}
	local maxItems = 0
	local currentItemIndex = 1
	local padding = 0
	local focusSize = 450
	local itemSize = 300
	local dataTable = nil

	local lastSelectedObject = nil
	local newSelectedObjectCn = nil
	this.OnNewFocusItem = Utility.Signal()

	local imageObjectToLoaderMap = {}

	this.Container = Utility.Create'ScrollingFrame'
	{
		Name = "ImageSliderContainer";
		Size = size;
		Position = position;
		BackgroundTransparency = 1;
		ClipsDescendants = false;
		ScrollingEnabled = false;
		Selectable = false;
		ScrollBarThickness = 0;
	}

	local function loadNewImage(item, dataIndex)
		if imageObjectToLoaderMap[item] then
			imageObjectToLoaderMap[item]:Cancel()
			imageObjectToLoaderMap[item] = nil
		end
		local iconId = dataTable[dataIndex].IconId
		local thumbLoader = ThumbnailLoader:Create(item, iconId,
			ThumbnailLoader.Sizes.Medium, ThumbnailLoader.AssetType.Icon)
		imageObjectToLoaderMap[item] = thumbLoader
		spawn(function()
			thumbLoader:LoadAsync()
			imageObjectToLoaderMap[item] = nil
		end)
	end

	local MAX_LEFT = 2
	local MAX_RIGHT = 6
	-- TODO: Figure out left/right better
	local function recalcPositionAndSize(tweenTime, isRight)
		local smIconYPos = (focusSize - itemSize) / 2
		local startPosition = 0
		if currentItemIndex == 1 then
			startPosition = 0
		elseif currentItemIndex == 2 then
			startPosition = -1
		elseif maxItems == #items then
			startPosition = 1 - currentItemIndex
		elseif maxItems - currentItemIndex  < MAX_RIGHT then
			startPosition = maxItems - currentItemIndex - MAX_RIGHT - MAX_LEFT
		else
			startPosition = -MAX_LEFT
		end

		-- put front at back
		if maxItems > #items then
			if isRight == true and startPosition == -MAX_LEFT and currentItemIndex > 3 then
				local front = items[1]
				local back = items[#items]
				front.Position = UDim2.new(0, back.Position.X.Offset + back.Size.X.Offset + padding, 0, smIconYPos)
				this:RemoveFromFront()
				this:AddItemToBack(front)
				if dataTable and dataTable[currentItemIndex + MAX_RIGHT] then
					loadNewImage(front, currentItemIndex + MAX_RIGHT)
				end
			-- put back in front
			elseif isRight == false and currentItemIndex < maxItems - MAX_RIGHT and startPosition == -MAX_LEFT then
				local back = items[#items]
				local front = items[1]
				back.Position = UDim2.new(0, front.Position.X.Offset - padding - itemSize, 0, smIconYPos)
				this:RemoveFromBack()
				this:AddToFront(back)
				if dataTable and dataTable[currentItemIndex - MAX_LEFT] then
					loadNewImage(back, currentItemIndex - MAX_LEFT)
				end
			end
		end

		local currentXPosition = itemSize * startPosition + padding * startPosition
		for i = 1, #items do
			if items[i] == GuiService.SelectedCoreObject then
				if tweenTime == 0 then
					items[i].Size = UDim2.new(0, focusSize, 0, focusSize)
					items[i].Position = UDim2.new(0, currentXPosition, 0, 0)
				else
					items[i]:TweenSizeAndPosition(UDim2.new(0, focusSize, 0, focusSize), UDim2.new(0, currentXPosition, 0, 0),
						Enum.EasingDirection.InOut, Enum.EasingStyle.Sine, tweenTime, true)
				end
				currentXPosition = currentXPosition + focusSize + padding
			else
				if tweenTime == 0 then
					items[i].Size = UDim2.new(0, itemSize, 0, itemSize)
					items[i].Position = UDim2.new(0, currentXPosition, 0, smIconYPos)
				else
					items[i]:TweenSizeAndPosition(UDim2.new(0, itemSize, 0, itemSize), UDim2.new(0, currentXPosition, 0, smIconYPos),
						Enum.EasingDirection.InOut, Enum.EasingStyle.Sine, tweenTime, true)
				end
				currentXPosition = currentXPosition + itemSize + padding
			end
		end
	end

	--[[ Events ]]--
	newSelectedObjectCn = GuiService.Changed:connect(function(property)
		if property == 'SelectedCoreObject' then
			local selectedObject = GuiService.SelectedCoreObject
			if selectedObject and selectedObject ~= lastSelectedObject and selectedObject:IsDescendantOf(this.Container) then
				local isRight = nil
				if lastSelectedObject then
					-- slide left
					if lastSelectedObject.AbsolutePosition.x < selectedObject.AbsolutePosition.x then
						currentItemIndex = currentItemIndex + 1
						isRight = true
					-- slide right
					else
						currentItemIndex = currentItemIndex - 1
						isRight = false
					end
				end
				recalcPositionAndSize(lastSelectedObject and 0.25 or 0, isRight)
				lastSelectedObject = selectedObject
				this.OnNewFocusItem:fire(currentItemIndex, isRight, selectedObject)
			end
		end
	end)

	--[[ Public API ]]--
	function this:SetPosition(position)
		self.Container.Position = position
	end

	function this:SetSize(size)
		self.Container.Size = size
	end

	function this:SetParent(parent)
		self.Container.Parent = parent
	end

	function this:SetFocusPosition(index)
		if index > 0 and index < #items + 1 then
			currentItemIndex = index
			GuiService.SelectedCoreObject = items[currentItemIndex]
		end
	end

	function this:SetDataTable(tbl)
		dataTable = tbl
	end

	function this:SetMaxItems(value)
		local index = nil
		-- we're near the end, so we need to update image content
		if currentItemIndex > maxItems - MAX_RIGHT then
			-- find the current selected index in relation to image pool
			for i = 1, #items do
				if lastSelectedObject and items[i] == GuiService.SelectedCoreObject then
					index = i
					break
				end
			end
			-- TODO: Still might be a visual bug in some cases
			if index then
				-- replace images from front of pool
				for i = 1, index - 3 do
					if dataTable[currentItemIndex + i] then
						local item = items[1]
						loadNewImage(item, currentItemIndex + (maxItems - currentItemIndex) + i)
						print("NEW IMAGE:", currentItemIndex, ":", maxItems, ":", i, ":", currentItemIndex + (maxItems - currentItemIndex) + i)
						self:RemoveFromFront()
						self:AddItemToBack(item)
					end
				end
			end
		end
		maxItems = value
		recalcPositionAndSize(0)
	end

	function this:SetPadding(newPadding)
		padding = newPadding
	end

	function this:AddToFront(newItem)
		table.insert(items, 1, newItem)
		newItem.Parent = this.Container
	end

	function this:AddItemToBack(newItem)
		if this and this.Container then
			items[#items + 1] = newItem
			newItem.Parent = this.Container
		end
	end

	function this:AddItem(newItem)
		self:AddItemToBack(newItem)
	end

	function this:RemoveFromFront()
		table.remove(items, 1)
	end

	function this:RemoveFromBack()
		table.remove(items)
	end

	function this:Destroy()
		if newSelectedObjectCn then
			newSelectedObjectCn:disconnect()
			newSelectedObjectCn = nil
		end
		this.Container:Destroy()
		this.OnNewFocus = nil
		this = nil
	end

	return this
end

return CreateImageSlider
