Quantcast
--This mod expands the functionality of the auto-name generation of blizzards in game mail system.
--Typically it will only auto-fill for names of players in your guild.
--This mod will expand it to include all your toons you've logged in as, B.Net Friends, and last 10 recently mailed individuals

local DB_PLAYER
local DB_RECENT
local currentPlayer
local currentRealm
local origHook = {}
local inboxAllButton
local inboxInfoText

local xanAutoMail = CreateFrame("frame","xanAutoMailFrame",UIParent)
xanAutoMail:SetScript("OnEvent", function(self, event, ...) if self[event] then return self[event](self, event, ...) end end)

local debugf = tekDebug and tekDebug:GetFrame("xanAutoMail")
local function Debug(...)
    if debugf then debugf:AddMessage(string.join(", ", tostringall(...))) end
end

--[[------------------------
	CORE
--------------------------]]

function xanAutoMail:PLAYER_LOGIN()

	currentPlayer = UnitName('player')
	currentRealm = GetRealmName()

	--do the db initialization
	self:StartupDB()

	--increase the mailbox history lines to 15
	SendMailNameEditBox:SetHistoryLines(15)

	--HOOKS FOR CHARACTER NAME HISTORY
	---------------------------------
	---------------------------------
	origHook["SendMailFrame_Reset"] = SendMailFrame_Reset
	SendMailFrame_Reset = self.SendMailFrame_Reset

	origHook["MailFrameTab_OnClick"] = MailFrameTab_OnClick
	MailFrameTab_OnClick = self.MailFrameTab_OnClick

	origHook["AutoComplete_Update"] = AutoComplete_Update
	AutoComplete_Update = self.AutoComplete_Update

	origHook[SendMailNameEditBox] = origHook[SendMailNameEditBox] or {}
	origHook[SendMailNameEditBox]["OnEditFocusGained"] = SendMailNameEditBox:GetScript("OnEditFocusGained")
	origHook[SendMailNameEditBox]["OnChar"] = SendMailNameEditBox:GetScript("OnChar")
	SendMailNameEditBox:SetScript("OnEditFocusGained", self.OnEditFocusGained)
	SendMailNameEditBox:SetScript("OnChar", self.OnChar)
	---------------------------------
	---------------------------------

	--make the open all button
	inboxAllButton = CreateFrame("Button", "xanAutoMail_OpenAllBTN", InboxFrame, "UIPanelButtonTemplate")
	inboxAllButton:SetWidth(100)
	inboxAllButton:SetHeight(20)
	inboxAllButton:SetPoint("CENTER", InboxFrame, "TOP", -80, -55)
	inboxAllButton:SetText("Open All")
	inboxAllButton:SetScript("OnClick", function() xanAutoMail.StartMailGrab() end)

	inboxInfoText = InboxFrame:CreateFontString("xanAutoMail_InfoText", "ARTWORK", "GameFontNormalSmall")
	inboxInfoText:SetJustifyH("LEFT")
	inboxInfoText:SetFontObject("GameFontNormal")
	inboxInfoText:SetPoint("TOPLEFT", inboxAllButton, "TOPRIGHT", 5, -5)

	self:UnregisterEvent("PLAYER_LOGIN")
	self.PLAYER_LOGIN = nil

end

function xanAutoMail:StartupDB()
	xanAutoMailDB = xanAutoMailDB or {}
	xanAutoMailDB[currentRealm] = xanAutoMailDB[currentRealm] or {}

	--player list
	xanAutoMailDB[currentRealm]["player"] = xanAutoMailDB[currentRealm]["player"] or {}
	DB_PLAYER = xanAutoMailDB[currentRealm]["player"]

	--recent list
	xanAutoMailDB[currentRealm]["recent"] = xanAutoMailDB[currentRealm]["recent"] or {}
	DB_RECENT = xanAutoMailDB[currentRealm]["recent"]

	--check for current user
	if DB_PLAYER[currentPlayer] == nil then DB_PLAYER[currentPlayer] = true end
