--Commands module (formerly players module) v2 for ULX GUI -- by Stickly Man!
--Handles all user-executable commands, such as kick, slay, ban, etc.

local cmds = xlib.makepanel{ parent=xgui.null }
cmds.selcmd = nil
cmds.mask = xlib.makepanel{ x=160, y=30, w=425, h=330, parent=cmds }
cmds.argslist = xlib.makelistlayout{ w=165, h=330, parent=cmds.mask }
cmds.argslist.secondaryPos = nil

cmds.argslist.scroll:SetVisible( false )

function cmds.argslist:Open( cmd, secondary )
	if secondary then
		if cmds.plist.open then
			cmds.plist:Close()
		elseif self.open then
			self:Close()
		end
	end
	self:openAnim( cmd, secondary )
	self.open = true
end
function cmds.argslist:Close()
	self:closeAnim( self.secondaryPos )
	self.open = false
end
cmds.plist = xlib.makelistview{ w=250, h=330, multiselect=true, parent=cmds.mask }
function cmds.plist:Open( arg )
	if cmds.argslist.secondaryPos == true then cmds.argslist:Close()
	elseif self.open then self:Close() end
	self:openAnim( arg )
	--Determine if the arguments should be visible after changing (If a valid player will be selected)
	local targets = cmds.calculateValidPlayers( arg )
	local playerWillBeSelected = false
	for _, line in ipairs( cmds.plist:GetSelected() ) do
		if table.HasValue( targets, line.ply ) then
			playerWillBeSelected = true
			break
		end
	end
	if playerWillBeSelected then
		cmds.argslist:Open( ULib.cmds.translatedCmds[arg.cmd], false )
	end
	self.open = true
end
function cmds.plist:Close()
	if cmds.argslist.open then cmds.argslist:Close() end
	self:closeAnim()
	self.open = false
end
cmds.plist.DoDoubleClick = function()
	cmds.runCmd( cmds.selcmd )
end
cmds.plist:SetVisible( false )
cmds.plist:AddColumn( "Name" )
cmds.plist:AddColumn( "Group" )

cmds.cmds = xlib.makelistlayout{ x=5, y=30, w=150, h=330, parent=cmds, padding=1, spacing=1 }
cmds.setselected = function( selcat, LineID )
	if selcat.Lines[LineID]:GetColumnText(2) == cmds.selcmd then 
		selcat:ClearSelection()
		if cmds.plist.open then cmds.plist:Close() else cmds.argslist:Close() end
		xlib.animQueue_start()
		cmds.selcmd = nil
		return 
	end
	
	for _, cat in pairs( cmds.cmd_contents ) do
		if cat ~= selcat then
			cat:ClearSelection()
		end
	end
	cmds.selcmd = selcat.Lines[LineID]:GetColumnText(2)
	
	if cmds.permissionChanged then cmds.refreshPlist() return end
	
	if xlib.animRunning then xlib.animQueue_forceStop() end
	
	local cmd = ULib.cmds.translatedCmds[cmds.selcmd]
	if cmd.args[2] and ( cmd.args[2].type == ULib.cmds.PlayersArg or cmd.args[2].type == ULib.cmds.PlayerArg ) then
		cmds.plist:Open( cmd.args[2] )
	else
		cmds.argslist:Open( cmd, true )
	end
	xlib.animQueue_start()
end

local meta = FindMetaTable("Player")

function meta:IsFakenamed() 
	return (self:GetNWString("fake_name", false) or self:GetNWString("FakeGroup", false))
end

