Quantcast
sdm_msgPrefix = "SupDupMac"
sdm_msgPrefixLength = string.len(sdm_msgPrefix)
sdm_msgCommandLength = 1
sdm_msgLengthLimit = 254
sdm_msgCommands = {
	SendFirst = "1", -- I'm sending you the first part of a macro
	SendMore = "2", -- I'm sending you more of the macro
	SendFailed = "3", -- I failed to send you the macro
	Receiving = "4", -- I just received the first part of your macro
	ReceivingDone = "5", -- I just saved the macro that you sent me
	ReceivingFailed = "6", -- I failed to receive your macro
}

function sdm_OnUpdate(self, elapsed) --used for sending macros
	self.TimeSinceLastUpdate = self.TimeSinceLastUpdate + elapsed
	if self.TimeSinceLastUpdate > sdm_updateInterval then
		if sdm_sending.i == 2 then
			sdm_sending.command = sdm_msgCommands.SendMore
		end
		SendAddonMessage(sdm_msgPrefix, sdm_sending.command..sdm_sending.lines[sdm_sending.i], sdm_sending.channel, sdm_sending.target)
		sdm_sendStatusBar:SetValue(sdm_sending.i)
		sdm_sending.i = sdm_sending.i+1
		if sdm_sending.i>sdm_sending.numLines then
			sdm_EndSending("|cff44ff00Sent to "..(sdm_sending.target or sdm_sending.channel).."|r")
		end
		self.TimeSinceLastUpdate = 0
	end
end

function sdm_RegisterMessages()
	sdm_eventFrame:RegisterEvent("CHAT_MSG_ADDON")
	local success = RegisterAddonMessagePrefix(sdm_msgPrefix)
	if not success then
		print(sdm_printPrefix.."You have too many addon prefixes registered. SDM will not be able to send or receive macros.")
	end
end

-- create an invisible frame that is used to measure time
local f = CreateFrame("Frame", "sdm_updateFrame", UIParent)
f:Hide()
f:SetScript("OnShow", function(self)
	self.TimeSinceLastUpdate = 0
end)
f:SetScript("OnUpdate", sdm_OnUpdate)

function sdm_SendMacro(mTab, chan, tar)
	if sdm_sending then
		print(sdm_printPrefix.."You are already sending something.")
		return
	end
	print(sdm_printPrefix.."Sending to the following people (if nobody is listed, then nobody is waiting):")
	sdm_RegisterMessages()
	local perCharacter=nil
	--make the string that will be split up and sent.  It consists of a bunch of values separated by commas.  They are, in order: the version the sender is running, the minimum version the receiver must have, the type of macro, the index of the icon, the perCharacter status ("<table value>" or "nil"), the length of the name, the length of the text, the name, and the text.  There is no comma between the name and the text.
	local textToSend = sdm_version..","..sdm_minVersion..","..mTab.type..","..tostring(mTab.icon)..","..tostring(mTab.characters)..","..mTab.name:len()..","..mTab.text:len()..","..mTab.name..mTab.text
	local comm = sdm_msgCommands.SendFirst -- if the command is SendFirst, it's the first part.  If it's SendMore, it's any part after the first.
	local lineLen = sdm_msgLengthLimit - sdm_msgPrefixLength - sdm_msgCommandLength
	local linesToSend={}
	local pos = 1
	while pos <= textToSend:len() do
		table.insert(linesToSend, textToSend:sub(pos, pos+lineLen-1))
		pos = pos+lineLen
	end
	sdm_sending={
		i=1,
		lines = linesToSend,
		numLines = getn(linesToSend),
		channel = chan,
		target = tar,
		command = comm
	}
	sdm_sendStatusBar:SetMinMaxValues(0, sdm_sending.numLines)
	sdm_sendStatusBar:SetValue(0)
	sdm_sendStatusBar_text:SetText("|cffffccffSending to "..(sdm_sending.target or sdm_sending.channel).."|r")
	sdm_cancelSendButton:Enable()
	sdm_sendButton:Disable()
	sdm_sendPartyRadio:Disable()
	sdm_sendRaidRadio:Disable()
	sdm_sendBattlegroundRadio:Disable()
	sdm_sendGuildRadio:Disable()
	sdm_sendTargetRadio:Disable()
	sdm_sendArbitraryRadio:Disable()
	sdm_sendInput:EnableMouse(nil)
	sdm_updateFrame:Show()