end


---------------------------------------------------
---------------CHARACTER NAME DB ------------------
---------------------------------------------------

--This is called when mailed is sent
function xanAutoMail:SendMailFrame_Reset()

	--first lets get the playername
	local playerName = strtrim(SendMailNameEditBox:GetText())

	--if we don't have something to work with then call original function
	if string.len(playerName) < 1 then
		return origHook["SendMailFrame_Reset"]()
	end

	--add the name to the history
	SendMailNameEditBox:AddHistoryLine(playerName)

	--add the name to our recent DB, first check to see if it's already there
	--if so then remove it, otherwise add it to the top of the list and remove the 11 entry from the table.
	--afterwards call the original function
	for k = 1, #DB_RECENT do
		if playerName == DB_RECENT[k] then
			tremove(DB_RECENT, k)
			break
		end
	end
	tinsert(DB_RECENT, 1, playerName)
	for k = #DB_RECENT, 11, -1 do
		tremove(DB_RECENT, k)
	end
	origHook["SendMailFrame_Reset"]()

	-- set the name to the auto fill
	SendMailNameEditBox:SetText(playerName)
	SendMailNameEditBox:HighlightText()
end

--this is called when one of the mailtabs is clicked
--we have to autofill the name when the tabs are clicked
function xanAutoMail:MailFrameTab_OnClick(tab)
	origHook["MailFrameTab_OnClick"](self, tab)
	if tab == 2 then
		local playerName = DB_RECENT[1]
		if playerName and SendMailNameEditBox:GetText() == "" then
			SendMailNameEditBox:SetText(playerName)
			SendMailNameEditBox:HighlightText()
		end
	end
end

--this function is called each time a character is pressed in the playername field of the mail window
function xanAutoMail:OnChar(...)
	if self:GetUTF8CursorPosition() ~= strlenutf8(self:GetText()) then return end
	local text = strupper(self:GetText())
	local textlen = strlen(text)
	local foundName

	--check player toons
	for k, v in pairs(DB_PLAYER) do
		if strfind(strupper(k), text, 1, 1) == 1 then
			foundName = k
			break
		end
	end

	--check our recent list
	if not foundName then
		for k = 1, #DB_RECENT do
			local playerName = DB_RECENT[k]
			if strfind(strupper(playerName), text, 1, 1) == 1 then
				foundName = playerName
				break
			end
		end
	end

	--Check our RealID friends
	if not foundName then
		local numBNetTotal, numBNetOnline = BNGetNumFriends()
		for i = 1, numBNetOnline do
			local presenceID, givenName, surname, toonName, toonID, client = BNGetFriendInfo(i)
			if (toonName and client == BNET_CLIENT_WOW and CanCooperateWithToon(toonID)) then
				if strfind(strupper(toonName), text, 1, 1) == 1 then
					foundName = toonName
					break
				end
			end
		end
	end

	--call the original onChar to display the dropdown
	origHook[SendMailNameEditBox]["OnChar"](self, ...)

	--if we found a name then override the one in the editbox
	if foundName then
		self:SetText(foundName)
		self:HighlightText(textlen, -1)
		self:SetCursorPosition(textlen)
	end

end

function xanAutoMail:OnEditFocusGained(...)
	SendMailNameEditBox:HighlightText()
end

function xanAutoMail:AutoComplete_Update(editBoxText, utf8Position, ...)
	if self ~= SendMailNameEditBox then
		origHook["AutoComplete_Update"](self, editBoxText, utf8Position, ...)
	end
end

---------------------------------------------------
---------------------------------------------------
---------------------------------------------------

--[[------------------------
	OPEN ALL MAIL
--------------------------]]

local delayCount = {}
local moneyCount = 0
local skipCount = 0
local errorCheckCount = 0
local currentStatus = "STOP"

