local y,w=...local O=w.DevTool function O.StartLogFunctionCalls(H,y,w)if not w then return end local C=H:GetLogFunctionCalls(y,w)if not C then local M=O.FromStrToObject(w)if not M then H:Print(H.colors.red:WrapTextInColorCode("\069\114\114\111\114\058 ")..("\067\097\110\110\111\116 \097\100\100 \102\117\110\099\116\105\111\110 \109\111\110\105\116\111\114\105\110\103\058 "..H.colors.lightblue:WrapTextInColorCode("\095\071\046"..(tostring(w).." \061\061 \110\105\108"))))return end C={parentTableName=w;fnName=y,active=false}table.insert(H.db.profile.logs,C)end H:ActivateLogFunctionCalls(C)end function O.ActivateLogFunctionCalls(w,y)if y.active then return end local H={O.FromStrToObject(y.parentTableName)or{}}local C=function(y)if#y==1 then return y[1]elseif#y==0 then return nil end return y end for M,T in pairs(H)do if type(T)=="\102\117\110\099\116\105\111\110"and(not y.fnName or M==y.fnName)then local y=w:GetOldFn(H,M,T)if not y then w:SaveOldFn(H,M,T)y=w:GetOldFn(H,M,T)end H[M]=function(...)local H={y(...)}local T={...}local I=w.colors.lightgreen:WrapTextInColorCode(M)..("\040"..(O.ArgsToString(T).."\041"))w:AddData({OUT=C(H);IN=C(T)},I)return unpack(H)end end end w:Print(w.colors.green:WrapTextInColorCode("\083\116\097\114\116")..(" \102\117\110\099\116\105\111\110 \109\111\110\105\116\111\114\105\110\103\058 "..w.colors.lightblue:WrapTextInColorCode(w:LogFunctionCallText(y))))y.active=true end function O.DeactivateLogFunctionCalls(w,y)if not y.active then return end local H={O.FromStrToObject(y.parentTableName)or{}}for O,C in pairs(H)do if type(C)=="\102\117\110\099\116\105\111\110"and(not y.fnName or O==y.fnName)then H[O]=w:GetOldFn(H,O,C)end end w:Print(w.colors.red:WrapTextInColorCode("\083\116\111\112")..(" \102\117\110\099\116\105\111\110 \109\111\110\105\116\111\114\105\110\103\058 "..w.colors.lightblue:WrapTextInColorCode(w:LogFunctionCallText(y))))y.active=false end function O.ToggleFnLogger(w,y)if y.active then w:DeactivateLogFunctionCalls(y)else w:ActivateLogFunctionCalls(y)end end function O.GetOldFn(H,y,w,O)if H.tempOldFns and(H.tempOldFns[y]and H.tempOldFns[y][w])then return H.tempOldFns[y][w]end end function O.SaveOldFn(H,y,w,O)if not H.tempOldFns then H.tempOldFns={}end if not H.tempOldFns[y]then H.tempOldFns[y]={}end if not O then H.tempOldFns[y][w]=nil end if not H.tempOldFns[y][w]then H.tempOldFns[y][w]=O end end function O.GetLogFunctionCalls(O,y,w)for O,H in pairs(O.db.profile.logs)do if H.parentTableName==w and y==H.fnName then return H end end end function O.LogFunctionCallText(w,y)if not y then return""end local O=y.parentTableName=="\095\071"and"\095\071"or"\095\071\046"..tostring(y.parentTableName)if y.fnName then return y.fnName..(" \102\110 \105\110 "..O)else return"\065\076\076 \102\110 \105\110 "..O end end
-- DevTool is a World of Warcraft® addon development tool.
-- Copyright (c) 2021-2024 Britt W. Yazel
-- Copyright (c) 2016-2021 Peter aka "Varren"
-- This code is licensed under the MIT license (see LICENSE for details)

local _, addonTable = ... --make use of the default addon namespace
local DevTool = addonTable.DevTool

