--[[
	DynamicData

	By sarf

	This mod allows you to access dynamic data in WoW without being forced to rely on strange Blizzard functions

	Thanks goes to the Sea people, the Cosmos team and finally the nice (but strange) people at 
	 #cosmostesters and Blizzard.
	
	CosmosUI URL:
	http://www.cosmosui.org/forums/viewtopic.php?t=NOT_YET_ANNOUNCED
	
   ]]


--[[
	Information returned by the DynamicData.item.get*Info() methods

An array is returned with the following values

	name					-- the name of the item
	strings					-- an array with strings that represent the tooltip of the item
	count					-- the number of items like this the user currently possesses
	texture
	cooldown = 				-- the cooldown of the item
		{ 
			start 			-- time when the cooldown started
			duration 		-- duration of the cooldown
			enable 			-- 1 or 0
		};
	
	-- each item may have these values:
	
	durability				-- current durability
	maxDurability			-- maximum durability
	itemType				-- type of item
	locked					-- ?

These values are 0 unless the item info derives from a ContainerInfo call (bag >= 0):

	quality					-- degree of commonness
	readable				-- ?

These values are 0 or "" unless the item info derives from a InventoryInfo call (bag == -1):

	broken					-- true if broken, false if not
	slotTexture 			-- background texture of the slot that the item resides in.


]]--



