--[[
Name: AutoBarSearch
Author: Toadkiller of Proudmoore
Website: http://www.wowace.com/
]]
-- Copyright 2007+ Toadkiller of Proudmoore.

-- http://www.wowace.com/wiki/AutoBar
-- http://www.curse-gaming.com/en/wow/addons-4430-1-autobar-toadkiller.html

-- AutoBarSearch.spaceRecycle
-- AutoBarSearch.space
--		{ itemID = {buttonsIndex, ...} }
--		our search space with all items to look for, with list of client slots that are looking

-- AutoBarSearch.itemsRecycle
-- AutoBarSearch.items[buttonsIndex]
--		{ itemID = {category, slotIndex, categoryIndex...} }
--		list of items to search for per slot.  No duplicates, highest priority overwrites.
--		priority is slotIndex.categoryIndex

-- AutoBarSearch.foundRecycle
-- AutoBarSearch.found[buttonsIndex]
--		{ itemID = {bag, slot}, ... }
--		list of found items for the slot
--		bag, slot synced to AutoBar.bags

-- AutoBarSearch.bagsRecycle
-- AutoBarSearch.bags[bag][slot]
--		{ {itemId | nil, [buttonsIndex]} ... }
--		buttonsIndex synced to AutoBar.found
-- 		map of bag contents
--		Verify / add items

-- AutoBarSearch.current[buttonsIndex]
--		{ 1 = itemId, ... }
--		priority ordered list of the found items for the slot
--		index into found
--		This is essentially the popup list

local AceOO = AceLibrary("AceOO-2.0")
AutoBarSearch = {}


function AutoBarSearch:ScanBags(bagIndex)

end


-- Recycle lists will avoid garbage collection and memory thrashing but potentially grow over time
-- A simple 2 list aproach that recycles objects specific to that type of list so the bulk of operations should be only initing recycled objects.
-- We shall see if this is a good thing
local Recycle = AceOO.Class()
Recycle.virtual = true -- this means that it cannot be instantiated. (cannot call :new())

function Recycle.prototype:init()
    Recycle.super.prototype.init(self) -- very important. Will fail without this.
	self.recycleList = {}
	self.dataList = {}
end

-- Returns a new or recycled list object
function Recycle.prototype:New()
	if (self.recycleList[1]) then
		local i = table.maxn(self.recycleList)
		local x = self.recycleList[i]
		self.recycleList[i] = nil
		return x
	else
		return {}
	end
end

-- Adds some trash to the recycle list
-- do delete trash from the original list.
function Recycle.prototype:Recycle(trash)
	table.insert(self.recycleList, trash)
end



-- The search space with all items to look for
-- Tracks client buttons that are looking (for proper deletion)
--		{ itemID = {buttonsIndex, ...} }
local SearchSpace = AceOO.Class(Recycle)

-- Add a list of itemIds for the given buttonsIndex
function SearchSpace.prototype:Add(itemList, buttonsIndex)
	for i, itemId in pairs(itemList) do
		local clientButtons = self.dataList[itemId]
		if (not clientButtons) then
			clientButtons = self:New()
			self.dataList[itemId] = clientButtons
		end
		clientButtons[buttonsIndex] = true
	end
end


-- Remove a list of itemIds for the given buttonsIndex
function SearchSpace.prototype:Delete(itemList, buttonsIndex)
	for i, itemId in pairs(itemList) do
		local clientButtons = self.dataList[itemId]
		if (clientButtons) then
			clientButtons[buttonsIndex] = nil
		end
		if (table.maxn(clientButtons) == 0) then
			self.Recycle(clientButtons)
			self.dataList[itemId] = nil
		end
	end
end


-- Return the search space list.
-- Do not manipulate the list.  It is only for performance when checking if an itemId is searched for
function SearchSpace.prototype:GetList()
	return self.dataList
end



-- List of items to search for per slot.  No duplicates, highest priority overwrites.
-- Synced to SearchSpace.  All SearchSpace changes are via Items
-- Should only be changed via Config, or hunter pet food swithes
-- Priority is slotIndex.categoryIndex
--		{ itemID = {category, slotIndex, categoryIndex} }
local Items = AceOO.Class(Recycle)

function Items.prototype:init()
    Items.super.prototype.init(self) -- very important. Will fail without this.
	for buttonsIndex = 1, AUTOBAR_MAXBUTTONS, 1 do
		self.dataList[buttonsIndex] = {}
	end
end

-- Add a list of itemIds for the given buttonsIndex
function Items.prototype:Add(itemList, buttonsIndex, category, slotIndex)
	for i, itemId in pairs(itemList) do
		local buttonItems = self.dataList[buttonsIndex]
		local itemData = buttonItems[itemId]
		if (not itemData) then
			itemData = self:New()
			buttonItems[itemId] = itemData
			itemData.category = category
			itemData.slotIndex = slotIndex
			itemData.categoryIndex = i
		elseif (slotIndex > itemData.slotIndex or (slotIndex == itemData.slotIndex and i >= itemData.categoryIndex)) then
			itemData.category = category
			itemData.slotIndex = slotIndex
			itemData.categoryIndex = i
		end
	end
end

-- Remove a list of itemIds for the given buttonsIndex
-- ToDo: on deletion reapply lower priority ones / track them from the start?
function Items.prototype:Delete(itemList, buttonsIndex, category, slotIndex)
	for i, itemId in pairs(itemList) do
		local buttonItems = self.dataList[buttonsIndex]
		local itemData = buttonItems[itemId]
		if (itemData and slotIndex == itemData.slotIndex and i == itemData.categoryIndex) then
			buttonItems[itemId] = nil
			self.Recycle(itemData)
		end
	end
end

