--[[
MacroFu
by Cilraaz of Cenarion Circle

Please note that this mod is a port of Titan MacroMenu by CyberSilver.  Some of the functions are largely based
on that mod.  This mod was ported with CyberSilver's express permission.

Welcome to MacroFu!  This is a FuBar port of Titan MacroMenu.  It is meant to give players an easily
accessible menu of macros.  The macros have been sorted by type (Account, Character, or SuperMacros) making it very
simple to quickly access the macro you want.

Change Log:
v2.3.0-3.0-0.1 (TOC 20300)
-- Updated TOC
v2.2.0-3.0-0.1 (TOC 20200)
-- Updated TOC
v2.0-2.0-0.1 (TOC 20000)
-- Updated to work with FuBar 2.0/Ace2/WoW 2.0
-- Updated to use numbering scheme similar to my other FuBar mods v(WoW Version)-(FuBar Version)-(Iteration)
v0.1.2 (TOC 11200)
-- Updated TOC
v0.1.1 (TOC 11100)
-- Updated TOC
v0.1.0 (TOC 11000)
-- Initial release

To do:
-- Add localization

]]

local dewdrop = AceLibrary("Dewdrop-2.0")
local tablet = AceLibrary("Tablet-2.0")
local L = AceLibrary("AceLocale-2.2"):new("FuBar_MacroFu")

MacroFu = AceLibrary("AceAddon-2.0"):new("FuBarPlugin-2.0", "AceDB-2.0", "AceConsole-2.0", "AceEvent-2.0")

MacroFu.version = "2.0-2.0-0.1"
MacroFu.hasIcon = "Interface\\Icons\\INV_Battery_02"
MacroFu.hasText = "MacroFu"

MacroFu:RegisterDB("MacroFuDB")
MacroFu:RegisterDefaults('profile', {
	ver = MacroFu.version,
})

FU_MAX_MACROS = 18;

MACROFU_TYPES = {
	[0] = "Account",
	[1] = "Character",
	[2] = "SuperMacro"
};

MACROFU_MACROS = {
  [0] = {},
  [1] = {},
  [2] = {}
};
	
function MacroFu:OnInitialize()
end

function MacroFu:OnEnable()
end

local options = {
	type = 'group',
	args = {}
}

MacroFu:RegisterChatCommand({ "/macrofu", "/macfu" }, options)

function MacroFu:OnTooltipUpdate()
	local cat = tablet:AddCategory()
		cat:AddLine(
			"text", L["LMBTEXT"]
		)
end

function MacroFu:OnMenuRequest(level, value)
	if (level == 1) then
		for k, v in pairs(MACROFU_TYPES) do
			local loaded = IsAddOnLoaded(v);
			if (k <= 1) or (loaded) then
				dewdrop:AddLine(
					'text', v,
					'value', k,
					'hasArrow', true
				)
			end
		end
		dewdrop:AddLine()
	end
	
	if (level == 2) then
		self:GetMacros()
		local mType = value;
		for k2, v2 in pairs(MACROFU_MACROS) do
			if (v2.type == mType) then
				local p = v2.id;
				local q = v2.type;
				dewdrop:AddLine(
					'text', v2.name,
					'arg1', p,
					'arg2', q,
					'func', function(p,q)
						self:HandleModClick(p,q) 
					end,
					'closeWhenClicked', true
				)
			end
		end
	end
end

function MacroFu:GetMacros()
	MACROFU_MACROS = {};
	
	local mType, numMacros;
	local numAccountMacros, numCharacterMacros = GetNumMacros();
	local name, texture, body;
	
	for j = 0, FU_MAX_MACROS, FU_MAX_MACROS do
		if (j == 0) then
			numMacros = numAccountMacros;
			mType = 0;
		else
			numMacros = numCharacterMacros;
			mType = 1;
		end;
		for i = 1, FU_MAX_MACROS do
			local macroID = i + j;
			
			if ( i <= numMacros ) then
			  name, _, body, _ = GetMacroInfo(macroID);
			  
			  if (strlen(body) > 0) then
					local info = {};
			    info.name = name;
			    info.type = mType;
			    info.id = macroID;
			  
			    tinsert(MACROFU_MACROS, info);
			  end
			end
		end
    end
	
	local supermac = IsAddOnLoaded("SuperMacro");
	if (supermac) then
    mType = 2; -- SuperMacro
    numMacros = GetNumSuperMacros();
    
    for i = 1, numMacros, 1 do
      name, texture, body = GetOrderedSuperMacroInfo(i);
      
      if (strlen(body) > 0) then
        local info = {};
        info.name = name;
        info.type = mType;
        info.id = name;
        
        tinsert(MACROFU_MACROS, info);
      end      
    end
  end
end

