--[[

	BagnonCore
		Core bag functionality
		
	BagSlots:
		-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?)
		
--]]

bgn_NORMAL_BG = "Interface\\DialogFrame\\UI-DialogBox-Background";
bgn_OPAQUE_BG = "Interface\\CharacterFrame\\UI-Party-Background";

--Local constants
local DEFAULT_COLS = 8;
local DEFAULT_SPACING = 2;

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

function BagnonCore_LoadSettings(frame, bags, maxSize, frameType)
	
	--make frame close on escape
	tinsert(UISpecialFrames, frame:GetName());
	
	--initialize variables for the frame if there are none
	if(not BagnonSets[frame:GetName()] ) then
		BagnonSets[frame:GetName()] = {};
	end
	
	if(not BagnonSets[frame:GetName()].bags) then
		BagnonSets[frame:GetName()].bags = bags;
	end
	
	--set the frame's transparency
	if(BagnonSets[frame:GetName()].alpha) then
		frame:SetAlpha(BagnonSets[frame:GetName()].alpha);
	end
	
	--set the frame's background
	if(BagnonSets[frame:GetName()].bg) then
		BagnonMenu_SetBackground(frame, bgn_OPAQUE_BG);
	end
	
	--load any settings related to BagnonForever
	if(BagnonForever) then
		frame.player = UnitName("player");
		frame.defaultBags = bags;
	end
	
	if(BagnonSets.reverseOrder) then
		table.sort(BagnonSets[frame:GetName()].bags, function (a, b) return a > b; end);
	end
	
	--1.10 todo:  make this not needed
	frame.MAX_SLOTS = maxSize; --a semi constant
	
	--set the frame's title
	getglobal(frame:GetName() .. "Title"):SetText(UnitName("player") .. "'s " .. frameType);
	frame:RegisterForClicks("LeftButtonDown", "LeftButtonUp", "RightButtonUp");
	
	--reposition actually handles rescaling
	BagnonCore_Reposition(frame);
	
	--This would probably be better named layout
	BagnonCore_LayoutFrame(frame, BagnonSets[frame:GetName()].cols, BagnonSets[frame:GetName()].space);
	BagnonCore_GenerateFrame(frame);
end

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

function BagnonCore_GenerateFrame(frame)
	frame.size = nil;
	local frameName = frame:GetName();
	
	--update money
	if( Bagnon_IsCachedFrame(frame) ) then
		MoneyFrame_Update(frameName .. "MoneyFrame", BagnonDB_GetMoney(frame.player));
		
		for bag in frame.defaultBags do
			BagnonCore_AddBag(frame, frame.defaultBags[bag]);
		end
	else
		MoneyFrame_Update(frameName .. "MoneyFrame", GetMoney());
		
		for bag in BagnonSets[frameName].bags do
			BagnonCore_AddBag(frame, BagnonSets[frameName].bags[bag]);
		end
	end
	
	BagnonCore_TrimToSize(frame);
	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
--]]

function BagnonCore_AddBag(frame, bagID)

	local frameName = frame:GetName();
	local slot, index, size, link; --slot refers to the itemslot in <frame>, index refers to the item slot in the bag
	
	if(not frame.size) then
		frame.size = 0;
		slot = 0;
	else
		slot = frame.size;
	end
	
	if( Bagnon_IsCachedBag(frame.player, bagID) ) then
		size = BagnonDB_GetBagData(frame.player, bagID);
	else
		size = GetContainerNumSlots(bagID);
	end
	
	if( getglobal(frameName .. "Bags"):IsShown() ) then
		BagnonBag_UpdateTexture(frame, bagID);
	end
	
	--update used slots
	for index = 1, size, 1 do
		slot = slot + 1;

		local item = getglobal( frameName .. "Item".. slot);
		
		--stuff to do in 1.10:  create a new item slot if there are not enough
		if(not item) then
			frame.size = slot - 1;
			return;
		end
		
		item:SetID(index);
		item.bag = bagID;
		item:Show();
		
		BagnonCore_UpdateItemInfo(item);
	end
	
	frame.size = slot;
end

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

