--[[
	BagnonFrame
		Functionality for Bagnon Inventory\Bank frames

	BagSlots:
		-2: Key (1.11)
		-1: Bank (24 slots)
		0:  Main Inventory (16 slots)
		1, 2, 3, 4: Inventory Bags (16 - 32?)
		5, 6, 7, 8, 9, 10:  Bank Bags (16 - 32?)

	TODO:
		Potentially, I can make the frame completely dynamically generated and merge Bagnon, Bagnon_Core, and Banknon
--]]

--Local constants
local FRAMESTRATA = {"LOW", "MEDIUM", "HIGH"}
local DEFAULT_COLS = 8
local DEFAULT_SPACING = 2

--[[
	Load settings for the frame
--]]

function BagnonFrame_Load(frame, bags, title)
	local frameName = frame:GetName()

	--make frame close on escape
	tinsert(UISpecialFrames, frameName)

	--initialize variables for the frame if there are none
	if not BagnonSets[frameName] then
		BagnonSets[frameName] = {
			stayOnScreen = 1,
		}
	end

	--add what bags are controlled by the frame
	if not BagnonSets[frameName].bags then
		BagnonSets[frameName].bags = bags
	end

	--set the frame's transparency
	if BagnonSets[frameName].alpha then
		frame:SetAlpha(BagnonSets[frameName].alpha)
	end

	--set the frame's background
	if not BagnonSets[frameName].bg or tonumber(BagnonSets[frameName].bg) then
		BagnonSets[frameName].bg = {r = 0, g = 0, b = 0, a = 1}
	end

	local bgSets= BagnonSets[frameName].bg
	frame:SetBackdropColor(bgSets.r, bgSets.g, bgSets.b, bgSets.a)
	frame:SetBackdropBorderColor(1, 1, 1, bgSets.a)

	--set the frame's layer (low, medium, or high)
	if BagnonSets[frameName].strata then
		BagnonFrame_SetStrata(frame, BagnonSets[frameName].strata)
	end

	local bagFrame = getglobal(frameName .. "Bags")
	if bagFrame and BagnonSets[frameName].bagsShown then
		bagFrame:Show()
		getglobal(frameName .. "ShowBags"):SetText(BAGNON_HIDEBAGS)
	end

	frame:SetClampedToScreen(BagnonSets[frameName].stayOnScreen)

	--load any settings needed if we have cached data
	if BagnonDB then
		frame.player = UnitName("player")
		frame.defaultBags = bags

		local dropdownButton = CreateFrame("Button", frameName .. "DropDown", frame, "BagnonDBUIDropDownButton")
		dropdownButton:SetAlpha(frame:GetAlpha())
		getglobal(frameName .. "Title"):SetPoint("TOPLEFT", dropdownButton, "TOPRIGHT", 2, 2)
	end

	BagnonFrame_OrderBags(frame, BagnonSets[frameName].reverse)

	--set the frame's title
	frame.title = title
	getglobal(frameName .. "Title"):SetText( format(frame.title, UnitName("player") ) )
	frame:RegisterForClicks("LeftButtonDown", "LeftButtonUp", "RightButtonUp")

	--create the frame
	--reposition actually handles rescaling
	BagnonFrame_Reposition(frame)
	BagnonFrame_Generate(frame)
end

--[[
	Generate the frame (add all items, resize)
--]]

function BagnonFrame_Generate(frame)
	frame.size = 0
	local frameName = frame:GetName()

	--generate a cached frame
	if Bagnon_IsCachedFrame(frame) then
		MoneyFrame_Update(frameName .. "MoneyFrame", BagnonDB.GetMoney(frame.player))

		for _, bagID in pairs(frame.defaultBags) do
			BagnonFrame_AddBag(frame, bagID)
		end
	--generate a normal frame
	else
		MoneyFrame_Update(frameName .. "MoneyFrame", GetMoney())

		for _, bagID in pairs(BagnonSets[frameName].bags) do
			BagnonFrame_AddBag(frame, bagID)
		end
	end

	BagnonFrame_Layout(frame, BagnonSets[frame:GetName()].cols, BagnonSets[frame:GetName()].space)
	frame:Show()
end

--[[
	Add all the slots of the given bag to the given frame.
	Increase the total size of frame to include the size of the bag
--]]

local function CreateDummyBag(parent, bagID)
	local dummyBag = CreateFrame("Frame", parent:GetName() .. "DummyBag" .. bagID, parent)
	dummyBag:SetID(bagID)

	return dummyBag
end

