Postal = AceLibrary("AceAddon-2.0"):new("AceEvent-2.0", "AceHook-2.0")

function Postal:OnInitialize()

	-- Allows the mail frame to be pushed
	if UIPanelWindows["MailFrame"] then
		UIPanelWindows["MailFrame"].pushable = 1
	else
		UIPanelWindows["MailFrame"] = { area = "left", pushable = 1 }
	end

	-- Close FriendsFrame will close if you try to open a mail with mailframe+friendsframe open
	if UIPanelWindows["FriendsFrame"] then
		UIPanelWindows["FriendsFrame"].pushable = 2
	else
		UIPanelWindows["FriendsFrame"] = { area = "left", pushable = 2 }
	end

	MailItem1:SetPoint("TOPLEFT", "InboxFrame", "TOPLEFT", 48, -80)
	for i=1,7 do
		getglobal("MailItem" .. i .. "ExpireTime"):SetPoint("TOPRIGHT", "MailItem" .. i, "TOPRIGHT", 10, -4)
		getglobal("MailItem" .. i):SetWidth(280)
	end

	ATTACHMENTS_MAX = 21
	ATTACHMENTS_PER_ROW_SEND = 7
	ATTACHMENTS_MAX_ROWS_SEND = 3

	Postal_SelectedItems = {}

	PostalTooltip:SetOwner(WorldFrame, 'ANCHOR_NONE')
end

function Postal:SendMailFrame_Update()

	MoneyFrame_Update('SendMailCostMoneyFrame', GetSendMailPrice() * max(1, self:GetNumMails()))

	local itemCount = 0
	local itemTitle
	local gap
	-- local last = 0
	local last = self:GetNumMails()
	for i=1,ATTACHMENTS_MAX do
		local btn = getglobal('PostalAttachment' .. i)

		local texture, count
		if btn.bag and btn.slot then
			texture, count = GetContainerItemInfo(btn.bag, btn.slot)
		end
		if not texture then
			btn:SetNormalTexture(nil)
			getglobal(btn:GetName()..'Count'):Hide()
			btn.slot = nil
			btn.bag = nil
		else
			btn:SetNormalTexture(texture)
			if count > 1 then
				getglobal(btn:GetName()..'Count'):Show()
				getglobal(btn:GetName()..'Count'):SetText(count)
			else
				getglobal(btn:GetName()..'Count'):Hide()
			end
		end
	end

	-- Determine how many rows of attachments to show
	local itemRowCount = 1
	local temp = last
	while temp > ATTACHMENTS_PER_ROW_SEND and itemRowCount < ATTACHMENTS_MAX_ROWS_SEND do
		itemRowCount = itemRowCount + 1;
		temp = temp - ATTACHMENTS_PER_ROW_SEND
	end

	if not gap and temp == ATTACHMENTS_PER_ROW_SEND and itemRowCount < ATTACHMENTS_MAX_ROWS_SEND then
		itemRowCount = itemRowCount + 1;
	end
	if SendMailFrame.maxRowsShown and last > 0 and itemRowCount < SendMailFrame.maxRowsShown then
		itemRowCount = SendMailFrame.maxRowsShown
	else
		SendMailFrame.maxRowsShown = itemRowCount
	end

	-- Compute sizes
	local cursorx = 0
	local cursory = itemRowCount - 1
	local marginxl = 8 + 6
	local marginxr = 40 + 6
	local areax = SendMailFrame:GetWidth() - marginxl - marginxr
	local iconx = PostalAttachment1:GetWidth() + 2
	local icony = PostalAttachment1:GetHeight() + 2
	local gapx1 = floor((areax - (iconx * ATTACHMENTS_PER_ROW_SEND)) / (ATTACHMENTS_PER_ROW_SEND - 1))
	local gapx2 = floor((areax - (iconx * ATTACHMENTS_PER_ROW_SEND) - (gapx1 * (ATTACHMENTS_PER_ROW_SEND - 1))) / 2)
	local gapy1 = 5
	local gapy2 = 6
	local areay = (gapy2 * 2) + (gapy1 * (itemRowCount - 1)) + (icony * itemRowCount)
	local indentx = marginxl + gapx2 + 17
	local indenty = 170 + gapy2 + icony - 13
	local tabx = (iconx + gapx1) - 3 --this magic number changes the attachment spacing
	local taby = (icony + gapy1)
	local scrollHeight = 249 - areay


	PostalHorizontalBarLeft:SetPoint('TOPLEFT', SendMailFrame, 'BOTTOMLEFT', 2 + 15, 184 + areay - 14)

	SendMailScrollFrame:SetHeight(scrollHeight)
	SendMailScrollChildFrame:SetHeight(scrollHeight)

	local SendMailScrollFrameTop = ({SendMailScrollFrame:GetRegions()})[3]
	SendMailScrollFrameTop:SetHeight(scrollHeight)
	SendMailScrollFrameTop:SetTexCoord(0, 0.484375, 0, scrollHeight / 256)

	StationeryBackgroundLeft:SetHeight(scrollHeight)
	StationeryBackgroundLeft:SetTexCoord(0, 1.0, 0, scrollHeight / 256)


	StationeryBackgroundRight:SetHeight(scrollHeight)
	StationeryBackgroundRight:SetTexCoord(0, 1.0, 0, scrollHeight / 256)


		-- Set Items
	for i=1,ATTACHMENTS_MAX do
		if cursory >= 0 then
			getglobal("PostalAttachment"..i):Enable()
			getglobal("PostalAttachment"..i):Show()
			getglobal("PostalAttachment"..i):SetPoint("TOPLEFT", "SendMailFrame", "BOTTOMLEFT", indentx + (tabx * cursorx), indenty + (taby * cursory));
			
			cursorx = cursorx + 1
			if cursorx >= ATTACHMENTS_PER_ROW_SEND then
				cursory = cursory - 1
				cursorx = 0
			end
		else
			getglobal("PostalAttachment"..i):Hide()
		end
	end
	for i=ATTACHMENTS_MAX+1,ATTACHMENTS_MAX do
		getglobal("PostalAttachment"..i):Hide()
	end

	if self:GetNumMails() > 0 then
		SendMailCODButton:Enable();
		SendMailCODButtonText:SetTextColor(NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b);
	else
		SendMailRadioButton_OnClick(1);
		SendMailCODButton:Disable();
		SendMailCODButtonText:SetTextColor(GRAY_FONT_COLOR.r, GRAY_FONT_COLOR.g, GRAY_FONT_COLOR.b);
	end

	Postal:SendMailFrame_CanSend()
