--[[
--	Fire
--	 	Trial by Fire
--	
--	By: Alexander Brazie
--	
--	Fire is a World of Warcraft in-game debugger. Using fire you can 
--	view the status of any variable in a customizable tree view, 
--	or using Fire.startTest() and Fire.stopTest() evaluate how
--	your program runs and how much memory it consumes per test. 
--	
--	$Id: Fire.lua 4156 2006-10-13 02:16:36Z geowar $
--	$Rev: 4156 $
--	$LastChangedBy: geowar $
--	$Date: 2006-10-12 21:16:36 -0500 (Thu, 12 Oct 2006) $
--]]

-- Data

-- Panes
Fire_Panes = {
	"FireFrameDebugger";
	"FireFrameStatusMonitor";
};

Fire_Pane_Tooltip = {
	"Lua Debugger";
	"System Details";
};

LIMIT = 0;
FIRE_EVAL_MAX_DEPTH = 15;

-- Functions
Fire = {
	--
	--	parseAndReturn(string data)
	--		Attempts to parse a string itself,
	--		Determine if it is a function
	--			If so, execute and return the args
	--		Detemine if is globally defined
	--			If so, return the value of the global
	--		Determine if it is an expression
	--			If so, determine the values and return
	--
	--	Args:
	--		data - string to be parsed
	--
	--	Returns:
	--		nil - undefined or invalid
	--		other - the value of that getglobal
	--
	parseAndReturn = function ( data, args )

		if ( not args ) then args = {} end
		local val = getglobal(data);
		local retVal = nil;
		
		if ( type(val) ~= "nil" ) then 			
			retVal = val;
		elseif ( tonumber(data) ) then 
			retVal = tonumber(data);
		else
			-- Check if its an = statement
			if ( string.find(data, "=" ) ) then 
				local sub = "%s*(.*)%s*\=%s*(.*)%s*%;?";

				-- Split into left and right segments
				local _, _, a, b = string.find(data, sub);

				a = Fire.parseAndReturn(a);
				b = Fire.parseAndReturn(b);
				
				Sea.io.error(a, "___", b);

			-- Check if its a table with dots
			elseif ( string.find(data,"%.") ) then
				local _, _, rootString, restString = string.find(data, "(.*)%.(.*)");

				local root = nil;
				if ( rootString ~= data ) then
					root = Fire.parseAndReturn(rootString);

					local restStrings = Sea.util.split(restString, ".");

					for k,v in ipairs(restStrings) do
						if ( type(root) == "table" ) then
							root = root[v];
						end
					end
				end

				retVal = root;
			-- Check if its a string
			elseif ( string.gsub(data, "%s*\"(.*)\".*", "%1") ~= data ) then
				local sub = "%s*\"(.*)\".*";
				local _, _, string = string.find(data, sub);

				retVal = string;
			-- Check if its a table
			elseif ( string.sub(data,1,1) == "{" and string.sub(data,string.len(data)) == "}" ) then
				local sub = "%s*%{(.*)%}.*";
				local _, _, t = string.find(data, sub);

				local a = Sea.util.split(t, ";,");
				
				for k,v in ipairs(a) do 
					a[k] = Fire.parseAndReturn(v);
				end

				retVal = a;

			-- Check if its a function call
			elseif ( string.find(data, "%(" ) and string.find(data, "%)" ) ) then
				local sub = "%s*(.*)%s*%((.*)%).*";

				-- Split into left and right segments
				local _, _, a, b = string.find(data, sub);

				Sea.io.print(a,b);
				-- Parse the function
				a = Fire.parseAndReturn(a);

				local args = Sea.util.split(b,",");
				for k,v in ipairs(args) do 
					args[k] = Fire.parseAndReturn(v);
				end

				Sea.io.error("Calling ", a, " with ", table.getn(args) );
				if ( type (a) == "function" ) then 
					local a1,a2,a3,a4,a5,a6,a7,a8,a9,a10 = a(unpack(args));

					retVal = {a1,a2,a3,a4,a5,a6,a7,a8,a9,a10};
				else
					retVal = FIRE_INVALIDFUNCTION;
				end
			end
		end
		
		return retVal;
	end;
};

--[[ Loads a string into the Gui ]]--
Fire_EvalStringAndLoadTree = function(input)
	local t = Fire.parseAndReturn (input);
	if ( type(t) ~= "table" ) then
		t = {t};
	end

	local key = FireFrameDebuggerFilterInput:GetText();
	if ( key == nil ) then key = ".*"; end

	-- Make a list of the keys to use
	local tKeys = {};
	for k,v in pairs(t) do
		table.insert(tKeys, k);
	end

	-- Copy data to ensure we don't foobar it
	for k,v in pairs(tKeys) do
		-- Sea.io causes major errors
		if ( string.find(tostring(v), key ) )then
		else
			tKeys[k] = nil;
		end
	end

	-- Sort the keys
	table.sort(tKeys, function(a,b) return tostring(a) < tostring(b); end);

	-- Count the original
	local tCount = 0;
	for k,v in pairs(t) do
		tCount = tCount+1;
	end

	-- Let the user know
	if ( tCount == 0 ) then
		FireTree_LoadEnhanced(FireFrameDebuggerContainerTree, {{ title=FIRE_NILRETURNED, titleColor=GRAY_FONT_COLOR }} );
	elseif ( table.getn(tKeys) == 0 ) then
		FireTree_LoadEnhanced(FireFrameDebuggerContainerTree, {{ title=FIRE_NOMATCH, titleColor=GRAY_FONT_COLOR }} );
	else
		--table.sort(render);
		FireTree_LoadTable(FireFrameDebuggerContainerTree, t, nil, tKeys, FIRE_EVAL_MAX_DEPTH);
	end

	FireTree_UpdateFrame(FireFrameDebuggerContainerTree);
