-- Item existence listeners
local Listeners = {};

function Selection.IsSelected(Item)
	-- Returns whether `Item` is selected or not

	-- Check and return item presence in index
	return Selection.ItemIndex[Item];

end;

function Selection.Add(Items, RegisterHistory)
	-- Adds the given items to the selection

	-- Get core API
	local Core = GetCore();

	-- Go through and validate each given item
	local SelectableItems = {};
	for _, Item in pairs(Items) do

		-- Make sure each item is valid and not already selected
		if Core.IsSelectable(Item) and not Selection.ItemIndex[Item] then
			table.insert(SelectableItems, Item);
		end;

	end;

	local OldSelection = Selection.Items;

	-- Go through the valid new selection items
	for _, Item in pairs(SelectableItems) do

		-- Add each valid item to the selection
		Selection.ItemIndex[Item] = true;

		-- Deselect items that are destroyed
		Listeners[Item] = Item.AncestryChanged:connect(function (Object, Parent)
			if Parent == nil then
				Selection.Remove({ Item });
			end;
		end);

	end;

	-- Update selected item list
	Selection.Items = Support.Keys(Selection.ItemIndex);

	-- Create a history record for this selection change, if requested
	if RegisterHistory and #SelectableItems > 0 then
		TrackSelectionChange(OldSelection);
	end;

	-- Create selection boxes for the selection
	CreateSelectionBoxes(SelectableItems);

	-- Fire relevant events
	Selection.ItemsAdded:fire(SelectableItems);
	Selection.Changed:fire();

end;

function Selection.Remove(Items, RegisterHistory)
	-- Removes the given items from the selection

	-- Go through and validate each given item
	local DeselectableItems = {};
	for _, Item in pairs(Items) do

		-- Make sure each item is actually selected
		if Selection.IsSelected(Item) then
			table.insert(DeselectableItems, Item);
		end;

	end;

	local OldSelection = Selection.Items;

	-- Go through the valid deselectable items
	for _, Item in pairs(DeselectableItems) do

		-- Remove item from selection
		Selection.ItemIndex[Item] = nil;

		-- Stop tracking item's parent
		Listeners[Item]:disconnect();
		Listeners[Item] = nil;

	end;

	-- Remove selection boxes from deselected items
	RemoveSelectionBoxes(DeselectableItems);

	-- Update selected item list
	Selection.Items = Support.Keys(Selection.ItemIndex);

	-- Create a history record for this selection change, if requested
	if RegisterHistory and #DeselectableItems > 0 then
		TrackSelectionChange(OldSelection);
	end;

	-- Fire relevant events
	Selection.ItemsRemoved:fire(DeselectableItems);
	Selection.Changed:fire();

end;

function Selection.Clear(RegisterHistory)
	-- Clears all items from selection

	-- Remove all selected items
	Selection.Remove(Selection.Items, RegisterHistory);

	-- Fire relevant events
	Selection.Cleared:fire();

end;

function Selection.Replace(Items, RegisterHistory)
	-- Replaces the current selection with the given new items

	-- Save old selection reference for history
	local OldSelection = Selection.Items;

	-- Clear current selection and select new items
	Selection.Clear(false);
	Selection.Add(Items, false);

	-- Create a history record for this selection change, if requested
	if RegisterHistory then
		TrackSelectionChange(OldSelection);
	end;

end;

function Selection.SetFocus(Item)
	-- Selects `Item` as the focused selection item

	-- Make sure the item is selected or is `nil`
	if not Selection.IsSelected(Item) and Item ~= nil then
		return;
	end;

	-- Set the item as the focus
	Selection.Focus = Item;

	-- Fire relevant events
	Selection.FocusChanged:fire(Item);

end;

function FocusOnLastSelectedPart()
	-- Sets the last part of the selection as the focus

	-- If selection is empty, clear the focus
	if #Selection.Items == 0 then
		Selection.SetFocus(nil);

	-- Otherwise, focus on the last part in the selection
	else
		Selection.SetFocus(Selection.Items[#Selection.Items]);
	end;

end;