end

function sdm_EndSending(text)
	sdm_updateFrame:Hide()
	sdm_sendStatusBar_text:SetText(text)
	sdm_sending=nil
	sdm_cancelSendButton:Disable()
	if sdm_currentEdit then
		sdm_sendButton:Enable()
	end
	sdm_sendPartyRadio:Enable()
	sdm_sendRaidRadio:Enable()
	sdm_sendBattlegroundRadio:Enable()
	sdm_sendGuildRadio:Enable()
	sdm_sendTargetRadio:Enable()
	sdm_sendArbitraryRadio:Enable()
	sdm_sendInput:EnableMouse(1)
end

function sdm_WaitForMacro(name)
	if sdm_receiving then
		print(sdm_printPrefix.."You are already receiving or waiting.")
		return
	end
	sdm_RegisterMessages()
	sdm_receiving = {playerName=name, first=true}
	sdm_receiveStatusBar:SetValue(0)
	sdm_receiveStatusBar_text:SetText("|cffffccffWaiting for "..sdm_receiving.playerName.."|r")
	sdm_cancelReceiveButton:Enable()
	sdm_receiveButton:Disable()
	sdm_receiveTargetRadio:Disable()
	sdm_receiveArbitraryRadio:Disable()
	sdm_receiveAnyoneRadio:Disable()
	sdm_receiveInput:EnableMouse(nil)
	sdm_SelectItem(nil)
	sdm_newFrame:Show()
	sdm_newMacroNameInput:ClearFocus()
	sdm_newMacroNameInput:SetText("Receiving macro...")
	sdm_newMacroNameInput:EnableMouse(nil)
	sdm_buttonRadio:Disable()
	sdm_floatingRadio:Disable()
	sdm_scriptRadio:Disable()
	sdm_globalRadio:Disable()
	sdm_charspecRadio:Disable()
	sdm_createMacroButton:Disable()
	sdm_saveAsText = nil
	sdm_saveAsIcon = nil
end

function sdm_InterpretAddonMessage(...)
	--print("debug: interpreting")
	local prefix, message, _, sender = ...
	if prefix ~= sdm_msgPrefix or sender==sdm_thisChar.name then
		return
	end
	local command = message:sub(1,1)
	local txt=message:sub(2)
	if sdm_receiving and not sdm_receiving.text then
		if sdm_receiving.playerName == "<ANYONE>" and command == sdm_msgCommands.SendFirst then
			sdm_receiving.playerName = sender
		end
		if sender:upper() ~= sdm_receiving.playerName:upper() then
			return
		end
		if command == sdm_msgCommands.SendFirst then -- this is the first part of the macro (also contains data such as name and type)
			sdm_ReceiveLine(txt, true)
		elseif command == sdm_msgCommands.SendMore then -- this is some part other than the first
			sdm_ReceiveLine(txt, false)
		elseif command == sdm_msgCommands.SendFailed then -- this is a reason why the sender failed to send us the macro
			print(sdm_printPrefix..sender.." failed to send the macro.  Reason: "..txt)
			sdm_EndReceiving("|cffff0000Failed|r")
		end
	elseif command == sdm_msgCommands.Receiving then
		print(sdm_printPrefix..sender.." has begun to receive your macro...")
	elseif command == sdm_msgCommands.ReceivingDone then -- the target has finished receiving
		print(sdm_printPrefix..sender.." has accepted your macro.")
	elseif command == sdm_msgCommands.ReceivingFailed then -- this is a reason why the recipient failed to receive our macro
		local version, reason = sdm_SplitString(txt, ",", 1)
		print(sdm_printPrefix..sender.." did not receive your macro.  Reason: "..reason)
	end