function cmds.refreshPlist( arg )
	if not arg then arg = ULib.cmds.translatedCmds[cmds.selcmd].args[2] end
	if not arg or ( arg.type ~= ULib.cmds.PlayersArg and arg.type ~= ULib.cmds.PlayerArg ) then return end
	
	local lastplys = {}
	for k, Line in pairs( cmds.plist.Lines ) do
		if ( Line:IsLineSelected() ) then table.insert( lastplys, Line:GetColumnText(1) ) end
	end
	
	local targets = cmds.calculateValidPlayers( arg )
	
	cmds.plist:Clear()
	cmds.plist:SetMultiSelect( arg.type == ULib.cmds.PlayersArg )
	for _, ply in ipairs( targets ) do
		if not ply:IsFakenamed() then
			local line = cmds.plist:AddLine( ply:Nick(), ply:GetUserGroup() )
			line.ply = ply
			line.OnSelect = function()
				if cmds.permissionChanged then return end
				
				if not xlib.animRunning and not cmds.argslist.open then
					cmds.argslist:Open( ULib.cmds.translatedCmds[cmds.selcmd], false )
					xlib.animQueue_start( )
				else
					if not cmds.clickedFlag then --Prevent this from happening multiple times.
						cmds.clickedFlag = true
						xlib.addToAnimQueue( function() if not cmds.argslist.open then
							cmds.argslist:Open( ULib.cmds.translatedCmds[cmds.selcmd], false ) end 
							cmds.clickedFlag = nil end )
					end
				end
			end
			
			--Select previously selected Lines
			if table.HasValue( lastplys, ply:Nick() ) then
				cmds.plist:SelectItem( line )
			end
		--Modified fix
		else
			local fline = cmds.plist:AddLine( ply:Nick(), ply:GetNWString( "FakeGroup", "user" ) )
			fline.ply = ply
			fline.OnSelect = function()
				if cmds.permissionChanged then return end
				
				if not xlib.animRunning and not cmds.argslist.open then
					cmds.argslist:Open( ULib.cmds.translatedCmds[cmds.selcmd], false )
					xlib.animQueue_start( )
				else
					if not cmds.clickedFlag then --Prevent this from happening multiple times.
						cmds.clickedFlag = true
						xlib.addToAnimQueue( function() if not cmds.argslist.open then
							cmds.argslist:Open( ULib.cmds.translatedCmds[cmds.selcmd], false ) end 
							cmds.clickedFlag = nil end )
					end
				end
			end
			
			if table.HasValue( lastplys, ply:Nick() ) then
				cmds.plist:SelectItem( fline )
			end
		--End Modified Fix
		end
	end
	--Select only the first item if multiselect is disabled.
	if not cmds.plist:GetMultiSelect() then
		local firstSelected = cmds.plist:GetSelected()[1]
		cmds.plist:ClearSelection()
		cmds.plist:SelectItem( firstSelected )
	end
	
	if not cmds.plist:GetSelectedLine() then
		if not xlib.animRunning then
			if cmds.argslist.open then
				cmds.argslist:Close()
				xlib.animQueue_start()
			end
		else
			if cmds.permissionChanged then
				xlib.addToAnimQueue( function() if cmds.argslist.open and cmds.plist.open then cmds.argslist:Close() end end )
			end
		end
	end
end

function cmds.calculateValidPlayers( arg )
	if not arg then arg = ULib.cmds.translatedCmds[cmds.selcmd].args[2] end

	local access, tag = LocalPlayer():query( arg.cmd )
	local restrictions = {}
	ULib.cmds.PlayerArg.processRestrictions( restrictions, LocalPlayer(), arg, ulx.getTagArgNum( tag, 1 ) )

	local targets = restrictions.restrictedTargets
	if targets == false then -- No one allowed
		targets = {}
	elseif targets == nil then -- Everyone allowed
		targets = player.GetAll()
	end
	return targets
end