end

function Postal:OnEnable()
	self:RegisterEvent('UI_ERROR_MESSAGE')
	self:RegisterEvent('MAIL_SEND_SUCCESS')
	self:RegisterEvent('MAIL_CLOSED')

	self:Hook('ContainerFrameItemButton_OnClick')
	self:Hook('PickupContainerItem')
	self:Hook('UseContainerItem')
	self:Hook('ClickSendMailItemButton')
	self:Hook('SendMailFrame_Update')
	self:Hook('SendMailFrame_CanSend')
	self:Hook('SetItemButtonDesaturated')
	self:Hook('InboxFrame_OnClick')
	self:Hook('InboxFrameItem_OnEnter')
	self:Hook('InboxFrame_Update')

	SendMailFrame:CreateTexture('PostalHorizontalBarLeft', 'BACKGROUND')
	PostalHorizontalBarLeft:SetTexture([[Interface\ClassTrainerFrame\UI-ClassTrainer-HorizontalBar]])
	PostalHorizontalBarLeft:SetWidth(256)
	PostalHorizontalBarLeft:SetHeight(16)
	PostalHorizontalBarLeft:SetTexCoord(0, 1, 0, 0.25)
	SendMailFrame:CreateTexture('PostalHorizontalBarRight', 'BACKGROUND')
	PostalHorizontalBarRight:SetTexture([[Interface\ClassTrainerFrame\UI-ClassTrainer-HorizontalBar]])
	PostalHorizontalBarRight:SetWidth(75)
	PostalHorizontalBarRight:SetHeight(16)
	PostalHorizontalBarRight:SetTexCoord(0, 0.29296875, 0.25, 0.5)
	PostalHorizontalBarRight:SetPoint('LEFT', PostalHorizontalBarLeft, 'RIGHT')

	do
		local background = ({SendMailPackageButton:GetRegions()})[1]
		background:Hide()
		local count = ({SendMailPackageButton:GetRegions()})[3]
		count:Hide()
		SendMailPackageButton:Disable()
		SendMailPackageButton:SetScript('OnReceiveDrag', nil)
		SendMailPackageButton:SetScript('OnDragStart', nil)
	end

	do
		SendMailMoneyText:SetPoint('TOPLEFT', 0, -2)
		SendMailMoney:ClearAllPoints()
		SendMailMoney:SetPoint('TOPLEFT', SendMailMoneyText, 'BOTTOMLEFT', 5, -3)
		SendMailSendMoneyButton:SetPoint('TOPLEFT', SendMailMoney, 'TOPRIGHT', 0, 12)
	end

	PostalMailButton:SetScript('OnClick', function()
		PostalGlobalFrame.first = true
		PostalGlobalFrame.to = SendMailNameEditBox:GetText()
		PostalGlobalFrame.subject = PostalSubjectEditBox:GetText() ~= '' and PostalSubjectEditBox:GetText()
		PostalGlobalFrame.body = SendMailBodyEditBox:GetText()
		PostalGlobalFrame.money = MoneyInputFrame_GetCopper(SendMailMoney)
		PostalGlobalFrame.cod = SendMailCODButton:GetChecked()
		PostalGlobalFrame.queue = Postal:AttachmentList()
		PostalGlobalFrame.total = getn(PostalGlobalFrame.queue)
		PostalGlobalFrame:Show()
		POSTAL_CANSENDNEXT = 1

		Postal:ClearItems()
		Postal:SendMailFrame_Update()
	end)

	-- hack to avoid automatic subject setting/button enabling
	SendMailMailButton:Hide()
	SendMailSubjectEditBox:Hide()
	SendMailSubjectEditBox.SetText = function(self, text) PostalSubjectEditBox:SetText(text) end
	SendMailNameEditBox:SetScript('OnTabPressed', function()
		PostalSubjectEditBox:SetFocus()
	end)
	SendMailNameEditBox:SetScript('OnEnterPressed', function()
		PostalSubjectEditBox:SetFocus()
	end)
	SendMailBodyEditBox:SetScript('OnTabPressed', function()
		if IsShiftKeyDown() then
			PostalSubjectEditBox:SetFocus()
		else
			SendMailMoneyGold:SetFocus()
		end
	end)

	Postal:SendMailFrame_Update()