function BagnonFrame_AddBag(frame, bagID)
	local frameName = frame:GetName()
	local slot = frame.size

	local bagSize
	if Bagnon_IsCachedBag(frame.player, bagID) then
		bagSize = (BagnonDB.GetBagData(frame.player, bagID)) or 0
	else
		if bagID == KEYRING_CONTAINER then
			bagSize = GetKeyRingSize()
		--HACK, GetContainerNumSlots does not return proper amounts for empty bank slots if you happen to move a bank bag to your bank slots
		elseif bagID <= 0 or GetInventoryItemTexture("player", ContainerIDToInventoryID(bagID)) then
			bagSize = GetContainerNumSlots(bagID)
		else
			bagSize = 0
		end
	end

	--update used slots
	local dummyBag = getglobal(frameName .. "DummyBag" .. bagID) or CreateDummyBag(frame, bagID)
	for index = 1, bagSize, 1 do
		slot = slot + 1
		local item = getglobal(frameName .. "Item".. slot) or BagnonItem_Create(frameName .. "Item".. slot, dummyBag)
		item:SetID(index)
		item:SetParent(dummyBag)
		item:Show()

		BagnonItem_Update(item)
	end

	frame.size = frame.size + bagSize
end

--[[
	Resize and hide any unusable bag slots in the frame.
		This function needs to know about the layout of the frame
--]]

function BagnonFrame_TrimToSize(frame)
	if not frame.space then return end

	local frameName = frame:GetName()
	local slot, height

	--hide unused slots
	if frame.size then
		local slot = frame.size + 1
		local button = getglobal(frameName .. "Item".. slot)

		while button do
			button:Hide()
			slot = slot + 1
			button = getglobal(frameName .. "Item".. slot)
		end
	end

	--set the frame's width
	--correction for any frame that's completely empty
	if not frame.size or frame.size == 0 then
		height = 64
		frame:SetWidth(256)
	else
		--15 is the estimated border width of the frame, 37 is the width of an item button
		if frame.size < frame.cols then
			frame:SetWidth((37 + frame.space) * frame.size + 16 - frame.space)
		else
			frame:SetWidth((37 + frame.space) * frame.cols + 16 - frame.space)
		end

		--set the frame's height, adjusted to fit a bag frame if its shown/there is one
		height = (37 + frame.space) * math.ceil(frame.size / frame.cols)  + 64 - frame.space
	end

	--adjust for the bag frame, if present
	local bagFrame = getglobal(frame:GetName() .. "Bags")

	if bagFrame and bagFrame:IsShown() then
		frame:SetHeight(height + bagFrame:GetHeight())

		if frame:GetWidth() < bagFrame:GetWidth() then
			--the +8 is for correcting for the border width of the frame
			frame:SetWidth(bagFrame:GetWidth() + 8)
		end
	else
		frame:SetHeight(height)
	end

	--this might no longer be necessary
	--save the frame's position, if it moved
	BagnonFrame_SavePosition(frame)
end


-- Update all item information for usable slots
function BagnonFrame_Update(frame, bagID)
	if not frame.size or Bagnon_IsCachedFrame(frame) then return end

	local frameName = frame:GetName()
	local startSlot = 1
	local endSlot

	--if we don't know the ID of the bag that updated, then update all slots, else only update the necessary slots.
	if not bagID then
		endSlot = frame.size
	else
		for _, bag in pairs(BagnonSets[frameName].bags) do
			if bag == bagID then
				if bag == KEYRING_CONTAINER then
					endSlot = startSlot + GetKeyRingSize() - 1
				elseif bag == -1 then
					endSlot = startSlot + 23
				else
					endSlot = startSlot + GetContainerNumSlots(bag) - 1
				end
				break
			else
				if bag == KEYRING_CONTAINER then
					startSlot = startSlot + GetKeyRingSize()
				elseif bag == -1 then
					startSlot = startSlot + 24
				else
					startSlot = startSlot + GetContainerNumSlots(bag)
				end
			end
		end
	end

	--update the necessary slots
	for slot = startSlot, endSlot do
		local item = getglobal(frameName .. "Item" .. slot)
		if item then
			BagnonItem_Update(item)
		end
	end
end

function BagnonFrame_UpdateLock(frame)
	if not frame.size or Bagnon_IsCachedFrame(frame) then return end

	local frameName = frame:GetName()

	for slot = 1, frame.size do
		local item = getglobal(frameName .. "Item" .. slot)
		local _, _, locked = GetContainerItemInfo(item:GetParent():GetID(), item:GetID())
		SetItemButtonDesaturated(item, locked, 0.5, 0.5, 0.5)
	end
end