function BagnonCore_TrimToSize(frame)
	if( not frame.space ) then
		return;
	end
	
	local frameName = frame:GetName();
	local slot, height;
	
	if(frame.size) then
		--hide unused slots
		for slot = frame.size + 1, frame.MAX_SLOTS, 1 do
			getglobal( frameName .. "Item".. slot ):Hide();
		end
	end
	
	if(not frame.size or frame.size == 0) then
		--correction for any frame that's completely empty
		height = 64;
		frame:SetWidth(256);
	else
		--set the frame's width
		--24 is the estimated border width of the frame, 37 is about the width of an item slot
		if(frame.size < frame.cols) then
			frame:SetWidth( ( 37 + frame.space ) * frame.size + 24 - frame.space );
		else
			frame:SetWidth( ( 37 + frame.space ) * frame.cols + 24 - 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
	local bagFrame = getglobal(frame:GetName() .. "Bags");	
	
	if( bagFrame and bagFrame:IsShown() ) then
		frame:SetHeight(height + bagFrame:GetHeight());

		if(frame:GetWidth() < bagFrame:GetWidth()) then
			frame:SetWidth(bagFrame:GetWidth());
		end
	else
		frame:SetHeight(height);
	end
	
	--reposition the frame to not be out of bounds
	Infield_Reposition(frame, UIParent);
	BagnonCore_SavePosition(frame);
end


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

	local frameName = frame:GetName();
	local slot;
	
	for slot = 1, frame.size, 1 do
		BagnonCore_UpdateItemInfo( getglobal( frameName .. "Item" .. slot ) );
	end
end


-- Update the texture, lock status, and other information about an item
function BagnonCore_UpdateItemInfo(item)

	if( Bagnon_IsCachedItem(item) ) then

		local link, texture, count = BagnonDB_GetItemData(item);
		
		--update texture and count
		SetItemButtonTexture( item, texture );
		SetItemButtonCount( item, count );
		BagnonItem_UpdateLinkBorder(item);
		
		if ( texture ) then
			item.hasItem = 1;
		else
			item.hasItem = nil;
		end
		
		item.isLink = 1;
		
		--hide cooldown since there isn't one for linked items
		getglobal(item:GetName() .. "Cooldown"):Hide();
	else
		local texture, itemCount, locked, quality, readable = GetContainerItemInfo(item.bag, item:GetID());

		BagnonItem_UpdateBorder(item);
		SetItemButtonTexture(item, texture);
		SetItemButtonCount(item, itemCount);
		SetItemButtonDesaturated(item, locked, 0.5, 0.5, 0.5);

		if ( texture ) then
			BagnonItem_UpdateCooldown(item.bag, item);
			item.hasItem = 1;
		else
			getglobal(item:GetName() .. "Cooldown"):Hide();
			item.hasItem = nil;
		end

		item.readable = readable;
		item.isLink = nil;
	end
end


-- Layout the frame with given number of columns and button spacing
function BagnonCore_LayoutFrame(frame, cols, space)
	if(frame.size and frame.size == 0) then
		return;
	end
	
	local frameName = frame:GetName();
	
	if(not cols) then
		cols = DEFAULT_COLS;
	end
	
	if(not space) then
		space = DEFAULT_SPACING;
	end
	
	if(cols == DEFAULT_COLS) then
		BagnonSets[frameName].cols = nil
	else
		BagnonSets[frameName].cols = cols
	end
	
	if(space == DEFAULT_SPACING) then
		BagnonSets[frameName].space = nil;
	else
		BagnonSets[frameName].space = space;
	end
	
	local rows = math.ceil( frame.MAX_SLOTS / cols );
	local index = 1;
	local button;
	
	--resize the frame
	frame.cols = cols;
	frame.space = space;
	
	
	button = getglobal(frameName .. "Item1");
	button:ClearAllPoints();
	button:SetPoint("TOPLEFT", button:GetParent(), "TOPLEFT", 12, -32);
	
	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
	
	BagnonCore_TrimToSize(frame);
end

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

function BagnonCore_Open(frameName)
	local frame = getglobal(frameName);
	if( frame ) then
		BagnonCore_GenerateFrame(frame);
	else
		LoadAddOn(frameName);
	end
end

function BagnonCore_Close(frameName)
	local frame = getglobal(frameName);
	if ( frame ) then
		frame:Hide();
	end
end

function BagnonCore_Toggle(frameName)
	local frame = getglobal(frameName);
	if( frame ) then
		if ( frame:IsVisible() ) then
			BagnonCore_Close(frameName);
		else
			BagnonCore_Open(frameName);
		end
	else
		LoadAddOn(frameName);
	end
end

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

function BagnonCore_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.bag == bagID) then
			item:LockHighlight();
		end
	end
end

function BagnonCore_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 BagnonCore_ToggleBagForFrame(frame, bagID)
	if(not Bagnon_FrameHasBag(frame, bagID) ) then
		table.insert(BagnonSets[frame:GetName()].bags, bagID);
	else
		local frameName = frame:GetName();
		local index;
		
		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

	BagnonCore_OrderBags(frame, BagnonSets.reverseOrder);
	
	if(frame:IsShown()) then
		BagnonCore_GenerateFrame(frame);
	end
end

--[[ 
	Frame Positioning Functions 
--]]

function BagnonCore_StartMoving(frame)
	 if(not BagnonSets[frame:GetName()].locked) then
		frame:StartMoving();
	end
end

function BagnonCore_StopMoving(frame)
	frame:StopMovingOrSizing();
	Infield_Reposition(frame, UIParent);
	BagnonCore_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 BagnonCore_Reposition(frame)
	local frameName = frame:GetName();
	
	if(not BagnonSets[frameName] or not 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 BagnonCore_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

--[[
	Rightclick Menu Stuff
--]]

--this function is there so that it can be overriden
function BagnonCore_OnDoubleClick(frame)
	return;
end

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

--[[
	Bag Sorting
--]]

function BagnonCore_OrderBags(frame, reverse)
	if(reverse) then
		if(frame) then
			table.sort(BagnonSets[frame:GetName()].bags, function (a, b) 
				if(a and b) then 
					return a > b; 
				end;
			end);
		end
	else
		if(frame) then
			table.sort(BagnonSets[frame:GetName()].bags);
		end
	end
end

--[[
	Ammo Count
--]]

function BagnonCore_GetAmmo(frame)
	local total = 0;
	local frameName = frame:GetName();
	for bag in frame.bags do
		if( Bagnon_IsAmmoBag( BagnonSets[frameName].bags ) ) then
			for slot = 1, GetContainerNumSlots( BagnonSets[frameName].bags[bag] ), 1 do
				local _, count = GetContainerItemInfo( BagnonSets[frameName].bags[bag] , slot);
				if(count) then
					total = total + count;
				end
			end
		end
	end
	
	return total;
end