end

function Postal:MAIL_CLOSED()
	PostalGlobalFrame:Hide()
	Postal:Inbox_Abort()
	Postal:ClearItems()

	-- Hides the minimap unread mail button if there are no unread mail on closing the mailbox.
	-- Does not scan past the first 50 items since only the first 50 are viewable.
	for i=1,GetInboxNumItems() do
		local wasRead = ({ GetInboxHeaderInfo(i) })[9]
		if not wasRead then
			return
		end
	end
	MiniMapMailFrame:Hide()
	-- There may be an UPDATE PENDING MAIL event after closing which would make the frame reappear, the following prevents that
	local t = GetTime()
	MiniMapMailFrame.Show = function()
		if GetTime() - t > 2 then
			MiniMapMailFrame.Show = Postal_MiniMapMailFrame_Show_Orig
			MiniMapMailFrame:Show()
		end
	end
end

function Postal:MAIL_SEND_SUCCESS() 
	POSTAL_CANSENDNEXT = 1 
end

function Postal:ContainerFrameItemButton_OnClick(btn, ignore)
	local bag, slot = this:GetParent():GetID(), this:GetID()
	if self:SelectedAttachment(bag, slot) or self:QueuedAttachment(bag, slot) then
		return
	end
	self.hooks["ContainerFrameItemButton_OnClick"].orig(btn, ignore)
end