xanAutoMail:RegisterEvent("MAIL_CLOSED")
xanAutoMail:RegisterEvent("MAIL_SHOW")
xanAutoMail:RegisterEvent("MAIL_INBOX_UPDATE")
xanAutoMail:RegisterEvent("UI_ERROR_MESSAGE")

local function colorMoneyText(value)
	if not value then return "" end
	local gold = abs(value / 10000)
	local silver = abs(mod(value / 100, 100))
	local copper = abs(mod(value, 100))

	local GOLD_ABRV = "g"
	local SILVER_ABRV = "s"
	local COPPER_ABRV = "c"

	local WHITE = "ffffff"
	local COLOR_COPPER = "eda55f"
	local COLOR_SILVER = "c7c7cf"
	local COLOR_GOLD = "ffd700"

	if value >= 10000 or value <= -10000 then
		return format("|cff%s%d|r|cff%s%s|r |cff%s%d|r|cff%s%s|r |cff%s%d|r|cff%s%s|r", WHITE, gold, COLOR_GOLD, GOLD_ABRV, WHITE, silver, COLOR_SILVER, SILVER_ABRV, WHITE, copper, COLOR_COPPER, COPPER_ABRV)
	elseif value >= 100 or value <= -100 then
		return format("|cff%s%d|r|cff%s%s|r |cff%s%d|r|cff%s%s|r", WHITE, silver, COLOR_SILVER, SILVER_ABRV, WHITE, copper, COLOR_COPPER, COPPER_ABRV)
	else
		return format("|cff%s%d|r|cff%s%s|r", WHITE, copper, COLOR_COPPER, COPPER_ABRV)
	end
end

local function freeSpace()
	Debug("freeSpace")
	local totalFree = 0
	for i=0, NUM_BAG_SLOTS do
		local numberOfFreeSlots = GetContainerNumFreeSlots(i)
		totalFree = totalFree + numberOfFreeSlots
	end
	return totalFree
end

local function inboxFullCheck()
	--sometimes the mailbox is full, if this happens we have to make changes to the button position
	local nItem, nTotal = GetInboxNumItems()
	if nItem and nTotal then
		if ( nTotal > nItem) or InboxTooMuchMail:IsVisible() and not inboxAllButton.movedBottom then
			inboxAllButton:ClearAllPoints()
			inboxAllButton:SetPoint("CENTER", InboxFrame, "BOTTOM", -60, 100)
			inboxAllButton.movedBottom = true
			inboxInfoText:ClearAllPoints()
			inboxInfoText:SetPoint("TOPLEFT", inboxAllButton, "TOPRIGHT", 5, -5)
			Debug("inboxFullCheck", "moved bottom")
		elseif (( nTotal < nItem) or not InboxTooMuchMail:IsVisible()) and inboxAllButton.movedBottom then
			inboxAllButton.movedBottom = nil
			inboxAllButton:ClearAllPoints()
			inboxAllButton:SetPoint("CENTER", InboxFrame, "TOP", -80, -55)
			inboxInfoText:ClearAllPoints()
			inboxInfoText:SetPoint("TOPLEFT", inboxAllButton, "TOPRIGHT", 5, -5)
			Debug("inboxFullCheck", "not moved")
		end
	end
end

xanAutoMail:SetScript("OnUpdate",
	function( self, elapsed )
		if #delayCount > 0 then
			for i = #delayCount, 1, -1 do
				if delayCount[i].endTime and delayCount[i].endTime <= GetTime() then
					local func = delayCount[i].callbackFunction
					tremove(delayCount, i)
					func()
				end
			end
		end
	end
)

function xanAutoMail:Delay(name, duration, callbackFunction, force)
	if not force and currentStatus == "STOP" then return end
	for k, q in ipairs(delayCount) do
		if q.name == name then
			--don't run the same delay more than once, we can however refresh it
			q.duration = duration
			q.endTime = (GetTime()+duration)
			q.callbackFunction = callbackFunction
			Debug("UPDATED", q.name, q.duration, q.endTime)
			return
		end
	end
	Debug("ADDED", name, duration, (GetTime()+duration))
	tinsert(delayCount, {name=name, duration=duration, endTime=(GetTime()+duration), callbackFunction=callbackFunction})