-----------------------------------------------------------------------------------------------
-- FUNCTION LOGGING
-----------------------------------------------------------------------------------------------
function DevTool:StartLogFunctionCalls(strFnToLog, strParentPath)
	--for now you have to tell exact table name can be _G can be something like DevTool.table.table
	if not strParentPath then
		return
	end

	local savedInfo = self:GetLogFunctionCalls(strFnToLog, strParentPath)

	if not savedInfo then
		local tParent = DevTool.FromStrToObject(strParentPath)
		if not tParent then
			self:Print(self.colors.red:WrapTextInColorCode("Error: ") ..
					"Cannot add function monitoring: " ..
					self.colors.lightblue:WrapTextInColorCode("_G." .. tostring(strParentPath) .. " == nil"))
			return
		end

		savedInfo = {
			parentTableName = strParentPath,
			fnName = strFnToLog,
			active = false
		}

		table.insert(self.db.profile.logs, savedInfo)
	end

	self:ActivateLogFunctionCalls(savedInfo)
end

function DevTool:ActivateLogFunctionCalls(info)
	if info.active then
		return
	end

	local tParent = { DevTool.FromStrToObject(info.parentTableName) or {} }

	local shrinkFn = function(table)
		if #table == 1 then
			return table[1]
		elseif #table == 0 then
			return nil
		end
		return table
	end

	for fnName, oldFn in pairs(tParent) do
		if type(oldFn) == "function" and
				(not info.fnName or fnName == info.fnName) then
			local savedOldFn = self:GetOldFn(tParent, fnName, oldFn)

			if not savedOldFn then
				self:SaveOldFn(tParent, fnName, oldFn)
				savedOldFn = self:GetOldFn(tParent, fnName, oldFn)
			end

			tParent[fnName] = function(...)
				local result = { savedOldFn(...) }
				local args = { ... }

				local fnNameWitArgs = self.colors.lightgreen:WrapTextInColorCode(fnName) ..
						"(" .. DevTool.ArgsToString(args) .. ")"

				self:AddData({
					OUT = shrinkFn(result),
					IN = shrinkFn(args)
				}, fnNameWitArgs)

				return unpack(result)
			end
		end
	end

	self:Print(self.colors.green:WrapTextInColorCode("Start") ..
			" function monitoring: " ..
			self.colors.lightblue:WrapTextInColorCode(self:LogFunctionCallText(info)))
	info.active = true
end

function DevTool:DeactivateLogFunctionCalls(info)
	if not info.active then
		return
	end

	local tParent = { DevTool.FromStrToObject(info.parentTableName) or {} }
	for fnName, oldFn in pairs(tParent) do
		if type(oldFn) == "function" and
				(not info.fnName or fnName == info.fnName) then
			tParent[fnName] = self:GetOldFn(tParent, fnName, oldFn)
		end
	end

	self:Print(self.colors.red:WrapTextInColorCode("Stop") .. " function monitoring: " ..
			self.colors.lightblue:WrapTextInColorCode(self:LogFunctionCallText(info)))
	info.active = false
end

function DevTool:ToggleFnLogger(info)
	if info.active then
		self:DeactivateLogFunctionCalls(info)
	else
		self:ActivateLogFunctionCalls(info)
	end
end

function DevTool:GetOldFn(tParent, fnName, oldFn)
	if self.tempOldFns and self.tempOldFns[tParent] and self.tempOldFns[tParent][fnName] then
		return self.tempOldFns[tParent][fnName]
	end
end

function DevTool:SaveOldFn(tParent, fnName, oldFn)
	if not self.tempOldFns then
		self.tempOldFns = {}
	end

	-- tParent is actual parent an not string name
	if not self.tempOldFns[tParent] then
		self.tempOldFns[tParent] = {}
	end

	-- clear
	if not oldFn then
		self.tempOldFns[tParent][fnName] = nil
	end

	--else save only if it doesn't exists
	if not self.tempOldFns[tParent][fnName] then
		self.tempOldFns[tParent][fnName] = oldFn
	end
end

function DevTool:GetLogFunctionCalls(strFnName, strParentTableName)
	for _, v in pairs(self.db.profile.logs) do
		if v.parentTableName == strParentTableName and strFnName == v.fnName then
			return v
		end
	end
end

function DevTool:LogFunctionCallText(info)
	if not info then
		return ""
	end

	local tableName = info.parentTableName == "_G" and "_G" or "_G." .. tostring(info.parentTableName)

	if info.fnName then
		return info.fnName .. " fn in " .. tableName

	else
		return "ALL fn in " .. tableName
	end
end