function Postal:PickupContainerItem(bag, slot)
	if self:SelectedAttachment(bag, slot) or self:QueuedAttachment(bag, slot) then
		return
	end
	if not CursorHasItem() then
		PostalFrame.bag = bag
		PostalFrame.slot = slot
	end
	self.hooks["PickupContainerItem"].orig(bag, slot)
end

function Postal:AttachItem(bag, slot)
	for i = 1, ATTACHMENTS_MAX do
		if not getglobal("PostalAttachment" .. i).slot then

			if not self:ItemIsMailable(bag, slot) then
				Postal:Print("Postal: Cannot attach item.", 1, 0.5, 0)
				return
			end

			self.hooks["PickupContainerItem"].orig(bag, slot)
			self:AttachmentButton_OnClick(getglobal("PostalAttachment" .. i))
			return
		end
	end
end

function Postal:UseContainerItem(bag, slot)
	if IsShiftKeyDown() or IsControlKeyDown() or IsAltKeyDown() then
		return self.hooks["UseContainerItem"].orig(bag, slot)
	end

	if self:SelectedAttachment(bag, slot) or self:QueuedAttachment(bag, slot) then
		return
	end
	if not CursorHasItem() then
		PostalFrame.bag = bag
		PostalFrame.slot = slot
	end
	if SendMailFrame:IsVisible() and not CursorHasItem() then
		self:AttachItem(bag, slot)
		return
	elseif TradeFrame:IsVisible() and not CursorHasItem() then
		for i = 1, 6 do
			if not GetTradePlayerItemLink(i) then
				self.hooks["PickupContainerItem"].orig(bag, slot)
				ClickTradeButton(i)
				return
			end
		end
	end

	self.hooks["UseContainerItem"].orig(bag, slot)
end

-- Handle the dragging of items
function Postal:AttachmentButton_OnClick(button)
	button = button or this
	if CursorHasItem() then
		local bag, slot = PostalFrame.bag, PostalFrame.slot
		if not bag or not slot then return end
		if not self:ItemIsMailable(bag, slot) then
			Postal:Print("Postal: Cannot attach item.", 1, 0.5, 0)
			ClearCursor()
			return
		end

		local oldBag, oldSlot = button.bag, button.slot
		button.bag, button.slot = bag, slot
		ClearCursor() -- triggers lock changed event

		if oldBag and oldSlot then
			self:PickupContainerItem(oldBag, oldSlot)
		else
			PostalFrame.bag, PostalFrame.slot = nil, nil
		end

	elseif button.bag and button.slot then
		PostalFrame.bag, PostalFrame.slot = button.bag, button.slot
		self.hooks["PickupContainerItem"].orig(button.bag, button.slot)
		button.bag, button.slot = nil, nil
	end

	Postal:SendMailFrame_Update()
end

function Postal:ItemIsMailable(bag, slot)
	-- Make sure tooltip is cleared
	for i=1,29 do
		getglobal("PostalTooltipTextLeft" .. i):SetText("")
	end

	PostalTooltip:SetBagItem(bag, slot)
	for i=1,PostalTooltip:NumLines() do
		local text = getglobal("PostalTooltipTextLeft" .. i):GetText()
		if text == ITEM_SOULBOUND or text == ITEM_BIND_QUEST or text == ITEM_CONJURED or text == ITEM_BIND_ON_PICKUP then
			return false
		end
	end
	return true
end

function Postal:SelectedAttachment(bag, slot)
	for i=1,ATTACHMENTS_MAX do
		local btn = getglobal("PostalAttachment" .. i)
		if btn.slot == slot and btn.bag == bag then
			return true
		end
	end
end

function Postal:QueuedAttachment(bag, slot)
	for _, attachment in ipairs(PostalGlobalFrame.queue or {}) do
		if attachment.slot == slot and attachment.bag == bag then
			return true
		end
	end
end

function Postal:GetNumMails()
	local num = 0
	for i=1,ATTACHMENTS_MAX do
		local btn = getglobal("PostalAttachment" .. i)
		if btn.slot and btn.bag then
			num = num + 1
		end
	end
	return num
end