end

function xanAutoMail:UpdateInfoText()
	local nItem, nTotal = GetInboxNumItems()
	if nTotal == nItem then
		inboxInfoText:SetText(format("Showing all %d mail.", nItem))
	else
		inboxInfoText:SetText(format("Showing %d of %d mail.", nItem, nTotal))
	end
	--hide the stupid icon if necessary
	if nTotal <= 0 and MiniMapMailFrame:IsVisible() then
		MiniMapMailFrame:Hide()
	end
end

function xanAutoMail:MAIL_SHOW()
	Debug("MAIL_SHOW")
	inboxFullCheck()
	CheckInbox()
	inboxInfoText:SetText("Waiting...")
	xanAutoMail:Delay("mailInfoText", 0.5, xanAutoMail.UpdateInfoText, true)
end

function xanAutoMail:MAIL_CLOSED()
	Debug("MAIL_CLOSED")
	xanAutoMail:StopMailGrab(true, 1)
end

function xanAutoMail:MAIL_INBOX_UPDATE()
	xanAutoMail:Delay("mailInfoText", 0.5, xanAutoMail.UpdateInfoText, true)
	if currentStatus == "STOP" then return end
	--keep increasing the delay before the next mail grab until all items are taken from current opened mail
	xanAutoMail:Delay("mailGrabNextItem", 0.5, xanAutoMail.GrabNextMailItem)
	Debug("MAIL_INBOX_UPDATE", "mailGrabNextItem")
end

function xanAutoMail:UI_ERROR_MESSAGE(event, num, msg)

	if currentStatus == "STOP" then return end
	local stopMailGrab = false

	if msg == ERR_MAIL_DATABASE_ERROR then
		DEFAULT_CHAT_FRAME:AddMessage("xanAutoMail: (ERROR) There was a mailbox database error from the server.")
		stopMailGrab = true
	elseif msg == ERR_INV_FULL then
		DEFAULT_CHAT_FRAME:AddMessage("xanAutoMail: (ERROR) Your inventory is full.")
		stopMailGrab = true
	elseif msg == ERR_ITEM_MAX_COUNT then
		DEFAULT_CHAT_FRAME:AddMessage("xanAutoMail: (ERROR) Cannot loot anymore unique items from Mailbox.")
		DEFAULT_CHAT_FRAME:AddMessage("xanAutoMail: Please the delete item(s) from the Mailbox before trying again.")
		stopMailGrab = true
	end

	if stopMailGrab then
		xanAutoMail:StopMailGrab(false, 2)
		return
	end
end

function xanAutoMail:IsMailItemEmpty(index)
	local packageIcon, stationeryIcon, sender, subject, money, COD, daysLeft, numItems, wasRead, wasReturned, textCreated, canReply, isGM, itemQuantity = GetInboxHeaderInfo(index)
	if isGM then return false end
	if canReply then return false end
	if numItems then return false end
	if COD > 0 then return false end
	if money > 0 then return false end
	if wasReturned then return false end
	return true
end