-- Return the buttons search list.
-- Do not manipulate the list.  It is only for performance when checking if an itemId is searched for
function Items.prototype:GetList(buttonsIndex)
	return self.dataList[buttonsIndex]
end



-- list of found items for the button
-- bag, slot synced to Bags
-- { itemID = {bag1, slot1, ...} }
local Found = AceOO.Class(Recycle)

function Found.prototype:init()
    Found.super.prototype.init(self) -- very important. Will fail without this.
	for buttonsIndex = 1, AUTOBAR_MAXBUTTONS, 1 do
		self.dataList[buttonsIndex] = {}
	end
end

-- Add the found item to the list of itemIds for the given buttonsIndex
function Found.prototype:Add(buttonsIndex, itemID, bag, slot)
	local buttonItems = self.dataList[buttonsIndex]
	local itemData = buttonItems[itemId]
	if (not itemData) then
		itemData = self:New()
		buttonItems[itemId] = itemData
		itemData[1] = bag
		itemData[2] = slot
		for i = table.maxn(), 3, -1 do
			itemData[i] = nil
		end
	else
		local bFound = false
		local nItems = table.maxn(itemData)
		for i = 1, nItems - 1, 2 do
			if (itemData[i] and itemData[i] == bag and itemData[i + 1] == slot) then
				bFound = true
			end
		end
		if (not found) then
			itemData[nItems + 1] = bag
			itemData[nItems + 2] = slot
		end
	end
end

-- Remove the found item from the list of itemIds for the given buttonsIndex
-- ToDo: on deletion reapply lower priority ones / track them from the start?
function Found.prototype:Delete(buttonsIndex, itemID, bag, slot)
	local buttonItems = self.dataList[buttonsIndex]
	local itemData = buttonItems[itemId]
	if (itemData) then
		local nItems = table.maxn(itemData)
		for i = 1, nItems - 1, 2 do
			if (itemData[i] and itemData[i] == bag and itemData[i + 1] == slot) then
				table.remove(itemData, i + 1);
				table.remove(itemData, i);
			end
		end
		if (not itemData[1]) then
			buttonItems[itemId] = nil
			self.Recycle(itemData)
		end
	end
end

-- Return the buttons found list.
-- Do not manipulate the list.  It is only for performance.
function Found.prototype:GetList(buttonsIndex)
	return self.dataList[buttonsIndex]
end



-- Map of bag contents
-- ButtonsIndex synced to Found
-- Verify / add items
-- [bag][slot] = { {itemId | nil, [buttonsIndex]} ... }
local Bags = AceOO.Class(Recycle)

function Bags.prototype:init()
    Bags.super.prototype.init(self) -- very important. Will fail without this.
	for bag = 0, 4, 1 do
		self.dataList[bag] = {}
	end
end

-- Add itemID at bag, slot
function Bags.prototype:Add(itemID, bag, slot)
	local bagItems = self.dataList[bag]
	local itemData = bagItems[slot]
	if (not itemData) then
		itemData = self:New()
		bagItems[slot] = itemData
		itemData[1] = itemID
		itemData[2] = bag
		itemData[3] = slot
		for i = table.maxn(), 3, -1 do
			itemData[i] = nil
		end
	else
		local bFound = false
		local nItems = table.maxn(itemData)
		for i = 1, nItems - 1, 2 do
			if (itemData[i] and itemData[i] == bag and itemData[i + 1] == slot) then
				bFound = true
			end
		end
		if (not found) then
			itemData[nItems + 1] = bag
			itemData[nItems + 2] = slot
		end
	end
end


-- Remove a list of itemIds for the given buttonsIndex
-- ToDo: on deletion reapply lower priority ones / track them from the start?
function Bags.prototype:Delete(buttonsIndex, itemID, bag, slot)
	local bagItems = self.dataList[buttonsIndex]
	local itemData = bagItems[slot]
	if (itemData) then
		local nItems = table.maxn(itemData)
		for i = 1, nItems - 1, 2 do
			if (itemData[i] and itemData[i] == bag and itemData[i + 1] == slot) then
				table.remove(itemData, i + 1);
				table.remove(itemData, i);
			end
		end
		if (not itemData[1]) then
			bagItems[itemId] = nil
			self.Recycle(itemData)
		end
	end
end


-- Return the buttons found list.
function Bags.prototype:Update(buttonsIndex)
	return self.dataList[buttonsIndex]
end



-- Sorted version of Current items for each button
-- Syncs to Items and Current
-- Verify / add items
-- [bag][slot] = { {itemId | nil, [buttonsIndex]} ... }
local Sorted = AceOO.Class(Recycle)

function Sorted.prototype:init()
    Sorted.super.prototype.init(self) -- very important. Will fail without this.
	for buttonsIndex = 1, AUTOBAR_MAXBUTTONS, 1 do
		self.dataList[buttonsIndex] = {}
	end
end



-- Sorted version of Current items for each button
-- Syncs to Items and Current
-- Verify / add items
-- [bag][slot] = { {itemId | nil, [buttonsIndex]} ... }
local Current = AceOO.Class(Recycle)

function Current.prototype:init()
    Current.super.prototype.init(self) -- very important. Will fail without this.
	for buttonsIndex = 1, AUTOBAR_MAXBUTTONS, 1 do
		self.dataList[buttonsIndex] = {}
	end
end




AutoBarSearch.space = SearchSpace:new()		-- All items to search for
AutoBarSearch.items = Items:new()			-- Items to search for for each button
AutoBarSearch.sorted = Sorted:new()			-- Sorted version of Current items
AutoBarSearch.current = Current:new()		-- Current items found for each button
AutoBarSearch.found = Found:new()			-- All items found in Bags
AutoBarSearch.bags = Bags:new()				-- Map of bag contents
--AutoBarSearch.current = {}