end

function sdm_ReceiveLine(line, send1)
	--print("debug: in recline")
	if sdm_receiving.first and send1 then --this is the first line
		sdm_receiving.nameAndText, sdm_receiving.textLen, sdm_receiving.playerNameLen, sdm_receiving.perCharacter, sdm_receiving.icon, sdm_receiving.type, sdm_receiving.minVersion, sdm_receiving.sendersVersion = sdm_SplitString(line, ",", 7)
		sdm_receiving.perCharacter = (sdm_receiving.perCharacter~="nil")
		if sdm_receiving.icon=="nil" then
			sdm_receiving.icon = nil
		else
			sdm_receiving.icon = sdm_receiving.icon
		end
		sdm_receiving.textLen = 0 + sdm_receiving.textLen
		sdm_receiving.playerNameLen = 0 + sdm_receiving.playerNameLen
		sdm_receiving.first = false
		sdm_receiveStatusBar:SetMinMaxValues(0, sdm_receiving.playerNameLen + sdm_receiving.textLen)
		sdm_receiveStatusBar_text:SetText("|cffffccffReceiving|r")
		if sdm_CompareVersions(sdm_receiving.sendersVersion, sdm_minVersion)==2 or sdm_CompareVersions(sdm_version, sdm_receiving.minVersion)==2 then
			print(sdm_printPrefix.."You failed to recieve the macro due to a version incompatibility.")
			SendAddonMessage(sdm_msgPrefix, sdm_msgCommands.ReceivingFailed.."Incompatible Versions,"..sdm_version, "WHISPER", sdm_receiving.playerName)
			sdm_EndReceiving("|cffff0000Failed|r")
			return
		else
			SendAddonMessage(sdm_msgPrefix, sdm_msgCommands.Receiving..sdm_version, "WHISPER", sdm_receiving.playerName)
		end
	elseif (not sdm_receiving.first) and (not send1) then
		sdm_receiving.nameAndText = sdm_receiving.nameAndText..line
	else
		return
	end
	local currLen = sdm_receiving.nameAndText:len()
	sdm_receiveStatusBar:SetValue(currLen)
	if currLen == (sdm_receiving.playerNameLen + sdm_receiving.textLen) then
		sdm_receiveStatusBar_text:SetText("|cffff9900Click \"Create\" to save|r")
		UIFrameFlash(sdm_createMacroButton_flash, 0.5, 0.5, 1e6, false)
		sdm_newMacroNameInput:EnableMouse(1)
		sdm_buttonRadio:Enable()
		sdm_floatingRadio:Enable()
		sdm_scriptRadio:Enable()
		sdm_globalRadio:Enable()
		sdm_charspecRadio:Enable()
		sdm_createMacroButton:Enable()
		if sdm_receiving.type=="b" then
			sdm_buttonRadio:Click()
		elseif sdm_receiving.type=="f" then
			sdm_floatingRadio:Click()
		elseif sdm_receiving.type=="s" then
			sdm_scriptRadio:Click()
		end
		if sdm_receiving.perCharacter then
			sdm_charspecRadio:Click()
		else
			sdm_globalRadio:Click()
		end
		sdm_receiving.name=sdm_receiving.nameAndText:sub(1,sdm_receiving.playerNameLen)
		sdm_newMacroNameInput:SetText(sdm_receiving.name)
		sdm_receiving.text=sdm_receiving.nameAndText:sub(sdm_receiving.playerNameLen+1,sdm_receiving.playerNameLen+sdm_receiving.textLen)
	end
end