end;

--[[ Loads a string into fire ]]--
function Fire_Debugger_LoadString(string)
	FireFrameDebuggerEvalInput:SetText(string);
	Fire_EvalStringAndLoadTree(FireFrameDebuggerEvalInput:GetText());

	if ( not FireFrame:IsVisible() ) then 
		FireFrame:Show();
	end
end;

--[[ Toggles Fire ]]--
function ToggleFire()
	if ( not FireFrame:IsVisible() ) then
		FireFrame:Show();
	else
		FireFrame:Hide();
	end
end

--[[ Toggles Subpanes ]]--
function ToggleFirePane(id) 
	for k,v in pairs(Fire_Panes) do
		getglobal(v):Hide();
	end

	getglobal(Fire_Panes[id]):Show();
end

--[[ Quick Eval Jump ]]--
function Fire_QuickEval ()
	if ( not FireFrame:IsVisible() ) then
		FireFrame:Show();
		ToggleFirePane(1); 
		if (Chronos) then
			Chronos.schedule(.04, function() FireFrameDebuggerEvalInput:SetFocus(); end );
		end
	else
		FireFrameDebuggerEvalInput:ClearFocus();
		FireFrame:Hide();
	end
end

--[[ Event Handlers ]]--
function Fire_Pane_OnClick()
	local id = this:GetID();	
	ToggleFirePane(id);
end

function Fire_Eval_OnLoad()
	this:SetScript("OnEscapePressed", Fire_Eval_OnEscapePressed);
	this:SetScript("OnEnterPressed", Fire_Eval_OnEnterPressed);
	this:SetScript("OnTabPressed", Fire_Eval_OnTabPressed);
end
function Fire_Eval_OnEscapePressed()
	this:ClearFocus();
end
function Fire_Eval_OnEnterPressed()
	Fire_EvalStringAndLoadTree(this:GetText());
	--this:ClearFocus();
end
function Fire_Eval_OnTabPressed()
	FireFrameDebuggerFilterInput:SetFocus();
end

function Fire_EvalButton_OnLoad()
	this:SetScript("OnClick", Fire_EvalButton_OnClick);
end
function Fire_EvalButton_OnClick()
	Fire_EvalStringAndLoadTree(FireFrameDebuggerEvalInput:GetText());
end

function Fire_Filter_OnLoad()
	this:SetScript("OnEscapePressed", Fire_Filter_OnEscapePressed);
	this:SetScript("OnEnterPressed", Fire_Filter_OnEnterPressed);
	this:SetScript("OnTabPressed", Fire_Filter_OnTabPressed);
end
function Fire_Filter_OnEscapePressed()
	this:ClearFocus();
end
function Fire_Filter_OnEnterPressed()
	Fire_EvalStringAndLoadTree(FireFrameDebuggerEvalInput:GetText());
	--this:ClearFocus();
end
function Fire_Filter_OnTabPressed()
	FireFrameDebuggerEvalInput:SetFocus();	
end
function Fire_FilterButton_OnLoad()
	this:SetScript("OnClick", Fire_FilterButton_OnClick);
end

function Fire_FilterButton_OnClick()
	Fire_EvalStringAndLoadTree(FireFrameDebuggerEvalInput:GetText());
end

function Fire_Tab_OnEnter()
	GameTooltip:SetOwner(this, "ANCHOR_RIGHT");
	GameTooltip:SetText(Fire_Pane_Tooltip[this:GetID()], 1.0,1.0,1.0 );
end
function Fire_Tab_OnLeave()
	GameTooltip:Hide();
end

function Fire_Frame_OnLoad()
	UIPanelWindows["FireFrame"] = { area = "center",	pushable = 0 };
	
	SlashCmdList[FIRE_EVAL_ID] = Fire_Debugger_LoadString;
	for i=1, #FIRE_EVAL_CMD do setglobal("SLASH_"..FIRE_EVAL_ID..i, FIRE_EVAL_CMD[i]); end;

	-- Frame Properties
	this.numTabs = 2;
	this.selectedTab = 1;
	getglobal(this:GetName().."HeaderText"):SetText(FIRE_TITLE_TEXT);
	
	--PanelTemplates_SetTab(this, 1);
	FireFrameDebuggerContainerTree.collapseAllArtwork = false;
end