-- Layout the frame with given number of columns and button spacing
function BagnonFrame_Layout(frame, cols, space)
	if not frame.size then return end

	local frameName = frame:GetName()

	if not cols then
		cols = DEFAULT_COLS
	end
	if cols == DEFAULT_COLS then
		BagnonSets[frameName].cols = nil
	else
		BagnonSets[frameName].cols = cols
	end

	if not space then
		space = DEFAULT_SPACING
	end
	if space == DEFAULT_SPACING then
		BagnonSets[frameName].space = nil
	else
		BagnonSets[frameName].space = space
	end

	local rows = math.ceil(frame.size / cols)
	local index = 1

	--resize the frame
	frame.cols = cols
	frame.space = space


	local button = getglobal(frameName .. "Item1")
	if button then
		button:ClearAllPoints()
		button:SetPoint("TOPLEFT", frame, "TOPLEFT", 8, -31)

		for i = 1, rows, 1 do
			for j = 1, cols, 1 do
				index = index + 1
				button = getglobal(frameName .. "Item" .. index)
				if not button then break end

				button:ClearAllPoints()
				button:SetPoint("LEFT", frameName .. "Item" .. index - 1, "RIGHT", space, 0)
			end

			button = getglobal(frameName .. "Item" .. index)
			if not button then break end

			button:ClearAllPoints()
			button:SetPoint("TOP", frameName .. "Item" .. index - cols, "BOTTOM", 0, -space)
		end
	end

	BagnonFrame_TrimToSize(frame)
end

--[[
	Safe Open/Close/Toggle <frame>
--]]

function BagnonFrame_Open(frameName, automatic)
	local frame = getglobal(frameName)
	if frame then
		BagnonFrame_Generate(frame)
	else
		LoadAddOn(frameName)
	end
	if frame and not automatic then
		frame.manOpened = 1
	end
end

function BagnonFrame_Close(frameName, automatic)
	local frame = getglobal(frameName)
	if  frame then
		if not(automatic and frame.manOpened) then
			frame:Hide()
			frame.manOpened = nil
		end
	end
end

function BagnonFrame_Toggle(frameName)
	local frame = getglobal(frameName)
	if frame then
		if frame:IsVisible() then
			BagnonFrame_Close(frameName)
		else
			BagnonFrame_Open(frameName)
		end
	else
		LoadAddOn(frameName)
	end
end

--[[
	Highlight all the slots of <bag>
--]]

function BagnonFrame_HighlightSlots(frame, bagID)
	if not frame.size then return end

	local frameName = frame:GetName()
	local slot

	--update only the slots the player can use
	for slot = 1, frame.size, 1 do
		local item = getglobal(frameName .. "Item" .. slot)

		if item:GetParent():GetID() == bagID then
			item:LockHighlight()
		end
	end
end

function BagnonFrame_UnhighlightAll(frame)
	if not frame.size then return end

	local frameName = frame:GetName()
	local slot

	--update only the slots the player can use
	for slot = 1, frame.size, 1 do
		getglobal(frameName .. "Item" .. slot):UnlockHighlight()
	end
end

--[[
	Add/Remove <Bag> from the frame
--]]

function BagnonFrame_ToggleBag(frame, bagID)
	if not frame then return end

	local frameName = frame:GetName()

	--add bag
	if not Bagnon_FrameHasBag(frameName, bagID) then
		table.insert(BagnonSets[frameName].bags, bagID)
	--remove bag
	else
		for index in BagnonSets[frameName].bags do
			if BagnonSets[frameName].bags[index] and BagnonSets[frameName].bags[index] == bagID then
				table.remove(BagnonSets[frame:GetName()].bags, index)
			end
		end
	end

	BagnonFrame_OrderBags(frame, BagnonSets[frameName].reverse)

	--update frame
	if frame:IsShown() then
		BagnonFrame_Generate(frame)
	end
end

--[[
	Frame Positioning Functions
--]]

function BagnonFrame_StartMoving(frame)
	if not BagnonSets[frame:GetName()].locked then
		frame.isMoving = 1
		frame:StartMoving()
	end
end

function BagnonFrame_StopMoving(frame)
	frame.isMoving = nil
	frame:StopMovingOrSizing()
	BagnonFrame_SavePosition(frame)
end

--Place the frame at the last place it was at.
--This is used when the frame first loads because the game currently does not remember the last position of a frame that's dynamically loaded
function BagnonFrame_Reposition(frame)
	local frameName = frame:GetName()

	if not(BagnonSets[frameName] and BagnonSets[frameName].top) then
		return
	end

	local ratio
	--this step is to take care of a potential flaw if the UI scale changes between loadings of the frame
	if BagnonSets[frameName].parentScale then
		ratio = BagnonSets[frameName].parentScale / frame:GetParent():GetScale()
	else
		ratio = 1
	end

	frame:ClearAllPoints()
	frame:SetScale(BagnonSets[frameName].scale)
	frame:SetPoint("TOPLEFT", frame:GetParent(), "BOTTOMLEFT", BagnonSets[frameName].left * ratio, BagnonSets[frameName].top * ratio)