function sdm_EndReceiving(text)
	sdm_receiveStatusBar_text:SetText(text)
	sdm_cancelReceiveButton:Disable()
	sdm_receiveButton:Enable()
	sdm_newButton:Enable()
	sdm_receiveTargetRadio:Enable()
	sdm_receiveArbitraryRadio:Enable()
	sdm_receiveAnyoneRadio:Enable()
	sdm_receiveInput:EnableMouse(1)
	sdm_newMacroNameInput:SetText("")
	sdm_newMacroNameInput:EnableMouse(1)
	sdm_buttonRadio:Enable()
	sdm_floatingRadio:Enable()
	sdm_scriptRadio:Enable()
	sdm_globalRadio:Enable()
	sdm_charspecRadio:Enable()
	sdm_createMacroButton:Enable()
	sdm_receiving=nil
end

function sdm_CancelSend()
	SendAddonMessage(sdm_msgPrefix, sdm_msgCommands.SendFailed.."Cancelled", sdm_sending.channel, sdm_sending.target)
	sdm_EndSending("|cffff0000Cancelled|r")
end

function sdm_CancelReceive()
	if sdm_receiving.playerName~="<ANYONE>" then
		SendAddonMessage(sdm_msgPrefix, sdm_msgCommands.ReceivingFailed.."Cancelled,"..sdm_version, "WHISPER", sdm_receiving.playerName)
	end
	sdm_EndReceiving("|cffff0000Cancelled|r")
	sdm_newFrame:Hide()
end

function sdm_SplitString(s, pattern, limit, ...) --iterates through "s", splitting it between occurrences of "pattern", and returning the split portions IN BACKWARDS ORDER. Splits a maximum of <limit> times (optional)
	if limit==0 then
		return s, ...
	end
	local index = s:find(pattern)
	if (not index) then
		return s, ...
	end
	return sdm_SplitString(s:sub(index+pattern:len()), pattern, limit-1, s:sub(1, index-1), ...)
end

function sdm_SendButtonClicked()
	local channel
	local target
	if sdm_sendPartyRadio:GetChecked() then
		channel="PARTY"
	elseif sdm_sendRaidRadio:GetChecked() then
		channel="RAID"
	elseif sdm_sendBattlegroundRadio:GetChecked() then
		channel="BATTLEGROUND"
	elseif sdm_sendGuildRadio:GetChecked() then
		channel="GUILD"
	elseif sdm_sendTargetRadio:GetChecked() then
		channel="WHISPER"
		if UnitIsPlayer("target") then
			target, realm = UnitName("target")
			if realm and realm~="" then
				target = target.."-"..realm
			end
		end
	elseif sdm_sendArbitraryRadio:GetChecked() then
		channel="WHISPER"
		target=sdm_sendInput:GetText()
	end
	if channel=="WHISPER" and ((not target) or target=="" or target==sdm_thisChar.name) then
		return
	end
	sdm_sendInput:ClearFocus()
	sdm_SendMacro(sdm_macros[sdm_currentEdit], channel, target)
end

function sdm_ReceiveButtonClicked()
	local sender
	if sdm_receiveTargetRadio:GetChecked() then
		if UnitIsPlayer("target") then
			sender, realm = UnitName("target")
			if realm and realm~="" then
				sender = sender.."-"..realm
			end
		end
	elseif sdm_receiveArbitraryRadio:GetChecked() then
		sender=sdm_receiveInput:GetText()
	elseif sdm_receiveAnyoneRadio:GetChecked() then
		sender = "<ANYONE>"
	end
	if ((not sender) or sender=="" or sender==sdm_thisChar.name) then return end
	sdm_receiveInput:ClearFocus()
	sdm_SaveConfirmationBox("sdm_WaitForMacro("..sdm_Stringer(sender)..")")
end

sdm_sending=nil --info about the macro you're trying to send
sdm_receiving=nil --info about the macro you're receiving (or waiting to receive)
sdm_updateInterval=0.25 --can be as low as 0.01 and still work, but it might disconnect you if there are other addons sending out messages too.  0.25 is slower but safer.
sdm_minVersion="2.4" --the oldest version that is compatible with this one for exchanging macros