SELECTION_VERSION = "1.00"

--[[
	Lua-controlled Selection Library by Noya
	
	Installation:
	- "require" this file inside your code in order to add the new API functions to the PlayerResource global
	- Additionally, ensure your game scripts custom_net_tables.txt has a "selection" entry
	- Finally, ensure that you have the following files correctly added and included in your panorama content folder
		selection.xml, and include line on custom_ui_manifest.xml, on layout/custom_game/ folder
		selection folder containing selection.js and selection_filter.js, on /scripts/ folder
	Usage:
	- Functions with unit_args can recieve an Entity Index, a NPC Handle, or a table of each type.
	- Functions with unit can recieve an Entity Index or NPC Handle
	* Create a new selection for the player
		PlayerResource:NewSelection(playerID, unit_args)
	* Add units to the current selection of the player
		PlayerResource:AddToSelection(playerID, unit_args)
	
	* Remove units by index from the player selection group
		PlayerResource:RemoveFromSelection(playerID, unit_args)
	
	* Returns the list of units by entity index that are selected by the player
		PlayerResource:GetSelectedEntities(playerID)
	* Deselect everything, selecting the main hero (which can be redirected to another entity)
		PlayerResource:ResetSelection(playerID)
	* Get the index of the first selected unit of the player
		PlayerResource:GetMainSelectedEntity(playerID)
	
	* Check if a unit is selected or not by a player, returns bool
		PlayerResource:IsUnitSelected(playerID, unit_args)
	* Force a refresh of the current selection on all players, useful after abilities are removed
		PlayerResource:RefreshSelection()
	
	* Redirects the selection of the main hero to another entity of choice
		PlayerResource:SetDefaultSelectionEntity(playerID, unit)
	
	* Redirects the selection of any entity to another entity of choice
		hero:SetSelectionOverride(unit)
	* Use -1 to reset to default
		PlayerResource:SetDefaultSelectionEntity(playerID, -1)
		hero:SetSelectionOverride(-1)
	Notes:
	- Enemy units that you don't control can't be added to the selection group of a player
	- This library requires "libraries/timers.lua" to be present in your vscripts directory.
--]]

function CDOTA_PlayerResource:NewSelection(playerID, unit_args)
	local player = self:GetPlayer(playerID)
	if player then
		local entities = Selection:GetEntIndexListFromTable(unit_args)
		CustomGameEventManager:Send_ServerToPlayer(player, "selection_new", {entities = entities})
	end
end 

function CDOTA_PlayerResource:AddToSelection(playerID, unit_args)
	local player = self:GetPlayer(playerID)
	if player then
		local entities = Selection:GetEntIndexListFromTable(unit_args)
		CustomGameEventManager:Send_ServerToPlayer(player, "selection_add", {entities = entities})
	end
end

function CDOTA_PlayerResource:RemoveFromSelection(playerID, unit_args)
	local player = self:GetPlayer(playerID)
	if player then
		local entities = Selection:GetEntIndexListFromTable(unit_args)
		CustomGameEventManager:Send_ServerToPlayer(player, "selection_remove", {entities = entities})
	end
end

function CDOTA_PlayerResource:ResetSelection(playerID)
	local player = self:GetPlayer(playerID)
	if player then
		CustomGameEventManager:Send_ServerToPlayer(player, "selection_reset", {})
	end
end

function CDOTA_PlayerResource:GetSelectedEntities(playerID)
	return Selection.entities[playerID] or {}
end

function CDOTA_PlayerResource:GetMainSelectedEntity(playerID)
	local selectedEntities = self:GetSelectedEntities(playerID) 
	return selectedEntities and selectedEntities["0"]
end

function CDOTA_PlayerResource:IsUnitSelected(playerID, unit)
	if not unit then return false end
	local entIndex = type(unit)=="number" and unit or IsValidEntity(unit) and unit:GetEntityIndex()
	if not entIndex then return false end
	
	local selectedEntities = self:GetSelectedEntities(playerID)
	for _,v in pairs(selectedEntities) do
		if v==entIndex then
			return true
		end
	end
	return false
end

function CDOTA_PlayerResource:RefreshSelection()
	Timers:CreateTimer(0.03, function()
		FireGameEvent("dota_player_update_selected_unit", {})
	end)
end

function CDOTA_PlayerResource:SetDefaultSelectionEntity(playerID, unit)
	if not unit then unit = -1 end
	local entIndex = type(unit)=="number" and unit or unit:GetEntityIndex()
	local hero = self:GetSelectedHeroEntity(playerID)
	if hero then
		hero:SetSelectionOverride(unit)
	end
end

function CDOTA_PlayerResource:SetDefaultSelectionEntities(playerID, unit_args)
	local player = self:GetPlayer(playerID)
	if player then
		CustomNetTables:SetTableValue("selection", tostring(player:GetAssignedHero():entindex()), {entity = unit_args})
	end
end

function CDOTA_BaseNPC:SetSelectionOverride(reselect_unit)
	local unit = self
	local reselectIndex = type(reselect_unit)=="number" and reselect_unit or reselect_unit:GetEntityIndex()

	CustomNetTables:SetTableValue("selection", tostring(unit:GetEntityIndex()), {entity = reselectIndex})
end

------------------------------------------------------------------------
-- Internal
------------------------------------------------------------------------

require('libraries/timers')

if not Selection then
	Selection = class({})
end

function Selection:Init()
	Selection.entities = {} --Stores the selected entities of each playerID
	CustomGameEventManager:RegisterListener("selection_update", Dynamic_Wrap(Selection, 'OnUpdate'))
end

function Selection:OnUpdate(event)
	local playerID = event.PlayerID
	Selection.entities[playerID] = event.entities
end

-- Internal function to build an entity index list out of various inputs
function Selection:GetEntIndexListFromTable(unit_args)
	local entities = {}
	if type(unit_args)=="number" then
		table.insert(entities, unit_args) -- Entity Index
	-- Check contents of the table
	elseif type(unit_args)=="table" then
		if unit_args.IsCreature then
			table.insert(entities, unit_args:GetEntityIndex()) -- NPC Handle
		else
			for _,arg in pairs(unit_args) do
				-- Table of entity index values
				if type(arg)=="number" then
					table.insert(entities, arg)
				-- Table of npc handles
				elseif type(arg)=="table" then
					if arg.IsCreature then
						table.insert(entities, arg:GetEntityIndex())
					end
				end
			end
		end
	end
	return entities
end

if not Selection.entities then Selection:Init() end