end

--Save the frame's current position.  Needed because frames don't remember their positions if dynamically loaded.
function BagnonFrame_SavePosition(frame)
	local frameName = frame:GetName()

	if not BagnonSets[frameName] then
		BagnonSets[frameName] = {}
	end

	BagnonSets[frameName].top = frame:GetTop()
	BagnonSets[frameName].left = frame:GetLeft()
	BagnonSets[frameName].scale = frame:GetScale()
	BagnonSets[frameName].parentScale = frame:GetParent():GetScale()
end

--set the layer of the frame
function BagnonFrame_SetStrata(frame, strata)
	BagnonSets[frame:GetName()].strata = strata
	frame:SetFrameStrata(FRAMESTRATA[strata])
end

--[[
	Tooltip Functions
--]]

--tooltips for the title
function BagnonFrame_OnEnter()
	if BagnonSets.showTooltips then
		GameTooltip:ClearAllPoints()
		GameTooltip:SetPoint("TOPLEFT", this, "BOTTOMLEFT", -2, 0)
		GameTooltip:SetOwner(this, "ANCHOR_PRESERVE")
		GameTooltip:SetText(this:GetText(), 1, 1, 1)
		GameTooltip:AddLine(BAGNON_TITLE_TOOLTIP)
		GameTooltip:Show()
	end
end

function BagnonFrame_OnLeave()
	GameTooltip:Hide()
end

--[[
	Money Frame
--]]

--for money frame tooltips, ment to be overriden by forever/kc
function BagnonFrameMoney_OnEnter()
	return
end

function BagnonFrameMoney_OnLeave()
	GameTooltip:Hide()
end

-- This is a hack that enables tooltips but still allows clicking on the money frame
function BagnonFrameMoney_OnClick()
	local parentName = this:GetParent():GetName()
	local parent = this:GetParent()

	if MouseIsOver(getglobal(parentName .. "GoldButton")) then
		OpenCoinPickupFrame(COPPER_PER_GOLD, MoneyTypeInfo[parent.moneyType].UpdateFunc(), parent)
		parent.hasPickup = 1
	elseif MouseIsOver(getglobal(parentName .. "SilverButton")) then
		OpenCoinPickupFrame(COPPER_PER_SILVER, MoneyTypeInfo[parent.moneyType].UpdateFunc(), parent)
		parent.hasPickup = 1
	elseif MouseIsOver(getglobal(parentName .. "CopperButton")) then
		OpenCoinPickupFrame(1, MoneyTypeInfo[parent.moneyType].UpdateFunc(), parent)
		parent.hasPickup = 1
	end
end

--[[
	Rightclick Menu Stuff
--]]

--hide any menus attached to the frame, if they're visible and we're hiding the frame
function BagnonFrame_OnHide()
	if BagnonMenu:IsVisible() and BagnonMenu.frame == this then
		BagnonMenu:Hide()
	end
end

--this function is here so that it can be overriden
function BagnonFrame_OnDoubleClick(frame)
	return
end

function BagnonFrame_OnClick(frame, mouseButton)
	if mouseButton == "RightButton" then
		BagnonMenu_Show(frame)
	end
end

--[[
	Bag Sorting
--]]

--when sorting in reverse, the keyring is always at the top of the frame
local function ReverseSort(a, b)
	if a == KEYRING_CONTAINER then
		return true
	elseif b == KEYRING_CONTAINER then
		return false
	elseif a and b then
		return a > b
	end
end

--when sorting in normal order, the keyring is always at the bottom of the frame
local function NormalSort(a, b)
	if a == KEYRING_CONTAINER then
		return false
	elseif b == KEYRING_CONTAINER then
		return true
	elseif a and b then
		return a < b
	end
end

function BagnonFrame_OrderBags(frame, reverse)
	if reverse then
		if frame then
			table.sort(BagnonSets[frame:GetName()].bags, ReverseSort)
			if frame.defaultBags then
				table.sort(frame.defaultBags, ReverseSort)
			end
		end
	else
		if frame then
			table.sort(BagnonSets[frame:GetName()].bags, NormalSort)
			if frame.defaultBags then
				table.sort(frame.defaultBags, NormalSort)
			end
		end
	end
end