function Postal:ClearItems()
	local num = 0
	for i=1,ATTACHMENTS_MAX do
		local btn = getglobal("PostalAttachment" .. i)
		btn.slot = nil
		btn.bag = nil
	end
	PostalMailButton:Disable()
	SendMailNameEditBox:SetText('')
	SendMailNameEditBox:SetFocus()
	PostalSubjectEditBox:SetText('')
	SendMailBodyEditBox:SetText('')
	MoneyInputFrame_ResetMoney(SendMailMoney)
	SendMailRadioButton_OnClick(1)
end

function Postal:SendMailFrame_CanSend()
	if strlen(SendMailNameEditBox:GetText()) > 0 and (SendMailSendMoneyButton:GetChecked() and MoneyInputFrame_GetCopper(SendMailMoney) or 0) + GetSendMailPrice() * self:GetNumMails() <= GetMoney() then
		PostalMailButton:Enable()
	else
		PostalMailButton:Disable()
	end
end

-- handle the weird built-in mail body textbox onclick
function Postal:ClickSendMailItemButton()
	ClearCursor()
	Postal.control.as_soon_as(function() return not ({GetContainerItemInfo(PostalFrame.bag or 0, PostalFrame.slot or 0)})[3] end, function()
		if PostalFrame.bag and PostalFrame.slot then
			self:AttachItem(PostalFrame.bag, PostalFrame.slot)
		end
	end)
end

function Postal:SendMail()

	local item = tremove(this.queue, 1)

	if item or this.first then

		local subject = this.subject or '[No Subject]'
		if this.total > 1 then
			subject = subject..format(' (Part %d of %d)', this.total - getn(this.queue), this.total)
		end

		if item then
			ClearCursor()
			self.hooks["ClickSendMailItemButton"].orig()
			ClearCursor()
			self.hooks["PickupContainerItem"].orig(item.bag, item.slot)
			self.hooks["ClickSendMailItemButton"].orig()

			local name, _, count = GetSendMailItem()

			if not name then 
				Postal:Print("Postal: An error occured in POSTAL. This might be related to lag, trying to send items with an item placed in the normal send mail window, or trying to send items that cannot be sent.", 1, 0, 0)
				PostalGlobalFrame:Hide()
				return
			end
		end

		if this.first then
			this.first = false

			if this.money then
				if this.cod then
					SetSendMailCOD(this.money)
				else
					SetSendMailMoney(this.money)
				end
			end
		end

		SendMail(this.to, subject, this.body)
		return
	end

	PostalGlobalFrame:Hide()
end

function Postal:AttachmentList()
	local arr = {}
	for i = 1, ATTACHMENTS_MAX do
		local btn = getglobal("PostalAttachment" .. i)
		if btn.slot and btn.bag then
			tinsert(arr, { slot = btn.slot, bag = btn.bag })
		end
	end
	return arr
end

function Postal:ProcessQueue(elapsed)
	if not POSTAL_CANSENDNEXT then
		return
	end
	POSTAL_CANSENDNEXT = nil
	self:SendMail()
end

function Postal:SetItemButtonDesaturated(itemButton, locked)
	local bag, slot = itemButton:GetParent():GetID(), itemButton:GetID()
	if self:SelectedAttachment(bag, slot) or self:QueuedAttachment(bag, slot) then
		return self.hooks["SetItemButtonDesaturated"].orig(itemButton, true)
	end
	return self.hooks["SetItemButtonDesaturated"].orig(itemButton, locked)
end