-- Item information - what the player is wearing and so on.
DynamicData.item = {

-- public functions	

	-- 
	-- addOnInventoryUpdateHandler (func)
	--
	--  Adds a function name that will be called on inventory updates.
	--  Function will have one paremeter - bag, which may be nil.
	--
	addOnInventoryUpdateHandler = function (func)
		for k, v in DynamicData.item.OnInventoryUpdateHandlers do
			if ( v == func ) then
				return false;
			end
		end
		table.insert(DynamicData.item.OnInventoryUpdateHandlers, func);
		return true;
	end;

	-- 
	-- removeOnInventoryUpdateHandler (func)
	--
	--  Removes the specified function, so that it will not be called on inventory updates.
	--
	removeOnInventoryUpdateHandler = function (func)
		local index = nil;
		for k, v in DynamicData.item.OnInventoryUpdateHandlers do
			if ( v == func ) then
				index = k;
				break;
			end
		end
		if ( index ) then
			table.remove(DynamicData.item.OnInventoryUpdateHandlers, index);
			return true;
		else
			return false;
		end
	end;

	-- 
	-- getEquippedSlotInfo (slot)
	--
	--  Retrieves information about the slot.
	--
	getEquippedSlotInfo = function (slot) 
		if ( ( DynamicData.item.player ) and ( DynamicData.item.player.itemsByBag )  and ( DynamicData.item.player.itemsByBag[-1] ) and ( DynamicData.item.player.itemsByBag[-1][slot] ) ) then
			return DynamicData.item.player.itemsByBag[-1][slot];
		else
			return DynamicData.item.defaultItem;
		end
	end;
	
	-- 
	-- getItemInfoByName (itemName)
	--
	--  Retrieves information about an item by name. Includes a new cute variable called totalCount.
	--
	getItemInfoByName = function (itemName) 
		if ( ( DynamicData.item.player ) and ( DynamicData.item.player.itemsByBag ) ) then
			local element = nil;
			for bagNumber, bag in DynamicData.item.player.itemsByBag do
				for slotNumber, slot in bag do
					if ( slot.name == itemName ) then
						if ( not element ) then
							element = DynamicData.item.createBaseItem(slot);
						end
						element.count = element.count + slot.count;
					end
				end
			end
			return element;
		else
			return DynamicData.item.defaultItem;
		end
	end;
	
	-- 
	-- getEquippedSlotCooldown (slot)
	--
	--  Retrieves cooldown information about the slot.
	--
	getEquippedSlotCooldown = function (slot) 
		if ( ( DynamicData.item.player ) and ( DynamicData.item.player.itemsByBag )  and ( DynamicData.item.player.itemsByBag[-1] ) and ( DynamicData.item.player.itemsByBag[-1][slot] ) ) then
			local cooldown = DynamicData.item.player.itemsByBag[-1][slot].cooldown;
			return cooldown[1], cooldown[2], cooldown[3];
		else
			return 0, 0, 0;
		end
	end;
	
	-- 
	-- getInventoryInfo (bag, slot)
	--
	--  Retrieves information about the bag and slot.
	--
	getInventoryInfo = function (bag, slot) 
		if ( ( DynamicData.item.player ) and ( DynamicData.item.player.itemsByBag ) and ( DynamicData.item.player.itemsByBag[bag] ) and ( DynamicData.item.player.itemsByBag[bag][slot] ) ) then
			return DynamicData.item.player.itemsByBag[bag][slot];
		else
			return DynamicData.item.defaultItem;
		end
	end;

	-- 
	-- getInventoryCooldownInfo (bag, slot)
	--
	--  Retrieves cooldown information about the bag and slot.
	--
	getInventoryCooldown = function (bag, slot) 
		if ( ( DynamicData.item.player ) and ( DynamicData.item.player.itemsByBag ) and ( DynamicData.item.player.itemsByBag[bag] ) and ( DynamicData.item.player.itemsByBag[bag][slot] ) ) then
			local cooldown = DynamicData.item.player.itemsByBag[bag][slot].cooldown;
			return cooldown[1], cooldown[2], cooldown[3];
		else
			return 0, 0, 0;
		end
	end;

	-- 
	-- getEquippedSlotInfoBySlotName (slotName)
	--
	--  Retrieves information about the slot with the specified name.
	--
	getEquippedSlotInfoBySlotName = function (slotName) 
		for key, value in DynamicData.item.inventorySlotNames do
			if ( value.name == slotName ) then
				return DynamicData.item.getEquippedSlotInfo(value.id);
			end
		end
		return DynamicData.item.defaultItem;
	end;
	
	-- 
	-- updateItems ()
	--
	--  Updates the inventory of the player. 
	--
	updateItems = function (bag) 
		params = {
			func = DynamicData.item.doUpdateItems,
			params = { bag },
			allowInitialUpdate = 1,
			schedulingName = "DynamicData_item_UpdateItems",
		};
		DynamicData.util.postpone(params);
	end;
	
	-- 
	-- updateItemCooldowns ()
	--
	--  Updates the cooldowns of the players items.
	--
	updateItemCooldowns = function ()
		DynamicData.item.updateItems();
	end;
	
	-- 
	-- updateItemLocks ()
	--
	--  Updates the locks of the players items.
	--
	updateItemLocks = function ()
		DynamicData.item.updateItems();
	end;
	
	-- 
	-- updateItemLocks ()
	--
	--  Updates the alerts of the players items.
	--  You can never have too many lerts.
	--
	updateItemAlerts = function ()
		DynamicData.item.updateItems();
	end;

-- protected functions


	-- 
	-- doUpdateSeperateBagItems (bag)
	--
	--  Updates a bag seperately from the rest of the inventory of the player. 
	--  Saves some time.
	--
	doUpdateSeperateBagItems = function (bag) 
		table.remove(DynamicData.item.player.itemsByBag, bag);
		DynamicData.item.doUpdateBagItems(bag);
	end;

	-- 
	-- doUpdateBagItems (bag)
	--
	--  Updates a bag of the inventory of the player. 
	--
	doUpdateBagItems = function (bag) 
		local strings = nil;
		local itemName = nil;
		local element = nil;
		local slot = nil;
		if ( bag > -1 ) then
			for slot = 1,GetContainerNumSlots(bag) do
				strings = DynamicData.util.getItemTooltipInfo(bag, slot);
				itemName = nil;
				if ( strings[1] ) then
					itemName = strings[1].left;
				end
				if ( itemName ) then
					local itemCurrentDurability, itemMaxDurability = DynamicData.util.getCurrentAndMaxDurability(strings);
					local itemStringType = DynamicData.util.getItemStringType(strings);
					local textureName, itemCount, itemLocked, itemQuality, itemReadable = GetContainerItemInfo(bag, slot);
					local ic_start, ic_duration, ic_enable = GetContainerItemCooldown(bag, slot);
					local itemCooldown = { };
					itemCooldown[1] = ic_start;
					itemCooldown[2] = ic_duration;
					itemCooldown[3] = ic_enable;
					itemCooldown.start = ic_start;
					itemCooldown.duration = ic_duration;
					itemCooldown.enable = ic_enable;
					element = DynamicData.item.createBaseItem();
					element.name = itemName;
					element.count = itemCount;
					element.strings = strings;
					element.durability = itemCurrentDurability;
					element.maxDurability = itemMaxDurability;
					element.texture = textureName;
					element.locked = itemLocked;
					element.quality = itemQuality;
					element.readable = itemReadable;
					element.cooldown = itemCooldown;
					element.itemType = itemStringType;
					if ( not DynamicData.item.player ) then
						DynamicData.item.player = {};
					end
					if ( not DynamicData.item.player.itemsByBag ) then
						DynamicData.item.player.itemsByBag = {};
					end
					if ( not DynamicData.item.player.itemsByBag[bag] ) then
						DynamicData.item.player.itemsByBag[bag] = {};
					end
					if ( not DynamicData.item.player.itemsByBag[bag][slot] ) then
						DynamicData.item.player.itemsByBag[bag][slot] = {};
					end
					DynamicData.item.player.itemsByBag[bag][slot] = element;
				end
			end
		else
			for k, v in DynamicData.item.inventorySlotNames do
				slot = v.id;
				strings = DynamicData.util.getItemTooltipInfo(-1, v.id);
				itemName = nil;
				if ( strings[1] ) then
					itemName = strings[1].left;
				end
				if ( itemName ) then
					local itemCurrentDurability, itemMaxDurability = DynamicData.util.getCurrentAndMaxDurability(strings);
					local itemCount = GetInventoryItemCount("player", v.id);
					local textureName = GetInventoryItemTexture("player", v.id);
					local ic_start, ic_duration, ic_enable = GetInventoryItemCooldown("player", v.id);
					local itemCooldown = { };
					itemCooldown[1] = ic_start;
					itemCooldown[2] = ic_duration;
					itemCooldown[3] = ic_enable;
					itemCooldown.start = ic_start;
					itemCooldown.duration = ic_duration;
					itemCooldown.enable = ic_enable;
					element = DynamicData.item.createBaseItem();
					element.itemType = DynamicData.util.getItemStringType(strings);
					element.name = itemName;
					element.count = itemCount;
					element.strings = strings;
					element.durability = itemCurrentDurability;
					element.maxDurability = itemMaxDurability;
					element.slotTexture = slotTextureName;
					element.texture = textureName;
					element.cooldown = itemCooldown;
					element.locked = IsInventoryItemLocked(v.id);
					element.broken = GetInventoryItemBroken("player", v.id);
					--element.quality = GetInventoryItemQuality("player", v.id);
					if ( not DynamicData.item.player ) then
						DynamicData.item.player = {};
					end
					if ( not DynamicData.item.player.itemsByBag ) then
						DynamicData.item.player.itemsByBag = {};
					end
					if ( not DynamicData.item.player.itemsByBag[bag] ) then
						DynamicData.item.player.itemsByBag[bag] = {};
					end
					if ( not DynamicData.item.player.itemsByBag[bag][v.id] ) then
						DynamicData.item.player.itemsByBag[bag][v.id] = {};
					end
					DynamicData.item.player.itemsByBag[bag][slot] = element;
				end
			end
		end
	end;

	-- 
	-- doUpdateItems ()
	--
	--  Updates the inventory of the player. 
	--
	doUpdateItems = function (bag) 
		local safe = true; --DynamicData.util.safeToUseTooltips({"MerchantFrame", "TradeSkillFrame", "AuctionFrame"});
		if ( not safe ) then
			if ( bag ) then
				Cosmos_ScheduleByName("DynamicData_item_doUpdateItems", 0.1, DynamicData.item.doUpdateItems, bag);
			else
				Cosmos_ScheduleByName("DynamicData_item_doUpdateItemsUnspecified", 0.1, DynamicData.item.doUpdateItems);
			end
			return;
		end
		if ( bag ) and ( DynamicData.item.player ) then
			DynamicData.item.doUpdateSeperateBagItems(bag);
		else
			DynamicData.item.player = {};
			for bag = 0,4 do
				DynamicData.item.doUpdateBagItems(bag);
			end
			DynamicData.item.doUpdateBagItems(-1);
			bag = nil;
		end
		local func = nil;
		for k, v in DynamicData.item.OnInventoryUpdateHandlers do
			func = v;
			func(bag);
		end
	end;
	
-- private functions	

	--
	-- OnLoad ()
	--
	--  Sets up the DynamicData.item for operation.
	--  In this case, it retrieves the IDs for the inventory slots.
	--
	OnLoad = function ()
		for key, value in DynamicData.item.inventorySlotNames do
			DynamicData.item.inventorySlotNames[key].id = GetInventorySlotInfo(value.name);
		end
		DynamicData.item.doUpdateItems();
	end;

-- variables

	-- Taken from ItemBuff - thanks, Telo!
	-- Used for mapping inventory (equipment) ids and slot names
	inventorySlotNames = {
		{ name = "HeadSlot" },
		{ name = "NeckSlot" },
		{ name = "ShoulderSlot" },
		{ name = "BackSlot" },
		{ name = "ChestSlot" },
		{ name = "ShirtSlot" },
		{ name = "TabardSlot" },
		{ name = "WristSlot" },
		{ name = "HandsSlot" },
		{ name = "WaistSlot" },
		{ name = "LegsSlot" },
		{ name = "FeetSlot" },
		{ name = "Finger0Slot" },
		{ name = "Finger1Slot" },
		{ name = "Trinket0Slot" },
		{ name = "Trinket1Slot" },
		{ name = "MainHandSlot" },
		{ name = "SecondaryHandSlot" },
		{ name = "RangedSlot" },
	};

	-- Contains item data about the player's items.
	player = {};

	-- Contains the function pointers to functions that want to be called whenever the inventory updates.
	-- Will be called AFTER the DynamicData has parsed the inventory.
	OnInventoryUpdateHandlers = {};	

	defaultItem = {
		name = "",
		strings = {
			[1] = {},
			[2] = {},
			[3] = {},
			[4] = {},
			[5] = {},
			[6] = {},
			[7] = {},
			[8] = {},
			[9] = {},
			[10] = {},
			[11] = {},
			[12] = {},
			[13] = {},
			[14] = {},
			[15] = {}
		},
		count = 0,
		slotCount = 0,
		texture = "",
		cooldown = {
			[1] = 0,
			[2] = 0,
			[3] = 0,
			start = 0,
			duration = 0,
			enable = 0
		},
		itemType = "",
		locked = 0,
		durability = nil,
		maxDurability = nil,
		quality = 0,
		readable = 0,
		broken = 0,
		slotTexture = ""
	};

	--
	-- createBaseItem ()
	-- 
	--  creates a basic item with all attributes set to defaults.
	--
	createBaseItem = function (baseItem)
		local newItem = {};
		if ( not baseItem ) then
			baseItem = DynamicData.item.defaultItem;
		end
		for k, v in baseItem do
			newItem[k] = v;
		end
		return newItem;
	end
};