if (not IsAddOnLoaded("SuperMacro")) then
--
  function RunMacro(index)
  	-- close edit boxes, then enter body line by line
  	if (MacroFrame_SaveMacro) then
  		MacroFrame_SaveMacro();
  	end
  	local body;
  	if (type(index) == "number") then
  		_, _, body, _ = GetMacroInfo(index);
  	elseif (type(index) == "string") then
  		_, _, body, _ = GetMacroInfo(GetMacroIndexByName(index));
  	end
  	if (not body) then return; end;
  
  	if (ChatFrameEditBox:IsVisible()) then
  		ChatEdit_OnEscapePressed(ChatFrameEditBox);
  	end
  	if (ReplaceAlias and ASFOptions.aliasOn) then
  		-- correct aliases
  		body = ReplaceAlias(body);
  	end
  	while (strlen(body) > 0) do
  		local block, line;
  		body, block, line = FindBlock(body);
  		if (block) then
  			RunScript(block);
  		else
  			RunLine(line);
  		end
  	end
  end
--  
  function FindBlock(body)
  	local a, b, block = strfind(body, "^/script (%-%-%-%-%[%[.-%-%-%-%-%]%])[\n]*");
  	if (block) then
  		body = strsub(body, b+1);
  		return body, block;
  	end
  	local a, b, line = strfind(body, "^([^\n]*)[\n]*");
  	if (line) then
  		body = strsub(body,b+1);
  		return body, nil, line;
  	end
  end
--  
  function RunBody(text)
  	local body=text;
  	local length = strlen(body);
  	for w in string.gmatch(body, "[^\n]+") do
  		RunLine(w);
  	end
  end
--  
  function RunLine(...)
  -- execute a line in a macro
  -- if script or cast, then rectify and RunScript
  -- else send to chat edit box
	local arg = {...};
	arg.n = select ("#", ...);
    for k = 1, arg.n do
    	local text = arg[k];
    	if (ReplaceAlias and ASFOptions.aliasOn) then
    		-- correct aliases
    		text = ReplaceAlias(text);
    	end;
    	if (string.find(text, "^/cast") ) then
    		local i, book = SM_FindSpell(gsub(text, "^%s*/cast%s*(%w.*[%w%)])%s*$", "%1"));
    		if (i) then
    			CastSpell(i, book);
    		end
    	else
    		if (string.find(text, "^/script ")) then
    			RunScript(gsub(text, "^/script ", ""));
    		else
    			text = gsub(text, "\n", ""); -- cannot send newlines, will disconnect
    			ChatFrameEditBox:SetText(text);
    			ChatEdit_SendText(ChatFrameEditBox);
    		end
    	end
    end -- for
  end -- RunLine()
--  
  function SM_FindSpell(spell)
  	local s = gsub(spell, "%s*(.-)%s*%(.*","%1");
  	local r;
  	local num = tonumber(gsub(spell, "%D*(%d+)%D*", "%1"), 10);
  	if (string.find(spell, "%(%s*[Rr]acial")) then
  		r = "racial";
  	elseif (string.find(spell, "%(%s*[Ss]ummon")) then
  		r = "summon";
  	elseif (string.find(spell, "%(%s*[Aa]pprentice")) then
  		r = "apprentice";
  	elseif (string.find(spell, "%(%s*[Jj]ourneyman")) then
  		r = "journeyman";
  	elseif (string.find(spell, "%(%s*[Ee]xpert")) then
  		r = "expert";
  	elseif (string.find(spell, "%(%s*[Aa]rtisan")) then
  		r = "artisan";
  	elseif (string.find(spell, "%(%s*[Mm]aster")) then
  		r = "master";
  	elseif (string.find(spell, "[Rr]ank%s*%d+") and num and num > 0) then
  		r = gsub(spell, ".*%(.*[Rr]ank%s*(%d+).*", "Rank "..num);
  	else
  		r = ""
  	end
  	return FindSpell(s,r);
  end
--  
  function FindSpell(spell, rank)
  	local i = 1;
  	local booktype = { "spell", "pet", };
  	--local booktype = "spell";
  	local s,r;
  	local ys, yr;
  	for k, book in pairs(booktype) do
  		while spell do
  		s, r = GetSpellName(i,book);
  		if ( not s ) then
  			i = 1;
  			break;
  		end
  		if ( string.lower(s) == string.lower(spell)) then ys=true; end;
  		if ( (r == rank) or (r and rank and string.lower(r) == string.lower(rank))) then yr=true; end;
  		if ( rank=='' and ys and (not GetSpellName(i+1, book) or string.lower(GetSpellName(i+1, book)) ~= string.lower(spell) )) then
  			yr = true; -- use highest spell rank if omitted
  		end
  		if ( ys and yr ) then
  			return i,book;
  		end
  		i=i+1;
  		ys = nil;
  		yr = nil;
  		end
  	end
  	return;
  end
end

function MacroFu:HandleModClick(p,q)
	if (q) then
		if (q == 2) then
			RunSuperMacro(p);
		else
			RunMacro(p);
		end
	end
end