function Postal:InboxFrameItem_OnEnter()
	local didSetTooltip
	if this.index then
		if GetInboxItem(this.index) then
			GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
			GameTooltip:SetInboxItem(this.index)
			didSetTooltip = 1
		end
	end
	if not didSetTooltip then
		GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
	end
	if this.money then
		GameTooltip:AddLine(ENCLOSED_MONEY, "", 1, 1, 1)
		SetTooltipMoney(GameTooltip, this.money)
		SetMoneyFrameColor("GameTooltipMoneyFrame", HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b)
	elseif this.cod then
		GameTooltip:AddLine(COD_AMOUNT, "", 1, 1, 1)
		SetTooltipMoney(GameTooltip, this.cod)
		if this.cod > GetMoney() then
			SetMoneyFrameColor("GameTooltipMoneyFrame", RED_FONT_COLOR.r, RED_FONT_COLOR.g, RED_FONT_COLOR.b)
		else
			SetMoneyFrameColor("GameTooltipMoneyFrame", HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b)
		end
	end
	if didSetTooltip and (this.money or this.cod) then
		GameTooltip:SetHeight(GameTooltip:GetHeight()+getglobal("GameTooltipTextLeft" .. GameTooltip:NumLines()):GetHeight())
		if GameTooltipMoneyFrame:IsVisible() then
			GameTooltip:SetHeight(GameTooltip:GetHeight()+GameTooltipMoneyFrame:GetHeight())
		end
	end
	GameTooltip:Show()
end

function Postal:UI_ERROR_MESSAGE()
	if event == "UI_ERROR_MESSAGE" and (arg1 == ERR_INV_FULL or arg1 == ERR_ITEM_MAX_COUNT) then
		if this.num then
			if arg1 == ERR_INV_FULL then
				Postal:Inbox_Abort()
				Postal:Print("Postal: Inventory full. Aborting.", 1, 0, 0)
			elseif arg1 == ERR_ITEM_MAX_COUNT then
				Postal:Print("Postal: You already have the maximum amount of that item. Skipping.", 1, 0, 0)
				if this.lastVal then
					for key, va in this.id do
						if va >= this.lastVal then
							this.id[key] = va + 1
						end
					end
				end
			end
		end
	end
end

function Postal:Print(msg, r, g, b)
	DEFAULT_CHAT_FRAME:AddMessage(msg, r, g, b)
end

function Postal_Inbox_SetSelected()
	local id = this:GetID() + (InboxFrame.pageNum - 1) * 7
	if not this:GetChecked() then
		for k, v in Postal_SelectedItems do
			if v == id then
				tremove(Postal_SelectedItems, k)
				break
			end
		end
	else
		tinsert(Postal_SelectedItems, id)
	end
end

function Postal_Inbox_OpenSelected(open_all)
	local selected = {}
	if open_all then
		for i = 1, GetInboxNumItems() do
			tinsert(selected, i)
		end
	else
		for _, i in Postal_SelectedItems do
			tinsert(selected, i)
		end
		sort(selected)
	end
	PostalInboxFrame.opening = true
	Postal:Inbox_DisableClicks()
	Postal.open.start(selected, function()
		PostalInboxFrame.opening = false
		Postal:Inbox_DisableClicks()
	end)
	Postal_SelectedItems = {}
end

function Postal:InboxFrame_Update()
	self.hooks["InboxFrame_Update"].orig()
	for i = 1, 7 do
		local index = (i + (InboxFrame.pageNum - 1) * 7)
		if index > GetInboxNumItems() then
			getglobal("PostalBoxItem" .. i .. "CB"):Hide()
		else
			getglobal("PostalBoxItem" .. i .. "CB"):Show()
			getglobal("PostalBoxItem" .. i .. "CB"):SetChecked(nil)
			for k, v in Postal_SelectedItems do
				if v == index then
					getglobal("PostalBoxItem" .. i .. "CB"):SetChecked(1)
					break
				end
			end
		end
	end
	Postal:Inbox_DisableClicks()
end

function Postal:Inbox_DisableClicks()
	for i=1,7 do
		getglobal('MailItem'..i..'ButtonIcon'):SetDesaturated(PostalInboxFrame.opening)
		if PostalInboxFrame.opening then
			getglobal('MailItem'..i..'Button'):SetChecked(nil)
		end
	end
end

function Postal:InboxFrame_OnClick(index)
	if PostalInboxFrame.opening then
		this:SetChecked(nil)
		return
	end
	self.hooks['InboxFrame_OnClick'].orig(index)
end

function Postal:Inbox_Abort()
	Postal.open.stop()
	PostalInboxFrame.opening = false
	Postal:Inbox_DisableClicks()
	Postal_SelectedItems = {}
end