function xanAutoMail:GrabNextMailItem()
	if currentStatus == "STOP" then return end
	Debug("GrabNextMailItem", "BEGIN")
	--do inbox check
	if currentStatus == "CHECK" then
		Debug("GrabNextMailItem", currentStatus)
		CheckInbox()
		currentStatus = "SKIPCHECK"
		xanAutoMail:Delay("mailGrabNextItem", 0.5, xanAutoMail.GrabNextMailItem)
		return
	end

	local nItem, nTotal = GetInboxNumItems()
	Debug("GrabNextMailItem", "START", nItem, nTotal)

	xanAutoMail:UpdateInfoText()

	--check to see if the last messages were read or if we have nothing to work with, or we have done more than 50 error checks, which is roughly about 10 to 11 seconds
	if nTotal <= 0 or skipCount >= nTotal or errorCheckCount > 50 then
		if errorCheckCount > 50 then
			DEFAULT_CHAT_FRAME:AddMessage("xanAutoMail: (ERROR) Mailbox latency error. (Try Again)")
		end
		Debug("GrabNextMailItem", 2, nItem, nTotal, skipCount, errorCheckCount)
		xanAutoMail:StopMailGrab(false, 4)
		return
	elseif freeSpace() < 1 then
		xanAutoMail:StopMailGrab(false, 5)
		DEFAULT_CHAT_FRAME:AddMessage("xanAutoMail: (ERROR) Your inventory is full.")
		return
	elseif (nItem <= 0 or skipCount >= nItem) and nTotal > 0 then
		--if we still have something to work with then fire in another 45 seconds
		currentStatus = "CHECK"
		errorCheckCount = 0
		xanAutoMail:Delay("mailInboxCheck", 45, xanAutoMail.GrabNextMailItem)
		inboxInfoText:SetText("Waiting 45 seconds")
		DEFAULT_CHAT_FRAME:AddMessage("xanAutoMail: Waiting 45 seconds for next mail batch.")
		Debug("GrabNextMailItem", 3, currentStatus, nItem, nTotal, skipCount)
		return
	end

	skipCount = 0 --reset

	for mIndex = nItem, 1, -1 do
		local _, _, _, _, money, COD, _, numItems, wasRead, _, _, _, isGM = GetInboxHeaderInfo(mIndex)

		if money > 0 or (numItems and numItems > 0) and COD <= 0 and not isGM then
			if money > 0 then moneyCount = moneyCount + money end
			TakeInboxMoney(mIndex)
			AutoLootMailItem(mIndex)
			--we looted something so lets wait for next update
			Debug("GrabNextMailItem", 5, money, mIndex)
			xanAutoMail:Delay("mailGrabNextItem", 0.5, xanAutoMail.GrabNextMailItem) --just in case
			return
		end
		Debug("GrabNextMailItem", "LOGIC_ELSE", money, COD, numItems, wasRead, isGM)
		if xanAutoMail:IsMailItemEmpty(mIndex) then
			Debug("GrabNextMailItem", "DeleteInboxItem", mIndex)
			DeleteInboxItem(mIndex)
			xanAutoMail:Delay("mailGrabNextItem", 0.5, xanAutoMail.GrabNextMailItem) --just in case
			return
		end

		skipCount = skipCount + 1
	end

	Debug("GrabNextMailItem", "skipCount", skipCount)

	xanAutoMail:Delay("mailGrabNextItem", 0.2, xanAutoMail.GrabNextMailItem)
	errorCheckCount = errorCheckCount + 1
	Debug("errorCheckCount", errorCheckCount)

end

function xanAutoMail:StartMailGrab()
	Debug("StartMailGrab")
	if GetInboxNumItems() == 0 then return end
	currentStatus = "START"
	inboxAllButton:Disable()
	moneyCount = 0
	skipCount = 0
	errorCheckCount = 0
	xanAutoMail:Delay("mailGrabNextItem", 0.2, xanAutoMail.GrabNextMailItem)
end

function xanAutoMail:StopMailGrab(force, flag)
	Debug("StopMailGrab", flag)
	currentStatus = "STOP"
	delayCount = {}
	inboxAllButton:Enable()
	if not force then
		if moneyCount > 0 then
			DEFAULT_CHAT_FRAME:AddMessage("xanAutoMail: Total money from mailbox ["..colorMoneyText(moneyCount).."]")
		end
		CheckInbox()
		xanAutoMail:Delay("mailInfoText", 0.5, xanAutoMail.UpdateInfoText, true)
	end
end


if IsLoggedIn() then xanAutoMail:PLAYER_LOGIN() else xanAutoMail:RegisterEvent("PLAYER_LOGIN") end