function cmds.buildArgsList( cmd )
	cmds.argslist:Clear()
	cmds.curargs = {}
	local argnum = 0
	local expectingplayers = cmd.args[2] and ( ( cmd.args[2].type == ULib.cmds.PlayersArg ) or ( cmd.args[2].type == ULib.cmds.PlayerArg ) ) or false
	for _, arg in ipairs( cmd.args ) do
		if not arg.type.invisible then
			argnum = argnum + 1
			if not ( argnum == 1 and expectingplayers ) then
				if arg.invisible ~= true then
					local curitem = arg
					if curitem.repeat_min then --This command repeats!
						local panel = xlib.makepanel{ h=20 }
						local choices = {}
						panel.argnum = argnum
						panel.xguiIgnore = true
						panel.arg = curitem
						panel.addbutton = xlib.makebutton{ label="Add", w=83, parent=panel }
						panel.addbutton.DoClick = function( self )
							local parent = self:GetParent()
							local ctrl = parent.arg.type.x_getcontrol( parent.arg, parent.argnum )
							cmds.argslist:Add( ctrl )
							ctrl:MoveToAfter( choices[#choices] )
							table.insert( choices, ctrl )
							table.insert( cmds.curargs, ctrl )
							panel.removebutton:SetDisabled( false )
							if parent.arg.repeat_max and #choices >= parent.arg.repeat_max then self:SetDisabled( true ) end
						end
						panel.removebutton = xlib.makebutton{ label="Remove", x=83, w=82, disabled=true, parent=panel }
						panel.removebutton.DoClick = function( self )
							local parent = self:GetParent()
							local ctrl = choices[#choices]
							ctrl:Remove()
							table.remove( choices )
							table.remove( cmds.curargs )
							panel.addbutton:SetDisabled( false )
							if #choices <= parent.arg.repeat_min then self:SetDisabled( true ) end
						end
						cmds.argslist:Add( panel )
						for i=1,curitem.repeat_min do
							local ctrl = arg.type.x_getcontrol( arg, argnum )
							cmds.argslist:Add( ctrl )
							table.insert( choices, ctrl )
							table.insert( cmds.curargs, ctrl )
						end
						cmds.argrptadd = panel.addbutton --For hacky workaround, see below
						cmds.argrptrem = panel.removebutton
					else
						local panel = arg.type.x_getcontrol( arg, argnum )
						table.insert( cmds.curargs, panel )
						if curitem.type == ULib.cmds.NumArg then
							panel.TextArea.OnEnter = function( self )
								cmds.runCmd( cmd.cmd )
							end
						elseif curitem.type == ULib.cmds.StringArg then
							panel.OnEnter = function( self )
								cmds.runCmd( cmd.cmd )
							end
						end
						cmds.argslist:Add( panel )
						cmds.argrptadd = nil --For hacky workaround, see below
						cmds.argrptrem = nil
					end
				end
			end
		end
	end
	if LocalPlayer():query( cmd.cmd ) then
		local panel = xlib.makebutton{ label=cmd.cmd }
		panel.xguiIgnore = true
		panel.DoClick = function()
			cmds.runCmd( cmd.cmd )
		end
		cmds.argslist:Add( panel )
	end
	if cmd.opposite and LocalPlayer():query( cmd.opposite ) then
		local panel = xlib.makebutton{ label=cmd.opposite }
		panel.DoClick = function()
			cmds.runCmd( cmd.opposite )
		end
		panel.xguiIgnore = true
		cmds.argslist:Add( panel )
	end
	if cmd.helpStr then --If the command has a string for help
		local panel = xlib.makelabel{ w=160, label=cmd.helpStr, wordwrap=true }
		panel.xguiIgnore = true
		cmds.argslist:Add( panel )
	end
	
	--Hacky workaround for bug where the order of panels in GetChildren() changes sometimes when a textbox is clicked...
	if cmds.argrptadd and cmds.argrptadd:IsValid() then
		cmds.argrptadd:DoClick()	--Simulate adding/removing an arg.
		cmds.argrptrem:DoClick()	--I have no idea why this fixes the problem, either.
	end
end

function cmds.runCmd( cmd )
	local cmd = string.Explode( " ", cmd )
	if cmds.plist:IsVisible() then
		local plys = {}
		for _, line in ipairs( cmds.plist:GetSelected() ) do
			table.insert( plys, "$" .. ULib.getUniqueIDForPlayer( line.ply ) )
			table.insert( plys, "," )
		end
		table.remove( plys ) --Removes the final comma
		table.insert( cmd, table.concat( plys ) )
	end
	
	for _, arg in ipairs( cmds.curargs ) do
		if not arg.xguiIgnore then
			table.insert( cmd, arg:GetValue() )
		end
	end
	RunConsoleCommand( unpack( cmd ) )
end

function cmds.playerNameChanged( ply, old, new )
	for i, line in ipairs( cmds.plist.Lines ) do
		if line:GetColumnText(1) == old then
			line:SetColumnText( 1, new )
		end
	end
end

cmds.refresh = function( permissionChanged )
	local lastcmd = cmds.selcmd
	cmds.cmds:Clear()
	cmds.cmd_contents = {}
	cmds.expandedcat = nil
	cmds.selcmd = nil
	cmds.permissionChanged = true
	
	local newcategories = {}
	local sortcategories = {}
	local matchedCmdFound = false
	for cmd, data in pairs( ULib.cmds.translatedCmds ) do
		local opposite = data.opposite or ""
		if opposite ~= cmd and ( LocalPlayer():query( data.cmd ) or LocalPlayer():query( opposite ) ) then
			local catname = data.category
			if catname == nil or catname == "" then catname = "_Uncategorized" end
			if not cmds.cmd_contents[catname] then
				--Make a new category
				cmds.cmd_contents[catname] = xlib.makelistview{ headerheight=0, multiselect=false, h=136 }
				cmds.cmd_contents[catname].OnRowSelected = function( self, LineID ) cmds.setselected( self, LineID ) end
				cmds.cmd_contents[catname]:AddColumn( "" )
				local cat = xlib.makecat{ label=catname, contents=cmds.cmd_contents[catname], expanded=false, parent=xgui.null }
				function cat.Header:OnMousePressed( mcode )
					if ( mcode == MOUSE_LEFT ) then
						self:GetParent():Toggle()
						if cmds.expandedcat then
							if cmds.expandedcat ~= self:GetParent() then
								cmds.expandedcat:Toggle()
							else
								cmds.expandedcat = nil
								return
							end
						end
						cmds.expandedcat = self:GetParent()
						return 
					end
					return self:GetParent():OnMousePressed( mcode )
				end
				newcategories[catname] = cat
				table.insert( sortcategories, catname )
			end
			local line = cmds.cmd_contents[catname]:AddLine( string.gsub( data.cmd, "ulx ", "" ), data.cmd )
			if data.cmd == lastcmd then
				cmds.cmd_contents[catname]:SelectItem( line )
				cmds.expandedcat = cmds.cmd_contents[catname]:GetParent()
				cmds.expandedcat:SetExpanded( true )
				matchedCmdFound = true
			end
		end
	end
	if not matchedCmdFound then
		if cmds.plist.open then
			cmds.plist:Close()
			xlib.animQueue_start()
		elseif cmds.argslist.open then
			cmds.argslist:Close()
			xlib.animQueue_start()
		end
	end
	
	table.sort( sortcategories )
	for _, catname in ipairs( sortcategories ) do
		local cat = newcategories[catname]
		cmds.cmds:Add( cat )
		cat.Contents:SortByColumn( 1 )
		cat.Contents:SetHeight( 17*#cat.Contents:GetLines() )
	end
	cmds.permissionChanged = nil
end

--------------
--ANIMATIONS--
--------------
function cmds.plist:openAnim( arg )
	xlib.addToAnimQueue( cmds.refreshPlist, arg )
	xlib.addToAnimQueue( "pnlSlide", { panel=self, startx=-250, starty=0, endx=0, endy=0, setvisible=true } )
end

function cmds.plist:closeAnim()
	xlib.addToAnimQueue( "pnlSlide", { panel=self, startx=0, starty=0, endx=-250, endy=0, setvisible=false } )
end

function cmds.argslist:openAnim( cmd, secondary )
	xlib.addToAnimQueue( function() cmds.argslist.secondaryPos = secondary end )
	xlib.addToAnimQueue( cmds.buildArgsList, cmd )
	if secondary then
		xlib.addToAnimQueue( "pnlSlide", { panel=self.scroll, startx=-170, starty=0, endx=0, endy=0, setvisible=true } )
	else
		xlib.addToAnimQueue( "pnlSlide", { panel=self.scroll, startx=80, starty=0, endx=255, endy=0, setvisible=true } )	
	end
end

function cmds.argslist:closeAnim( secondary )
	if secondary then
		xlib.addToAnimQueue( "pnlSlide", { panel=self.scroll, startx=0, starty=0, endx=-170, endy=0, setvisible=false } )
	else
		xlib.addToAnimQueue( "pnlSlide", { panel=self.scroll, startx=255, starty=0, endx=80, endy=0, setvisible=false } )
	end
	xlib.addToAnimQueue( function() cmds.argslist.secondaryPos = nil end )
end
--------------

cmds.refresh()
hook.Add( "UCLChanged", "xgui_RefreshPlayerCmds", cmds.refresh )
hook.Add( "ULibPlayerNameChanged", "xgui_plyUpdateCmds", cmds.playerNameChanged )
xgui.addModule( "Cmds", cmds, "icon16/user_gray.png" )