<commit_before>TOOL.Category		= "Wire - Tools"
TOOL.Name			= "Debugger"
TOOL.Command		= nil
TOOL.ConfigName		= ""
TOOL.Tab			= "Wire"

if ( CLIENT ) then
	language.Add( "Tool_wire_debugger_name", "Debugging Tool" )
	language.Add( "Tool_wire_debugger_desc", "Shows selected components info on the HUD." )
	language.Add( "Tool_wire_debugger_0", "Primary: Add component to HUD, Secondary: Remove component from HUD, Reload: Clear HUD" )
	language.Add( "Tool_wire_debugger_showports", "Show overlay of ports in HUD" )
	language.Add( "Tool_wire_debugger_orientvertical", "Orient the Inputs/Outputs Vertically" )
end

TOOL.ClientConVar[ "showports" ] = "1"
TOOL.ClientConVar[ "orientvertical" ] = "1"

local Components = {}

local function IsWire(entity) --try to find out if the entity is wire
	if entity.IsWire == true then return true end --this shold always be true if the ent is wire compatible, but only is if the base of the entity is "base_wire_entity" THIS NEEDS TO BE FIXED
	if entity.Inputs or entity.Outputs then return true end --this is how the wire STool gun does it
	return false
end

function TOOL:LeftClick(trace)
	if (not trace.Entity:IsValid()) then return end
	if (CLIENT) then return true end

	if(!IsWire(trace.Entity)) then return end

	ply_idx = self:GetOwner()
	Components[ply_idx] = Components[ply_idx] or {}

	for k,cmp in ipairs(Components[ply_idx]) do
		if (cmp == trace.Entity) then return end
	end

	table.insert(Components[ply_idx], trace.Entity)

	return true
end


function TOOL:RightClick(trace)
	if (not trace.Entity:IsValid()) then return end
	if (CLIENT) then return true end

	if(!IsWire(trace.Entity)) then return end

	ply_idx = self:GetOwner()
	if not Components[ply_idx] then return end

	for k,cmp in ipairs(Components[ply_idx]) do
		if (cmp == trace.Entity) then
			table.remove(Components[ply_idx], k)
			return true
		end
	end
	if not next(Components[ply_idx]) then
		umsg.Start("WireDbgLineCount", ply)
			umsg.Short(0)
		umsg.End()
		Components[ply_idx] = nil
	end
end

local LastOverBoxInput = ""
local LastOverBoxOutput = ""

function TOOL:Think() --get and transmit the info on the overlay, but only when needed
	local ply = self:GetOwner()
	if(CLIENT) then return end
	local Trace = ply:GetEyeTraceNoCursor()

	local ShowPorts = self:GetClientNumber("showports") ~= 0

	if(Trace.Hit and Trace.Entity and Trace.Entity:IsValid() and IsWire(Trace.Entity) and ShowPorts) then
		local Ent = Trace.Entity
		--get the inputs and put their current use state in-between
		if(Ent.Inputs) then
			local InputString = ""
			for InputIdx, CurInput in pairs_sortvalues(Ent.Inputs, WireLib.PortComparator) do
				InputString = InputString..InputIdx
				if(CurInput.Type != "NORMAL") then
					InputString = InputString.." ["..CurInput.Type.."]"
				end
				if(CurInput.Src and CurInput.Src:IsValid()) then -- check whether the input is wired up or not
					InputString = InputString..",W\n"
				else
					InputString = InputString..",N\n"
				end
			end

			if(InputString != LastOverBoxInput) then
				self:GetWeapon():SetNetworkedString("WireDebugOverlayInputs", InputString)
				LastOverBoxInput = InputString
			end
		else
			if(LastOverBoxInput != "") then
				LastOverBoxInput = ""
				self:GetWeapon():SetNetworkedString("WireDebugOverlayInputs", "")
			end
		end

		--get the outputs
		if(Ent.Outputs) then
			local OutputString = ""
			for OutputIdx, CurOutput in pairs_sortvalues(Ent.Outputs, WireLib.PortComparator) do
				OutputString = OutputString..OutputIdx
				if(CurOutput.Type != "NORMAL") then
					OutputString = OutputString.." ["..CurOutput.Type.."]"
				end
				OutputString = OutputString.."\n"
			end

			if(OutputString != LastOverBoxOutput) then
				self:GetWeapon():SetNetworkedString("WireDebugOverlayOutputs", OutputString)
				LastOverBoxOutput = OutputString
			end
		else
			if(LastOverBoxOutput != "") then
				LastOverBoxOutput = ""
				self:GetWeapon():SetNetworkedString("WireDebugOverlayOutputs", "")
			end
		end
	else
		if(LastOverBoxInput != "") then
			LastOverBoxInput = ""
			self:GetWeapon():SetNetworkedString("WireDebugOverlayInputs", "")
		end

		if(LastOverBoxOutput != "") then
			LastOverBoxOutput = ""
			self:GetWeapon():SetNetworkedString("WireDebugOverlayOutputs", "")
		end
	end

end

if CLIENT then

	function TOOL:DrawHUD()
		local InputText = self:GetWeapon():GetNetworkedString("WireDebugOverlayInputs") or ""
		local OutputText = self:GetWeapon():GetNetworkedString("WireDebugOverlayOutputs") or ""

		if(InputText != "") then
			surface.SetFont("Trebuchet24")
			local Inputs = string.Explode("\n",InputText)
			local InputsUsed = {}
			for i, Input in ipairs(Inputs) do
				InputsUsed[i] = string.Right(Input,2) == ",W"
				Inputs[i] = string.sub(Inputs[i],1,-3)
			end

			local FontHeight = draw.GetFontHeight("Trebuchet24")+1
			local MaxWidth = 0
			for i, Input in ipairs(Inputs) do
				local W, H = surface.GetTextSize(Input)
				if(W > MaxWidth) then
					MaxWidth = W
				end
			end


			draw.RoundedBox(8,
				ScrW()/2-(MaxWidth+16)-20,
				ScrH()/2-#Inputs*FontHeight/2-8,
				MaxWidth+16,
				(#Inputs-1)*FontHeight+16,
				Color(109,146,129,192)
			)

			for i, Input in ipairs(Inputs) do
				local TextCol = Color(255,255,255)
				if(InputsUsed[i] == true) then
					TextCol = Color(255,0,0)
				end
				draw.Text({
					text = Input or "",
					font = "Trebuchet24",
					pos = {ScrW()/2-(MaxWidth+16)-12, (FontHeight)*(i-1)+(ScrH()/2-#Inputs*FontHeight/2)},
					color = TextCol
				})
			end

		end

		if(OutputText != "") then
			surface.SetFont("Trebuchet24")
			local Outputs = string.Explode("\n",OutputText)

			local FontHeight = draw.GetFontHeight("Trebuchet24")+1
			local MaxWidth = 0
			for i, Output in ipairs(Outputs) do
				local W, H = surface.GetTextSize(Output)
				if(W > MaxWidth) then
					MaxWidth = W
				end
			end


			draw.RoundedBox(8,
				ScrW()/2+20,
				ScrH()/2-#Outputs*FontHeight/2-8,
				MaxWidth+16,
				(#Outputs-1)*FontHeight+16,
				Color(109,146,129,192)
			)

			for i, Output in ipairs(Outputs) do
				draw.Text({
					text = Output or "",
					font = "Trebuchet24",
					pos = {ScrW()/2+28, (FontHeight)*(i-1)+(ScrH()/2-#Outputs*FontHeight/2)},
					color = Color(255,255,255)
				})
			end
		end


	end
end

function TOOL:Reload(trace)
	if (CLIENT) then return end
	umsg.Start("WireDbgLineCount", ply)
		umsg.Short(0)
	umsg.End()
	Components[self:GetOwner()] = nil
end


if (SERVER) then

	local dbg_line_cache = {}
	local dbg_line_time = {}

	local formatPort = {}
	function formatPort.NORMAL(value)
		return string.format("%.3f",value)
	end

	function formatPort.STRING(value)
		return '"' .. value .. '"'
	end

	function formatPort.VECTOR(value)
		return string.format("(%.1f,%.1f,%.1f)", value[1], value[2], value[3])
	end

	function formatPort.ANGLE(value)
		return string.format("(%.1f,%.1f,%.1f)", value.p, value.y, value.r)
	end

	formatPort.ENTITY = function(ent)
		if not validEntity(ent) then return "(null)" end -- this uses validEntity from E2, which is faster, but maybe we shouldn't use it.
		return tostring(ent)
	end
	formatPort.BONE = e2_tostring_bone

	function formatPort.MATRIX(value)
		local RetText = "[11="..value[1]..",12="..value[2]..",13="..value[3]
			  RetText = RetText..",21="..value[4]..",22="..value[5]..",23="..value[6]
			  RetText = RetText..",31="..value[7]..",32="..value[8]..",33="..value[9].."]"
		return RetText
	end

	function formatPort.MATRIX2(value)
		local RetText = "[11="..value[1]..",12="..value[2]
			  RetText = RetText..",21="..value[3]..",22="..value[4].."]"
		return RetText
	end

	function formatPort.MATRIX4(value)
		local RetText = "[11="..value[1]..",12="..value[2]..",13="..value[3]..",14="..value[4]
			  RetText = RetText..",21="..value[5]..",22="..value[6]..",23="..value[7]..",24="..value[8]
			  RetText = RetText..",31="..value[9]..",32="..value[10]..",33="..value[11]..",34="..value[12]
			  RetText = RetText..",41="..value[13]..",42="..value[14]..",43="..value[15]..",44="..value[16].."]"
		return RetText
	end

	function formatPort.ARRAY(value, OrientVertical)
		local RetText = ""
		local ElementCount = 0
		for Index, Element in ipairs(value) do
			ElementCount = ElementCount+1
			if(ElementCount > 10) then
				break
			end
			RetText = RetText..Index.."="
			--Check for array element type
			if(type(Element) == "number") then --number
				RetText = RetText..formatPort.NORMAL(Element)
			elseif((type(Element) == "table" and #Element == 3) or type(Element) == "Vector") then --vector
				RetText = RetText..formatPort.VECTOR(Element)
			elseif(type(Element) == "table" and #Element == 2) then --vector2
				RetText = RetText..formatPort.VECTOR2(Element)
			elseif(type(Element) == "table" and #Element == 4) then --vector4
				RetText = RetText..formatPort.VECTOR4(Element)
			elseif((type(Element) == "table" and #Element == 3) or type(Element) == "Angle") then --angle
				if(type(Element) == "Angle") then
					RetText = RetText..formatPort.ANGLE(Element)
				else
					RetText = RetText.."(" .. math.Round(Element[1]*10)/10 .. "," .. math.Round(Element[2]*10)/10 .. "," .. math.Round(Element[3]*10)/10 .. ")"
				end
			elseif(type(Element) == "table" and #Element == 9) then --matrix
				RetText = RetText..formatPort.MATRIX(Element)
			elseif(type(Element) == "table" and #Element == 16) then --matrix4
				RetText = RetText..formatPort.MATRIX4(Element)
			elseif(type(Element) == "string") then --string
				RetText = RetText..formatPort.STRING(Element)
			elseif(type(Element) == "Entity") then --entity
				RetText = RetText..formatPort.ENTITY(Element)
			elseif(type(Element) == "Player") then --player
				RetText = RetText..tostring(Element)
			elseif(type(Element) == "Weapon") then --weapon
				RetText = RetText..tostring(Element)..Element:GetClass()
			elseif(type(Element) == "PhysObj" and e2_tostring_bone(Element) != "(null)") then --Bone
				RetText = RetText..formatPort.BONE(Element)
			else
				RetText = RetText.."No Display for "..type(Element)
			end
			--TODO: add matrix 2
			if OrientVertical then
				RetText = RetText..",\n"
			else
				RetText = RetText..", "
			end
		end
		RetText = string.sub(RetText,1,-3)
		return "{"..RetText.."}"
	end

	function formatPort.TABLE(value, OrientVertical)
		local RetText = ""
		local ElementCount = 0
		for Index, Element in pairs(value) do
			ElementCount = ElementCount+1
			if(ElementCount > 7) then
				break
			end

			local long_typeid = string.sub(Index,1,1) == "x"
			local typeid = string.sub(Index,1,long_typeid and 3 or 1)
			local IdxID = string.sub(Index,(long_typeid and 3 or 1)+1)

			RetText = RetText..IdxID.."="
			--Check for array element type
			if(typeid == "n") then --number
				RetText = RetText..formatPort.NORMAL(Element)
			elseif((type(Element) == "table" and #Element == 3) or type(Element) == "Vector") then --vector
				RetText = RetText..formatPort.VECTOR(Element)
			elseif(type(Element) == "table" and #Element == 2) then --vector2
				RetText = RetText..formatPort.VECTOR2(Element)
			elseif(type(Element) == "table" and #Element == 4 and typeid == "v4") then --vector4
				RetText = RetText..formatPort.VECTOR4(Element)
			elseif((type(Element) == "table" and #Element == 3) or type(Element) == "Angle") then --angle
				if(type(Element) == "Angle") then
					RetText = RetText..formatPort.ANGLE(Element)
				else
					RetText = RetText.."(" .. math.Round(Element[1]*10)/10 .. "," .. math.Round(Element[2]*10)/10 .. "," .. math.Round(Element[3]*10)/10 .. ")"
				end
			elseif(type(Element) == "table" and #Element == 9) then --matrix
				RetText = RetText..formatPort.MATRIX(Element)
			elseif(type(Element) == "table" and #Element == 16) then --matrix4
				RetText = RetText..formatPort.MATRIX4(Element)
			elseif(typeid == "s") then --string
				RetText = RetText..formatPort.STRING(Element)
			elseif(type(Element) == "Entity" and typeid == "e") then --entity
				RetText = RetText..formatPort.ENTITY(Element)
			elseif(type(Element) == "Player") then --player
				RetText = RetText..tostring(Element)
			elseif(type(Element) == "Weapon") then --weapon
				RetText = RetText..tostring(Element)..Element:GetClass()
			elseif(typeid == "b") then
				RetText = RetText..formatPort.BONE(Element)
			else
				RetText = RetText.."No Display for "..type(Element)
			end
			--TODO: add matrix 2
			if OrientVertical then
				RetText = RetText..",\n"
			else
				RetText = RetText..", "
			end
		end
		RetText = string.sub(RetText,1,-3)
		return "{"..RetText.."}"
	end

	-- Shouldn't this be in WireLib instead???
	function WireLib.registerDebuggerFormat(typename, func)
		formatPort[typename:upper()] = func
	end

	hook.Add("Think", "Wire_DebuggerThink", function()
		for ply,cmps in pairs(Components) do

			if ( !ply ) or ( !ply:IsValid() ) or ( !ply:IsPlayer() ) then -- if player has left, clear the hud

				Components[ply] = nil

			else

				OrientVertical = ply:GetInfoNum("wire_debugger_orientvertical") ~= 0

				-- TODO: Add EntityRemoved hook to clean up Components array.
				table.Compact(cmps, function(cmp) return cmp:IsValid() end)

				-- TODO: only send in TOOL:*Click/Reload hooks maybe.
				umsg.Start("WireDbgLineCount", ply)
					umsg.Short(#cmps)
				umsg.End()

				if #cmps == 0 then Components[ply] = nil end

				for l,cmp in ipairs(cmps) do
					local dbginfo = cmp.WireDebugName
					if not dbginfo or dbginfo == "No Name" then
						dbginfo = cmp:GetClass()
					end
					dbginfo = dbginfo .. " (" ..cmp:EntIndex() .. ") - "

					if (cmp.Inputs and table.Count(cmp.Inputs) > 0) then
						if OrientVertical then
							dbginfo = dbginfo .. "\n"
						end
						dbginfo = dbginfo .. "IN: "
						if OrientVertical then
							dbginfo = dbginfo .. "\n"
						end
						for k, Input in pairs_sortvalues(cmp.Inputs, WireLib.PortComparator) do
							if formatPort[Input.Type] then
								dbginfo = dbginfo .. k .. ":" .. formatPort[Input.Type](Input.Value, OrientVertical)
								if OrientVertical then
									dbginfo = dbginfo .. "\n"
								else
									dbginfo = dbginfo .. "  "
								end
							end
						end
					end

					if (cmp.Outputs and table.Count(cmp.Outputs) > 0) then
						if(cmp.Inputs and table.Count(cmp.Inputs) > 0) then
							dbginfo = dbginfo .. "\n "
						end
						if(!cmp.Inputs and OrientVertical) then
							dbginfo = dbginfo .. "\n"
						end
						dbginfo = dbginfo .. "OUT: "
						if OrientVertical then
							dbginfo = dbginfo .. "\n"
						end
						for k, Output in pairs_sortvalues(cmp.Outputs, WireLib.PortComparator) do
							if formatPort[Output.Type] then
								dbginfo = dbginfo .. k .. ":" .. formatPort[Output.Type](Output.Value, OrientVertical)
								if OrientVertical then
									dbginfo = dbginfo .. "\n"
								else
									dbginfo = dbginfo .. " "
								end
							end
						end
					end

					if (not cmp.Inputs) and (not cmp.Outputs) then
						dbginfo = dbginfo .. "No info"
					end

					dbg_line_cache[ply] = dbg_line_cache[ply] or {}
					dbg_line_time[ply] = dbg_line_time[ply] or {}
					if (dbg_line_cache[ply][l] ~= dbginfo) then
						if (not dbg_line_time[ply][l]) or (CurTime() > dbg_line_time[ply][l]) then
							--split the message up into managable chuncks and send them
							local NumOfUMSG = 1
							if(string.len(dbginfo)>200) then
								NumOfUMSG = math.ceil(string.len(dbginfo)/200)
							end

							for i=1, NumOfUMSG do
								local MsgPart = string.sub(dbginfo,(i-1)*200+1, i*200)
								umsg.Start("WireDbgLine", ply)
									umsg.Short(l)
									umsg.Short(NumOfUMSG)
									umsg.Short(i)
									umsg.Bool(OrientVertical)
									umsg.String(MsgPart)
								umsg.End()
							end

							dbg_line_cache[ply][l] = dbginfo
							if (SinglePlayer()) then
								dbg_line_time[ply][l] = CurTime() + 0.05
							else
								dbg_line_time[ply][l] = CurTime() + 0.2
							end
						end
					end
				end

			end

		end
	end)

end


if (CLIENT) then

	local dbg_line_count = 0
	local dbg_lines = {}
	local UMSG_Buffer = {}
	local dgb_orient_vert = false
	local BoxWidth = 300
	local LastBoxUpdate = CurTime()-5

	local function DebuggerDrawHUD()
		local dbginfo = ""
		if (dbg_line_count <= 0) then return end

		--setup the font
		surface.SetFont("Default")

		--buid the table of entries
		local Entry_Count = dbg_line_count
		local Line_Count = 0
		local ColorType = 0
		local Entries = {}
		for i = 1,dbg_line_count do
			local Line = dbg_lines[i]<commit_msg>[Debugger] Lua errors in the debugger's think hook no longer kill the debugger for everyone.<commit_after>TOOL.Category		= "Wire - Tools"
TOOL.Name			= "Debugger"
TOOL.Command		= nil
TOOL.ConfigName		= ""
TOOL.Tab			= "Wire"

if ( CLIENT ) then
	language.Add( "Tool_wire_debugger_name", "Debugging Tool" )
	language.Add( "Tool_wire_debugger_desc", "Shows selected components info on the HUD." )
	language.Add( "Tool_wire_debugger_0", "Primary: Add component to HUD, Secondary: Remove component from HUD, Reload: Clear HUD" )
	language.Add( "Tool_wire_debugger_showports", "Show overlay of ports in HUD" )
	language.Add( "Tool_wire_debugger_orientvertical", "Orient the Inputs/Outputs Vertically" )
end

TOOL.ClientConVar[ "showports" ] = "1"
TOOL.ClientConVar[ "orientvertical" ] = "1"

local Components = {}

local function IsWire(entity) --try to find out if the entity is wire
	if entity.IsWire == true then return true end --this shold always be true if the ent is wire compatible, but only is if the base of the entity is "base_wire_entity" THIS NEEDS TO BE FIXED
	if entity.Inputs or entity.Outputs then return true end --this is how the wire STool gun does it
	return false
end

function TOOL:LeftClick(trace)
	if (not trace.Entity:IsValid()) then return end
	if (CLIENT) then return true end

	if(!IsWire(trace.Entity)) then return end

	ply_idx = self:GetOwner()
	Components[ply_idx] = Components[ply_idx] or {}

	for k,cmp in ipairs(Components[ply_idx]) do
		if (cmp == trace.Entity) then return end
	end

	table.insert(Components[ply_idx], trace.Entity)

	return true
end


function TOOL:RightClick(trace)
	if (not trace.Entity:IsValid()) then return end
	if (CLIENT) then return true end

	if(!IsWire(trace.Entity)) then return end

	ply_idx = self:GetOwner()
	if not Components[ply_idx] then return end

	for k,cmp in ipairs(Components[ply_idx]) do
		if (cmp == trace.Entity) then
			table.remove(Components[ply_idx], k)
			return true
		end
	end
	if not next(Components[ply_idx]) then
		umsg.Start("WireDbgLineCount", ply)
			umsg.Short(0)
		umsg.End()
		Components[ply_idx] = nil
	end
end

local LastOverBoxInput = ""
local LastOverBoxOutput = ""

function TOOL:Think() --get and transmit the info on the overlay, but only when needed
	local ply = self:GetOwner()
	if(CLIENT) then return end
	local Trace = ply:GetEyeTraceNoCursor()

	local ShowPorts = self:GetClientNumber("showports") ~= 0

	if(Trace.Hit and Trace.Entity and Trace.Entity:IsValid() and IsWire(Trace.Entity) and ShowPorts) then
		local Ent = Trace.Entity
		--get the inputs and put their current use state in-between
		if(Ent.Inputs) then
			local InputString = ""
			for InputIdx, CurInput in pairs_sortvalues(Ent.Inputs, WireLib.PortComparator) do
				InputString = InputString..InputIdx
				if(CurInput.Type != "NORMAL") then
					InputString = InputString.." ["..CurInput.Type.."]"
				end
				if(CurInput.Src and CurInput.Src:IsValid()) then -- check whether the input is wired up or not
					InputString = InputString..",W\n"
				else
					InputString = InputString..",N\n"
				end
			end

			if(InputString != LastOverBoxInput) then
				self:GetWeapon():SetNetworkedString("WireDebugOverlayInputs", InputString)
				LastOverBoxInput = InputString
			end
		else
			if(LastOverBoxInput != "") then
				LastOverBoxInput = ""
				self:GetWeapon():SetNetworkedString("WireDebugOverlayInputs", "")
			end
		end

		--get the outputs
		if(Ent.Outputs) then
			local OutputString = ""
			for OutputIdx, CurOutput in pairs_sortvalues(Ent.Outputs, WireLib.PortComparator) do
				OutputString = OutputString..OutputIdx
				if(CurOutput.Type != "NORMAL") then
					OutputString = OutputString.." ["..CurOutput.Type.."]"
				end
				OutputString = OutputString.."\n"
			end

			if(OutputString != LastOverBoxOutput) then
				self:GetWeapon():SetNetworkedString("WireDebugOverlayOutputs", OutputString)
				LastOverBoxOutput = OutputString
			end
		else
			if(LastOverBoxOutput != "") then
				LastOverBoxOutput = ""
				self:GetWeapon():SetNetworkedString("WireDebugOverlayOutputs", "")
			end
		end
	else
		if(LastOverBoxInput != "") then
			LastOverBoxInput = ""
			self:GetWeapon():SetNetworkedString("WireDebugOverlayInputs", "")
		end

		if(LastOverBoxOutput != "") then
			LastOverBoxOutput = ""
			self:GetWeapon():SetNetworkedString("WireDebugOverlayOutputs", "")
		end
	end

end

if CLIENT then

	function TOOL:DrawHUD()
		local InputText = self:GetWeapon():GetNetworkedString("WireDebugOverlayInputs") or ""
		local OutputText = self:GetWeapon():GetNetworkedString("WireDebugOverlayOutputs") or ""

		if(InputText != "") then
			surface.SetFont("Trebuchet24")
			local Inputs = string.Explode("\n",InputText)
			local InputsUsed = {}
			for i, Input in ipairs(Inputs) do
				InputsUsed[i] = string.Right(Input,2) == ",W"
				Inputs[i] = string.sub(Inputs[i],1,-3)
			end

			local FontHeight = draw.GetFontHeight("Trebuchet24")+1
			local MaxWidth = 0
			for i, Input in ipairs(Inputs) do
				local W, H = surface.GetTextSize(Input)
				if(W > MaxWidth) then
					MaxWidth = W
				end
			end


			draw.RoundedBox(8,
				ScrW()/2-(MaxWidth+16)-20,
				ScrH()/2-#Inputs*FontHeight/2-8,
				MaxWidth+16,
				(#Inputs-1)*FontHeight+16,
				Color(109,146,129,192)
			)

			for i, Input in ipairs(Inputs) do
				local TextCol = Color(255,255,255)
				if(InputsUsed[i] == true) then
					TextCol = Color(255,0,0)
				end
				draw.Text({
					text = Input or "",
					font = "Trebuchet24",
					pos = {ScrW()/2-(MaxWidth+16)-12, (FontHeight)*(i-1)+(ScrH()/2-#Inputs*FontHeight/2)},
					color = TextCol
				})
			end

		end

		if(OutputText != "") then
			surface.SetFont("Trebuchet24")
			local Outputs = string.Explode("\n",OutputText)

			local FontHeight = draw.GetFontHeight("Trebuchet24")+1
			local MaxWidth = 0
			for i, Output in ipairs(Outputs) do
				local W, H = surface.GetTextSize(Output)
				if(W > MaxWidth) then
					MaxWidth = W
				end
			end


			draw.RoundedBox(8,
				ScrW()/2+20,
				ScrH()/2-#Outputs*FontHeight/2-8,
				MaxWidth+16,
				(#Outputs-1)*FontHeight+16,
				Color(109,146,129,192)
			)

			for i, Output in ipairs(Outputs) do
				draw.Text({
					text = Output or "",
					font = "Trebuchet24",
					pos = {ScrW()/2+28, (FontHeight)*(i-1)+(ScrH()/2-#Outputs*FontHeight/2)},
					color = Color(255,255,255)
				})
			end
		end


	end
end

function TOOL:Reload(trace)
	if (CLIENT) then return end
	umsg.Start("WireDbgLineCount", ply)
		umsg.Short(0)
	umsg.End()
	Components[self:GetOwner()] = nil
end


if (SERVER) then

	local dbg_line_cache = {}
	local dbg_line_time = {}

	local formatPort = {}
	function formatPort.NORMAL(value)
		return string.format("%.3f",value)
	end

	function formatPort.STRING(value)
		return '"' .. value .. '"'
	end

	function formatPort.VECTOR(value)
		return string.format("(%.1f,%.1f,%.1f)", value[1], value[2], value[3])
	end

	function formatPort.ANGLE(value)
		return string.format("(%.1f,%.1f,%.1f)", value.p, value.y, value.r)
	end

	formatPort.ENTITY = function(ent)
		if not validEntity(ent) then return "(null)" end -- this uses validEntity from E2, which is faster, but maybe we shouldn't use it.
		return tostring(ent)
	end
	formatPort.BONE = e2_tostring_bone

	function formatPort.MATRIX(value)
		local RetText = "[11="..value[1]..",12="..value[2]..",13="..value[3]
			  RetText = RetText..",21="..value[4]..",22="..value[5]..",23="..value[6]
			  RetText = RetText..",31="..value[7]..",32="..value[8]..",33="..value[9].."]"
		return RetText
	end

	function formatPort.MATRIX2(value)
		local RetText = "[11="..value[1]..",12="..value[2]
			  RetText = RetText..",21="..value[3]..",22="..value[4].."]"
		return RetText
	end

	function formatPort.MATRIX4(value)
		local RetText = "[11="..value[1]..",12="..value[2]..",13="..value[3]..",14="..value[4]
			  RetText = RetText..",21="..value[5]..",22="..value[6]..",23="..value[7]..",24="..value[8]
			  RetText = RetText..",31="..value[9]..",32="..value[10]..",33="..value[11]..",34="..value[12]
			  RetText = RetText..",41="..value[13]..",42="..value[14]..",43="..value[15]..",44="..value[16].."]"
		return RetText
	end

	function formatPort.ARRAY(value, OrientVertical)
		local RetText = ""
		local ElementCount = 0
		for Index, Element in ipairs(value) do
			ElementCount = ElementCount+1
			if(ElementCount > 10) then
				break
			end
			RetText = RetText..Index.."="
			--Check for array element type
			if(type(Element) == "number") then --number
				RetText = RetText..formatPort.NORMAL(Element)
			elseif((type(Element) == "table" and #Element == 3) or type(Element) == "Vector") then --vector
				RetText = RetText..formatPort.VECTOR(Element)
			elseif(type(Element) == "table" and #Element == 2) then --vector2
				RetText = RetText..formatPort.VECTOR2(Element)
			elseif(type(Element) == "table" and #Element == 4) then --vector4
				RetText = RetText..formatPort.VECTOR4(Element)
			elseif((type(Element) == "table" and #Element == 3) or type(Element) == "Angle") then --angle
				if(type(Element) == "Angle") then
					RetText = RetText..formatPort.ANGLE(Element)
				else
					RetText = RetText.."(" .. math.Round(Element[1]*10)/10 .. "," .. math.Round(Element[2]*10)/10 .. "," .. math.Round(Element[3]*10)/10 .. ")"
				end
			elseif(type(Element) == "table" and #Element == 9) then --matrix
				RetText = RetText..formatPort.MATRIX(Element)
			elseif(type(Element) == "table" and #Element == 16) then --matrix4
				RetText = RetText..formatPort.MATRIX4(Element)
			elseif(type(Element) == "string") then --string
				RetText = RetText..formatPort.STRING(Element)
			elseif(type(Element) == "Entity") then --entity
				RetText = RetText..formatPort.ENTITY(Element)
			elseif(type(Element) == "Player") then --player
				RetText = RetText..tostring(Element)
			elseif(type(Element) == "Weapon") then --weapon
				RetText = RetText..tostring(Element)..Element:GetClass()
			elseif(type(Element) == "PhysObj" and e2_tostring_bone(Element) != "(null)") then --Bone
				RetText = RetText..formatPort.BONE(Element)
			else
				RetText = RetText.."No Display for "..type(Element)
			end
			--TODO: add matrix 2
			if OrientVertical then
				RetText = RetText..",\n"
			else
				RetText = RetText..", "
			end
		end
		RetText = string.sub(RetText,1,-3)
		return "{"..RetText.."}"
	end

	function formatPort.TABLE(value, OrientVertical)
		local RetText = ""
		local ElementCount = 0
		for Index, Element in pairs(value) do
			ElementCount = ElementCount+1
			if(ElementCount > 7) then
				break
			end

			local long_typeid = string.sub(Index,1,1) == "x"
			local typeid = string.sub(Index,1,long_typeid and 3 or 1)
			local IdxID = string.sub(Index,(long_typeid and 3 or 1)+1)

			RetText = RetText..IdxID.."="
			--Check for array element type
			if(typeid == "n") then --number
				RetText = RetText..formatPort.NORMAL(Element)
			elseif((type(Element) == "table" and #Element == 3) or type(Element) == "Vector") then --vector
				RetText = RetText..formatPort.VECTOR(Element)
			elseif(type(Element) == "table" and #Element == 2) then --vector2
				RetText = RetText..formatPort.VECTOR2(Element)
			elseif(type(Element) == "table" and #Element == 4 and typeid == "v4") then --vector4
				RetText = RetText..formatPort.VECTOR4(Element)
			elseif((type(Element) == "table" and #Element == 3) or type(Element) == "Angle") then --angle
				if(type(Element) == "Angle") then
					RetText = RetText..formatPort.ANGLE(Element)
				else
					RetText = RetText.."(" .. math.Round(Element[1]*10)/10 .. "," .. math.Round(Element[2]*10)/10 .. "," .. math.Round(Element[3]*10)/10 .. ")"
				end
			elseif(type(Element) == "table" and #Element == 9) then --matrix
				RetText = RetText..formatPort.MATRIX(Element)
			elseif(type(Element) == "table" and #Element == 16) then --matrix4
				RetText = RetText..formatPort.MATRIX4(Element)
			elseif(typeid == "s") then --string
				RetText = RetText..formatPort.STRING(Element)
			elseif(type(Element) == "Entity" and typeid == "e") then --entity
				RetText = RetText..formatPort.ENTITY(Element)
			elseif(type(Element) == "Player") then --player
				RetText = RetText..tostring(Element)
			elseif(type(Element) == "Weapon") then --weapon
				RetText = RetText..tostring(Element)..Element:GetClass()
			elseif(typeid == "b") then
				RetText = RetText..formatPort.BONE(Element)
			else
				RetText = RetText.."No Display for "..type(Element)
			end
			--TODO: add matrix 2
			if OrientVertical then
				RetText = RetText..",\n"
			else
				RetText = RetText..", "
			end
		end
		RetText = string.sub(RetText,1,-3)
		return "{"..RetText.."}"
	end

	-- Shouldn't this be in WireLib instead???
	function WireLib.registerDebuggerFormat(typename, func)
		formatPort[typename:upper()] = func
	end

	local function Wire_DebuggerThink()
		for ply,cmps in pairs(Components) do

			if ( !ply ) or ( !ply:IsValid() ) or ( !ply:IsPlayer() ) then -- if player has left, clear the hud

				Components[ply] = nil

			else

				OrientVertical = ply:GetInfoNum("wire_debugger_orientvertical") ~= 0

				-- TODO: Add EntityRemoved hook to clean up Components array.
				table.Compact(cmps, function(cmp) return cmp:IsValid() end)

				-- TODO: only send in TOOL:*Click/Reload hooks maybe.
				umsg.Start("WireDbgLineCount", ply)
					umsg.Short(#cmps)
				umsg.End()

				if #cmps == 0 then Components[ply] = nil end

				for l,cmp in ipairs(cmps) do
					local dbginfo = cmp.WireDebugName
					if not dbginfo or dbginfo == "No Name" then
						dbginfo = cmp:GetClass()
					end
					dbginfo = dbginfo .. " (" ..cmp:EntIndex() .. ") - "

					if (cmp.Inputs and table.Count(cmp.Inputs) > 0) then
						if OrientVertical then
							dbginfo = dbginfo .. "\n"
						end
						dbginfo = dbginfo .. "IN: "
						if OrientVertical then
							dbginfo = dbginfo .. "\n"
						end
						for k, Input in pairs_sortvalues(cmp.Inputs, WireLib.PortComparator) do
							if formatPort[Input.Type] then
								dbginfo = dbginfo .. k .. ":" .. formatPort[Input.Type](Input.Value, OrientVertical)
								if OrientVertical then
									dbginfo = dbginfo .. "\n"
								else
									dbginfo = dbginfo .. "  "
								end
							end
						end
					end

					if (cmp.Outputs and table.Count(cmp.Outputs) > 0) then
						if(cmp.Inputs and table.Count(cmp.Inputs) > 0) then
							dbginfo = dbginfo .. "\n "
						end
						if(!cmp.Inputs and OrientVertical) then
							dbginfo = dbginfo .. "\n"
						end
						dbginfo = dbginfo .. "OUT: "
						if OrientVertical then
							dbginfo = dbginfo .. "\n"
						end
						for k, Output in pairs_sortvalues(cmp.Outputs, WireLib.PortComparator) do
							if formatPort[Output.Type] then
								dbginfo = dbginfo .. k .. ":" .. formatPort[Output.Type](Output.Value, OrientVertical)
								if OrientVertical then
									dbginfo = dbginfo .. "\n"
								else
									dbginfo = dbginfo .. " "
								end
							end
						end
					end

					if (not cmp.Inputs) and (not cmp.Outputs) then
						dbginfo = dbginfo .. "No info"
					end

					dbg_line_cache[ply] = dbg_line_cache[ply] or {}
					dbg_line_time[ply] = dbg_line_time[ply] or {}
					if (dbg_line_cache[ply][l] ~= dbginfo) then
						if (not dbg_line_time[ply][l]) or (CurTime() > dbg_line_time[ply][l]) then
							--split the message up into managable chuncks and send them
							local NumOfUMSG = 1
							if(string.len(dbginfo)>200) then
								NumOfUMSG = math.ceil(string.len(dbginfo)/200)
							end

							for i=1, NumOfUMSG do
								local MsgPart = string.sub(dbginfo,(i-1)*200+1, i*200)
								umsg.Start("WireDbgLine", ply)
									umsg.Short(l)
									umsg.Short(NumOfUMSG)
									umsg.Short(i)
									umsg.Bool(OrientVertical)
									umsg.String(MsgPart)
								umsg.End()
							end

							dbg_line_cache[ply][l] = dbginfo
							if (SinglePlayer()) then
								dbg_line_time[ply][l] = CurTime() + 0.05
							else
								dbg_line_time[ply][l] = CurTime() + 0.2
							end
						end
					end
				end

			end

		end
	end
	hook.Add("Think", "Wire_DebuggerThink", function() PCallError(Wire_DebuggerThink) end)

end


if (CLIENT) then

	local dbg_line_count = 0
	local dbg_lines = {}
	local UMSG_Buffer = {}
	local dgb_orient_vert = false
	local BoxWidth = 300
	local LastBoxUpdate = CurTime()-5

	local function DebuggerDrawHUD()
		local dbginfo = ""
		if (dbg_line_count <= 0) then return end

		--setup the font
		surface.SetFont("Default")

		--buid the table of entries
		local Entry_Count = dbg_line_count
		local Line_Count = 0
		local ColorType = 0
		local Entries = {}
		for i = 1,dbg_line_count do
			local Line = dbg_lines[i]
{'fraction_non_alphanumeric': 0.14585541657833367, 'fraction_numerical': 0.020230776219752264, 'mean_word_length': 3.2453072769349447, 'pattern_counts': {'":': 4, '<': 5, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6605349', 'n_tokens_mistral': 13955, 'n_tokens_neox': 11360, 'n_words': 2696}