vanilla-wow-addons – Rev 1

Subversion Repositories:
Rev:
--[[

        LootLink 3.5: An in-game item database
                copyright 2004 by Telo
        
        - Watches all chat links you see to cache link color and link data
        - Automatically extracts data from items already in or added to your inventory
        - Automatically caches link data from items already in or added to your bank
        - Automatically inspects your target and extracts data for each of their equipped items
        - Automatically gets link data from your auction queries
        - Can perform a fully automatic scan of the entire auction house inventory
        - Stores sell prices for items that you've had in inventory when you've talked to a merchant
          and displays them in the tooltips for stacks of items that you are looting, stacks of items
          in your inventory and entries in the LootLink browse window
        - Converts green loot messages into correctly colored item messages if the item is cached
        - Provides a browsable, searchable window that allows you to find any item in the cache
        - Allows you to shift-click items in the browse window to insert links in the chat edit box
        
]]

--------------------------------------------------------------------------------------------------
-- Local LootLink variables
--------------------------------------------------------------------------------------------------

-- Function hooks
local lOriginal_CanSendAuctionQuery;
local lOriginal_AuctionFrameBrowse_OnEvent;
local lOriginal_ContainerFrameItemButton_OnEnter;
local lOriginal_ContainerFrame_Update;
local lOriginal_GameTooltip_SetLootItem;
local lOriginal_GameTooltip_SetOwner;
local lOriginal_GameTooltip_OnHide;
local lOriginal_GameTooltip_ClearMoney;
local lOriginal_GameTooltip_ClearMoney_Temp;
local lOriginal_ShoppingTooltip1_SetMerchantCompareItem;
local lOriginal_ShoppingTooltip2_SetMerchantCompareItem;
local lOriginal_ShoppingTooltip1_SetAuctionCompareItem;
local lOriginal_ShoppingTooltip2_SetAuctionCompareItem;
local lOriginal_SetItemRef;
local lOriginal_OnTooltipAddMoney;

--local ll = {};

-- If non-nil, kick off a full auction scan next time auctioneer is used
local lScanAuction;

-- Used for scanning inventory items for their sell prices at a merchant
local lBagID;
local lSlotID;

-- If non-nil, don't add extra information to the tooltip on GameTooltip_ClearMoney
local lSuppressInfoAdd;

-- If non-nil, check for appearance of GameTooltip for adding information
local lCheckTooltip;

-- Timer for frequency of tooltip checks
local lCheckTimer = 0;

-- The current owner of the GameTooltip
local lGameToolTipOwner;

-- Cache of auction item information
local lAuctionItemInfo;

-- Used to remember that confirmation is needed of irreversible commands
local lResetNeedsConfirm;
local lMakeHomeNeedsConfirm;
local lLightModeNeedsConfirm;

-- If non-nil, the data version upgrade reminder is not displayed on every /lootlink or /ll command
local lDisableVersionReminder;

-- The current server name and index
local lServer;
local lServerIndex;

-- The number of items in the database, total and for this server
local lItemLinksSizeTotal;
local lItemLinksSizeServer;

local STATE_NAME = 0;
local STATE_BOUND = 1;
local STATE_UNIQUE = 2;
local STATE_LOCATION = 3;
local STATE_TYPE = 4;
local STATE_DAMAGE = 5;
local STATE_DPS = 6;
local STATE_ARMOR = 7;
local STATE_BLOCK = 8;
local STATE_CONTAINER = 9;
local STATE_REQUIRES = 10;
local STATE_FINISH = 11;
local STATE_SPACE = 12
local STATE_SET = 13;

local tooltipSpace = " "..string.char(10);

local BINDS_DOES_NOT_BIND = 0;
local BINDS_EQUIP = 1;
local BINDS_PICKUP = 2;
local BINDS_USED = 3;

local TYPE_ARMOR = 0;
local TYPE_WEAPON = 1;
local TYPE_SHIELD = 2;
local TYPE_RECIPE = 3;
local TYPE_CONTAINER = 4;
local TYPE_MISC = 5;

local SUBTYPE_ARMOR_CLOTH = 0;
local SUBTYPE_ARMOR_LEATHER = 1;
local SUBTYPE_ARMOR_MAIL = 2;
local SUBTYPE_ARMOR_PLATE = 3;
local SUBTYPE_ARMOR_RELIC = 4;

local lColorSortTable = { };
lColorSortTable["ffff8000"] = 1;        -- legendary, orange
lColorSortTable["ffa335ee"] = 2;        -- epic, purple
lColorSortTable["ff0070dd"] = 3;        -- rare, blue
lColorSortTable["ff1eff00"] = 4;        -- uncommon, green
lColorSortTable["ffffffff"] = 5;        -- common, white
lColorSortTable["ff9d9d9d"] = 6;        -- poor, gray
lColorSortTable["ff40ffc0"] = 100;      -- unknown, teal

local ArmorSubtypes = { };
ArmorSubtypes[CLOTH] = SUBTYPE_ARMOR_CLOTH;
ArmorSubtypes[LEATHER] = SUBTYPE_ARMOR_LEATHER;
ArmorSubtypes[MAIL] = SUBTYPE_ARMOR_MAIL;
ArmorSubtypes[PLATE] = SUBTYPE_ARMOR_PLATE;
ArmorSubtypes[INVTYPE_RELIC] = SUBTYPE_ARMOR_RELIC;

local SUBTYPE_WEAPON_AXE = 0;
local SUBTYPE_WEAPON_BOW = 1;
local SUBTYPE_WEAPON_DAGGER = 2;
local SUBTYPE_WEAPON_MACE = 3;
local SUBTYPE_WEAPON_FISHING_POLE = 4;
local SUBTYPE_WEAPON_STAFF = 5;
local SUBTYPE_WEAPON_SWORD = 6;
local SUBTYPE_WEAPON_GUN = 7;
local SUBTYPE_WEAPON_WAND = 8;
local SUBTYPE_WEAPON_THROWN = 9;
local SUBTYPE_WEAPON_POLEARM = 10;
local SUBTYPE_WEAPON_FIST_WEAPON = 11;
local SUBTYPE_WEAPON_CROSSBOW = 12;

local WeaponSubtypes = { };
WeaponSubtypes[AXE] = SUBTYPE_WEAPON_AXE;
WeaponSubtypes[BOW] = SUBTYPE_WEAPON_BOW;
WeaponSubtypes[DAGGER] = SUBTYPE_WEAPON_DAGGER;
WeaponSubtypes[MACE] = SUBTYPE_WEAPON_MACE;
WeaponSubtypes[FISHING_POLE] = SUBTYPE_WEAPON_FISHING_POLE;
WeaponSubtypes[STAFF] = SUBTYPE_WEAPON_STAFF;
WeaponSubtypes[SWORD] = SUBTYPE_WEAPON_SWORD;
WeaponSubtypes[GUN] = SUBTYPE_WEAPON_GUN;
WeaponSubtypes[WAND] = SUBTYPE_WEAPON_WAND;
WeaponSubtypes[THROWN] = SUBTYPE_WEAPON_THROWN;
WeaponSubtypes[POLEARM] = SUBTYPE_WEAPON_POLEARM;
WeaponSubtypes[FIST_WEAPON] = SUBTYPE_WEAPON_FIST_WEAPON;
WeaponSubtypes[CROSSBOW] = SUBTYPE_WEAPON_CROSSBOW;

local SUBTYPE_SHIELD_BUCKLER = 0;
local SUBTYPE_SHIELD_SHIELD = 1;

local ShieldSubtypes = { };
ShieldSubtypes["Buckler"] = SUBTYPE_SHIELD_BUCKLER;
ShieldSubtypes[SHIELD] = SUBTYPE_SHIELD_SHIELD;

local SUBTYPE_RECIPE_ALCHEMY = 0;
local SUBTYPE_RECIPE_BLACKSMITHING = 1;
local SUBTYPE_RECIPE_COOKING = 2;
local SUBTYPE_RECIPE_ENCHANTING = 3;
local SUBTYPE_RECIPE_ENGINEERING = 4;
local SUBTYPE_RECIPE_LEATHERWORKING = 5;
local SUBTYPE_RECIPE_TAILORING = 6;
local SUBTYPE_RECIPE_FIRST_AID = 7;
local SUBTYPE_RECIPE_FISHING = 8;

local RecipeSubtypes = { };
RecipeSubtypes[ALCHEMY] = SUBTYPE_RECIPE_ALCHEMY;
RecipeSubtypes[BLACKSMITHING] = SUBTYPE_RECIPE_BLACKSMITHING;
RecipeSubtypes[COOKING] = SUBTYPE_RECIPE_COOKING;
RecipeSubtypes[ENCHANTING] = SUBTYPE_RECIPE_ENCHANTING;
RecipeSubtypes[ENGINEERING] = SUBTYPE_RECIPE_ENGINEERING;
RecipeSubtypes[LEATHERWORKING] = SUBTYPE_RECIPE_LEATHERWORKING;
RecipeSubtypes[TAILORING] = SUBTYPE_RECIPE_TAILORING;
RecipeSubtypes[FIRST_AID] = SUBTYPE_RECIPE_FIRST_AID;
RecipeSubtypes[FISHING] = SUBTYPE_RECIPE_FISHING;

local SUBTYPE_CONTAINER_BAG = 0;
local SUBTYPE_CONTAINER_AMMO_POUCH = 1;
local SUBTYPE_CONTAINER_QUIVER = 2;
local SUBTYPE_CONTAINER_SOUL_BAG = 3;
local SUBTYPE_CONTAINER_HERB_BAG = 4;
local SUBTYPE_CONTAINER_ENCHANTING_BAG = 5;
local SUBTYPE_CONTAINER_ENGINEERING_BAG = 6;

local ContainerSubtypes = { };
ContainerSubtypes[BAG] = SUBTYPE_CONTAINER_BAG;
ContainerSubtypes[AMMO_POUCH] = SUBTYPE_CONTAINER_AMMO_POUCH;
ContainerSubtypes[QUIVER] = SUBTYPE_CONTAINER_QUIVER;
ContainerSubtypes[SOUL_BAG] = SUBTYPE_CONTAINER_SOUL_BAG;
ContainerSubtypes[HERB_BAG] = SUBTYPE_CONTAINER_HERB_BAG;
ContainerSubtypes[ENCHANTING_BAG] = SUBTYPE_CONTAINER_ENCHANTING_BAG;
ContainerSubtypes[ENGINEERING_BAG] = SUBTYPE_CONTAINER_ENGINEERING_BAG;

local lTypeAndSubtypeToSkill = { };
lTypeAndSubtypeToSkill[TYPE_ARMOR] = { };
lTypeAndSubtypeToSkill[TYPE_ARMOR][SUBTYPE_ARMOR_CLOTH] = CLOTH;
lTypeAndSubtypeToSkill[TYPE_ARMOR][SUBTYPE_ARMOR_LEATHER] = LEATHER;
lTypeAndSubtypeToSkill[TYPE_ARMOR][SUBTYPE_ARMOR_MAIL] = MAIL;
lTypeAndSubtypeToSkill[TYPE_ARMOR][SUBTYPE_ARMOR_PLATE] = PLATE_MAIL;
lTypeAndSubtypeToSkill[TYPE_ARMOR][SUBTYPE_ARMOR_RELIC] = INVTYPE_RELIC;
lTypeAndSubtypeToSkill[TYPE_WEAPON] = { };
lTypeAndSubtypeToSkill[TYPE_WEAPON][SUBTYPE_WEAPON_AXE] = { }
lTypeAndSubtypeToSkill[TYPE_WEAPON][SUBTYPE_WEAPON_AXE][0] = AXES;
lTypeAndSubtypeToSkill[TYPE_WEAPON][SUBTYPE_WEAPON_AXE][1] = TWO_HANDED_AXES;
lTypeAndSubtypeToSkill[TYPE_WEAPON][SUBTYPE_WEAPON_BOW] = BOWS;
lTypeAndSubtypeToSkill[TYPE_WEAPON][SUBTYPE_WEAPON_DAGGER] = DAGGERS;
lTypeAndSubtypeToSkill[TYPE_WEAPON][SUBTYPE_WEAPON_MACE] = { }
lTypeAndSubtypeToSkill[TYPE_WEAPON][SUBTYPE_WEAPON_MACE][0] = MACES;
lTypeAndSubtypeToSkill[TYPE_WEAPON][SUBTYPE_WEAPON_MACE][1] = TWO_HANDED_MACES;
lTypeAndSubtypeToSkill[TYPE_WEAPON][SUBTYPE_WEAPON_FISHING_POLE] = FISHING_POLE;
lTypeAndSubtypeToSkill[TYPE_WEAPON][SUBTYPE_WEAPON_STAFF] = STAVES;
lTypeAndSubtypeToSkill[TYPE_WEAPON][SUBTYPE_WEAPON_SWORD] = { };
lTypeAndSubtypeToSkill[TYPE_WEAPON][SUBTYPE_WEAPON_SWORD][0] = SWORDS;
lTypeAndSubtypeToSkill[TYPE_WEAPON][SUBTYPE_WEAPON_SWORD][1] = TWO_HANDED_SWORDS;
lTypeAndSubtypeToSkill[TYPE_WEAPON][SUBTYPE_WEAPON_GUN] = GUNS;
lTypeAndSubtypeToSkill[TYPE_WEAPON][SUBTYPE_WEAPON_WAND] = WANDS;
lTypeAndSubtypeToSkill[TYPE_WEAPON][SUBTYPE_WEAPON_THROWN] = THROWN;
lTypeAndSubtypeToSkill[TYPE_WEAPON][SUBTYPE_WEAPON_POLEARM] = POLEARMS;         --@todo Telo: unconfirmed
lTypeAndSubtypeToSkill[TYPE_WEAPON][SUBTYPE_WEAPON_FIST_WEAPON] = UNARMED;
lTypeAndSubtypeToSkill[TYPE_WEAPON][SUBTYPE_WEAPON_CROSSBOW] = CROSSBOWS;
lTypeAndSubtypeToSkill[TYPE_SHIELD] = { };
lTypeAndSubtypeToSkill[TYPE_SHIELD][SUBTYPE_SHIELD_BUCKLER] = SHIELD;                   --@todo Telo: deprecated subtype, should remove
lTypeAndSubtypeToSkill[TYPE_SHIELD][SUBTYPE_SHIELD_SHIELD] = SHIELD;
lTypeAndSubtypeToSkill[TYPE_RECIPE] = { };
lTypeAndSubtypeToSkill[TYPE_RECIPE][SUBTYPE_RECIPE_ALCHEMY] = ALCHEMY;
lTypeAndSubtypeToSkill[TYPE_RECIPE][SUBTYPE_RECIPE_BLACKSMITHING] = BLACKSMITHING;
lTypeAndSubtypeToSkill[TYPE_RECIPE][SUBTYPE_RECIPE_COOKING] = COOKING;
lTypeAndSubtypeToSkill[TYPE_RECIPE][SUBTYPE_RECIPE_ENCHANTING] = ENCHANTING;
lTypeAndSubtypeToSkill[TYPE_RECIPE][SUBTYPE_RECIPE_ENGINEERING] = ENGINEERING;
lTypeAndSubtypeToSkill[TYPE_RECIPE][SUBTYPE_RECIPE_LEATHERWORKING] = LEATHERWORKING;
lTypeAndSubtypeToSkill[TYPE_RECIPE][SUBTYPE_RECIPE_TAILORING] = TAILORING;
lTypeAndSubtypeToSkill[TYPE_RECIPE][SUBTYPE_RECIPE_FIRST_AID] = FIRST_AID;
lTypeAndSubtypeToSkill[TYPE_RECIPE][SUBTYPE_RECIPE_FISHING] = FISHING;
lTypeAndSubtypeToSkill[TYPE_CONTAINER] = { };
lTypeAndSubtypeToSkill[TYPE_CONTAINER][SUBTYPE_CONTAINER_BAG] = BAG;
lTypeAndSubtypeToSkill[TYPE_CONTAINER][SUBTYPE_CONTAINER_AMMO_POUCH] = AMMO_POUCH;
lTypeAndSubtypeToSkill[TYPE_CONTAINER][SUBTYPE_CONTAINER_QUIVER] = QUIVER;
lTypeAndSubtypeToSkill[TYPE_CONTAINER][SUBTYPE_CONTAINER_SOUL_BAG] = SOUL_BAG;
lTypeAndSubtypeToSkill[TYPE_CONTAINER][SUBTYPE_CONTAINER_ENCHANTING_BAG] = ENCHANTING_BAG;

local LocationTypes = { };
LocationTypes[INVTYPE_HOLDABLE]         = { i = 0, type = TYPE_MISC };
LocationTypes[INVTYPE_CLOAK]            = { i = 1, type = TYPE_ARMOR, subtypes = ArmorSubtypes };
LocationTypes[INVTYPE_WEAPON]           = { i = 2, type = TYPE_WEAPON, subtypes = WeaponSubtypes };
LocationTypes[INVTYPE_2HWEAPON]         = { i = 3, type = TYPE_WEAPON, subtypes = WeaponSubtypes };
LocationTypes[INVTYPE_WEAPONOFFHAND]    = { i = 4, type = TYPE_SHIELD, subtypes = ShieldSubtypes };
LocationTypes[INVTYPE_WRIST]            = { i = 5, type = TYPE_ARMOR, subtypes = ArmorSubtypes };
LocationTypes[INVTYPE_CHEST]            = { i = 6, type = TYPE_ARMOR, subtypes = ArmorSubtypes };
LocationTypes[INVTYPE_LEGS]             = { i = 7, type = TYPE_ARMOR, subtypes = ArmorSubtypes };
LocationTypes[INVTYPE_FEET]             = { i = 8, type = TYPE_ARMOR, subtypes = ArmorSubtypes };
LocationTypes[INVTYPE_BODY]             = { i = 9, type = TYPE_MISC };
LocationTypes[INVTYPE_RANGED]           = { i = 10, type = TYPE_WEAPON, subtypes = WeaponSubtypes };
LocationTypes[INVTYPE_WEAPONMAINHAND]   = { i = 11, type = TYPE_WEAPON, subtypes = WeaponSubtypes };
LocationTypes[INVTYPE_WAIST]            = { i = 12, type = TYPE_ARMOR, subtypes = ArmorSubtypes };
LocationTypes[INVTYPE_HEAD]             = { i = 13, type = TYPE_ARMOR, subtypes = ArmorSubtypes };
LocationTypes[GUN]                      = { i = 14, type = TYPE_WEAPON, subtype = SUBTYPE_WEAPON_GUN };
LocationTypes[INVTYPE_FINGER]           = { i = 15, type = TYPE_MISC };
LocationTypes[INVTYPE_HAND]             = { i = 16, type = TYPE_ARMOR, subtypes = ArmorSubtypes };
LocationTypes[INVTYPE_SHOULDER]         = { i = 17, type = TYPE_ARMOR, subtypes = ArmorSubtypes };
LocationTypes[WAND]                     = { i = 18, type = TYPE_WEAPON, subtype = SUBTYPE_WEAPON_WAND };
LocationTypes[INVTYPE_TRINKET]          = { i = 19, type = TYPE_MISC };
LocationTypes[INVTYPE_TABARD]           = { i = 20, type = TYPE_MISC };
LocationTypes[INVTYPE_NECK]             = { i = 21, type = TYPE_MISC };
LocationTypes[THROWN]                   = { i = 22, type = TYPE_WEAPON, subtype = SUBTYPE_WEAPON_THROWN };
LocationTypes[CROSSBOW]                 = { i = 23, type = TYPE_WEAPON, subtype = SUBTYPE_WEAPON_CROSSBOW };
LocationTypes[INVTYPE_RELIC]            = { i = 24, type = TYPE_ARMOR, subtype = SUBTYPE_ARMOR_RELIC };

local INVENTORY_SLOT_LIST = {
        { 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" },
};

local ChatMessageTypes = {
        "CHAT_MSG_SYSTEM",
        "CHAT_MSG_SAY",
        "CHAT_MSG_TEXT_EMOTE",
        "CHAT_MSG_YELL",
        "CHAT_MSG_WHISPER",
        "CHAT_MSG_PARTY",
        "CHAT_MSG_GUILD",
        "CHAT_MSG_OFFICER",
        "CHAT_MSG_CHANNEL",
        "CHAT_MSG_RAID",
        "CHAT_MSG_LOOT",
};

local LOOTLINK_DROPDOWN_LIST = {
        { name = SORT_NAME, sortType = "name" },
        { name = SORT_RARITY, sortType = "rarity" },
        { name = SORT_LOCATION, sortType = "location" },
        { name = SORT_TYPE, sortType = "type" },
        { name = SORT_SUBTYPE, sortType = "subtype" },
        { name = SORT_LEVEL, sortType = "level" },
        { name = SORT_BINDS, sortType = "binds" },
        { name = SORT_UNIQUE, sortType = "unique" },
        { name = SORT_ARMOR, sortType = "armor" },
        { name = SORT_BLOCK, sortType = "block" },
        { name = SORT_MINDAMAGE, sortType = "minDamage" },
        { name = SORT_MAXDAMAGE, sortType = "maxDamage" },
        { name = SORT_DPS, sortType = "DPS" },
        { name = SORT_SPEED, sortType = "speed" },
        { name = SORT_SLOTS, sortType = "slots" },
        { name = SORT_SKILL, sortType = "skill" },
        { name = SORT_SET, sortType = "set" },
};

local LLS_RARITY_LIST = {
        { name = ANY, value = nil },
        { name = UNKNOWN, value = "ff40ffc0", r = 64 / 255, g = 255 / 255, b = 192 / 255 },
        { name = POOR, value = "ff9d9d9d", r = 157 / 255, g = 157 / 255, b = 157 / 255 },
        { name = COMMON, value = "ffffffff", r = 1, g = 1, b = 1 },
        { name = UNCOMMON, value = "ff1eff00", r = 30 / 255, g = 1, b = 0 },
        { name = RARE, value = "ff0070dd", r = 0, g = 70 / 255, b = 221 / 255 },
        { name = EPIC, value = "ffa335ee", r = 163 / 255, g = 53 / 255, b = 238 / 255 },
        { name = LEGENDARY, value = "ffff8000", r = 1, g = 128 / 255, b = 0 },
};

local LLS_BINDS_LIST = {
        { name = ANY, value = nil },
        { name = DOES_NOT, value = BINDS_DOES_NOT_BIND },
        { name = ON_EQUIP, value = BINDS_EQUIP },
        { name = ON_PICKUP, value = BINDS_PICKUP },
        { name = ON_USE, value = BINDS_USE },
};

local LLS_TYPE_LIST = {
        { name = ANY, value = nil },
        { name = ARMOR, value = TYPE_ARMOR },
        { name = CONTAINER, value = TYPE_CONTAINER },
        { name = OTHER, value = TYPE_MISC },
        { name = RECIPE, value = TYPE_RECIPE },
        { name = SHIELD, value = TYPE_SHIELD },
        { name = WEAPON, value = TYPE_WEAPON },
};

local LLS_SUBTYPE_ARMOR_LIST = {
        { name = ANY, value = nil },
        { name = CLOTH, value = SUBTYPE_ARMOR_CLOTH },
        { name = LEATHER, value = SUBTYPE_ARMOR_LEATHER },
        { name = MAIL, value = SUBTYPE_ARMOR_MAIL },
        { name = PLATE, value = SUBTYPE_ARMOR_PLATE },
        { name = INVTYPE_RELIC, value = SUBTYPE_ARMOR_RELIC },
};

local LLS_SUBTYPE_WEAPON_LIST = {
        { name = ANY, value = nil },
        { name = AXE, value = SUBTYPE_WEAPON_AXE },
        { name = BOW, value = SUBTYPE_WEAPON_BOW },
        { name = CROSSBOW, value = SUBTYPE_WEAPON_CROSSBOW },
        { name = DAGGER, value = SUBTYPE_WEAPON_DAGGER },
        { name = FISHING_POLE, value = SUBTYPE_WEAPON_FISHING_POLE },
        { name = FIST_WEAPON, value = SUBTYPE_WEAPON_FIST_WEAPON },
        { name = GUN, value = SUBTYPE_WEAPON_GUN },
        { name = MACE, value = SUBTYPE_WEAPON_MACE },
        { name = POLEARM, value = SUBTYPE_WEAPON_POLEARM },
        { name = STAFF, value = SUBTYPE_WEAPON_STAFF },
        { name = SWORD, value = SUBTYPE_WEAPON_SWORD },
        { name = THROWN, value = SUBTYPE_WEAPON_THROWN },
        { name = WAND, value = SUBTYPE_WEAPON_WAND },
};

local LLS_SUBTYPE_SHIELD_LIST = {
        { name = ANY, value = nil },
        { name = BUCKLER, value = SUBTYPE_SHIELD_BUCKLER },
        { name = SHIELD, value = SUBTYPE_SHIELD_SHIELD },
};

local LLS_SUBTYPE_RECIPE_LIST = {
        { name = ANY, value = nil },
        { name = ALCHEMY, value = SUBTYPE_RECIPE_ALCHEMY },
        { name = BLACKSMITHING, value = SUBTYPE_RECIPE_BLACKSMITHING },
        { name = COOKING, value = SUBTYPE_RECIPE_COOKING },
        { name = ENCHANTING, value = SUBTYPE_RECIPE_ENCHANTING },
        { name = ENGINEERING, value = SUBTYPE_RECIPE_ENGINEERING },
        { name = LEATHERWORKING, value = SUBTYPE_RECIPE_LEATHERWORKING },
        { name = TAILORING, value = SUBTYPE_RECIPE_TAILORING },
};

local LLS_SUBTYPE_CONTAINER_LIST = {
        { name = ANY, value = nil },
        { name = BAG, value = SUBTYPE_CONTAINER_BAG },
        { name = AMMO_POUCH, value = SUBTYPE_CONTAINER_AMMO_POUCH },
        { name = QUIVER, value = SUBTYPE_CONTAINER_QUIVER },
        { name = ENCHANTING_BAG, value = SUBTYPE_CONTAINER_ENCHANTING_BAG },
        { name = ENGINEERING_BAG, value = SUBTYPE_CONTAINER_ENGINEERING_BAG },
        { name = HERB_BAG, value = SUBTYPE_CONTAINER_HERB_BAG },
        { name = SOUL_BAG, value = SUBTYPE_CONTAINER_SOUL_BAG },
};

local LLS_LOCATION_LIST = {
        { name = ANY, },
        { name = NONE, },
        { name = INVTYPE_CLOAK, },
        { name = INVTYPE_CHEST, },
        { name = CROSSBOW, },
        { name = INVTYPE_FEET, },
        { name = INVTYPE_FINGER, },
        { name = INVTYPE_HAND, },
        { name = INVTYPE_HEAD, },
        { name = INVTYPE_HOLDABLE, },
        { name = GUN, },
        { name = INVTYPE_LEGS, },
        { name = INVTYPE_WEAPONMAINHAND, },
        { name = INVTYPE_NECK, },
        { name = INVTYPE_WEAPONOFFHAND, },
        { name = INVTYPE_WEAPON, },
        { name = INVTYPE_RANGED, },
        { name = INVTYPE_BODY, },
        { name = INVTYPE_SHOULDER, },
        { name = INVTYPE_TABARD, },
        { name = THROWN, },
        { name = INVTYPE_TRINKET, },
        { name = INVTYPE_2HWEAPON, },
        { name = INVTYPE_WAIST, },
        { name = WAND, },
        { name = INVTYPE_WRIST, },
};

local LLS_EXTRA_LIST = {
        { name = ALL, },
        { name = ONLY, },
        { name = NONE, },
};

local LLO_RARITY_LIST = {
        { name = NONE },
        { name = "|cff9d9d9d"..POOR },
        { name = "|cffffffff"..COMMON },
        { name = "|cff1eff00"..UNCOMMON },
        { name = "|cff0070dd"..RARE },
        { name = "|cffa335ee"..EPIC },
        { name = "|cffff8000"..LEGENDARY },
};

local lMagicCharacters = { };
lMagicCharacters["^"] = 1;
lMagicCharacters["$"] = 1;
lMagicCharacters["("] = 1;
lMagicCharacters[")"] = 1;
lMagicCharacters["%"] = 1;
lMagicCharacters["."] = 1;
lMagicCharacters["["] = 1;
lMagicCharacters["]"] = 1;
lMagicCharacters["*"] = 1;
lMagicCharacters["+"] = 1;
lMagicCharacters["-"] = 1;
lMagicCharacters["?"] = 1;

local lBankBagIDs = { BANK_CONTAINER, 5, 6, 7, 8, 9, 10, };
        
-- Item types that can't have enchants or "of" stat bonuses
local lNoBonuses = {
        [""] = 2,
        ["INVTYPE_TRINKET"] = 2,
        ["INVTYPE_AMMO"] = 2,
        ["INVTYPE_THROWN"] = 2,
        ["INVTYPE_BAG"] = 2,
        ["INVTYPE_TABARD"] = 2,
        ["INVTYPE_BODY"] = 2,
        ["INVTYPE_HOLDABLE"] = 1,
        ["INVTYPE_FINGER"] = 1,
        ["INVTYPE_WAIST"] = 1,
}

local lRarityFilter = {
        ff40ffc0 = -1,
        ff9d9d9d = 0,
        ffffffff = 1,
        ff1eff00 = 2,
        ff0070dd = 3,
        ffa335ee = 4,
        ffff8000 = 5,
};

--------------------------------------------------------------------------------------------------
-- Global LootLink variables
--------------------------------------------------------------------------------------------------

LOOTLINK_VERSION = 350; -- version 3.50

LOOTLINK_ITEM_HEIGHT = 16;
LOOTLINK_ITEMS_SHOWN = 22;

UIPanelWindows["LootLinkFrame"] =               { area = "left",        pushable = 11,  whileDead = 1 };
UIPanelWindows["LootLinkSearchFrame"] =         { area = "center",      pushable = 0,   whileDead = 1 };
UIPanelWindows["LootLinkOptionsFrame"] =        { area = "center",      pushable = 0,   whileDead = 1 };

--------------------------------------------------------------------------------------------------
-- Internal functions
--------------------------------------------------------------------------------------------------

--
-- Functions with no other functional dependencies
--

local function LootLink_MakeIntFromHexString(str)
        local remain = str;
        local amount = 0;
        while( remain ~= "" ) do
                amount = amount * 16;
                local byteVal = string.byte(strupper(strsub(remain, 1, 1)));
                if( byteVal >= string.byte("0") and byteVal <= string.byte("9") ) then
                        amount = amount + (byteVal - string.byte("0"));
                elseif( byteVal >= string.byte("A") and byteVal <= string.byte("F") ) then
                        amount = amount + 10 + (byteVal - string.byte("A"));
                end
                remain = strsub(remain, 2);
        end
        return amount;
end

local function LootLink_GetRGBFromHexColor(hexColor)
        local red = LootLink_MakeIntFromHexString(strsub(hexColor, 3, 4)) / 255;
        local green = LootLink_MakeIntFromHexString(strsub(hexColor, 5, 6)) / 255;
        local blue = LootLink_MakeIntFromHexString(strsub(hexColor, 7, 8)) / 255;
        return red, green, blue;
end

local function LootLink_MouseIsOver(frame)
        local x, y = GetCursorPosition();
        
        if( not frame ) then
                return nil;
        end
        
        x = x / frame:GetEffectiveScale();
        y = y / frame:GetEffectiveScale();

        local left = frame:GetLeft();
        local right = frame:GetRight();
        local top = frame:GetTop();
        local bottom = frame:GetBottom();
        
        if( not left or not right or not top or not bottom ) then
                return nil;
        end
        
        if( (x > left and x < right) and (y > bottom and y < top) ) then
                return 1;
        else
                return nil;
        end
end

local function LootLink_GetDataVersion()
        if( not LootLinkState or not LootLinkState.DataVersion ) then
                return 0;
        end
        return LootLinkState.DataVersion;
end

local function LootLink_SetDataVersion(version)
        if( not LootLinkState ) then
                LootLinkState = { };
        end
        if( not LootLinkState.DataVersion or LootLinkState.DataVersion < version ) then
                LootLinkState.DataVersion = version;
        end
end

local function LootLink_AddServer(name)
        if( not LootLinkState ) then
                LootLinkState = { };
        end
        
        if( not LootLinkState.Servers ) then
                LootLinkState.Servers = 0;
                LootLinkState.ServerNamesToIndices = { };
        end
        
        if( not LootLinkState.ServerNamesToIndices[name] ) then
                LootLinkState.ServerNamesToIndices[name] = LootLinkState.Servers;
                LootLinkState.Servers = LootLinkState.Servers + 1;
        end
        
        return LootLinkState.ServerNamesToIndices[name];
end

local function LootLink_ConvertServerFormat(item)
        if( item.servers ) then
                local i, v;
                local list;
                for i, v in item.servers do
                        if( not list ) then
                                list = TEXT(i);
                        else
                                list = list.." "..i;
                        end
                end
                item.s = list;
                item.servers = nil;
        end
end

local function LootLink_CheckItemServerRaw(item, serverIndex)
        if( not item.s ) then
                return nil;
        end
        
        local server;
        for server in string.gfind(item.s, "(%d+)") do
                if( tonumber(server) == serverIndex ) then
                        return 1;
                end
        end

        return nil;
end

local function LootLink_AddItemServer(item, serverIndex)
        if( not item.s ) then
                item.s = TEXT(serverIndex);
        elseif( not LootLink_CheckItemServerRaw(item, serverIndex) ) then
                item.s = item.s.." "..serverIndex;
        end
end

local function LootLink_EscapeString(string)
        return string.gsub(string, "\"", "\\\"");
end

local function LootLink_UnescapeString(string)
        return string.gsub(string, "\\\"", "\"");
end

local function LootLink_EscapePattern(string)
        local result = "";
        local remain = string;
        local char;
        
        while( remain ~= "" ) do
                char = strsub(remain, 1, 1);
                if( lMagicCharacters[char] ) then
                        result = result.."%"..char;
                else
                        result = result..char;
                end
                remain = strsub(remain, 2);
        end
        
        return result;
end

local function LootLink_CheckNumeric(string)
        local remain = string;
        local hasNumber;
        local hasPeriod;
        local char;
        
        while( remain ~= "" ) do
                char = strsub(remain, 1, 1);
                if( char >= "0" and char <= "9" ) then
                        hasNumber = 1;
                elseif( char == "." and not hasPeriod ) then
                        hasPeriod = 1;
                else
                        return nil;
                end
                remain = strsub(remain, 2);
        end
        
        return hasNumber;
end

local function LootLink_NameFromLink(link)
        local name;
        if( not link ) then
                return nil;
        end
        for name in string.gfind(link, "|c%x+|Hitem:%d+:%d+:%d+:%d+|h%[(.-)%]|h|r") do
                return name;
        end
        return nil;
end

function LootLink_MoneyToggle(force)
        if( lOriginal_GameTooltip_ClearMoney_Temp ) then
                GameTooltip_ClearMoney = lOriginal_GameTooltip_ClearMoney_Temp;
                lOriginal_GameTooltip_ClearMoney_Temp = nil;
        elseif ( not force ) then
                lOriginal_GameTooltip_ClearMoney_Temp = GameTooltip_ClearMoney;
                GameTooltip_ClearMoney = LootLink_GameTooltip_ClearMoney_Temp;
        else
                DEFAULT_CHAT_FRAME:AddMessage( "LootLink_MoneyToggle just tried to break?" );
        end
end

local function LootLink_MatchType(left, right)
        local lt = LocationTypes[left];
        local _type;
        local subtype;
        
        if( lt ) then
                local subtypes;
                
                -- Check for weapon override of base type
                if( WeaponSubtypes[right] ) then
                        _type = TYPE_WEAPON;
                        subtypes = WeaponSubtypes;
                else
                        _type = lt.type;
                        subtypes = lt.subtypes;
                end
                
                if( subtypes ) then
                        subtype = subtypes[right];
                else
                        subtype = lt.subtype;
                end
                return nil, lt.i, _type, subtype;
        end
        
        return 1, nil, TYPE_MISC, nil;
end

local function LootLink_HideTooltipMoney()
        LootLinkTooltipMoneyFrame:SetPoint("LEFT", "LootLinkTooltipTextLeft1", "LEFT", 0, 0);
        LootLinkTooltipMoneyFrame:Hide();
end

local function LootLink_SetTooltipMoney(frame, count, money, stack)
        if( count and count > 1 ) then
                money = money * count;
                frame:AddLine(format(LOOTLINK_SELL_PRICE_N, count), 1.0, 1.0, 1.0);
        elseif( stack ) then
                frame:AddLine(LOOTLINK_SELL_PRICE_EACH, 1.0, 1.0, 1.0);
        else
                frame:AddLine(LOOTLINK_SELL_PRICE, 1.0, 1.0, 1.0);
        end
        
        local numLines = frame:NumLines();
        local moneyFrame = getglobal(frame:GetName().."MoneyFrame");
        local newLine = frame:GetName().."TextLeft"..numLines;
        
        moneyFrame:SetPoint("LEFT", newLine, "RIGHT", 4, 0);
        moneyFrame:Show();
        MoneyFrame_Update(moneyFrame:GetName(), money);
        frame:SetMinimumWidth(moneyFrame:GetWidth() + getglobal(newLine):GetWidth() - 10);
end

local function LL_SearchData(item, tag)
        if( item.d ) then
                local s, e;
                local value;

                s, e, value = string.find(item.d, tag.."(.-)·")
                if( value ) then
                        return tonumber(value);
                end
        end
        return nil;
end

--
-- Functions that are dependent on preceding local functions, ordered appropriately
--

local function LootLink_GetName( elem )
        if ( type(elem) == "table" ) then
                return elem[1], elem[2];
        else
                return elem;
        end
end

local function LootLink_GetValue( elem, index, link )
        local name;
        if ( type(elem) == "table" ) then
                index = elem[2];
                elem = elem[1];
        end
        if ( link and ItemLinks[elem] and ItemLinks[elem].i ~= link ) then
                if ( ItemLinks[elem].m ) then
                        for k, v in ItemLinks[elem].m do
                                if v.i == link then
                                        index = k;
                                        break;
                                end
                        end
                        if ( not index ) then
                                --DEFAULT_CHAT_FRAME:AddMessage( elem.." "..link.." doesn't appear to exist yet?" );
                                return nil;
                        end
                end
        end
        if ( index and ItemLinks[elem] and ItemLinks[elem].m ) then
                return ItemLinks[elem].m[index], index;
        elseif ( ItemLinks[elem] ) then
                return ItemLinks[elem];
        else
                return nil;
        end
end

local function LootLink_InitSizes(serverIndex)
        local index;
        local value;

        lItemLinksSizeTotal = 0;
        lItemLinksSizeServer = 0;
        
        for index, value in ItemLinks do
                lItemLinksSizeTotal = lItemLinksSizeTotal + 1;
                if( LootLink_CheckItemServer(value, serverIndex) ) then
                        lItemLinksSizeServer = lItemLinksSizeServer + 1;
                end
        end
end

local function LootLink_GetSize(serverIndex)
        if( not serverIndex ) then
                return lItemLinksSizeTotal;
        end
        return lItemLinksSizeServer;
end

local function LootLink_Status()
        DEFAULT_CHAT_FRAME:AddMessage(format(LOOTLINK_STATUS_HEADER, LOOTLINK_VERSION / 100));
        if( LootLinkState ) then
                DEFAULT_CHAT_FRAME:AddMessage(format(LOOTLINK_DATA_VERSION, LootLink_GetSize(), LootLink_GetSize(lServerIndex), lServer, LootLink_GetDataVersion() / 100));
                if( LootLinkState.HideInfo ) then
                        DEFAULT_CHAT_FRAME:AddMessage(LOOTLINK_INFO_HIDDEN);
                else
                        DEFAULT_CHAT_FRAME:AddMessage(LOOTLINK_INFO_SHOWN);
                end
                if( LootLinkState.LightMode ) then
                        DEFAULT_CHAT_FRAME:AddMessage(LOOTLINK_LIGHT_MODE);
                else
                        DEFAULT_CHAT_FRAME:AddMessage(LOOTLINK_FULL_MODE);
                end
        else
                DEFAULT_CHAT_FRAME:AddMessage(LOOTLINK_INFO_SHOWN);
        end
        if( lScanAuction ) then
                DEFAULT_CHAT_FRAME:AddMessage(LOOTLINK_SCHEDULED_AUCTION_SCAN);
        end
end

local function LootLink_GetHyperlink(name, index)
        local itemLink = LootLink_GetValue( name, index );
        if( itemLink and itemLink.i --[[and LootLink_CheckItemServer(itemLink, lServerIndex)]] ) then
                -- Remove instance-specific data that we captured from the link we return
                local item = string.gsub(itemLink.i, "(%d+):(%d+):(%d+):(%d+)", "%1:%2:%3:0");
                return "item:"..item;
        end
        return nil;
end

local function LootLink_GetLink(name, index)
        local itemLink = LootLink_GetValue(name, index);
        if( itemLink and itemLink.c and itemLink.i --[[and (IsAltKeyDown() or LootLink_CheckItemServer(itemLink, lServerIndex))]] ) then
                local link = "|c"..itemLink.c.."|H"..LootLink_GetHyperlink(name, index).."|h["..name.."]|h|r";
                return link;
        end
        return nil;
end

local function LootLink_GetSet(set)
        if ( not LootLinkState.sets ) then
                LootLinkState.sets = {}
        end
        local sets = LootLinkState.sets;
        for k, v in sets do
                if ( v == set ) then
                        return k;
                end
        end
        tinsert(sets, set);
        return getn(sets);
end

function LootLink_BuildSearchData(name, value, alternate)
        local state, itemLink, loop, index, field, left, right, iStart, iEnd, val1, val2, val3, foundRequires, _type, subtype = STATE_NAME, LootLink_GetHyperlink(name, alternate);
        
        if( not itemLink ) then
                return nil;
        end
        
        -- This is the only place the tooltip hyperlink is set directly, therefore
        -- this should only be called when we know that the tooltip will be valid

        -- Protect money frame while we set hidden tooltip
        LootLink_MoneyToggle();
        LLHiddenTooltip:SetOwner(WorldFrame,"ANCHOR_NONE");
        LLHiddenTooltip:SetHyperlink(itemLink);
        LootLink_MoneyToggle(1);
        
        -- Double check that the data is good
        local realname, link, quality, level, _, _, _, count = GetItemInfo(itemLink);
        if ( not realname ) then
                LootLink_ScheduleItem(name,LootLink_GetValue(name,alternate).i);
                return nil;
        else
                local tooltipname = LLHiddenTooltipTextLeft1:GetText();
                if ( realname ~= tooltipname ) then
                        -- if this returns, we're in trouble.
                        return nil;
                end
        end

        local savedD = value.d;
        local savedT = value.t;

        value.d = "";
        if( not LootLinkState.LightMode ) then
                value.t = "";
        end
        
        value.c = string.gsub( ITEM_QUALITY_COLORS[quality].hex, "|c(.+)", "%1" );
        
        for index = 1, 30, 1 do
                field = getglobal("LLHiddenTooltipTextLeft"..index);
                if( field and field:IsShown() ) then
                        left = field:GetText();
                else
                        left = nil;
                end
                
                field = getglobal("LLHiddenTooltipTextRight"..index);
                if( field and field:IsShown() ) then
                        right = field:GetText();
                else
                        right = nil;
                end
                
                if( left ) then
                        if( not LootLinkState.LightMode ) then
                                -- cull set data to show 0/# and don't save durability
                                left = string.gsub( left, "(.+ %()%d+(%/%d%))", "%10%2" );
                                if ( string.sub( left, 1, 10 ) ~= "Durability" ) then
                                        value.t = value.t..left.."·";
                                end
                        end
                else
                        left = "";
                end
                if( right ) then
                        if( not LootLinkState.LightMode ) then
                                value.t = value.t..right.."·";
                        end
                else
                        right = "";
                end
                
                loop = 1;
                while( loop ) do
                        if( state == STATE_NAME ) then
                                state = STATE_BOUND;
                                loop = nil;
                        elseif( state == STATE_BOUND ) then
                                iStart, iEnd, val1 = string.find(left, "Binds when (.+)");
                                if( val1 ) then
                                        if( val1 == "equipped" ) then
                                                value.d = value.d.."bi"..BINDS_EQUIP.."·";
                                        elseif( val1 == "picked up" ) then
                                                value.d = value.d.."bi"..BINDS_PICKUP.."·";
                                        elseif( val1 == "used" ) then
                                                value.d = value.d.."bi"..BINDS_USED.."·";
                                        end
                                        loop = nil;
                                else
                                        value.d = value.d.."bi"..BINDS_DOES_NOT_BIND.."·";
                                end
                                state = STATE_UNIQUE;
                        elseif( state == STATE_UNIQUE ) then
                                if( string.find(left, "Unique") ) then
                                        value.d = value.d.."un1·";
                                        loop = nil;
                                end
                                state = STATE_LOCATION;
                        elseif( state == STATE_LOCATION ) then
                                local location;
                                loop, location, _type, subtype = LootLink_MatchType(left, right);
                                if( location ) then
                                        value.d = value.d.."lo"..location.."·";
                                end
                                if( _type == TYPE_WEAPON ) then
                                        state = STATE_DAMAGE;
                                elseif( _type == TYPE_ARMOR or _type == TYPE_SHIELD ) then
                                        state = STATE_ARMOR;
                                else
                                        state = STATE_CONTAINER;
                                end
                        elseif( state == STATE_DAMAGE ) then
                                iStart, iEnd, val1, val2 = string.find(left, "(%d+) %- (%d+) Damage");
                                if( val1 and val2 ) then
                                        value.d = value.d.."mi"..val1.."·ma"..val2.."·";
                                end
                                iStart, iEnd, val1 = string.find(right, "Speed (.+)");
                                if( val1 ) then
                                        value.d = value.d.."sp"..val1.."·";
                                end
                                state = STATE_DPS;
                                loop = nil;
                        elseif( state == STATE_DPS ) then
                                iStart, iEnd, val1 = string.find(left, "%((.+) damage per second%)");
                                if( val1 ) then
                                        value.d = value.d.."dp"..val1.."·";
                                end
                                state = STATE_REQUIRES;
                                loop = nil;
                        elseif( state == STATE_ARMOR ) then
                                iStart, iEnd, val1 = string.find(left, "(%d+) Armor");
                                if( val1 ) then
                                        value.d = value.d.."ar"..val1.."·";
                                end
                                if( _type == TYPE_SHIELD ) then
                                        state = STATE_BLOCK;
                                else
                                        state = STATE_REQUIRES;
                                end
                                loop = nil;
                        elseif( state == STATE_BLOCK ) then
                                iStart, iEnd, val1 = string.find(left, "(%d+) Block");
                                if( val1 ) then
                                        value.d = value.d.."bl"..val1.."·";
                                end
                                state = STATE_REQUIRES;
                                loop = nil;
                        elseif( state == STATE_CONTAINER ) then
                                iStart, iEnd, val1, val2 = string.find(left, "(%d+) Slot (.+)");
                                if( val1 ) then
                                        _type = TYPE_CONTAINER;
                                        subtype = ContainerSubtypes[val2];
                                        value.d = value.d.."sl"..val1.."·";
                                        loop = nil;
                                end
                                state = STATE_REQUIRES;
                        elseif( state == STATE_REQUIRES ) then
                                iStart, iEnd, val1 = string.find(left, "Requires (.+)");
                                if( val1 ) then
                                        iStart, iEnd, val2 = string.find(val1, "Level (%d+)");
                                        if( val2 ) then
                                                value.d = value.d.."le"..val2.."·";
                                        else
                                                iStart, iEnd, val2, val3 = string.find(val1, "(.+) %((%d+)%)");
                                                if( val2 and val3 ) then
                                                        val1 = RecipeSubtypes[val2];
                                                        if( val1 and _type == TYPE_MISC ) then
                                                                _type = TYPE_RECIPE;
                                                                subtype = val1;
                                                                value.d = value.d.."sk"..val3.."·";
                                                        end
                                                end
                                        end
                                else
                                        state = STATE_SPACE;
                                end
                                loop = nil;
                        elseif( state == STATE_SPACE ) then
                                if ( left == tooltipSpace ) then
                                        state = STATE_SET;
                                end
                                loop = nil;
                        elseif( state == STATE_SET ) then
                                if ( left ~= "" ) then
                                        iStart, iEnd, val1, val2 = string.find(left, "(.+) %(%d+%/(%d+)%)");
                                        if( val2 ) then
                                                local set = LootLink_GetSet(val1);
                                                value.d = value.d.."se"..set.."·";
                                                state = STATE_FINISH;
                                        end
                                end
                                loop = nil;
                        elseif( state == STATE_FINISH ) then
                                loop = nil;
                        end
                end
        end
        if( _type ) then
                value.d = value.d.."ty".._type.."·";
        end
        if( subtype ) then
                value.d = value.d.."su"..subtype.."·";
        end
        
        if ( realname and name ~= realname ) then
                if ( not ItemLinks[realname] ) then
                        ItemLinks[realname] = {};
                        ItemLinks[realname] = value;
                else
                        if ( ItemLinks[realname].i ~= value.i and ItemLinks[realname].m ) then
                                local found;
                                for k, v in ItemLinks[realname].m do
                                        if v.i == value.i then
                                                found = 1;
                                                break;
                                        end
                                end
                                if ( not found ) then
                                        tinsert( ItemLinks[realname].m, value );
                                end
                        end
                end
        end
                
        if ( savedD ~= value.d or savedT ~= value.t ) then
                return 2;
        end
        return 1;
end

local function BuildUsabilityData(data)
        local nSkills;
        local iSkill;
        local HeaderData = { };
        local name, header, isExpanded, rank;
        local Collapse = { };
        local nToCollapse = 0;
        local iCollapse;

        data.class = UnitClass("player");
        data.race = UnitRace("player");
        data.level = UnitLevel("player");
        data.skills = { };
        
        -- We need to expand all of the skills, but first want to save off their state
        nSkills = GetNumSkillLines();
        for iSkill = 1, nSkills do
                local name, header, isExpanded, rank = GetSkillLineInfo(iSkill);
                if( header and not isExpanded ) then
                        -- Since we don't know the final index for this item yet, we'll store it by name
                        HeaderData[name] = 0;
                end
        end
        
        -- Now expand everything and save off our known skills
        ExpandSkillHeader(0);
        nSkills = GetNumSkillLines()
        for iSkill = 1, nSkills do
                local name, header, isExpanded, rank = GetSkillLineInfo(iSkill);
                if( not header ) then
                        data.skills[name] = rank;
                elseif( HeaderData[name] ) then
                        -- We now know the final index for this header item
                        HeaderData[name] = iSkill;
                end
        end
        
        -- Finally, return the skills page to its original state
        for name, iSkill in HeaderData do
                Collapse[nToCollapse + 1] = iSkill;
                nToCollapse = nToCollapse + 1;
        end
        if( nToCollapse > 0 ) then
                table.sort(Collapse);
                for iCollapse = nToCollapse, 1, -1 do
                        CollapseSkillHeader(Collapse[iCollapse]);
                end
        end
end

local function LootLink_GetSkillRank(ud, _type, subtype, location)
        local entry = lTypeAndSubtypeToSkill[_type][subtype];
        
        if( type(entry) == "table" ) then
                -- Two-handed vs. One-handed weapon
                if( location ) then
                        if( location == 3 ) then
                                return ud.skills[entry[1]];
                        else
                                return ud.skills[entry[0]];
                        end
                else
                        return nil;
                end
        else
                -- Everything else
                return ud.skills[entry];
        end
end

local function LootLink_SetHyperlink(tooltip, name, link, index)
        -- If the link isn't in the local cache, it may not be valid
        if ( not link ) then
                return nil;
        end
        local value = ItemLinks[name];
        if ( index and value.m ) then
                value = value.m[index];
        end
        if( not GetItemInfo(link) ) then
                -- To avoid disconnects, we'll create our own lame tooltip for these
                if( value ) then
                        local extraSkip = 0;
                        local lines = 1;
                        local usabilityData;
                        
                        -- Name, in rarity color
                        tooltip:SetText("|c"..value.c..name.."|r");
                        
                        -- Binds on equip, binds on pickup
                        local binds = LL_SearchData(value, "bi");
                        if( binds == BINDS_EQUIP ) then
                                tooltip:AddLine(ITEM_BIND_ON_EQUIP, 1, 1, 1);
                                lines = lines + 1;
                        elseif( binds == BINDS_PICKUP ) then
                                tooltip:AddLine(ITEM_BIND_ON_PICKUP, 1, 1, 1);
                                lines = lines + 1;
                        elseif( binds == BINDS_USED ) then
                                tooltip:AddLine(ITEM_BIND_ON_USE, 1, 1, 1);
                                lines = lines + 1;
                        end
                        
                        -- Unique?
                        local unique = LL_SearchData(value, "un");
                        if( unique ) then
                                tooltip:AddLine("Unique", 1, 1, 1);
                                lines = lines + 1;
                        end
                        
                        local _type = LL_SearchData(value, "ty");
                        local subtype = LL_SearchData(value, "su");

                        -- Equip location and type/subtype
                        local location = LL_SearchData(value, "lo");
                        if( location ) then
                                local subtypes;
                                local name;
                                for i, v in LocationTypes do
                                        if( v.i == location ) then
                                                name = i;
                                                subtypes = v.subtypes;
                                                break;
                                        end
                                end
                                if( name ) then
                                        tooltip:AddLine(name, 1, 1, 1);
                                        lines = lines + 1;
                                        if( subtype ) then
                                                if( _type == TYPE_WEAPON ) then
                                                        subtypes = WeaponSubtypes;
                                                end
                                                if( subtypes ) then
                                                        for i, v in subtypes do
                                                                if( v == subtype ) then
                                                                        if( i == name ) then
                                                                                local line = getglobal(tooltip:GetName().."TextLeft"..lines);
                                                                                
                                                                                if( not usabilityData ) then
                                                                                        usabilityData = { };
                                                                                        BuildUsabilityData(usabilityData);
                                                                                end
                                                                                if( not LootLink_GetSkillRank(usabilityData, _type, subtype, location) ) then
                                                                                        line:SetTextColor(1, 0, 0);
                                                                                end
                                                                        else
                                                                                local line = getglobal(tooltip:GetName().."TextRight"..lines);
                                                                                line:SetText(i);

                                                                                if( not usabilityData ) then
                                                                                        usabilityData = { };
                                                                                        BuildUsabilityData(usabilityData);
                                                                                end
                                                                                if( LootLink_GetSkillRank(usabilityData, _type, subtype, location) ) then
                                                                                        line:SetTextColor(1, 1, 1);
                                                                                else
                                                                                        line:SetTextColor(1, 0, 0);
                                                                                end
                                                                                
                                                                                line:Show();
                                                                                extraSkip = extraSkip + 1;
                                                                                break;
                                                                        end
                                                                end
                                                        end
                                                end
                                        end
                                end
                        end
                        
                        -- Now do type specific data
                        if( _type == TYPE_ARMOR ) then
                                local armor = LL_SearchData(value, "ar");
                                if( armor ) then
                                        tooltip:AddLine(armor.." Armor", 1, 1, 1);
                                        lines = lines + 1;
                                end
                        elseif( _type == TYPE_WEAPON ) then
                                local min = LL_SearchData(value, "mi");
                                local max = LL_SearchData(value, "ma");
                                local speed = LL_SearchData(value, "sp");
                                local dps = LL_SearchData(value, "dp");
                                
                                if( min and max ) then
                                        tooltip:AddLine(min.." - "..max.." Damage", 1, 1, 1);
                                        lines = lines + 1;
                                        if( speed ) then
                                                local line = getglobal(tooltip:GetName().."TextRight"..lines);
                                                line:SetText(format("Speed %.2f", tonumber(speed)));
                                                line:SetTextColor(1, 1, 1);
                                                line:Show();
                                                extraSkip = extraSkip + 1;
                                        end
                                end
                                if( dps ) then
                                        tooltip:AddLine("("..dps.." damage per second)", 1, 1, 1);
                                        lines = lines + 1;
                                end
                        elseif( _type == TYPE_SHIELD ) then
                                local armor = LL_SearchData(value, "ar");
                                local block = LL_SearchData(value, "bl");
                                if( armor ) then
                                        tooltip:AddLine(armor.." Armor", 1, 1, 1);
                                        lines = lines + 1;
                                end
                                if( block ) then
                                        tooltip:AddLine(block.." Block", 1, 1, 1);
                                        lines = lines + 1;
                                end
                        elseif( _type == TYPE_RECIPE ) then
                                local skill = LL_SearchData(value, "sk");
                                if( skill and subtype ) then
                                        for i, v in RecipeSubtypes do
                                                if( v == subtype ) then
                                                        if( not usabilityData ) then
                                                                usabilityData = { };
                                                                BuildUsabilityData(usabilityData);
                                                        end
                                                        local rank = LootLink_GetSkillRank(usabilityData, _type, subtype, location);
                                                        if( not rank or rank < skill ) then
                                                                tooltip:AddLine("Requires "..i.." ("..skill..")", 1, 0, 0);
                                                        else
                                                                tooltip:AddLine("Requires "..i.." ("..skill..")", 1, 1, 1);
                                                        end
                                                        lines = lines + 1;
                                                        break;
                                                end
                                        end
                                end
                        elseif( _type == TYPE_CONTAINER ) then
                                local slots = LL_SearchData(value, "sl");
                                if( slots ) then
                                        local subtype = LL_SearchData(value, "su") or 0;
                                        local bag = lTypeAndSubtypeToSkill[_type][subtype]; -- repurposed skill table, fix later?
                                        tooltip:AddLine(slots.." Slot "..bag, 1, 1, 1);
                                        lines = lines + 1;
                                end
                        end
                        
                        local level = LL_SearchData(value, "le");

                        -- Now add any extra text data that we have
                        if( value.t ) then
                                local skip = lines + extraSkip;
                                local piece;
                                for piece in string.gfind(value.t, "(.-)·") do
                                        if( lines < 29 ) then
                                                if( skip == 0 ) then
                                                        if( string.find(piece, "Requires Level .*") ) then
                                                                if( level and tonumber(level) > UnitLevel("player") ) then
                                                                        tooltip:AddLine(piece, 1, 0, 0, 1, 1);
                                                                else
                                                                        tooltip:AddLine(piece, 1, 1, 1, 1, 1);
                                                                end
                                                        elseif( string.find(piece, ":") ) then
                                                                if( string.find(piece, "^Class") ) then
                                                                        if( string.find(piece, "^Class.*"..UnitClass("player")) ) then
                                                                                tooltip:AddLine(piece, 1, 1, 1, 1, 1);
                                                                        else
                                                                                tooltip:AddLine(piece, 1, 0, 0, 1, 1);
                                                                        end
                                                                else
                                                                        tooltip:AddLine(piece, 0, 1, 0, 1, 1);
                                                                end
                                                        elseif( string.find(piece, "\"") or string.find(piece, "%(%d+/%d+%)") ) then
                                                                tooltip:AddLine(piece, 1, 0.8235, 0, 1, 1);
                                                        elseif( string.find(piece, "^  ") ) then
                                                                tooltip:AddLine(piece, 0.5, 0.5, 0.5, 1, 1);
                                                        else
                                                                tooltip:AddLine(piece, 1, 1, 1, 1, 1);
                                                        end
                                                        lines = lines + 1;
                                                else
                                                        skip = skip - 1;
                                                end
                                        end
                                end
                        end
                        
                        -- And, after all that, let the user know that we faked this tooltip
                        if( lines < 30 ) then
                                tooltip:AddLine("|cff40ffc0<"..LOOTLINK_TOOLTIP_GENERATED..">|r");
                                lines = lines + 1;
                        end
                        
                        if( lines < 30 and not LootLink_CheckItemServer(value, lServerIndex) ) then
                                tooltip:AddLine("|cff40ffc0<"..LOOTLINK_TOOLTIP_SERVER..">|r");
                                lines = lines + 1;
                        end
                        
                        -- Finally, show the tooltip, which adjusts its size
                        tooltip:Show();
                end
        else
                -- Get the actual tooltip from the cache
                tooltip:SetHyperlink(link);
                
                -- After setting the tooltip, parse its data
                LootLink_ScheduleItem(name, value.i);
                --LootLink_BuildSearchData(name, value, index);
                        
                if( not LootLink_ValidateItem(name, link) ) then
                        tooltip:AddLine("|cff40ffc0<"..LOOTLINK_TOOLTIP_BAD_ITEM..">|r");
                elseif( not LootLink_CheckItemServerRaw(value, lServerIndex) ) then
                        tooltip:AddLine("|cff40ffc0<"..LOOTLINK_TOOLTIP_SERVER..">|r");
                        --LootLink_AddItemServer(value, lServerIndex);
                        --lItemLinksSizeServer = lItemLinksSizeServer + 1;
                        --return 1;
                end
        end
end

local function LootLink_SetTitle()
        local lootLinkTitle = getglobal("LootLinkTitleText");
        local total = LootLink_GetSize(lServerIndex);
        local size;
        
        if( not DisplayIndices ) then
                size = total;
        else
                size = DisplayIndices.onePastEnd - 1;
        end
        if( size < total ) then
                if( size == 1 ) then
                        lootLinkTitle:SetText(TEXT(LOOTLINK_TITLE_FORMAT_PARTIAL_SINGULAR));
                else
                        lootLinkTitle:SetText(format(TEXT(LOOTLINK_TITLE_FORMAT_PARTIAL_PLURAL), size));
                end
        else
                if( size == 1 ) then
                        lootLinkTitle:SetText(TEXT(LOOTLINK_TITLE_FORMAT_SINGULAR));
                else
                        lootLinkTitle:SetText(format(TEXT(LOOTLINK_TITLE_FORMAT_PLURAL), size));
                end
        end
end

local function LootLink_MatchesSearch(name, value, ud)
        if( not value ) then
                return nil;
        end
        if( LootLinkState ) then
                if ( LootLinkState.spoof and name ) then
                        if ( not LootLink_ValidateItem( name, value.i ) ) then
                                return nil;
                        end
                end
                if ( LootLinkState.server ) then
                        if ( not LootLink_CheckItemServer(value, lServerIndex) ) then
                                return nil;
                        end
                end
                if ( LootLinkState.rarityfilter ) then
                        if ( value.c and lRarityFilter[value.c] < LootLinkState.rarityfilter ) then
                                return nil;
                        end
                end
                if ( LootLinkState.noname ) then
                        if ( ItemLinks[name] and ItemLinks[name].i and ItemLinks[name].i ~= value.i ) then
                                return nil;
                        end
                else
                        if ( LootLinkState.novariants ) then
                                if ( string.gsub(value.i, "%d+:%d+:(%d+):%d+", "%1") ~="0" and ItemLinks[name] and ItemLinks[name].i and ItemLinks[name].i ~= value.i ) then
                                        return nil;
                                end
                        end
                        if ( LootLinkState.enchants ) then
                                if ( string.gsub(value.i, "%d+:(%d+):%d+:%d+", "%1") ~= "0" ) then
                                        return nil;
                                end
                        end
                end
        end
        if( not LootLinkFrame.SearchParams or not name ) then
                return 1;
        end
        
        if( value.d ) then
                local sp = LootLinkFrame.SearchParams; 
                
                if( sp.server ) then
                        if ( not LootLink_CheckItemServer(value, lServerIndex) ) then
                                return nil;
                        end
                end
                
                if( sp.spoof ) then
                        if ( LootLink_ValidateItem( name, value.i ) ) then
                                return nil;
                        end
                end
                
                if( sp.text ) then
                        if( not value.t ) then
                                return nil;
                        end
                        if( not string.find(string.lower(value.t), string.lower(sp.text), 1, sp.plain) ) then
                                return nil;
                        end
                end
                
                if( sp.name ) then
                        if( not string.find(string.lower(name), string.lower(sp.name), 1, sp.plain) ) then
                                return nil;
                        end
                end
                
                if( sp.rarity ) then
                        if( LLS_RARITY_LIST[sp.rarity].value ~= value.c ) then
                                return nil;
                        end
                end
                
                if( sp.binds ) then
                        if( LLS_BINDS_LIST[sp.binds].value ~= LL_SearchData(value, "bi") ) then
                                return nil;
                        end
                end
                
                if( sp.unique ) then
                        if( sp.unique ~= LL_SearchData(value, "un") ) then
                                return nil;
                        end
                end
                
                if( sp.usable ) then
                        local _type = LL_SearchData(value, "ty");
                        local subtype = LL_SearchData(value, "su");
                        local level = LL_SearchData(value, "le");
                        local skill = LL_SearchData(value, "sk");
                        
                        -- Check for the required skill
                        if( _type ) then
                                if( subtype ) then
                                        local rank = LootLink_GetSkillRank(ud, _type, subtype, LL_SearchData(value, "lo"));
                                        if( not rank or (skill and skill > rank) ) then
                                                return nil;
                                        end
                                end
                        end
                        
                        -- Check for the required class
                        if( value.t and string.find(value.t, "·Class") and not string.find(value.t, "·Class.*"..UnitClass("player")) ) then
                                return nil;
                        end
                        
                        -- Check level requirement
                        if( level and level > ud.level ) then
                                return nil;
                        end
                end
                
                if( sp.location ) then
                        local location = LL_SearchData(value, "lo");
                
                        if( sp.location == 2 ) then
                                if( location ) then
                                        return nil;
                                end
                        elseif( sp.location ~= 1 ) then
                                if( LocationTypes[LLS_LOCATION_LIST[sp.location].name].i ~= location ) then
                                        return nil;
                                end
                        end
                end
                
                if( sp.minLevel ) then
                        local level = LL_SearchData(value, "le");
                        if( not level or level < sp.minLevel ) then
                                return nil;
                        end
                end
                
                if( sp.maxLevel ) then
                        local level = LL_SearchData(value, "le");
                        if( level and level > sp.maxLevel ) then
                                return nil;
                        end
                end
                
                if( sp.set ) then
                        local set = LL_SearchData(value, "se");
                        if( not set ) then
                                return nil;
                        end
                end
                
                if( sp.type ) then
                        local _type = LLS_TYPE_LIST[sp.type].value;
                        if( _type ~= LL_SearchData(value, "ty") ) then
                                return nil;
                        end
                        if( sp.subtype ) then
                                local subtype;
                                if( _type == TYPE_ARMOR ) then
                                        subtype = LLS_SUBTYPE_ARMOR_LIST[sp.subtype].value;
                                elseif( _type == TYPE_SHIELD ) then
                                        subtype = LLS_SUBTYPE_SHIELD_LIST[sp.subtype].value;
                                elseif( _type == TYPE_WEAPON ) then
                                        subtype = LLS_SUBTYPE_WEAPON_LIST[sp.subtype].value;
                                elseif( _type == TYPE_RECIPE ) then
                                        subtype = LLS_SUBTYPE_RECIPE_LIST[sp.subtype].value;
                                elseif( _type == TYPE_CONTAINER ) then
                                        subtype = LLS_SUBTYPE_CONTAINER_LIST[sp.subtype].value;
                                end
                                if( subtype and subtype ~= LL_SearchData(value, "su") ) then
                                        return nil;
                                end
                        end
                        
                        if( _type == TYPE_WEAPON ) then
                                if( sp.minMinDamage ) then
                                        local damage = LL_SearchData(value, "mi");
                                        if( not damage or damage < sp.minMinDamage ) then
                                                return nil;
                                        end
                                end

                                if( sp.minMaxDamage ) then
                                        local damage = LL_SearchData(value, "ma");
                                        if( not damage or damage < sp.minMaxDamage ) then
                                                return nil;
                                        end
                                end

                                if( sp.maxSpeed ) then
                                        local speed = LL_SearchData(value, "sp");
                                        if( not speed or speed > sp.maxSpeed ) then
                                                return nil;
                                        end
                                end

                                if( sp.minDPS ) then
                                        local DPS = LL_SearchData(value, "dp");
                                        if( not DPS or DPS < sp.minDPS ) then
                                                return nil;
                                        end
                                end
                        elseif( _type == TYPE_ARMOR or _type == TYPE_SHIELD ) then
                                if( sp.minArmor ) then
                                        local armor = LL_SearchData(value, "ar");
                                        if( not armor or armor < sp.minArmor ) then
                                                return nil;
                                        end
                                end
                                
                                if( _type == TYPE_SHIELD ) then
                                        if( sp.minBlock ) then
                                                local block = LL_SearchData(value, "bl");
                                                if( not block or block < sp.minBlock ) then
                                                        return nil;
                                                end
                                        end
                                end
                        elseif( _type == TYPE_CONTAINER ) then
                                if( sp.minSlots ) then
                                        local slots = LL_SearchData(value, "sl");
                                        if( not slots or slots < sp.minSlots ) then
                                                return nil;
                                        end
                                end
                        elseif( _type == TYPE_RECIPE ) then
                                if( sp.minSkill ) then
                                        local skill = LL_SearchData(value, "sk");
                                        if( not skill or skill < sp.minSkill ) then
                                                return nil;
                                        end
                                end
                                
                                if( sp.maxSkill ) then
                                        local skill = LL_SearchData(value, "sk");
                                        if( not skill or skill > sp.maxSkill ) then
                                                return nil;
                                        end
                                end
                        end
                end
        else
                return nil;
        end

        return 1;
end

local function LootLink_NameComparison(elem1, elem2)
        local v1, i1 = elem1[1], elem1[3];
        local v2, i2 = elem2[1], elem2[3];
        
        if ( v1 == v2 ) then
                if ( i1 and i2 ) then
                        return i1 < i2;
                elseif ( i1 ) then
                        return false;
                elseif ( i2 ) then
                        return true;
                end
        end
        return v1 < v2;
end

local function LootLink_ColorComparison(elem1, elem2)
        local color1;
        local color2;
        local v1 = elem1[2];
        local v2 = elem2[2];
        
        if( v1 and v1.c ) then
                color1 = v1.c;
        else
                color1 = "ffffffff";
        end
        if( v2 and v2.c ) then
                color2 = v2.c;
        else
                color2 = "ffffffff";
        end
        
        if( color1 == color2 ) then
                return LootLink_NameComparison( elem1, elem2 );
        end
        
        if( not lColorSortTable[color1] and not lColorSortTable[color2] ) then
                return color1 < color2;
        end
        
        if( lColorSortTable[color1] ) then
                if( lColorSortTable[color2] ) then
                        return lColorSortTable[color1] < lColorSortTable[color2];
                end
                return nil;
        end
        return 1;
end

local function LootLink_GenericComparison(elem1, elem2, v1, v2)
        if( v1 == v2 ) then
                return LootLink_NameComparison( elem1, elem2 );
        end
        if( not v1 ) then
                return 1;
        end
        if( not v2 ) then
                return nil;
        end
        if( tonumber(v1) and tonumber(v2) ) then
                return tonumber(v1) < tonumber(v2);
        end
        return v1 < v2;
end

local function LootLink_BindsComparison(elem1, elem2)
        local v1, v2;
        
        local value1 = elem1[2];
        local value2 = elem2[2];
        
        if( value1 and value1.d ) then
                v1 = LL_SearchData(ItemLinks[elem1], "bi");
        end
        if( value2 and value2.d ) then
                v2 = LL_SearchData(ItemLinks[elem2], "bi");
        end
        
        return LootLink_GenericComparison(elem1, elem2, v1, v2);
end

local function LootLink_UniqueComparison(elem1, elem2)
        local v1, v2;
        
        local value1 = elem1[2];
        local value2 = elem2[2];
        
        if( value1 and value1.d ) then
                v1 = LL_SearchData(value1, "un");
        end
        if( value2 and value2.d ) then
                v2 = LL_SearchData(value2, "un");
        end
        
        return LootLink_GenericComparison(elem1, elem2, v1, v2);
end

local function LootLink_LocationComparison(elem1, elem2)
        local v1, v2;
        
        local value1 = elem1[2];
        local value2 = elem2[2];
        
        if( value1 and value1.d ) then
                v1 = LL_SearchData(value1, "lo");
        end
        if( value2 and value2.d ) then
                v2 = LL_SearchData(value2, "lo");
        end
        
        return LootLink_GenericComparison(elem1, elem2, v1, v2);
end

local function LootLink_TypeComparison(elem1, elem2)
        local v1, v2;
        
        local value1 = elem1[2];
        local value2 = elem2[2];
        
        if( value1 and value1.d ) then
                v1 = LL_SearchData(value1, "ty");
        end
        if( value2 and value2.d ) then
                v2 = LL_SearchData(value2, "ty");
        end
        
        return LootLink_GenericComparison(elem1, elem2, v1, v2);
end

local function LootLink_SubtypeComparison(elem1, elem2)
        local v1, v2;
        
        local value1 = elem1[2];
        local value2 = elem2[2];
        
        if( value1 and value1.d ) then
                v1 = LL_SearchData(value1, "su");
        end
        if( value2 and value2.d ) then
                v2 = LL_SearchData(value2, "su");
        end
        
        return LootLink_GenericComparison(elem1, elem2, v1, v2);
end

local function LootLink_MinDamageComparison(elem1, elem2)
        local v1, v2;
        
        local value1 = elem1[2];
        local value2 = elem2[2];
        
        if( value1 and value1.d ) then
                v1 = LL_SearchData(value1, "mi");
        end
        if( value2 and value2.d ) then
                v2 = LL_SearchData(value2, "mi");
        end
        
        return LootLink_GenericComparison(elem1, elem2, v1, v2);
end

local function LootLink_MaxDamageComparison(elem1, elem2)
        local v1, v2;
        
        local value1 = elem1[2];
        local value2 = elem2[2];
        
        if( value1 and value1.d ) then
                v1 = LL_SearchData(value1, "ma");
        end
        if( value2 and value2.d ) then
                v2 = LL_SearchData(value2, "ma");
        end
        
        return LootLink_GenericComparison(elem1, elem2, v1, v2);
end

local function LootLink_SpeedComparison(elem1, elem2)
        local v1, v2;
        
        local value1 = elem1[2];
        local value2 = elem2[2];
        
        if( value1 and value1.d ) then
                v1 = LL_SearchData(value1, "sp");
        end
        if( value2 and value2.d ) then
                v2 = LL_SearchData(value2, "sp");
        end
        
        return LootLink_GenericComparison(elem1, elem2, v1, v2);
end

local function LootLink_DPSComparison(elem1, elem2)
        local v1, v2;
        
        local value1 = elem1[2];
        local value2 = elem2[2];
        
        if( value1 and value1.d ) then
                v1 = LL_SearchData(value1, "dp");
        end
        if( value2 and value2.d ) then
                v2 = LL_SearchData(value2, "dp");
        end
        
        return LootLink_GenericComparison(elem1, elem2, v1, v2);
end

local function LootLink_ArmorComparison(elem1, elem2)
        local v1, v2;
        
        local value1 = elem1[2];
        local value2 = elem2[2];
        
        if( value1 and value1.d ) then
                v1 = LL_SearchData(value1, "ar");
        end
        if( value2 and value2.d ) then
                v2 = LL_SearchData(value2, "ar");
        end
        
        return LootLink_GenericComparison(elem1, elem2, v1, v2);
end

local function LootLink_BlockComparison(elem1, elem2)
        local v1, v2;
        
        local value1 = elem1[2];
        local value2 = elem2[2];
        
        if( value1 and value1.d ) then
                v1 = LL_SearchData(value1, "bl");
        end
        if( value2 and value2.d ) then
                v2 = LL_SearchData(value2, "bl");
        end
        
        return LootLink_GenericComparison(elem1, elem2, v1, v2);
end

local function LootLink_SlotsComparison(elem1, elem2)
        local v1, v2;
        
        local value1 = elem1[2];
        local value2 = elem2[2];
        
        if( value1 and value1.d ) then
                v1 = LL_SearchData(value1, "sl");
        end
        if( value2 and value2.d ) then
                v2 = LL_SearchData(value2, "sl");
        end
        
        return LootLink_GenericComparison(elem1, elem2, v1, v2);
end

local function LootLink_SkillComparison(elem1, elem2)
        local v1, v2;
        
        local value1 = elem1[2];
        local value2 = elem2[2];
        
        if( value1 and value1.d ) then
                v1 = LL_SearchData(value1, "sk");
        end
        if( value2 and value2.d ) then
                v2 = LL_SearchData(value2, "sk");
        end
        
        return LootLink_GenericComparison(elem1, elem2, v1, v2);
end

local function LootLink_LevelComparison(elem1, elem2)
        local v1, v2;
        
        local value1 = elem1[2];
        local value2 = elem2[2];
        
        if( value1 and value1.d ) then
                v1 = LL_SearchData(value1, "le");
        end
        if( value2 and value2.d ) then
                v2 = LL_SearchData(value2, "le");
        end
        
        return LootLink_GenericComparison(elem1, elem2, v1, v2);
end

-- Ugly, but functional
local function LootLink_SetComparison(elem1, elem2)
        local v1, v2;
        
        local value1 = elem1[2];
        local value2 = elem2[2];
        
        local sets = LootLinkState.sets;
        
        if ( not sets ) then
                return LootLink_NameComparison(elem1, elem2);
        end
        
        if( value1 and value1.d ) then
                v1 = LL_SearchData(value1, "se");
                v1 = sets[v1];
        end
        if( value2 and value2.d ) then
                v2 = LL_SearchData(value2, "se");
                v2 = sets[v2];
        end
        
        if ( not v1 and not v2 ) then
                return LootLink_NameComparison(elem1, elem2);
        elseif ( not v1 ) then
                return nil;
        elseif ( not v2 ) then
                return 1;
        else
                if ( v1 == v2 ) then
                        return LootLink_NameComparison(elem1, elem2);
                else
                        return v1 < v2;
                end
        end
end

local function LootLink_Sort()
        if( LOOTLINK_DROPDOWN_LIST[UIDropDownMenu_GetSelectedID(LootLinkFrameDropDown)].sortType ) then
                local sortType = LOOTLINK_DROPDOWN_LIST[UIDropDownMenu_GetSelectedID(LootLinkFrameDropDown)].sortType;
                if( sortType == "name" ) then
                        table.sort(DisplayIndices, LootLink_NameComparison);
                elseif( sortType == "rarity" ) then
                        table.sort(DisplayIndices, LootLink_ColorComparison);
                elseif( sortType == "binds" ) then
                        table.sort(DisplayIndices, LootLink_BindsComparison);
                elseif( sortType == "unique" ) then
                        table.sort(DisplayIndices, LootLink_UniqueComparison);
                elseif( sortType == "location" ) then
                        table.sort(DisplayIndices, LootLink_LocationComparison);
                elseif( sortType == "type" ) then
                        table.sort(DisplayIndices, LootLink_TypeComparison);
                elseif( sortType == "subtype" ) then
                        table.sort(DisplayIndices, LootLink_SubtypeComparison);
                elseif( sortType == "minDamage" ) then
                        table.sort(DisplayIndices, LootLink_MinDamageComparison);
                elseif( sortType == "maxDamage" ) then
                        table.sort(DisplayIndices, LootLink_MaxDamageComparison);
                elseif( sortType == "speed" ) then
                        table.sort(DisplayIndices, LootLink_SpeedComparison);
                elseif( sortType == "DPS" ) then
                        table.sort(DisplayIndices, LootLink_DPSComparison);
                elseif( sortType == "armor" ) then
                        table.sort(DisplayIndices, LootLink_ArmorComparison);
                elseif( sortType == "block" ) then
                        table.sort(DisplayIndices, LootLink_BlockComparison);
                elseif( sortType == "slots" ) then
                        table.sort(DisplayIndices, LootLink_SlotsComparison);
                elseif( sortType == "skill" ) then
                        table.sort(DisplayIndices, LootLink_SkillComparison);
                elseif( sortType == "level" ) then
                        table.sort(DisplayIndices, LootLink_LevelComparison);
                elseif( sortType == "set" ) then
                        table.sort(DisplayIndices, LootLink_SetComparison);
                end
        end
end

local function LootLink_BuildDisplayIndices()
        local iNew = 1;
        local index;
        local value;
        local UsabilityData;
        
        if( LootLinkFrame.SearchParams and LootLinkFrame.SearchParams.usable ) then
                UsabilityData = { };
                BuildUsabilityData(UsabilityData);
        end
        
        DisplayIndices = { };
        for index, value in ItemLinks do
                if( LootLink_MatchesSearch(index, value, UsabilityData) ) then
                        DisplayIndices[iNew] = { index, value, nil };
                        iNew = iNew + 1;
                end
                if( value.m ) then
                        for i, altvalue in value.m do
                                if( LootLink_MatchesSearch(index, altvalue, UsabilityData) ) then
                                        DisplayIndices[iNew] = { index, altvalue, i };
                                        iNew = iNew + 1;
                                end
                        end
                end     
        end
        DisplayIndices.onePastEnd = iNew;
        LootLink_SetDataVersion(100); -- version 1.00
        LootLink_Sort();
        LootLink_SetTitle();
        LootLinkQuickSearch_Clear();
end

local function LootLink_Reset()
        ItemLinks = { };

        LootLink_SetDataVersion(110);   -- version 1.10

        LootLink_InitSizes(lServerIndex);
        
        if( DisplayIndices ) then
                LootLink_BuildDisplayIndices();
                LootLink_Update();
        end
end

local function LootLink_MakeHome()
        local index;
        local value;

        LootLink_SetDataVersion(110);   -- version 1.10
        
        for index, value in ItemLinks do
                if( not value._ ) then
                        -- If this item predates multiple server support, mark is as seen on this server
                        LootLink_AddItemServer(value, lServerIndex);
                else
                        -- Otherwise just wipe the flag since it only applies to pre-1.10 data
                        value._ = nil;
                end
        end

        LootLink_InitSizes(lServerIndex);
end

local function LootLink_LightMode()
        local index;
        local value;

        for index, value in ItemLinks do
                value.t = nil;
        end
        LootLinkState.LightMode = 1;
end

--
-- Uncategorized functions; will sort later
--

local function LootLinkFrameDropDown_Initialize()
        local info;
        for i = 1, getn(LOOTLINK_DROPDOWN_LIST), 1 do
                info = { };
                info.text = LOOTLINK_DROPDOWN_LIST[i].name;
                info.func = LootLinkFrameDropDownButton_OnClick;
                UIDropDownMenu_AddButton(info);
        end
end

local function LLS_RarityDropDown_Initialize()
        local info;
        for i = 1, getn(LLS_RARITY_LIST), 1 do
                info = { };
                info.text = LLS_RARITY_LIST[i].name;
                info.func = LLS_RarityDropDown_OnClick;
                if( LLS_RARITY_LIST[i].value ) then
                        info.textR = LLS_RARITY_LIST[i].r;
                        info.textG = LLS_RARITY_LIST[i].g;
                        info.textB = LLS_RARITY_LIST[i].b;
                end
                UIDropDownMenu_AddButton(info);
        end
end

local function LLO_RarityDropDown_Initialize()
        local info;
        for i = 1, getn(LLO_RARITY_LIST), 1 do
                info = { };
                info.text = LLO_RARITY_LIST[i].name;
                info.func = LLO_RarityDropDown_OnClick;
                UIDropDownMenu_AddButton(info);
        end
end

local function LLS_BindsDropDown_Initialize()
        local info;
        for i = 1, getn(LLS_BINDS_LIST), 1 do
                info = { };
                info.text = LLS_BINDS_LIST[i].name;
                info.func = LLS_BindsDropDown_OnClick;
                UIDropDownMenu_AddButton(info);
        end
end

local function LLS_LocationDropDown_Initialize()
        local info;
        for i = 1, getn(LLS_LOCATION_LIST), 1 do
                info = { };
                info.text = LLS_LOCATION_LIST[i].name;
                info.func = LLS_LocationDropDown_OnClick;
                UIDropDownMenu_AddButton(info);
        end
end

local function LLS_TypeDropDown_Initialize()
        local info;
        for i = 1, getn(LLS_TYPE_LIST), 1 do
                info = { };
                info.text = LLS_TYPE_LIST[i].name;
                info.func = LLS_TypeDropDown_OnClick;
                UIDropDownMenu_AddButton(info);
        end
end

local function LLS_SubtypeDropDownArmor_Initialize()
        local info;
        for i = 1, getn(LLS_SUBTYPE_ARMOR_LIST), 1 do
                info = { };
                info.text = LLS_SUBTYPE_ARMOR_LIST[i].name;
                info.func = LLS_SubtypeDropDown_OnClick;
                UIDropDownMenu_AddButton(info);
        end
end

local function LLS_SubtypeDropDownShield_Initialize()
        local info;
        for i = 1, getn(LLS_SUBTYPE_SHIELD_LIST), 1 do
                info = { };
                info.text = LLS_SUBTYPE_SHIELD_LIST[i].name;
                info.func = LLS_SubtypeDropDown_OnClick;
                UIDropDownMenu_AddButton(info);
        end
end

local function LLS_SubtypeDropDownWeapon_Initialize()
        local info;
        for i = 1, getn(LLS_SUBTYPE_WEAPON_LIST), 1 do
                info = { };
                info.text = LLS_SUBTYPE_WEAPON_LIST[i].name;
                info.func = LLS_SubtypeDropDown_OnClick;
                UIDropDownMenu_AddButton(info);
        end
end

local function LLS_SubtypeDropDownRecipe_Initialize()
        local info;
        for i = 1, getn(LLS_SUBTYPE_RECIPE_LIST), 1 do
                info = { };
                info.text = LLS_SUBTYPE_RECIPE_LIST[i].name;
                info.func = LLS_SubtypeDropDown_OnClick;
                UIDropDownMenu_AddButton(info);
        end
end

local function LLS_SubtypeDropDownContainer_Initialize()
        local info;
        for i = 1, getn(LLS_SUBTYPE_CONTAINER_LIST), 1 do
                info = { };
                info.text = LLS_SUBTYPE_CONTAINER_LIST[i].name;
                info.func = LLS_SubtypeDropDown_OnClick;
                UIDropDownMenu_AddButton(info);
        end
end

local function LootLink_UIDropDownMenu_SetSelectedID(frame, id, names)
        UIDropDownMenu_SetSelectedID(frame, id);
        if( not frame ) then
                frame = this;
        end
        UIDropDownMenu_SetText(names[id].name, frame);
end

local function LootLink_SetupTypeUI(iType, iSubtype)
        local _type = LLS_TYPE_LIST[iType].value;
        
        if( not iSubtype ) then
                iSubtype = 1;
        end

        -- Hide all of the variable labels and fields to start
        getglobal("LLS_SubtypeLabel"):Hide();
        getglobal("LLS_SubtypeDropDown"):Hide();
        getglobal("LLS_MinimumArmorLabel"):Hide();
        getglobal("LLS_MinimumBlockLabel"):Hide();
        getglobal("LLS_MinimumDamageLabel"):Hide();
        getglobal("LLS_MaximumDamageLabel"):Hide();
        getglobal("LLS_MaximumSpeedLabel"):Hide();
        getglobal("LLS_MinimumDPSLabel"):Hide();
        getglobal("LLS_MinimumSlotsLabel"):Hide();
        getglobal("LLS_MinimumSkillLabel"):Hide();
        getglobal("LLS_MaximumSkillLabel"):Hide();
        getglobal("LLS_MinimumArmorEditBox"):Hide();
        getglobal("LLS_MinimumBlockEditBox"):Hide();
        getglobal("LLS_MinimumDamageEditBox"):Hide();
        getglobal("LLS_MaximumDamageEditBox"):Hide();
        getglobal("LLS_MaximumSpeedEditBox"):Hide();
        getglobal("LLS_MinimumDPSEditBox"):Hide();
        getglobal("LLS_MinimumSlotsEditBox"):Hide();
        getglobal("LLS_MinimumSkillEditBox"):Hide();
        getglobal("LLS_MaximumSkillEditBox"):Hide();
        
        if( _type ~= TYPE_OTHER ) then
                local label = getglobal("LLS_SubtypeLabel");
                local dropdown = getglobal("LLS_SubtypeDropDown");
                local initfunc;
                
                -- Show the dropdown and its label
                label:Show();
                dropdown:Show();
                
                if( _type == TYPE_ARMOR ) then
                        label:SetText(LLS_SUBTYPE_ARMOR);
                        initfunc = LLS_SubtypeDropDownArmor_Initialize;
                        
                        getglobal("LLS_MinimumArmorLabel"):Show();
                        getglobal("LLS_MinimumArmorEditBox"):Show();
                elseif( _type == TYPE_SHIELD ) then
                        label:SetText(LLS_SUBTYPE_SHIELD);
                        initfunc = LLS_SubtypeDropDownShield_Initialize;

                        getglobal("LLS_MinimumArmorLabel"):Show();
                        getglobal("LLS_MinimumBlockLabel"):Show();
                        getglobal("LLS_MinimumArmorEditBox"):Show();
                        getglobal("LLS_MinimumBlockEditBox"):Show();
                elseif( _type == TYPE_WEAPON ) then
                        label:SetText(LLS_SUBTYPE_WEAPON);
                        initfunc = LLS_SubtypeDropDownWeapon_Initialize;
                        
                        getglobal("LLS_MinimumDamageLabel"):Show();
                        getglobal("LLS_MaximumDamageLabel"):Show();
                        getglobal("LLS_MaximumSpeedLabel"):Show();
                        getglobal("LLS_MinimumDPSLabel"):Show();
                        getglobal("LLS_MinimumDamageEditBox"):Show();
                        getglobal("LLS_MaximumDamageEditBox"):Show();
                        getglobal("LLS_MaximumSpeedEditBox"):Show();
                        getglobal("LLS_MinimumDPSEditBox"):Show();
                elseif( _type == TYPE_CONTAINER ) then
                        label:SetText(LLS_SUBTYPE_CONTAINER);
                        initfunc = LLS_SubtypeDropDownContainer_Initialize;

                        getglobal("LLS_MinimumSlotsLabel"):Show();
                        getglobal("LLS_MinimumSlotsEditBox"):Show();
                else
                        label:SetText(LLS_SUBTYPE_RECIPE);
                        initfunc = LLS_SubtypeDropDownRecipe_Initialize;

                        getglobal("LLS_MinimumSkillLabel"):Show();
                        getglobal("LLS_MaximumSkillLabel"):Show();
                        getglobal("LLS_MinimumSkillEditBox"):Show();
                        getglobal("LLS_MaximumSkillEditBox"):Show();
                end
                
                UIDropDownMenu_Initialize(dropdown, initfunc);
                UIDropDownMenu_SetSelectedID(LLS_SubtypeDropDown, iSubtype);
        end
        
        LLS_TextEditBox:SetFocus();
end

local function LootLink_InspectSlot(unit, id)
        local link = GetInventoryItemLink(unit, id);
        if( link ) then
                local name = LootLink_ProcessLinks(link, 1);
                if( name and ItemLinks[name] ) then
                        local count = GetInventoryItemCount(unit, id);
                        if( count > 1 ) then
                                ItemLinks[name].x = 1;
                        end
                        LootLink_Event_InspectSlot(name, count, ItemLinks[name], unit, id);
                end
        end
end

function LootLink_Inspect(who)
        if ( not UnitIsPlayer(who) ) then return; end
        if ( who == "mouseover" ) then
                if ( not LLInspect_List ) then
                        LLInspect_List = {};
                end
                local name = UnitName(who);
                local time = GetTime();
                local lasttime = LLInspect_List[name];
                if ( lasttime and time - lasttime < 120 ) then
                        return nil;
                end
                if ( not lasttime ) then
                        LLInspect_List.total = (LLInspect_List.total or 0) + 1;
                end
                if ( LLInspect_List.total and LLInspect_List.total > 250 ) then
                        local list = 0;
                        for k, v in LLInspect_List do
                                list = list + 1;
                                v = nil;
                                if ( list == 100 ) then
                                        break;
                                end
                        end
                end
                                
                LLInspect_List[name] = time;
        end     
                        
        local index;
        
        for index = 1, getn(INVENTORY_SLOT_LIST), 1 do
                LootLink_InspectSlot(who, INVENTORY_SLOT_LIST[index].id)
        end
end

local function LootLink_ScanInventory()
        local bagid;
        local size;
        local slotid;
        local link;
        
        for bagid = 0, 4, 1 do
                size = GetContainerNumSlots(bagid);
                if( size ) then
                        for slotid = size, 1, -1 do
                                link = GetContainerItemLink(bagid, slotid);
                                if( link ) then
                                        local name = LootLink_ProcessLinks(link, 1);
                                        if( name and ItemLinks[name] ) then
                                                local texture, count, locked, quality, readable = GetContainerItemInfo(bagid, slotid);
                                                if( count > 1 ) then
                                                        ItemLinks[name].x = 1;
                                                end
                                                LootLink_Event_ScanInventory(name, count, ItemLinks[name], bagid, slotid);
                                        end
                                end
                        end
                end
        end
end

local function LootLink_ScanSellPrices()
        local bagid;
        local size;
        local slotid;
        local link;
        
        LootLink_MoneyToggle();
        for bagid = 0, 4, 1 do
                lBagID = bagid;
                size = GetContainerNumSlots(bagid);
                if( size ) then
                        for slotid = size, 1, -1 do
                                lSlotID = slotid;
                                LLHiddenTooltip:SetBagItem(bagid, slotid);
                        end
                end
        end
        
        lBagID = nil;
        lSlotID = nil;
        
        LootLink_MoneyToggle(1);
end

local function LootLink_StartAuctionScan()
        -- Start with the first page
        lCurrentAuctionPage = nil;

        -- Hook the functions that we need for the scan
        if( not lOriginal_CanSendAuctionQuery ) then
                lOriginal_CanSendAuctionQuery = CanSendAuctionQuery;
                CanSendAuctionQuery = LootLink_CanSendAuctionQuery;
        end
        if( not lOriginal_AuctionFrameBrowse_OnEvent ) then
                lOriginal_AuctionFrameBrowse_OnEvent = AuctionFrameBrowse_OnEvent;
                AuctionFrameBrowse_OnEvent = LootLink_AuctionFrameBrowse_OnEvent;
        end
        
        LootLink_Event_StartAuctionScan();
end

local function LootLink_StopAuctionScan()
        LootLink_Event_StopAuctionScan();
        
        -- Unhook the scanning functions
        if( lOriginal_CanSendAuctionQuery ) then
                CanSendAuctionQuery = lOriginal_CanSendAuctionQuery;
                lOriginal_CanSendAuctionQuery = nil;
        end
        if( lOriginal_AuctionFrameBrowse_OnEvent ) then
                AuctionFrameBrowse_OnEvent = lOriginal_AuctionFrameBrowse_OnEvent;
                lOriginal_AuctionFrameBrowse_OnEvent = nil;
        end
end

local function LootLink_AuctionNextQuery()
        if( lCurrentAuctionPage ) then
                local numBatchAuctions, totalAuctions = GetNumAuctionItems("list");
                local maxPages = floor(totalAuctions / NUM_AUCTION_ITEMS_PER_PAGE);
                
                if( lCurrentAuctionPage < maxPages ) then
                        lCurrentAuctionPage = lCurrentAuctionPage + 1;
                        BrowseNoResultsText:SetText(format(LOOTLINK_AUCTION_PAGE_N, lCurrentAuctionPage + 1, maxPages + 1));
                else
                        lScanAuction = nil;
                        LootLink_StopAuctionScan();
                        if( totalAuctions > 0 ) then
                                BrowseNoResultsText:SetText(LOOTLINK_AUCTION_SCAN_DONE);
                                LootLink_Event_FinishedAuctionScan();
                        end
                        return;
                end
        else
                lCurrentAuctionPage = 0;
                BrowseNoResultsText:SetText(LOOTLINK_AUCTION_SCAN_START);
        end
        QueryAuctionItems("", "", "", nil, nil, nil, lCurrentAuctionPage, nil, nil);
        LootLink_Event_AuctionQuery(lCurrentAuctionPage);
end

local function LootLink_ScanAuction()
        local numBatchAuctions, totalAuctions = GetNumAuctionItems("list");
        local auctionid;
        local link;

        if( numBatchAuctions > 0 ) then
                for auctionid = 1, numBatchAuctions do
                        link = GetAuctionItemLink("list", auctionid);
                        if( link ) then
                                local name = LootLink_ProcessLinks(link, 1);
                                if( name and ItemLinks[name] ) then
                                        local name_, texture, count, quality, canUse, level, minBid, minIncrement, buyoutPrice, bidAmount, highBidder, owner = GetAuctionItemInfo("list", auctionid);
                                        if( count > 1 ) then
                                                ItemLinks[name].x = 1;
                                        end
                                        LootLink_Event_ScanAuction(name, count, ItemLinks[name], lCurrentAuctionPage, auctionid);
                                end
                        end
                end
        end
end

local function LootLink_ScanBank()
        local index;
        local bagid;
        local size;
        local slotid;
        local link;
        
        for index, bagid in lBankBagIDs do
                size = GetContainerNumSlots(bagid);
                if( size ) then
                        for slotid = size, 1, -1 do
                                link = GetContainerItemLink(bagid, slotid);
                                if( link ) then
                                        local name = LootLink_ProcessLinks(link, 1);
                                        if( name and ItemLinks[name] ) then
                                                local texture, count, locked, quality, readable = GetContainerItemInfo(bagid, slotid);
                                                if( count > 1 ) then
                                                        ItemLinks[name].x = 1;
                                                end
                                                LootLink_Event_ScanBank(name, count, ItemLinks[name], bagid, slotid);
                                        end
                                end
                        end
                end
        end
end

local function LootLink_ScanSellPrice(price)
        local link = GetContainerItemLink(lBagID, lSlotID);
        local texture, itemCount, locked, quality, readable = GetContainerItemInfo(lBagID, lSlotID);
        local name;
        
        if( itemCount and itemCount > 1 ) then
                price = price / itemCount;
        end
        
        name = LootLink_NameFromLink(link);
        if( name and ItemLinks[name] ) then
                ItemLinks[name].p = price;
                if( itemCount and itemCount > 1 ) then
                        ItemLinks[name].x = 1;
                end
        end
end

local function LootLink_CheckTooltipInfo()
        if( GameTooltip:IsVisible() ) then
                -- If we've already added our information or money is already showing, no need to do it again
                if( GameTooltip.llDone or GameTooltipMoneyFrame:IsVisible() ) then
                        return nil;
                end
        
                -- Don't add information to tooltips generated while the mouse is over the minimap
                if( LootLink_MouseIsOver(MinimapCluster) ) then
                        return nil;
                end
        
                local field = getglobal("GameTooltipTextLeft1");
                if( field and field:IsShown() ) then
                        local name = field:GetText();
                        if( name ) then
                                if( ItemLinks[name] ) then
                                        LootLink_AddTooltipInfo(name, GameTooltip);
                                end
                                -- Whether or not we had information for this item, there's no need to check it again
                                return nil;
                        end
                end
        end
        
        return 1;
end

local function LootLink_CheckVersionReminder()
        local index;
        local value;
        local version;
        
        version = LootLink_GetDataVersion();
        for index, value in LOOTLINK_DATA_UPGRADE_HELP do
                if( version < value.version ) then
                        DEFAULT_CHAT_FRAME:AddMessage(value.text);
                end
        end
end

local function LootLink_UpgradeData()
        local index;
        local item;
        
        for index, item in ItemLinks do
                LootLink_ConvertServerFormat(item);
                if( item.item ) then
                        item.i = item.item;
                        item.item = nil;
                end
                if( item.color ) then
                        item.c = item.color;
                        item.color = nil;
                end
                if( item.price ) then
                        item.p = item.price;
                        item.price = nil;
                end
                if( item.stack ) then
                        item.x = item.stack;
                        item.stack = nil;
                end
                
                if( item.SearchData ) then
                        local data = "";

                        if( item.SearchData.type ) then
                                data = data.."ty"..item.SearchData.type.."·";
                        end
                        if( item.SearchData.location ) then
                                data = data.."lo"..LocationTypes[item.SearchData.location].i.."·";
                        end
                        if( item.SearchData.subtype ) then
                                data = data.."su"..item.SearchData.subtype.."·";
                        end
                        if( item.SearchData.binds ) then
                                data = data.."bi"..item.SearchData.binds.."·";
                        end
                        if( item.SearchData.level ) then
                                data = data.."le"..item.SearchData.level.."·";
                        end
                        if( item.SearchData.armor ) then
                                data = data.."ar"..item.SearchData.armor.."·";
                        end
                        if( item.SearchData.minDamage ) then
                                data = data.."mi"..item.SearchData.minDamage.."·";
                        end
                        if( item.SearchData.maxDamage ) then
                                data = data.."ma"..item.SearchData.maxDamage.."·";
                        end
                        if( item.SearchData.speed ) then
                                data = data.."sp"..item.SearchData.speed.."·";
                        end
                        if( item.SearchData.DPS ) then
                                data = data.."dp"..item.SearchData.DPS.."·";
                        end
                        if( item.SearchData.unique ) then
                                data = data.."un"..item.SearchData.unique.."·";
                        end
                        if( item.SearchData.block ) then
                                data = data.."bl"..item.SearchData.block.."·";
                        end
                        if( item.SearchData.slots ) then
                                data = data.."sl"..item.SearchData.slots.."·";
                        end
                        if( item.SearchData.skill ) then
                                data = data.."sk"..item.SearchData.skill.."·";
                        end
                        
                        if( item.SearchData.text ) then
                                item.t = item.SearchData.text;
                        end
                        
                        item.d = data;
                        item.SearchData = nil;
                end
        end
end

local function LootLink_VariablesLoaded()
        local index;
        local value;

        if( not LootLinkState ) then
                LootLinkState = { };
        elseif ( LootLinkState.options ) then
                for k, v in LootLinkState.options do
                        LootLinkState[k] = v;
                end
                LootLinkState.options = nil;
        end
        
        if( not ItemLinks ) then
                LootLink_Reset();
        end
        
        lServer = GetCVar("realmName");
        lServerIndex = LootLink_AddServer(lServer);
        
        LootLink_UpgradeData();
        
        LootLink_InitSizes(lServerIndex);
        
        -- Display and move minimap button --
        if ( not LootLinkState.hideminimap ) then
                LootLinkMinimapButton:Show();
                LootLink_Minimap_Position();
        end
        
        if ( LootLinkState.mouseover ) then     
                this:RegisterEvent("UPDATE_MOUSEOVER_UNIT");
        end
        if ( LootLinkState.sortType ) then
                LootLink_UIDropDownMenu_SetSelectedID(LootLinkFrameDropDown, LootLinkState.sortType, LOOTLINK_DROPDOWN_LIST);
        end
end

--------------------------------------------------------------------------------------------------
-- OnFoo functions
--------------------------------------------------------------------------------------------------
function LootLink_OnLoad()
        local index;
        local value;

        for index, value in ChatMessageTypes do
                this:RegisterEvent(value);
        end
        
        for index = 1, getn(INVENTORY_SLOT_LIST), 1 do
                INVENTORY_SLOT_LIST[index].id = GetInventorySlotInfo(INVENTORY_SLOT_LIST[index].name);
        end
        
        this:RegisterEvent("PLAYER_TARGET_CHANGED");
        this:RegisterEvent("PLAYER_ENTERING_WORLD");
        this:RegisterEvent("PLAYER_LEAVING_WORLD");
        this:RegisterEvent("BANKFRAME_OPENED");
        this:RegisterEvent("VARIABLES_LOADED");
        this:RegisterEvent("AUCTION_HOUSE_SHOW");
        this:RegisterEvent("AUCTION_HOUSE_CLOSE");
        this:RegisterEvent("AUCTION_ITEM_LIST_UPDATE");
        this:RegisterEvent("MERCHANT_SHOW");
        --this:RegisterEvent("PLAYER_LOGOUT");
        this:RegisterEvent("MEMORY_EXHAUSTED");
        this:RegisterEvent("MEMORY_RECOVERED");

        -- Register our slash command
        SLASH_LOOTLINK1 = "/lootlink";
        SLASH_LOOTLINK2 = "/ll";
        SlashCmdList["LOOTLINK"] = function(msg)
                LootLink_SlashCommandHandler(msg);
        end
        
        -- Hook the GameTooltip:SetOwner function
        lOriginal_GameTooltip_SetOwner = GameTooltip.SetOwner;
        GameTooltip.SetOwner = LootLink_GameTooltip_SetOwner;
        
        -- Hook the ContainerFrameItemButton_OnEnter, ContainerFrame_Update and GameTooltip:SetLootItem functions
        lOriginal_ContainerFrameItemButton_OnEnter = ContainerFrameItemButton_OnEnter;
        ContainerFrameItemButton_OnEnter = LootLink_ContainerFrameItemButton_OnEnter;
        lOriginal_ContainerFrame_Update = ContainerFrame_Update;
        ContainerFrame_Update = LootLink_ContainerFrame_Update;
        lOriginal_GameTooltip_SetLootItem = GameTooltip.SetLootItem;
        GameTooltip.SetLootItem = LootLink_GameTooltip_SetLootItem;
        
        -- Hook GameTooltip_OnHide and GameTooltip_ClearMoney
        lOriginal_GameTooltip_OnHide = GameTooltip_OnHide;
        GameTooltip_OnHide = LootLink_GameTooltip_OnHide;
        lOriginal_GameTooltip_ClearMoney = GameTooltip_ClearMoney;
        GameTooltip_ClearMoney = LootLink_GameTooltip_ClearMoney;
        
        -- Hook the shopping tooltip functions
        lOriginal_ShoppingTooltip1_SetMerchantCompareItem = ShoppingTooltip1.SetMerchantCompareItem;
        ShoppingTooltip1.SetMerchantCompareItem = LootLink_ShoppingTooltip1_SetMerchantCompareItem;
        lOriginal_ShoppingTooltip2_SetMerchantCompareItem = ShoppingTooltip2.SetMerchantCompareItem;
        ShoppingTooltip2.SetMerchantCompareItem = LootLink_ShoppingTooltip2_SetMerchantCompareItem;

        lOriginal_ShoppingTooltip1_SetAuctionCompareItem = ShoppingTooltip1.SetAuctionCompareItem;
        ShoppingTooltip1.SetAuctionCompareItem = LootLink_ShoppingTooltip1_SetAuctionCompareItem;
        lOriginal_ShoppingTooltip2_SetAuctionCompareItem = ShoppingTooltip2.SetAuctionCompareItem;
        ShoppingTooltip2.SetAuctionCompareItem = LootLink_ShoppingTooltip2_SetAuctionCompareItem;
        
        -- Hook SetItemRef
        lOriginal_SetItemRef = SetItemRef;
        SetItemRef = LootLink_SetItemRef;
        
        -- Hook our hidden tooltip's OnTooltipAddMoney
        lOriginal_OnTooltipAddMoney = LLHiddenTooltip:GetScript("OnTooltipAddMoney");
        LLHiddenTooltip:SetScript("OnTooltipAddMoney", LootLink_OnTooltipAddMoney);
        
        -- Hook chat edit box
        lOriginal_ChatEdit_OnTabPressed = ChatEdit_OnTabPressed;
        ChatEdit_OnTabPressed = LootLink_ChatEdit_OnTabPressed;
        lOriginal_ChatEdit_OnTextChanged = ChatEdit_OnTextChanged;
        ChatEdit_OnTextChanged = LootLink_ChatEdit_OnTextChanged;
        
        -- Hook CloseWindows
        lOriginal_CloseWindows = CloseWindows;
        CloseWindows = LootLink_CloseWindows;

        if( DEFAULT_CHAT_FRAME ) then
                DEFAULT_CHAT_FRAME:AddMessage(LOOTLINK_LOADED);
        end
        UIErrorsFrame:AddMessage(LOOTLINK_LOADED, 1.0, 1.0, 1.0, 1.0, UIERRORS_HOLD_TIME);
end

function LootLink_Minimap_OnEnter()
        GameTooltip:SetOwner(this, "ANCHOR_BOTTOMLEFT");
        GameTooltip:AddLine(LOOTLINK_TITLE);
        if ( not LootLinkState.lockminimap ) then
                GameTooltip:AddLine("|cffaaaaaa "..LOOTLINK_MINIMAP_DRAG);
        end
        if ( LootLinkFrame.process ) then
                GameTooltip:AddLine("|cffaaaaaa "..getglobal("LLP_"..LootLinkFrame.process.."_LABEL"));
                if ( LootLink_Process:IsVisible() ) then
                        GameTooltip:AddLine("|cffaaaaaa "..LOOTLINK_MINIMAP_PROCESS_SHOW);
                else
                        GameTooltip:AddLine("|cffaaaaaa "..LOOTLINK_MINIMAP_PROCESS_HIDE);
                end
        end
        GameTooltip:Show();
end

function LootLink_Minimap_Position(x,y)
        if ( x or y ) then
                if ( x ) then if ( x < 0 ) then x = x + 360; end LootLinkState.x = x; end
                if ( y ) then LootLinkState.y = y; end
        end
        x, y = LootLinkState.x, LootLinkState.y

        if ( LootLinkOptionsFrame:IsVisible() ) then
                LLO_MinimapPos:SetValue( x or LLO_MINIMAP_X );
                LLO_MinimapOffset:SetValue( y or LLO_MINIMAP_Y );
        end
        LootLinkMinimapButton:SetPoint("TOPLEFT","Minimap","TOPLEFT",53-((80+(y or LLO_MINIMAP_Y))*cos(x or LLO_MINIMAP_X)),((80+(y or LLO_MINIMAP_Y))*sin(x or LLO_MINIMAP_X))-55);
end

function LootLink_Minimap_DragStart()
        if ( LootLinkState.lockminimap ) then return; end
        this:SetScript("OnUpdate", LootLink_Minimap_DragUpdate);
end
function LootLink_Minimap_DragStop()
        LootLinkMinimapButton:UnlockHighlight()
        this:SetScript("OnUpdate", nil);
end
function LootLink_Minimap_DragUpdate()
        -- Thanks to Gello for making this a ton shorter
        LootLinkMinimapButton:LockHighlight();
        local curX, curY = GetCursorPosition();
        local mapX, mapY = Minimap:GetCenter();
        local x, y;
        if ( IsShiftKeyDown() ) then
                y = math.pow( math.pow(curY - mapY * Minimap:GetEffectiveScale(), 2) + math.pow(mapX * Minimap:GetEffectiveScale() - curX, 2), 0.5) - 70;
                y = min( max( y, -30 ), 30 );
        end
        x = math.deg(math.atan2( curY - mapY * Minimap:GetEffectiveScale(), mapX * Minimap:GetEffectiveScale() - curX ));
        LootLink_Minimap_Position(x,y);
end

function LootLink_CanSendAuctionQuery()
        local value = lOriginal_CanSendAuctionQuery();
        if( value ) then
                LootLink_AuctionNextQuery();
                return nil;
        end
        return value;
end

function LootLink_AuctionFrameBrowse_OnEvent()
        -- Intentionally empty; don't allow the auction UI to update while we're scanning
end

function LootLink_GameTooltip_SetOwner(this, owner, anchor)
        lOriginal_GameTooltip_SetOwner(this, owner, anchor);
        lGameToolTipOwner = owner;
end

function LootLink_GameTooltip_ClearMoney()
        lOriginal_GameTooltip_ClearMoney();
        if( not lSuppressInfoAdd ) then
                lCheckTooltip = LootLink_CheckTooltipInfo();
        end
end

function LootLink_GameTooltip_ClearMoney_Temp()
        -- Intentionally empty; don't clear money while we use our hidden tooltip
end

function LootLink_ContainerFrameItemButton_OnEnter()
        LootLink_AutoInfoOff();
        lOriginal_ContainerFrameItemButton_OnEnter();
        if( not InRepairMode() and not MerchantFrame:IsVisible() ) then
                local frameID = this:GetParent():GetID();
                local buttonID = this:GetID();
                local link = GetContainerItemLink(frameID, buttonID);
                local name = LootLink_NameFromLink(link);
                
                if( name and ItemLinks[name] ) then
                        local texture, itemCount, locked, quality, readable = GetContainerItemInfo(frameID, buttonID);
                        LootLink_AddTooltipInfo(name, GameTooltip, itemCount);
                        GameTooltip:Show();
                end
        end
        LootLink_AutoInfoOn();
end

function LootLink_ContainerFrame_Update(frame)
        LootLink_AutoInfoOff();
        lOriginal_ContainerFrame_Update(frame);
        if( not InRepairMode() and not MerchantFrame:IsVisible() and GameTooltip:IsVisible() ) then
                local frameID = frame:GetID();
                local frameName = frame:GetName();
                local iButton;
                for iButton = 1, frame.size do
                        local button = getglobal(frameName.."Item"..iButton);
                        if( GameTooltip:IsOwned(button) ) then
                                local buttonID = button:GetID();
                                local link = GetContainerItemLink(frameID, buttonID);
                                local name = LootLink_NameFromLink(link);
                                
                                if( name and ItemLinks[name] ) then
                                        local texture, itemCount, locked, quality, readable = GetContainerItemInfo(frameID, buttonID);
                                        LootLink_AddTooltipInfo(name, GameTooltip, itemCount);
                                        GameTooltip:Show();
                                end
                        end
                end
        end
        LootLink_AutoInfoOn();
end

function LootLink_GameTooltip_OnHide()
        lOriginal_GameTooltip_OnHide();
        GameTooltip.llDone = nil;
        lCheckTooltip = nil;
end

function LootLink_GameTooltip_SetLootItem(this, slot)
        LootLink_AutoInfoOff();
        lOriginal_GameTooltip_SetLootItem(this, slot);
        
        local link = GetLootSlotLink(slot);
        local name = LootLink_NameFromLink(link);
        if( name and ItemLinks[name] and ItemLinks[name].p ) then
                local texture, item, quantity, quality = GetLootSlotInfo(slot);
                LootLink_AddTooltipInfo(name, GameTooltip, quantity);
        end
        LootLink_AutoInfoOn();
end

function LootLink_ShoppingTooltip1_SetMerchantCompareItem(this, id, num)
        LootLink_MoneyToggle();
        local retval = lOriginal_ShoppingTooltip1_SetMerchantCompareItem(this, id, num);
        LootLink_MoneyToggle(1);
        return retval;
end

function LootLink_ShoppingTooltip2_SetMerchantCompareItem(this, id, num)
        LootLink_MoneyToggle();
        local retval = lOriginal_ShoppingTooltip2_SetMerchantCompareItem(this, id, num);
        LootLink_MoneyToggle(1);
        return retval;
end

function LootLink_ShoppingTooltip1_SetAuctionCompareItem(this, _type, index, num)
        LootLink_MoneyToggle();
        local retval = lOriginal_ShoppingTooltip1_SetAuctionCompareItem(this, _type, index, num);
        LootLink_MoneyToggle(1);
        return retval;
end

function LootLink_ShoppingTooltip2_SetAuctionCompareItem(this, _type, index, num)
        LootLink_MoneyToggle();
        local retval = lOriginal_ShoppingTooltip2_SetAuctionCompareItem(this, _type, index, num);
        LootLink_MoneyToggle(1);
        return retval;
end

function LootLink_SetItemRef(link, text, button)
        lOriginal_SetItemRef(link, text, button);

        -- If this is a tooltip-creating link, add our information to it
        if( strsub(link, 1, 6) ~= "Player" ) then
                ItemRefTooltipMoneyFrame:SetPoint("LEFT", "ItemRefTooltipTextLeft1", "LEFT", 0, 0);
                ItemRefTooltipMoneyFrame:Hide();

                if( ItemRefTooltip:IsVisible() ) then
                        local field = getglobal("ItemRefTooltipTextLeft1");
                        if( field and field:IsShown() ) then
                                local name = field:GetText();
                                if( name and ItemLinks[name] ) then
                                        LootLink_AddTooltipInfo(name, ItemRefTooltip);
                                end
                        end
                end
        end
end

function LootLink_OnTooltipAddMoney()
        lOriginal_OnTooltipAddMoney();
        if( lBagID and lSlotID ) then
                LootLink_ScanSellPrice(arg1);
        end
end

function LootLink_OnEvent()
        if( event == "PLAYER_TARGET_CHANGED" ) then
                if( UnitIsUnit("target", "player") ) then
                        return;
                elseif( UnitIsPlayer("target") ) then
                        LootLink_Inspect("target");
                end
        elseif( event == "UPDATE_MOUSEOVER_UNIT" ) then
                LootLink_Inspect("mouseover");
        elseif( event == "PLAYER_ENTERING_WORLD" ) then
                this:RegisterEvent("UNIT_INVENTORY_CHANGED");
                -- Now that we're in the world, we want to watch for inventory changes
                this:RegisterEvent("UNIT_INVENTORY_CHANGED");

                -- Check to see if the user needs to upgrade their database
                LootLink_CheckVersionReminder();
                
                -- Do initial scan of inventory and equipped items
                LootLink_ScanInventory();
                LootLink_Inspect("player");
        elseif( event == "PLAYER_LEAVING_WORLD" ) then
                -- When we leave the world, we don't need to watch for inventory changes,
                -- especially since zoning will cause one UNIT_INVENTORY_CHANGED event for
                -- each item in the player's inventory and that they have equipped
                this:UnregisterEvent("UNIT_INVENTORY_CHANGED");
        elseif( event == "UNIT_INVENTORY_CHANGED" ) then
                if( arg1 == "player" ) then
                        LootLink_ScanInventory();
                        LootLink_Inspect("player");
                end
        elseif( event == "BANKFRAME_OPENED" ) then
                LootLink_ScanBank();
        elseif( event == "VARIABLES_LOADED" ) then
                LootLink_VariablesLoaded();
        elseif( event == "AUCTION_HOUSE_SHOW" ) then
                if( lScanAuction ) then
                        LootLink_StartAuctionScan();
                end
        elseif( event == "AUCTION_HOUSE_CLOSED" ) then
                LootLink_StopAuctionScan();
        elseif( event == "AUCTION_ITEM_LIST_UPDATE" ) then
                LootLink_ScanAuction();
        elseif( event == "MERCHANT_SHOW" ) then
                LootLink_ScanSellPrices();
        --elseif( event == "PLAYER_LOGOUT" ) then
                --LootLink_ClearDelayed();
        elseif( event == "MEMORY_EXHAUSTED" ) then
                LLP_OUTOFMEM = 1;
        elseif( event == "MEMORY_RECOVERED" ) then
                LLP_OUTOFMEM = nil;
        else
                local name = LootLink_ProcessLinks(arg1, event == "CHAT_MSG_LOOT");
                if( name and ItemLinks[name] ) then
                        LootLink_Event_ScanChat(name, ItemLinks[name], arg1);
                end
        end
end

function LootLink_RemoveItem(name, link, displayindex, index)
        local value = ItemLinks[name];
        if ( not value ) then
                return;
        end
        local found;
        if ( link and string.sub(link, 1, 5) == "item:" ) then
                link = string.sub( link, 6 );
        end
        if ( displayindex and not link and not index ) then
                link = DisplayIndices[displayindex][2].i;
        end
        if ( not link and not index ) then
                index = 0;
        end
        if ( link or index ) then
                if ( (index and index == 0) or (value.i and value.i == link) ) then
                        found = 1;
                        if ( value.m and value.m[1] ) then
                                for k, v in value.m[1] do
                                        value[k] = v;
                                end
                                tremove( value.m, 1 );

                                if ( getn(value.m) == 0 ) then
                                        value.m = nil;
                                end
                        else
                                ItemLinks[name] = nil;
                                di = 0;
                        end
                else
                        if ( value.m ) then
                                for i=1, getn(value.m) do
                                        if ( (index and index == i) or (value.m[i].i == link) ) then
                                                found = 1;
                                                tremove( value.m, i );
                                                break;
                                        end
                                end
                        end
                end
                
                if ( found ) then
                        LootLink_SortAlternates( name );
                
                        if ( DisplayIndices and LootLinkFrame:IsVisible() ) then
                                local dName, start;
                                local i = 1;
                                if ( DisplayIndices[i] ) then
                                        dName = DisplayIndices[i][1];
                                end
                                while dName do
                                        if dName == name then
                                                if not start then
                                                        start = i;
                                                end
                                        elseif start then
                                                break;
                                        end
                                        i = i+1;
                                        if ( DisplayIndices[i] ) then
                                                dName = DisplayIndices[i][1];
                                        else
                                                dName = nil;
                                        end
                                end
                                if ( start and i ) then
                                        tremove(DisplayIndices, i-1);
                                        DisplayIndices.onePastEnd = DisplayIndices.onePastEnd - 1;
                                        if ( displayindex ) then
                                                LootLink_Update();
                                                LootLink_SetTitle();
                                        end
                                end
                        else
                                DisplayIndices = nil;
                        end
                        return 1;
                end
        end
end

function LootLinkItemButton_OnClick(button)
        local name = this:GetText();
        local index = this.i;
        if( button == "LeftButton" ) then
                local data = DisplayIndices[this:GetID()+FauxScrollFrame_GetOffset(LootLinkListScrollFrame)];
                if( IsShiftKeyDown() and ChatFrameEditBox:IsVisible() ) then
                        if ( not LootLink_CheckItemServer(data[2], lServerIndex) ) then
                                local dialog = StaticPopup_Show("LOOTLINK_LINK_CONFIRM", LOOTLINK_STATICPOPUP_SERVER, LOOTLINK_STATICPOPUP_LINKITEM);
                                if ( dialog ) then
                                        dialog.data = data;
                                end
                        elseif ( not GetItemInfo("item:"..data[2].i) ) then
                                local dialog = StaticPopup_Show("LOOTLINK_LINK_CONFIRM", LOOTLINK_STATICPOPUP_INVALID, LOOTLINK_STATICPOPUP_LINKITEM);
                                if ( dialog ) then
                                        dialog.data = data;
                                end
                        else                    
                                ChatFrameEditBox:Insert(LootLink_GetLink(name, data[3]));
                        end
                end
                if( IsControlKeyDown() ) then
                        if ( not LootLink_CheckItemServer(data[2], lServerIndex) ) then
                                local dialog = StaticPopup_Show("LOOTLINK_DRESSUP_CONFIRM", LOOTLINK_STATICPOPUP_SERVER, LOOTLINK_STATICPOPUP_DRESSUP);
                                if ( dialog ) then
                                        dialog.data = data;
                                end
                        elseif ( not GetItemInfo("item:"..data[2].i) ) then
                                local dialog = StaticPopup_Show("LOOTLINK_DRESSUP_CONFIRM", LOOTLINK_STATICPOPUP_INVALID, LOOTLINK_STATICPOPUP_DRESSUP);
                                if ( dialog ) then
                                        dialog.data = data;
                                end
                        else                    
                                DressUpItemLink(LootLink_GetLink(name, data[3]));
                        end
                end
        elseif( button == "RightButton" ) then
                if( IsShiftKeyDown() and IsControlKeyDown() ) then
                        local data = DisplayIndices[this:GetID()+FauxScrollFrame_GetOffset(LootLinkListScrollFrame)];
                        local dialog = StaticPopup_Show("LOOTLINK_DELETEITEM_CONFIRM", data[2].c, name);
                        if ( dialog ) then
                                dialog.data = data;
                        end
                        --if ( LootLink_RemoveItem(name, nil, this:GetID()+FauxScrollFrame_GetOffset(LootLinkListScrollFrame)) ) then
                        --      LootLink_SortAlternates(name);
                        --end
                end
        end
        --@todo Telo: add a method to correct the color of items
end

function LootLink_OnShow()
        PlaySound("igMainMenuOpen");
        LootLink_Update();
end

function LootLink_OnHide()
        PlaySound("igMainMenuClose");
end

function LootLink_ClearDelayed()
        if ( LootLinkFrame.schedule ) then
                for k, v in LootLinkFrame.schedule do
                        local value, index = LootLink_GetValue( v[1], nil, v[2] );
                        if ( v and not v.t ) then
                                if ( LootLink_RemoveItem(v[1], v[2]) ) then
                                        LootLink_SortAlternates(v[1]);
                                end
                        end
                end
        end
        LootLinkFrame.schedule = nil;
end

function LootLink_ScheduleItem(name,link,time)
        if ( not LootLinkFrame.schedule ) then
                LootLinkFrame.schedule = {};
        end
        for k, v in LootLinkFrame.schedule do
                if ( v[1] == name and v[2] == link ) then
                        return;
                end
        end
        LLHiddenTooltip:SetHyperlink("item:"..link);
        tinsert( LootLinkFrame.schedule, {name,link,(time or LOOTLINK_SCHEDULE_DELAY)} );
end
        
function LootLink_OnUpdate(elapsed)
        if( lCheckTooltip ) then
                lCheckTooltip = LootLink_CheckTooltipInfo();
        end
        if ( LootLinkFrame.process ) then
                LootLinkProcess_Run();
        end
        local list = LootLinkFrame.schedule;
        if ( list ) then
                for k, v in list do
                        v[3] = v[3] - elapsed;
                        if ( v[3] < 0 ) then
                                local value, index = LootLink_GetValue( v[1], nil, v[2] );
                                if ( not value ) then
                                        -- Item was remove from ItemLinks table, stop trying to check for it.
                                        tremove( list, k );
                                        return; 
                                elseif ( LootLink_BuildSearchData( v[1], value, index ) ) then
                                        tremove( list, k );
                                        return;
                                else
                                        -- Still don't have information from server.
                                        if ( v[4] and v[4] == 60 ) then
                                                -- we've tried long enough, fail this item;
                                                if ( LootLink_RemoveItem(v[1], v[2]) ) then
                                                        LootLink_SortAlternates(name);
                                                end
                                                tremove( list, k );
                                                return;
                                        end
                                        v[4] = (v[4] or 0) + 1;
                                        v[3] = LOOTLINK_SCHEDULE_DELAY;
                                end
                        end
                end
                if ( getn(list) == 0 ) then
                        list = nil;
                end
        end
end

function LootLinkItemButton_OnEnter()
        local link = LootLink_GetHyperlink(this:GetText(), this.i);
        if( link ) then
                LootLinkFrame.TooltipButton = this:GetID();
                LootLinkTooltip:SetOwner(this, "ANCHOR_BOTTOMRIGHT");
                if ( LootLink_SetHyperlink(LootLinkTooltip, this:GetText(), link, this.i) ) then
                        LootLink_Update();
                end
                LootLink_AddTooltipTexture(link);
                LootLink_AddTooltipInfo(this:GetText(), nil, nil, this.i);
        end
end

function LootLinkItemButton_OnLeave()
        if( LootLinkFrame.TooltipButton ) then
                LootLinkFrame.TooltipButton = nil;
                HideUIPanel(LootLinkTooltip);
        end
        LootLink_AddTooltipTexture();
end

function LootLinkFrameDropDown_OnLoad()
        UIDropDownMenu_Initialize(LootLinkFrameDropDown, LootLinkFrameDropDown_Initialize);
        LootLink_UIDropDownMenu_SetSelectedID(LootLinkFrameDropDown, 1, LOOTLINK_DROPDOWN_LIST);
        UIDropDownMenu_SetWidth(80);
        UIDropDownMenu_SetButtonWidth(24);
        UIDropDownMenu_JustifyText("LEFT", LootLinkFrameDropDown)
end

function LootLinkFrameDropDownButton_OnClick()
        local oldID = UIDropDownMenu_GetSelectedID(LootLinkFrameDropDown);
        UIDropDownMenu_SetSelectedID(LootLinkFrameDropDown, this:GetID());
        if( oldID ~= this:GetID() ) then
                LootLink_Refresh();
        end
        LootLinkState.sortType = this:GetID();
end

function LLS_RarityDropDown_OnLoad()
        UIDropDownMenu_SetWidth(90);
        UIDropDownMenu_SetButtonWidth(24);
        UIDropDownMenu_JustifyText("LEFT", LLS_RarityDropDown);
end

function LLS_RarityDropDown_OnClick()
        UIDropDownMenu_SetSelectedID(LLS_RarityDropDown, this:GetID());
end

function LLS_BindsDropDown_OnLoad()
        UIDropDownMenu_SetWidth(90);
        UIDropDownMenu_SetButtonWidth(24);
        UIDropDownMenu_JustifyText("LEFT", LLS_BindsDropDown);
end

function LLS_BindsDropDown_OnClick()
        UIDropDownMenu_SetSelectedID(LLS_BindsDropDown, this:GetID());
end

function LLS_LocationDropDown_OnLoad()
        UIDropDownMenu_SetWidth(90);
        UIDropDownMenu_SetButtonWidth(24);
        UIDropDownMenu_JustifyText("LEFT", LLS_LocationDropDown);
end

function LLS_LocationDropDown_OnClick()
        UIDropDownMenu_SetSelectedID(LLS_LocationDropDown, this:GetID());
end

function LLS_TypeDropDown_OnLoad()
        UIDropDownMenu_SetWidth(90);
        UIDropDownMenu_SetButtonWidth(24);
        UIDropDownMenu_JustifyText("LEFT", LLS_TypeDropDown);
end

function LLS_TypeDropDown_OnClick()
        local oldID = UIDropDownMenu_GetSelectedID(LLS_TypeDropDown);
        UIDropDownMenu_SetSelectedID(LLS_TypeDropDown, this:GetID());
        if( oldID ~= this:GetID() ) then
                LootLink_SetupTypeUI(this:GetID(), 1);
        end
end

function LLS_SubtypeDropDown_OnLoad()
        UIDropDownMenu_SetWidth(90);
        UIDropDownMenu_SetButtonWidth(24);
        UIDropDownMenu_JustifyText("LEFT", LLS_SubtypeDropDown);
end

function LLS_SubtypeDropDown_OnClick()
        UIDropDownMenu_SetSelectedID(LLS_SubtypeDropDown, this:GetID());
end

function LLO_RarityDropDown_OnLoad()
        UIDropDownMenu_SetWidth(90);
        UIDropDownMenu_SetButtonWidth(24);
        UIDropDownMenu_JustifyText("LEFT", LLO_RarityDropDown);
end

function LLO_RarityDropDown_OnClick()
        UIDropDownMenu_SetSelectedID(LLO_RarityDropDown, this:GetID());
end

--------------------------------------------------------------------------------------------------
-- Callback functions
--------------------------------------------------------------------------------------------------
function ToggleLootLink()
        if( LootLinkFrame:IsVisible() ) then
                HideUIPanel(LootLinkFrame);
        else
                ShowUIPanel(LootLinkFrame);
        end
end

function LootLink_SlashCommandHandler(msg)
        local reset;
        local makehome;
        local light;
        local aborted;

        if( not lDisableVersionReminder ) then
                LootLink_CheckVersionReminder();
        end
        if( not msg or msg == "" ) then
                ToggleLootLink();
        else
                local command = string.lower(msg);
                if( command == LOOTLINK_HELP ) then
                        local index = 0;
                        local value = getglobal("LOOTLINK_HELP_TEXT"..index);
                        while( value ) do
                                DEFAULT_CHAT_FRAME:AddMessage(value);
                                index = index + 1;
                                value = getglobal("LOOTLINK_HELP_TEXT"..index);
                        end
                elseif( command == LOOTLINK_STATUS ) then
                        LootLink_Status();
                elseif( command == LOOTLINK_AUCTION or command == LOOTLINK_SCAN ) then
                        if( AuctionFrame and AuctionFrame:IsVisible() ) then
                                local iButton;
                                local button;

                                -- Hide the UI from any current results, show the no results text so we can use it
                                BrowseNoResultsText:Show();
                                for iButton = 1, NUM_BROWSE_TO_DISPLAY do
                                        button = getglobal("BrowseButton"..iButton);
                                        button:Hide();
                                end
                                BrowsePrevPageButton:Hide();
                                BrowseNextPageButton:Hide();
                                BrowseSearchCountText:Hide();

                                LootLink_StartAuctionScan();
                        else
                                lScanAuction = 1;
                                LootLink_Status();
                        end
                elseif( command == LOOTLINK_SHOWINFO ) then
                        LootLinkState.HideInfo = nil;
                        LootLink_Status();
                elseif( command == LOOTLINK_HIDEINFO ) then
                        LootLinkState.HideInfo = 1;
                        LootLink_Status();
                elseif( command == LOOTLINK_FULLMODE ) then
                        LootLinkState.LightMode = nil;
                        LootLink_Status();
                else
                        local iStart;
                        local iEnd;
                        local args;
                        
                        iStart, iEnd, command, args = string.find(command, "^(%w+)%s*(.*)$");
        
                        if( command == LOOTLINK_RESET ) then
                                if( lResetNeedsConfirm ) then
                                        if( args == LOOTLINK_CONFIRM ) then
                                                LootLink_Reset();
                                                lResetNeedsConfirm = nil;
                                                lDisableVersionReminder = nil
                                                DEFAULT_CHAT_FRAME:AddMessage(LOOTLINK_RESET_DONE);
                                        end
                                else
                                        reset = 1;
                                        lResetNeedsConfirm = 1;
                                        lDisableVersionReminder = 1;
                                end
                        elseif( LootLink_GetDataVersion() < 110 and command == LOOTLINK_MAKEHOME ) then
                                if( lMakeHomeNeedsConfirm ) then
                                        if( args == LOOTLINK_CONFIRM ) then
                                                LootLink_MakeHome();
                                                lMakeHomeNeedsConfirm = nil;
                                                lDisableVersionReminder = nil;
                                                DEFAULT_CHAT_FRAME:AddMessage(LOOTLINK_MAKEHOME_DONE);
                                        end
                                else
                                        makehome = 1;
                                        lMakeHomeNeedsConfirm = 1;
                                        lDisableVersionReminder = 1;
                                end
                        elseif( command == LOOTLINK_LIGHTMODE ) then
                                if( lLightModeNeedsConfirm ) then
                                        if( args == LOOTLINK_CONFIRM ) then
                                                LootLink_LightMode();
                                                lLightModeNeedsConfirm = nil;
                                                DEFAULT_CHAT_FRAME:AddMessage(LOOTLINK_LIGHTMODE_DONE);
                                        end
                                else
                                        light = 1;
                                        lLightModeNeedsConfirm = 1;
                                end
                        end
                end
        end
        
        if( not reset ) then
                if( lResetNeedsConfirm ) then
                        aborted = 1;
                        lResetNeedsConfirm = nil;
                        DEFAULT_CHAT_FRAME:AddMessage(LOOTLINK_RESET_ABORTED);
                end
        end
        
        if( not makehome ) then
                if( lMakeHomeNeedsConfirm ) then
                        aborted = 1;
                        lMakeHomeNeedsConfirm = nil;
                        DEFAULT_CHAT_FRAME:AddMessage(LOOTLINK_MAKEHOME_ABORTED);
                end
        end
        
        if( not light ) then
                if( lLightModeNeedsConfirm ) then
                        lLightModeNeedsConfirm = nil;
                        DEFAULT_CHAT_FRAME:AddMessage(LOOTLINK_LIGHTMODE_ABORTED);
                end
        end
        
        if( reset ) then
                DEFAULT_CHAT_FRAME:AddMessage(LOOTLINK_RESET_NEEDS_CONFIRM);
        end
        
        if( makehome ) then
                DEFAULT_CHAT_FRAME:AddMessage(LOOTLINK_MAKEHOME_NEEDS_CONFIRM);
        end
        
        if( light ) then
                DEFAULT_CHAT_FRAME:AddMessage(LOOTLINK_LIGHTMODE_NEEDS_CONFIRM);
        end
        
        if( aborted and not reset and not makehome ) then
                lDisableVersionReminder = nil
        end
end

function LootLink_Update()
        local iItem;
        
        if( not DisplayIndices ) then
                LootLink_BuildDisplayIndices();
        end
        FauxScrollFrame_Update(LootLinkListScrollFrame, DisplayIndices.onePastEnd - 1, LOOTLINK_ITEMS_SHOWN, LOOTLINK_ITEM_HEIGHT, nil, nil, nil, LootLinkHighlightFrame, 293, 316);
        LootLinkHighlightFrame:Hide();
        for iItem = 1, LOOTLINK_ITEMS_SHOWN, 1 do
                local itemIndex = iItem + FauxScrollFrame_GetOffset(LootLinkListScrollFrame);
                local lootLinkItem = getglobal("LootLinkItem"..iItem);
                
                if( itemIndex < DisplayIndices.onePastEnd ) then
                        local value = DisplayIndices[itemIndex][2]; --LootLink_GetValue(DisplayIndices[itemIndex]);
                        if ( not value ) then
                                LootLink_Refresh();
                                return;
                        end
                        if ( value ) then               
                                local color = { };
                                local name = DisplayIndices[itemIndex][1]; --LootLink_GetName(DisplayIndices[itemIndex]);
                                local index = DisplayIndices[itemIndex][3];
                                local extra = getglobal(lootLinkItem:GetName().."Type");
                                lootLinkItem.i = nil;
                                local text = getglobal(lootLinkItem:GetName().."Text");
                                local indexed = getglobal(lootLinkItem:GetName().."Indexed");
                                text:SetPoint( "LEFT",  20, 0 );
                                text:SetWidth(275);
                                getglobal(lootLinkItem:GetName().."Indexed"):Hide();
                                if ( index and index > 0 ) then
                                        lootLinkItem.i = index;
                                        if ( DisplayIndices[itemIndex-1] and DisplayIndices[itemIndex-1][1] == name ) then
                                                local _,_, enchant, bonus = string.find(value.i, "%d+:(%d+):(%d+):%d+");
                                                bonus = tonumber(bonus);
                                                enchant = tonumber(enchant);
                                                if ( bonus > 0 or enchant > 0 ) then
                                                        text:SetPoint( "LEFT",  30, 0 );
                                                        text:SetWidth(245);
                                                        indexed:Show();
                                                end
                                                if ( enchant > 0 ) then
                                                        indexed:SetVertexColor(0,1,0);
                                                else
                                                        indexed:SetVertexColor(1,1,1);
                                                end
                                        end
                                end
                                local link = "item:"..value.i;
                                local realname
                                if ( link ) then
                                        realname = GetItemInfo( link );
                                end
                                local onserver = LootLink_CheckItemServer(value, lServerIndex);
                                extra:SetVertexColor(1.0,1.0,1.0)
                                SetDesaturation(extra,0);
                                extra:SetAlpha(1.0);
                                if ( not onserver ) then
                                        extra:SetTexture("Interface\\GossipFrame\\TaxiGossipIcon");
                                        extra:Show();
                                elseif ( not realname ) then
                                        extra:SetTexture("Interface\\GossipFrame\\ActiveQuestIcon");
                                        extra:SetAlpha(0.5);
                                        SetDesaturation(extra,1);
                                        extra:Show();
                                elseif ( not LootLink_ValidateItem(name, link) ) then
                                        extra:SetTexture("Interface\\GossipFrame\\AvailableQuestIcon");
                                        extra:SetVertexColor(1.0,0,0)
                                        extra:Show();
                                else
                                        extra:Hide();
                                end

                                lootLinkItem:SetText(name);
                                if( value.c ) then
                                        color.r, color.g, color.b = LootLink_GetRGBFromHexColor(value.c);
                                        lootLinkItem:SetTextColor(color.r, color.g, color.b);
                                        lootLinkItem.r = color.r;
                                        lootLinkItem.g = color.g;
                                        lootLinkItem.b = color.b;
                                else
                                        lootLinkItem.r = 0;
                                        lootLinkItem.g = 0;
                                        lootLinkItem.b = 0;
                                end
                                if ( LootLinkFrame.qs and LootLinkFrame.qs == itemIndex ) then
                                        if ( color ) then
                                                LootLinkHighlight:SetVertexColor(color.r, color.g, color.b);
                                        else
                                                LootLinkHighlight:SetVertexColor(0.5, 0.5, 0.5);
                                        end
                                        lootLinkItem:SetTextColor(HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b);
                                        LootLinkHighlightFrame:SetPoint("TOPLEFT", "LootLinkItem"..iItem, "TOPLEFT", 0, 0);
                                        LootLinkHighlightFrame:Show();
                                end
                                lootLinkItem:Show();

                                if( LootLinkFrame.TooltipButton and LootLinkFrame.TooltipButton == iItem ) then
                                        if( value.i ) then
                                                local link = LootLink_GetHyperlink(name, index);                                
                                                LootLinkTooltip:SetOwner(lootLinkItem, "ANCHOR_BOTTOMRIGHT");
                                                LootLink_SetHyperlink(LootLinkTooltip, name, link, index);
                                                LootLink_AddTooltipInfo(name);
                                                LootLink_AddTooltipTexture(link);
                                                LootLinkTooltip:Show();
                                        else
                                                LootLinkItemButton_OnLeave();
                                        end
                                end
                        end
                else
                        lootLinkItem:Hide();
                end
        end
end

function LootLink_Search()
        LootLinkSearchFrame:Show();
end

function LootLink_Refresh()
        FauxScrollFrame_SetOffset(LootLinkListScrollFrame, 0);
        getglobal("LootLinkListScrollFrameScrollBar"):SetValue(0);
        LootLink_BuildDisplayIndices();
        LootLink_Update();
end

function LootLink_DoSearch()
        LootLink_Refresh();
end

function LootLinkSearch_LoadValues()
        local sp = LootLinkFrame.SearchParams;
        local field;
        
        if( LootLinkState.LightMode ) then
                getglobal("LLS_TextDisabled"):Show();
                getglobal("LLS_TextEditBox"):Hide();
                getglobal("LLS_NameEditBox"):SetFocus();
        else
                getglobal("LLS_TextDisabled"):Hide();
                field = getglobal("LLS_TextEditBox");
                field:Show();
                field:SetFocus();
                if( sp and sp.text ) then
                        field:SetText(sp.text);
                else
                        field:SetText("");
                end
        end
        
        field = getglobal("LLS_NameEditBox");
        if( sp and sp.name ) then
                field:SetText(sp.name);
        else
                field:SetText("");
        end
        
        UIDropDownMenu_Initialize(LLS_RarityDropDown, LLS_RarityDropDown_Initialize);
        if( sp and sp.rarity ) then
                LootLink_UIDropDownMenu_SetSelectedID(LLS_RarityDropDown, sp.rarity, LLS_RARITY_LIST);
        else
                LootLink_UIDropDownMenu_SetSelectedID(LLS_RarityDropDown, 1, LLS_RARITY_LIST);
        end
        
        if( sp and sp.server ) then
                getglobal("LLS_ServerCheckButton"):SetChecked(1);
        else
                getglobal("LLS_ServerCheckButton"):SetChecked(0);
        end
        
        UIDropDownMenu_Initialize(LLS_BindsDropDown, LLS_BindsDropDown_Initialize);
        if( sp and sp.binds ) then
                LootLink_UIDropDownMenu_SetSelectedID(LLS_BindsDropDown, sp.binds, LLS_BINDS_LIST);
        else
                LootLink_UIDropDownMenu_SetSelectedID(LLS_BindsDropDown, 1, LLS_BINDS_LIST);
        end
        
        if( sp and sp.unique ) then
                getglobal("LLS_UniqueCheckButton"):SetChecked(1);
        else
                getglobal("LLS_UniqueCheckButton"):SetChecked(0);
        end
        
        UIDropDownMenu_Initialize(LLS_LocationDropDown, LLS_LocationDropDown_Initialize);
        if( sp and sp.location ) then
                LootLink_UIDropDownMenu_SetSelectedID(LLS_LocationDropDown, sp.location, LLS_LOCATION_LIST);
        else
                LootLink_UIDropDownMenu_SetSelectedID(LLS_LocationDropDown, 1, LLS_LOCATION_LIST);
        end
        
        if( LootLinkState and LootLinkState.spoof ) then
                LLS_SpoofCheckButton:Hide();
        else
                LLS_SpoofCheckButton:Show();
                if( sp and sp.spoof ) then
                        getglobal("LLS_SpoofCheckButton"):SetChecked(1);
                else
                        getglobal("LLS_SpoofCheckButton"):SetChecked(0);
                end
        end
        
        if( sp and sp.usable ) then
                getglobal("LLS_UsableCheckButton"):SetChecked(1);
        else
                getglobal("LLS_UsableCheckButton"):SetChecked(0);
        end
        
        field = getglobal("LLS_MinimumLevelEditBox");
        if( sp and sp.minLevel ) then
                field:SetText(sp.minLevel);
        else
                field:SetText("");
        end

        field = getglobal("LLS_MaximumLevelEditBox");
        if( sp and sp.maxLevel ) then
                field:SetText(sp.maxLevel);
        else
                field:SetText("");
        end
        
        UIDropDownMenu_Initialize(LLS_TypeDropDown, LLS_TypeDropDown_Initialize);
        if( sp and sp.type ) then
                LootLink_UIDropDownMenu_SetSelectedID(LLS_TypeDropDown, sp.type, LLS_TYPE_LIST);
        else
                LootLink_UIDropDownMenu_SetSelectedID(LLS_TypeDropDown, 1, LLS_TYPE_LIST);
        end
        
        if( sp and sp.type ) then
                LootLink_SetupTypeUI(sp.type, sp.subtype);
        else
                LootLink_SetupTypeUI(1, 1);
        end

        field = getglobal("LLS_MinimumDamageEditBox");
        if( sp and sp.minMinDamage ) then
                field:SetText(sp.minMinDamage);
        else
                field:SetText("");
        end
        
        field = getglobal("LLS_MaximumDamageEditBox");
        if( sp and sp.minMaxDamage ) then
                field:SetText(sp.minMaxDamage);
        else
                field:SetText("");
        end
        
        field = getglobal("LLS_MaximumSpeedEditBox");
        if( sp and sp.maxSpeed ) then
                field:SetText(sp.maxSpeed);
        else
                field:SetText("");
        end
        
        field = getglobal("LLS_MinimumDPSEditBox");
        if( sp and sp.minDPS ) then
                field:SetText(sp.minDPS);
        else
                field:SetText("");
        end
        
        field = getglobal("LLS_MinimumArmorEditBox");
        if( sp and sp.minArmor ) then
                field:SetText(sp.minArmor);
        else
                field:SetText("");
        end
        
        field = getglobal("LLS_MinimumBlockEditBox");
        if( sp and sp.minBlock ) then
                field:SetText(sp.minBlock);
        else
                field:SetText("");
        end
        
        field = getglobal("LLS_MinimumSlotsEditBox");
        if( sp and sp.minSlots ) then
                field:SetText(sp.minSlots);
        else
                field:SetText("");
        end
        
        field = getglobal("LLS_MinimumSkillEditBox");
        if( sp and sp.minSkill ) then
                field:SetText(sp.minSkill);
        else
                field:SetText("");
        end
        
        field = getglobal("LLS_MaximumSkillEditBox");
        if( sp and sp.maxSkill ) then
                field:SetText(sp.maxSkill);
        else
                field:SetText("");
        end
        
        --[[if( sp and sp.set ) then
                getglobal("LLS_SetCheckButton"):SetChecked(1);
        else
                getglobal("LLS_SetCheckButton"):SetChecked(0);
        end]]
end

function LootLinkSearch_SaveValues()
        local sp;
        local interesting;
        local field;
        local value;
        
        LootLinkFrame.SearchParams = { };
        sp = LootLinkFrame.SearchParams;
        
        field = getglobal("LLS_TextEditBox");
        value = field:GetText();
        if( value and value ~= "" ) then
                sp.text = value;
                interesting = 1;
        end
        
        field = getglobal("LLS_NameEditBox");
        value = field:GetText();
        if( value and value ~= "" ) then
                sp.name = value;
                interesting = 1;
        end
        
        value = UIDropDownMenu_GetSelectedID(LLS_RarityDropDown);
        if( value and value ~= 1 ) then
                sp.rarity = value;
                interesting = 1;
        end
        
        field = getglobal("LLS_ServerCheckButton");
        value = field:GetChecked();
        if( value ) then
                sp.server = value;
                interesting = 1;
        end
        
        value = UIDropDownMenu_GetSelectedID(LLS_BindsDropDown);
        if( value and value ~= 1 ) then
                sp.binds = value;
                interesting = 1;
        end
        
        field = getglobal("LLS_UniqueCheckButton");
        value = field:GetChecked();
        if( value ) then
                sp.unique = value;
                interesting = 1;
        end
        
        value = UIDropDownMenu_GetSelectedID(LLS_LocationDropDown);
        if( value and value ~= 1 ) then
                sp.location = value;
                interesting = 1;
        end
        
        field = getglobal("LLS_UsableCheckButton");
        value = field:GetChecked();
        if( value ) then
                sp.usable = value;
                interesting = 1;
        end
        
        field = getglobal("LLS_MinimumLevelEditBox");
        value = field:GetText();
        if( value and LootLink_CheckNumeric(value) ) then
                sp.minLevel = tonumber(value);
                interesting = 1;
        end
        
        field = getglobal("LLS_MaximumLevelEditBox");
        value = field:GetText();
        if( value and LootLink_CheckNumeric(value) ) then
                sp.maxLevel = tonumber(value);
                interesting = 1;
        end

        value = UIDropDownMenu_GetSelectedID(LLS_TypeDropDown);
        if( value and value ~= 1 ) then
                sp.type = value;
                interesting = 1;
        end
        
        field = getglobal("LLS_SpoofCheckButton");
        value = field:GetChecked();
        if( value ) then
                sp.spoof = value;
                interesting = 1;
        end
        
        value = UIDropDownMenu_GetSelectedID(LLS_SubtypeDropDown);
        if( value and value ~= 1 ) then
                sp.subtype = value;
                if( sp.type and sp.type ~= 1 ) then
                        interesting = 1;
                end
        end

        field = getglobal("LLS_MinimumDamageEditBox");
        value = field:GetText();
        if( value and LootLink_CheckNumeric(value) ) then
                sp.minMinDamage = tonumber(value);
                interesting = 1;
        end
        
        field = getglobal("LLS_MaximumDamageEditBox");
        value = field:GetText();
        if( value and LootLink_CheckNumeric(value) ) then
                sp.minMaxDamage = tonumber(value);
                interesting = 1;
        end
        
        field = getglobal("LLS_MaximumSpeedEditBox");
        value = field:GetText();
        if( value and LootLink_CheckNumeric(value) ) then
                sp.maxSpeed = tonumber(value);
                interesting = 1;
        end
        
        field = getglobal("LLS_MinimumDPSEditBox");
        value = field:GetText();
        if( value and LootLink_CheckNumeric(value) ) then
                sp.minDPS = tonumber(value);
                interesting = 1;
        end
        
        field = getglobal("LLS_MinimumArmorEditBox");
        value = field:GetText();
        if( value and LootLink_CheckNumeric(value) ) then
                sp.minArmor = tonumber(value);
                interesting = 1;
        end
        
        field = getglobal("LLS_MinimumBlockEditBox");
        value = field:GetText();
        if( value and LootLink_CheckNumeric(value) ) then
                sp.minBlock = tonumber(value);
                interesting = 1;
        end

        field = getglobal("LLS_MinimumSlotsEditBox");
        value = field:GetText();
        if( value and LootLink_CheckNumeric(value) ) then
                sp.minSlots = tonumber(value);
                interesting = 1;
        end

        field = getglobal("LLS_MinimumSkillEditBox");
        value = field:GetText();
        if( value and LootLink_CheckNumeric(value) ) then
                sp.minSkill = tonumber(value);
                interesting = 1;
        end

        field = getglobal("LLS_MaximumSkillEditBox");
        value = field:GetText();
        if( value and LootLink_CheckNumeric(value) ) then
                sp.maxSkill = tonumber(value);
                interesting = 1;
        end
        
        --[[value = getglobal("LLS_SetCheckButton"):GetChecked();
        if ( value ) then
                sp.set = value;
                interesting = 1;
        end]]

        -- Only save search params if we had interesting data on the page       
        if( not interesting ) then
                LootLinkFrame.SearchParams = nil;
        else
                if( IsControlKeyDown() ) then
                        sp.plain = nil;
                else
                        sp.plain = 1;
                end
        end
end

function LootLinkSearchFrame_SaveSearchParams()
        LootLinkSearchFrame.OldSearchParams = LootLinkFrame.SearchParams;
end

function LootLinkSearchFrame_RestoreSearchParams()
        LootLinkFrame.SearchParams = LootLinkSearchFrame.OldSearchParams;
end

function LootLinkSearchFrame_ChangeFocus()
        local frames = {"LLS_TextEditBox",
                "LLS_NameEditBox",
                "LLS_MinimumLevelEditBox",
                "LLS_MaximumLevelEditBox",
                "LLS_MinimumSlotsEditBox",
                "LLS_MinimumDamageEditBox",
                "LLS_MaximumDamageEditBox",
                "LLS_MaximumSpeedEditBox",
                "LLS_MinimumDPSEditBox",
                "LLS_MinimumArmorEditBox",
                "LLS_MinimumBlockEditBox",
                "LLS_MinimumSkillEditBox",
                "LLS_MaximumSkillEditBox",
        }
        
        local name = this:GetName();
        for i=1, 13 do
                if ( frames[i] == name ) then
                        if ( IsShiftKeyDown() ) then
                                i = i-1;
                        else
                                i = i+1;
                        end
                        while frames[i] do
                                local nextframe = getglobal(frames[i]);
                                if ( nextframe:IsVisible() ) then
                                        nextframe:SetFocus();
                                        return;
                                end
                                if ( IsShiftKeyDown() ) then
                                        i = i-1;
                                else
                                        i = i+1;
                                end
                        end
                        return;
                end
        end
end

function LootLinkSearchFrame_Cancel()
        PlaySound("gsTitleOptionExit");
        LootLinkSearchFrame:Hide();
        LootLinkSearchFrame_RestoreSearchParams();
end

function LootLinkSearchFrame_Okay()
        PlaySound("gsTitleOptionOK");
        LootLinkSearchFrame:Hide();
        LootLinkSearch_SaveValues();
        LootLink_DoSearch();
end

function LootLinkQuickSearch_Search(Next)
        if ( not Next ) then
                Next = 0;
        end
        local text = LootLinkFrameQuickSearch:GetText();
        local length = string.len( text );
        local last = LootLinkFrameQuickSearch.last;
        local function DoScroll(i)
                local offset = FauxScrollFrame_GetOffset(LootLinkListScrollFrame);
                if ( i < offset or i > offset + LOOTLINK_ITEMS_SHOWN ) then
                        if ( i > DisplayIndices.onePastEnd - LOOTLINK_ITEMS_SHOWN ) then
                                i = DisplayIndices.onePastEnd - LOOTLINK_ITEMS_SHOWN;
                        end
                        i = i-1;
                        local val = (i * 16) - 0.5
                        LootLinkListScrollFrameScrollBar:SetValue(val);
                        LootLinkListScrollFrame.offset = i;
                end
        end
        local function DoSearch(index, offset)
                if ( not offset ) then
                        offset = getn(DisplayIndices);
                end
                for i=index+Next, offset do
                        local name = LootLink_GetName(DisplayIndices[i]);
                        if ( name and string.find( string.lower(name), string.lower(text), 1, 1 ) ) then
                                LootLinkFrame.qs = i;
                                DoScroll(i);
                                return 1;
                        end
                end
        end
        if ( text and text ~= "" ) then
                if ( not last or length < last ) then
                        if ( not DoSearch(LootLinkFrame.qs) and not DoSearch(1, LootLinkFrame.qs+1) ) then
                                LootLinkFrameQuickSearch.last = length;
                                LootLinkFrameQuickSearch:SetTextColor(1,0,0);
                                LootLinkFrame.qs = 0;
                        else
                                LootLinkFrameQuickSearch:SetTextColor(1,1,1);
                                LootLinkFrameQuickSearch.last = nil;
                        end
                end
        else
                LootLinkFrame.qs = 0;
                LootLinkFrameQuickSearch:SetTextColor(1,1,1);
        end
        LootLink_Update();
end

function LootLinkQuickSearch_FullSearch()
        local name = this:GetText();
        if ( name ) then
                if ( name == "" ) then
                        name = nil;
                end
                if ( name and not LootLinkFrame.SearchParams ) then
                        LootLinkFrame.SearchParams = { };
                end
                if ( LootLinkFrame.SearchParams ) then
                        LootLinkFrame.SearchParams.name = name;
                end
        end
        LootLink_DoSearch();
        LootLinkFrameQuickSearch:SetText(name or "");
end

function LootLinkQuickSearch_Clear()
        LootLinkFrameQuickSearch:SetText("");
        LootLinkFrameQuickSearch.last = nil;
end

LootLinkOptionsList = {
        hideminimap     = { frame = "LLO_HideMinimap", flip = 1 },
        lockminimap     = { frame = "LLO_LockMinimap" },
        hideicon        = { frame = "LLO_IconCheckButton", flip = 1 },
        HideInfo        = { frame = "LLO_ExtraInfoCheckButton", flip = 1 },
        mouseover       = { frame = "LLO_MouseoverCheckButton" },
        typelinks       = { frame = "LLO_TypeLinksCheckButton" },
        autotypelinks   = { frame = "LLO_AutoTypeLinksCheckButton", dependancy = "typelinks" },
        
        server          = { frame = "LLO_ServerCheckButton", refresh = 1 },
        spoof           = { frame = "LLO_SpoofCheckButton", refresh = 1 },
        nosame          = { frame = "LLO_SameNameCheckButton", flip = 1, refresh = 1 },
        novariants      = { frame = "LLO_VariantCheckButton", flip = 1, dependancy = "nosame", refresh = 1 },
        enchants        = { frame = "LLO_EnchantsCheckButton", flip = 1, dependancy = "nosame", refresh = 1 },

        x               = { frame = "LLO_MinimapPos", slider = LLO_MINIMAP_X},
        y               = { frame = "LLO_MinimapOffset", slider = LLO_MINIMAP_Y },

        rarityfilter    = { frame = "LLO_RarityDropDown", dropdown = {LLO_RarityDropDown_Initialize, 2, LLO_RARITY_LIST}, refresh = 1 },
        };
        
function LootLinkOptions_LoadValues()
        local op = LootLinkState;
        
        for k, v in LootLinkOptionsList do
                local frame = getglobal( v.frame );
                if ( frame ) then
                        if ( v.dropdown ) then
                                UIDropDownMenu_Initialize(frame, v.dropdown[1]);
                                LootLink_UIDropDownMenu_SetSelectedID(frame, 1, v.dropdown[3]);
                        end
                        local value = op[k];
                        if value then
                                if ( v.slider ) then
                                        frame:SetValue( value );
                                        LootLinkOptionsFrame[k] = value;
                                elseif ( v.dropdown ) then
                                        LootLink_UIDropDownMenu_SetSelectedID(frame, value+v.dropdown[2], v.dropdown[3]);
                                end
                        elseif ( v.slider ) then
                                frame:SetValue( v.slider );
                                LootLinkOptionsFrame[k] = nil;
                        end
                        if ( not v.slider and not v.dropdown ) then
                                if ( v.flip ) then
                                        frame:SetChecked(1-(value or 0))
                                else
                                        frame:SetChecked(value)
                                end
                        end
                        if ( v.dependancy ) then
                                if ( getglobal(LootLinkOptionsList[v.dependancy].frame):GetChecked() ) then
                                        frame:Enable();
                                        getglobal(frame:GetName().."Text"):SetTextColor(1, 0.82, 0);
                                else
                                        frame:Disable();
                                        getglobal(frame:GetName().."Text"):SetTextColor(0.3, 0.3, 0.3);
                                end
                        end
                else
                        ChatFrame1:AddMessage(k.." no frame?");
                end
        end
        
        LootLinkOptionsFrame.needsave = 1;
end

function LootLinkOptions_SaveValues()
        local op = LootLinkState;
        local refresh;
        
        for k, v in LootLinkOptionsList do
                local frame = getglobal( v.frame );
                if ( frame ) then
                        local value = op[k];
                        if ( v.dropdown ) then
                                value = UIDropDownMenu_GetSelectedID(frame);
                                if ( value == 1 ) then
                                        value = nil;
                                else
                                        value = value - (v.dropdown[2] or 0);
                                end
                        elseif ( not v.slider ) then
                                local checked = frame:GetChecked();
                                if ( v.flip ) then
                                        if ( checked ) then
                                                checked = nil
                                        else
                                                checked = 1;
                                        end
                                end
                                value = checked;                
                        end
                        if ( v.refresh and op[k] ~= value ) then
                                refresh = 1;
                        end
                        op[k] = value;
                else
                        ChatFrame1:AddMessage(k.." no frame?");
                end
        end
        
        if ( refresh ) then
                LootLink_Refresh();
        end
        LootLinkOptionsFrame.needsave = nil;
end

function LootLinkOptionsFrame_Defaults()
        local op = LootLinkState;
        for k, v in LootLinkOptionsList do
                op[k] = nil;
        end
        LootLinkOptions_LoadValues();
end

function LootLinkOptionsFrame_Cancel()
        if ( LootLinkOptionsFrame.needsave ) then
                LootLinkOptionsFrame.needsave = nil;
                if ( LootLinkOptionsFrame.x or LootLinkOptionsFrame.y ) then
                        LootLink_Minimap_Position( LootLinkOptionsFrame.x, LootLinkOptionsFrame.y );
                end
        end
        PlaySound("gsTitleOptionExit");
        LootLinkOptionsFrame:Hide();
end

function LootLinkOptionsFrame_Okay()
        PlaySound("gsTitleOptionOK");
        LootLinkOptions_SaveValues();
        LootLinkOptionsFrame:Hide();
end

function LootLink_CloseWindows(ignoreCenter)
        if ( LootLinkOptionsFrame:IsVisible() ) then
                LootLinkOptionsFrame_Cancel();
                return 1;
        end
        if ( LootLinkSearchFrame:IsVisible() ) then
                LootLinkSearchFrame_Cancel();
                return 1;
        end
        return lOriginal_CloseWindows(ignoreCenter);
end

function LootLink_ValidateItem(name, link, fixlink)
        -- Basic validation of enchants and stat bonuses
        -- todo : more complex evaluation of the stat bonuses taking in to account item level and base stats
        if ( not link ) then return nil; 
        elseif ( string.sub(link, 1, 5) ~= "item:" ) then
                link = "item:"..link;
        end
        local realname, ilink, quality, level, sType, sSubType, count, equip = GetItemInfo(link);
        if ( realname ) then
                if ( realname ~= name ) then
                        -- quickly kill anything that's really wrong.
                        if ( not fixlink ) then return nil; end
                        name = realname;
                end
                
                local _,_, itemid, enchant, stat = string.find( link, "(%d+):(%d+):(%d+):%d+" );
                
                if ( tonumber(stat) > 0 ) then
                        if ( quality ~= 2 and quality ~= 3 and itemid ~= "20039" ) then -- special case for Dark Iron Boots, grr.
                                -- Nothing but Uncommon and Rare items have stat bonuses
                                if ( not fixlink ) then return nil; end
                                stat = 0;
                        elseif( lNoBonuses[equip] == 2 ) then
                                -- Nothing that can't be equiped can't have stat bonuses
                                if ( not fixlink ) then return nil; end
                                stat = 0;
                        end
                end
                if ( tonumber(enchant) > 0 ) then
                        if ( lNoBonuses[equip] ) then
                                -- Nothing that isn't armor or weapons can have enchants
                                if ( not fixlink ) then return nil; end
                                enchant = 0;
                        end
                end
                
                return name, itemid..":"..enchant..":"..stat..":0";
        else
                if ( not fixlink ) then return 1; end
                return name, string.sub(link,6);
        end
end


--------------------------------------------------------------------------------------------------
-- Clean and Purge functions
--------------------------------------------------------------------------------------------------

local LLPtodo;
local LLPvars;

function LootLink_CheckDuplicate(name)
        local links = {};
        local value = ItemLinks[name];
        local dups = 0;
        if ( value and value.m ) then
                links[value.i] = 1;
                local i = 1;
                --while value and value.m and value.m[i] do
                for i=getn(value.m), 1, -1 do
                        if ( links[value.m[i].i] ) then
                                dups = dups + 1;
                                LootLink_RemoveItem(name, nil, nil, i);
                        else
                                links[value.m[i].i] = 1;
                        end
                end
        end
        return dups;
end

function LootLink_Process_Cancel()
        LootLink_Process:Hide();
        LootLink_Process:SetScript("OnUpdate", nil);
        LootLink_Process.fade = nil;
        LootLinkFrame.process = nil;
        LootLink_Process_ToggleButtons(1);
        
        if ( LLPtodo ) then
                local text = getglobal("LLP_"..LLPvars.functype.."_CANCEL");
                text = string.gsub( text, "%%r", LLPvars.total-LLPvars.done );
                text = string.gsub( text, "%%u", LLPvars.updated );
                text = string.gsub( text, "%%t", LLPvars.total );
                text = string.gsub( text, "%%f", LLPvars.failed );
                DEFAULT_CHAT_FRAME:AddMessage(text);

                LootLink_Process_Time:SetText("");

                LLPvars = nil;
                LLPtodo = nil;
        end
end

function LootLink_Process_Memory()
        LootLink_Process_Cancel();
        DEFAULT_CHAT_FRAME:AddMessage(LLP_ABORT1);
        DEFAULT_CHAT_FRAME:AddMessage(LLP_ABORT2);
end

function LootLink_Process_ToggleButtons(on)
        if ( on ) then
                LLO_Purge:Enable();
                LLO_Fix:Enable();
        else
                LLO_Purge:Disable();
                LLO_Fix:Disable();
        end
end

function LootLinkProcess_PURGE()
        local todo = LLPtodo[LLPvars.done+1];
        
        local name, link = todo[1], todo[2];    
        local _,_,itemid,enchantid,suffixid=string.find(link, "(%d+):(%d+):(%d+):%d+");
        
        LootLink_SortAlternates(name);
        local op = LootLinkState;
        local value = ItemLinks[name];
        
        if ( value and value.m ) then
                if ( op.nosame or (op.novariants and tonumber(suffixid) > 0) ) then
                        LLPvars.updated = LLPvars.updated + getn(value.m);
                        value.m = nil;
                end
                if ( op.enchants ) then
                        if ( tonumber(enchantid) > 0 ) then
                                if ( LootLink_RemoveItem(name, link) ) then
                                        return 1;
                                end
                        end
                end
        end
        value = LootLink_GetValue(name, link);
        if ( not value ) then
                return 2;
        end
        if ( value.c and op.rarityfilter ) then
                local rarity = lRarityFilter[value.c];
                if ( rarity and rarity < op.rarityfilter ) then
                        if ( LootLink_RemoveItem(name, link) ) then
                                return 1;
                        end
                end
        end
        if ( LootLinkState.spoof ) then
                local valid = LootLink_ValidateItem(name, link)
                if ( not valid ) then
                        if ( LootLink_RemoveItem(name, link) ) then
                                return 1;
                        end
                elseif ( valid == 1 ) then
                        return 2;
                end
        end
end

function LootLinkProcess_FIXCACHE()
        local todo = LLPtodo[LLPvars.done+1];
        local name, link, index = todo[1], todo[2], todo[3];
        local realname = GetItemInfo("item:"..link);
        local value = LootLink_GetValue( name, nil, link );
        if ( not value ) then return; end
        value.i = string.gsub(link,"(%d+:%d+:%d+:)%d+","%10");
        if ( realname ) then
                if ( LootLink_BuildSearchData( name, value, index ) == 2 ) then
                        return 1;
                end
        elseif ( not value.t or value.t == "" ) then
                value.t = name.."·Bad Search Data·";
                return 1;
        else
                local changes;
                if ( value.t ) then
                        local t = value.t;
                        local d = value.d;
                        local s, e, val = string.find(t, "^(.-)·");
                        if ( not val or not string.find(name, val) ) then
                                return 2;
                        end
                        if ( string.find(t, "%/") ) then
                                -- cull out durability data.
                                t = string.gsub(t, "Durability %d+ %/ %d+·", "");
                                -- check set data.
                                s,e,val = string.find(t, ".+·(.-) %(%d%/%d+%)·.+");
                                if ( val ) then
                                        t = string.gsub(t, "(.+ %()%d+(%/%d+%)·.+)", "%10%2");
                                        local newset = LootLink_GetSet(val);
                                        local setnum = LL_SearchData(value, "se");
                                        if ( d ) then
                                                if ( not setnum ) then
                                                        d = d.."se"..newset.."·";
                                                        changes = 1;
                                                elseif( newset ~= setnum ) then
                                                        d = string.gsub(d, "(.+·se)%d+(·.+)", "%1"..newset.."%2");
                                                        changes = 1;
                                                end
                                        end
                                end
                        end
                        
                        if ( value.t ~= t ) then
                                changes = 1;
                        end
                        value.t = t;
                        value.d = d;
                end
                if ( changes ) then
                        return 1;
                end
                return 2;
        end
end

function LootLinkProcess_Process(func)
        if ( not LLPvars ) then
                LootLink_Process_ToggleButtons();
                
                LLPvars = {};
                
                LLPvars.func = getglobal( "LootLinkProcess_"..func );
                
                if ( not LLPvars.func ) then
                        LootLink_Process_ToggleButtons(1);
                        preprocess = nil;
                        return;
                end
                
                HideUIPanel(LootLinkFrame);
                
                LootLink_Process_Header:SetText(getglobal("LLP_"..func.."_LABEL") );
                LootLink_Process_Num:SetText("Preprocessing");
                LootLink_Process_Bar:SetMinMaxValues(0,1);
                LootLink_Process_Bar:SetValue(0);
                
                LLPvars.functype = func;
                LLPvars.elapsed = 0;
                LLPvars.done = 0;
                LLPvars.updated = 0;
                LLPvars.failed = 0;
                LLPvars.wait = 0;
                LLPvars.total = 0;
                
                LootLink_Process:SetAlpha( 1 );
                LootLink_Process:Show();
                
                LootLinkFrame.process = func;
        end
end

function LootLinkProcess_Run()
        local thisprocess = 0;
        local func = LLPvars.func;
        
        if ( LLP_OUTOFMEM ) then
                LootLink_Process_Memory();
        end
        
        if ( LLPvars.wait ) then
                LLPvars.wait = LLPvars.wait - arg1;
                if ( LLPvars.wait <= 0 ) then
                        LLPvars.wait = nil;
                end
                return;
        end
        
        if ( not LLPtodo ) then
                local normal, mult = 0,0;
                LLPtodo = {};
                for k, v in ItemLinks do
                        if ( strlen(k) == 0 ) then
                                ItemLinks[k] = nil;
                                LLPvars.updated = LLPvars.updated + 1;
                        elseif ( v.i ) then
                                normal = normal + 1;
                                tinsert( LLPtodo, {k, v.i} );
                                if ( GetItemInfo("item:"..v.i) ) then
                                        LLHiddenTooltip:SetHyperlink("item:"..v.i);
                                end
                                if ( v.m ) then
                                        for i=1, getn(v.m) do
                                                if v.m[i].i then
                                                        mult = mult + 1;
                                                        tinsert( LLPtodo, {k, v.m[i].i, i} );
                                                        if ( GetItemInfo("item:"..v.m[i].i) ) then
                                                                LLHiddenTooltip:SetHyperlink("item:"..v.m[i].i);
                                                        end
                                                else
                                                        LootLink_RemoveItem(k, i);
                                                        LLPvars.updated = LLPvars.updated + 1;
                                                end
                                        end
                                end
                        else
                                LootLink_RemoveItem(k);
                                LLPvars.updated = LLPvars.updated + 1;
                        end
                end
                LLPvars.total = normal + mult;
                LootLink_Process_Bar:SetValue(0)
                LootLink_Process_Bar:SetMinMaxValues(0, LLPvars.total)
                LLPvars.wait = 2;
                return;
        end
        
        local todo = LLPtodo;
        
        LLPvars.elapsed = LLPvars.elapsed + arg1;
        
        while todo and todo[LLPvars.done+1] do
                local ret = func();
                if ( ret ) then
                        if ( ret == 1 ) then
                                LLPvars.updated = LLPvars.updated + 1;
                        else
                                LLPvars.failed = LLPvars.failed + 1;
                        end
                end
                
                LLPvars.updated = LLPvars.updated + LootLink_CheckDuplicate(todo[LLPvars.done+1][1]);
                LootLink_SortAlternates(todo[LLPvars.done+1][1]);
                
                thisprocess = thisprocess + 1;
                LLPvars.done = LLPvars.done + 1;
                
                if ( thisprocess == 50 ) then
                        thisprocess = 0;
                        LootLink_Process_Bar:SetValue( LLPvars.done );
                        LootLink_Process_Num:SetText( LLPvars.done .. "/".. LLPvars.total );
                        local time = (LLPvars.elapsed / LLPvars.done ) * (LLPvars.total - LLPvars.done);
                        local s = mod( time, 60 );
                        local m = (time-s) / 60;
                        s = floor(s)
                        if ( strlen(s) == 1 ) then
                                s = "0"..s;
                        end
                        LootLink_Process_Time:SetText( "Estimated "..m..":"..s.." remaining" );
                        
                        if ( DisplayIndices and LootLinkFrame:IsVisible() ) then
                                LootLink_Update();
                                LootLink_SetTitle();
                        end
                        
                        return;
                end
        end
        
        if ( todo and not todo[LLPvars.done+1] ) then
                local text = getglobal("LLP_"..LLPvars.functype.."_END");
                text = string.gsub( text, "%%u", LLPvars.updated );
                text = string.gsub( text, "%%t", LLPvars.total );
                text = string.gsub( text, "%%f", LLPvars.failed );
                DEFAULT_CHAT_FRAME:AddMessage(text);
                LootLink_Process_Time:SetText("");
                LootLink_Process_Num:SetText("Finished");
                LootLink_Process_Bar:SetValue(LLPvars.total);

                LootLink_Process_ToggleButtons(1);
                
                LLPvars = nil;
                LLPtodo = nil;
                LootLinkFrame.process = nil;
                
                LootLink_Process.fade = 2;
                LootLink_Process:SetScript("OnUpdate", LootLink_Process_Fade);
        end
end

function LootLink_Process_Fade()
        if ( LootLink_Process.fade ) then
                LootLink_Process.fade = LootLink_Process.fade - (arg1 / 2);
                if ( LootLink_Process.fade < 1 ) then
                        this:SetAlpha( LootLink_Process.fade );
                        if ( LootLink_Process.fade <= 0 ) then
                                this:Hide();
                                LootLink_Process.fade = nil;
                                LootLink_Process:SetScript("OnUpdate", nil);
                        end
                end
        end
end

--------------------------------------------------------------------------------------------------
-- External functions
--------------------------------------------------------------------------------------------------

-- Looks for and deconstructs links contained in a text string; if trusted, existing information will be verified
-- Trusted sources are everything except chat links; since these can be spoofed, we want to allow them to be replaced
function LootLink_ProcessLinks(text, trusted)
        local enchants, spoof, nosame, rarityfilter, lastName = LootLinkState.enchants, LootLinkState.spoof, LootLinkState.nosame, LootLinkState.rarityfilter;
        if( text ) then
                for color, item, name in string.gfind(text, "|c(%x+)|Hitem:(%d+:%d+:%d+:%d+)|h%[(.-)%]|h|r") do
                        if ( item ~= "0:0:0:0" and ( not rarityfilter or lRarityFilter[color] > rarityfilter ) and LootLink_CheckSame(name, item) ) then
                                local fixedname, fixeditem;
                                if ( not trusted ) then
                                        if ( spoof ) then
                                                name, item = LootLink_ValidateItem(name, item, 1);
                                        else
                                                fixedname, fixeditem = LootLink_ValidateItem(name, item, 1);
                                                if ( fixedname and fixedname ~= name ) then
                                                        LootLink_AddItem(fixedname, fixeditem, color, trusted);
                                                end
                                        end
                                end
                                local enchant;
                                if ( not enchants and not nosame ) then
                                        enchant = string.gsub(item, "^(%d+):(%d+):(%d+):%d+$", "%1:%2:%3:0");
                                end
                                item = string.gsub(item, "^(%d+):%d+:(%d+):%d+$", "%1:0:%2:0");
                                LootLink_AddItem(name, item, color, trusted);
                                if ( enchant and enchant ~= item ) then
                                        LootLink_AddItem(name, enchant, color, trusted);
                                end
                                LootLink_SortAlternates(name);
                                lastName = name;
                        end
                end
        end     
        return lastName;
end

function LootLink_CheckSame(name, item)
        local _,_,itemid,suffixid=string.find(item, "(%d+):%d+:(%d+):%d+");
        local value = ItemLinks[name];
        if ( value and (LootLinkState.nosame or LootLinkState.novariants) ) then
                -- Assumes no same name items are both different itemid and have suffixids
                if ( value.i and value.i ~= item ) then
                        for id, suf in string.gfind( value.i, "(%d+):%d+:(%d+):") do
                                if ( nosame and tonumber(id) > tonumber(itemid) ) then
                                        return;
                                elseif ( tonumber(suf) > tonumber(suffixid) ) then
                                        return;
                                end
                        end
                end
        end
        return 1;
end

-- old filter function, keeping for posterity
--[[function LootLink_FilterCheck(name, item, color)
        if ( item == "0:0:0:0" ) then
                return;
        end
        local op = LootLinkState;
        if ( op.rarityfilter ) then
                local rarity = lRarityFilter[color];
                if ( rarity and rarity < op.rarityfilter ) then
                        return;
                end
        end             
        local _,_,itemid,enchantid,suffixid=string.find(item, "(%d+):(%d+):(%d+):%d+");
        if ( not tonumber(enchantid) ) then
                --ChatFrame1:AddMessage((name or "??").." no enchant id "..(item or "???"));
                return;
        end
        local value = ItemLinks[name];
        if ( value and (op.nosame or op.novariants) ) then
                -- Assumes no same name items are both different itemid and have suffixids
                if ( value.i and value.i ~= item ) then
                        for id, suf in string.gfind( value.i, "(%d+):%d+:(%d+):") do
                                if ( op.nosame and tonumber(id) > tonumber(itemid) ) then
                                        return;
                                elseif ( tonumber(suf) > tonumber(suffixid) ) then
                                        return;
                                end
                        end
                end
        end
        return 1;
end]]

-- Add items to the database
function LootLink_AddItem(name, link, color, trusted)
        local nosame, value, index = LootLinkState.nosame;
        if ( name and link ) then
                if ( not ItemLinks[name] ) then
                        ItemLinks[name] = { };
                        lItemLinksSizeTotal = lItemLinksSizeTotal + 1;
                        if( LootLink_GetDataVersion() < 110 ) then
                                -- Set a flag to indicate that this item is new and should be skipped on a makehome
                                ItemLinks[name]._ = 1;
                        end
                end
                value = ItemLinks[name];
                if ( not nosame and LootLinkState.novariants ) then
                        for suffixid in string.gfind(link, "%d+:%d+:(%d+):%d+") do
                                if ( tonumber(suffixid) > 0 ) then
                                        nosame = 1;
                                end
                        end
                end
                if ( not nosame and value.i and value.i ~= link ) then
                        if ( value.m and getn(value.m) > 0 ) then
                                for k, v in value.m do
                                        if ( v.i == link ) then
                                                if ( not trusted ) then
                                                        return nil;
                                                end
                                                index = k;
                                                break;
                                        end
                                end
                        end
                        if ( index ) then
                                value = value.m[index];
                        else
                                if ( not value.m ) then
                                        value.m = {};
                                end
                                tinsert( value.m, {} );
                                index = getn(value.m);
                                value = value.m[index];
                                lItemLinksSizeTotal = lItemLinksSizeTotal + 1;
                        end
                end
                
                value.i = link;
                value.c = color;
                
                LootLink_BuildSearchData(name, value, index);
                if( not LootLink_CheckItemServerRaw(value, lServerIndex) ) then
                        LootLink_AddItemServer(value, lServerIndex);
                end
                return value;
        end
end

-- Sort same name items by ids
function LootLink_SortAlternates(name)
        if ( not name ) then return; end
        local value = ItemLinks[name];
        local newval;
        local values = {};
        local itemval = {};

        -- cleaned up by Adrine
        function sortByLink(e1, e2)
                if ( not e1 ) then return true; elseif ( not e2 ) then return false; end
                local _,_,a,b,c = string.find(e1.i, "(%d+):(%d+):(%d+)");
                local _,_,d,e,f = string.find(e2.i, "(%d+):(%d+):(%d+)");

                if a ~= d then return tonumber(a) > tonumber(d); end
                if c ~= f then return tonumber(c) > tonumber(f); end
                if b ~= e then return tonumber(b) < tonumber(e); end
                return false;
        end
                        
        if value and value.m then
                if ( getn(value.m) == 0 ) then
                        value.m = nil;
                        return nil;
                end
                for k, v in value do
                        if ( k ~= "m" ) then
                                itemval[k] = v;
                        end
                end
                tinsert( values, itemval );
                for k, v in value.m do
                        tinsert( values, v );
                end
                
                table.sort(values, sortByLink);
                
                newval = values[1];
                newval.m = {};
                for i=2, getn(values) do
                        tinsert( newval.m, values[i] );
                end
        else
                return nil;
        end
        ItemLinks[name] = {};
        ItemLinks[name] = newval;
end

-- Adds icon of the item if it's valid
function LootLink_AddTooltipTexture(link)
        if ( link and not LootLinkState.hideicon) then
                local _, _, _, _, _, _, _, _, itemTexture = GetItemInfo(link);
                if ( itemTexture ) then
                        local iItem = LootLinkFrame.TooltipButton or 0;
                        LootLinkTooltipIconTexture:SetTexture( itemTexture );
                        LootLinkTooltipIcon:ClearAllPoints();
                        LootLinkTooltipIcon:SetPoint("BOTTOMLEFT", getglobal("LootLinkItem"..iItem), "BOTTOMRIGHT");
                        LootLinkTooltipIcon:Show();
                        return;
                end
        end
        LootLinkTooltipIcon:Hide();
end                     

-- Adds extra tooltip information for the item with the given name
function LootLink_AddTooltipInfo(name, tooltip, quantity, index)
        if( not tooltip ) then
                tooltip = LootLinkTooltip;
        end
        if( not quantity ) then
                quantity = 1;
        end
        if( tooltip == LootLinkTooltip ) then
                LootLink_HideTooltipMoney();
        end
        if( not LootLinkState.HideInfo and ItemLinks[name] ) then
                if( tooltip == GameTooltip ) then
                        GameTooltip.llDone = 1;
                end
                if( ItemLinks[name].p ) then
                        LootLink_SetTooltipMoney(tooltip, quantity, ItemLinks[name].p, ItemLinks[name].x);
                end
                LootLink_AddExtraTooltipInfo(tooltip, name, quantity, ItemLinks[name]);
                tooltip:Show();
        end
end

-- This will set up a tooltip with item information for the given name if it's known
function LootLink_SetTooltip(tooltip, name, quantity)
        local link;
        
        if( tooltip and name ) then
                link = LootLink_GetHyperlink(name);
                if( link ) then
                        LootLink_SetHyperlink(tooltip, name, link);
                        if( quantity ) then
                                quantity = tonumber(quantity);
                        else
                                quantity = 1;
                        end
                        if( quantity > 0 ) then
                                LootLink_AddTooltipInfo(name, tooltip, quantity);
                        end
                end
        end
end

-- 
function LootLink_ChatEdit_OnTabPressed()
        
        lOriginal_ChatEdit_OnTabPressed();
end

function LootLink_ChatEdit_OnTextChanged()
        local text = this:GetText();
        if (string.sub(text, 1, 7) ~= "/script" or string.sub(text, 1, 5) ~= "/dump") then
                --text = string.gsub(text, "([|]?[h]?)%[(.-)%]([|]?[h]?)", FLT_LinkifyName);
                --end
        end
        lOriginal_ChatEdit_OnTextChanged();
end

-- Calling this will allow LootLink to automatically add information to tooltips when needed
function LootLink_AutoInfoOn()
        lSuppressInfoAdd = nil;
end

-- Calling this will prevent LootLink from automatically adding information to tooltips
function LootLink_AutoInfoOff()
        lSuppressInfoAdd = 1;
end

-- Use this function to get the current server name from LootLink's perspective
function LootLink_GetCurrentServerName()
        return lServer;
end

-- Use this function to get the current server index
function LootLink_GetCurrentServerIndex()
        return lServerIndex;
end

-- Use this function to map a server name to the server index for the ItemLinks[name].servers array
function LootLink_GetServerIndex(name)
        if( not LootLinkState or not LootLinkState.ServerNamesToIndices ) then
                return nil;
        end
        return LootLinkState.ServerNamesToIndices[name];
end

-- Use this function to check whether an ItemLinks[name] entry is valid for a given server index
function LootLink_CheckItemServer(item, serverIndex)
        -- If we haven't converted and this item predates multiple server support, count it as valid
        if( LootLink_GetDataVersion() < 110 and not item._ ) then
                return 1;
        end
        return LootLink_CheckItemServerRaw(item, serverIndex);
end

-- Used for debugging changes in item data and tooltip format
function LootLink_Validate()
        for index, value in ItemLinks do
                if( not value.d ) then
                        DEFAULT_CHAT_FRAME:AddMessage(index.." has no search data");
--              else
--                      if( not LL_SearchData(value, "su") ) then
--                              DEFAULT_CHAT_FRAME:AddMessage(index.." has no subtype");
--                      end
                end
        end
end

--------------------------------------------------------------------------------------------------
-- Hookable callback functions
--------------------------------------------------------------------------------------------------

-- Hook this function to add any extra information you like to the tooltip
function LootLink_AddExtraTooltipInfo(tooltip, name, quantity, item)
        -- tooltip: the current tooltip frame
        -- name: the name of the item
        -- quantity: the number of items, if known, else 1
        -- item: ItemLinks[name]; LootLink's data for this item
end

-- Hook this function to be called whenever an equipment slot is successfully inspected
function LootLink_Event_InspectSlot(name, count, item, unit, slotid)
        -- name: the name of the item
        -- count: the number of items, if known, else 1
        -- item: ItemLinks[name]; LootLink's data for this item
        -- unit: "target", "player", etc.
        -- slotid: the id of the slot inspected
end

-- Hook this function to be called whenever an inventory slot is successfully inspected
function LootLink_Event_ScanInventory(name, count, item, bagid, slotid)
        -- name: the name of the item
        -- count: the number of items, if known, else 1
        -- item: ItemLinks[name]; LootLink's data for this item
        -- bagid: the id of the bag containing the item
        -- slotid: the id of the slot inspected
end

-- Hook this function to be called whenever a bank slot is successfully inspected
function LootLink_Event_ScanBank(name, count, item, bagid, slotid)
        -- name: the name of the item
        -- count: the number of items, if known, else 1
        -- item: ItemLinks[name]; LootLink's data for this item
        -- bagid: the id of the bag containing the item
        -- slotid: the id of the slot inspected
end

-- Hook this function to be called whenever an auction entry is successfully inspected
function LootLink_Event_ScanAuction(name, count, item, auctionpage, auctionid)
        -- name: the name of the item
        -- count: the number of items, if known, else 1
        -- item: ItemLinks[name]; LootLink's data for this item
        -- auctionpage: the page number this item was found on
        -- auctionid: the id of the inspected item
end

-- Hook this function to be called whenever a chat message is successfully inspected
function LootLink_Event_ScanChat(name, item, text)
        -- name: the name of the last item in the chat message
        -- item: ItemLinks[name]; LootLink's data for this item
        -- text: the inspected chat message
end

-- Hook this function to be called whenever LootLink starts an auction scan
function LootLink_Event_StartAuctionScan()
end

-- Hook this function to be called whenever LootLink stops an auction scan
function LootLink_Event_StopAuctionScan()
end

-- Hook this function to be called whenever LootLink completes a full auction scan
function LootLink_Event_FinishedAuctionScan()
end

-- Hook this function to be called whenever LootLink sends a new auction query
function LootLink_Event_AuctionQuery(auctionpage)
        -- auctionpage: the page number for the query that was just sent
end


--------------------------------------------------------------------------------------------------
-- Static Popup Dialogs
--------------------------------------------------------------------------------------------------

StaticPopupDialogs["LOOTLINK_DELETEITEM_CONFIRM"] = {
        text = TEXT(LOOTLINK_STATICPOPUP_DELETE_ITEM_CONFIRM),
        button1 = TEXT(YES),
        button2 = TEXT(NO),
        OnAccept = function(data)
                if ( not data ) then return; end
                if ( LootLink_RemoveItem(data[1], data[2].i, data[3] ) ) then
                        LootLink_Update();
                        LootLink_SetTitle();
                end
        end,
        showAlert = 1,
        timeout = 0,
        exclusive = 1,
        whileDead = 1,
        hideOnEscape = 1,
        };

StaticPopupDialogs["LOOTLINK_LITEMODE_CONFIRM"] = {
        text = TEXT(LOOTLINK_STATICPOPUP_LITE_MODE_CONFIRM),
        button1 = TEXT(OKAY),
        button2 = TEXT(CANCEL),
        OnCancel = function()
                LLO_LiteModeCheckButton:SetChecked();
        end,
        showAlert = 1,
        timeout = 0,
        exclusive = 1,
        whileDead = 1,
        hideOnEscape = 1,
        };

StaticPopupDialogs["LOOTLINK_LINK_CONFIRM"] = {
        text = TEXT(LOOTLINK_STATICPOPUP_ITEM_CONFIRM),
        button1 = TEXT(OKAY),
        button2 = TEXT(CANCEL),
        OnAccept = function(data)
                ChatFrameEditBox:Insert(LootLink_GetLink(data[1], data[3]));
        end,
        showAlert = 1,
        timeout = 0,
        exclusive = 1,
        whileDead = 1,
        hideOnEscape = 1,
        };

StaticPopupDialogs["LOOTLINK_DRESSUP_CONFIRM"] = {
        text = TEXT(LOOTLINK_STATICPOPUP_ITEM_CONFIRM),
        button1 = TEXT(OKAY),
        button2 = TEXT(CANCEL),
        OnAccept = function(data)
                DressUpItemLink(LootLink_GetLink(data[1], data[3]));
        end,
        showAlert = 1,
        timeout = 0,
        exclusive = 1,
        whileDead = 1,
        hideOnEscape = 1,
        };
        

--------------------------------------------------------------------------------------------------
-- Type links - borrowed from Gazmik Fizzwidget's Linkerator
--------------------------------------------------------------------------------------------------

function LootLink_TypeLinks_LinkifyName(head, text, tail)
        if (head ~= "|h" and tail ~= "|h") then -- only linkify things text that isn't linked already
                local link = LootLink_GetLink(text);
                if (link) then return link; end
        end
        return head.."["..text.."]"..tail;
end

function LootLink_TypeLinks_ParseChatMessage(text)
        return string.gsub(text, "([|]?[h]?)%[(.-)%]([|]?[h]?)", LootLink_TypeLinks_LinkifyName);
end

-- thank you ckknight
function LootLink_TypeLinks_AutoComplete(text)
        local _,_,body,name = string.find( text, "(.*%[)(%S[^%[^%]]+)$" );
        if ( name ) then
                local textlen, namelen, found = strlen(text), strlen(name);
                if ( this.lastLength and this.lastItem and textlen > this.lastLength ) then
                        if string.find(strlower(this.lastItem), strlower(name)) then
                                this.lastLength = textlen;
                                this:SetText(body..this.lastItem);
                                this:HighlightText(textlen);
                                return 1;
                        end
                end
                this.lastLength = textlen;
                for k, v in ItemLinks do
                        if ( strlen(k) >= namelen and strlower(strsub(k,1,namelen)) == strlower(name) ) then
                                found = 1;
                                if ( (not this.itemComplete or not this.itemComplete[k]) ) then
                                        if ( not this.itemComplete ) then
                                                this.itemComplete = {};
                                        end
                                        this.ignoreTextChange = 1;
                                        this:SetText(body..k);
                                        this:HighlightText(textlen);
                                        this.lastItem = k;
                                        this.itemComplete[k] = 1;
                                        return 1;
                                end
                        end
                end
                if ( found ) then
                        this.lastLength = nil;
                        this.lastItem = nil;
                        this.itemComplete = nil;
                        return LootLink_TypeLinks_AutoComplete(text);
                end
        end
        this.lastItem = nil;
        this.itemComplete = nil;
end

-- Hooks

lOrig_ChatEdit_OnTextChanged = ChatEdit_OnTextChanged;
function ChatEdit_OnTextChanged()
        if ( LootLinkState.typelinks ) then
                local text = this:GetText();
                if (string.find(text, "^/script") or string.find(text, "^/dump")) then
                        -- don't parse
                else
                        local newtext = LootLink_TypeLinks_ParseChatMessage(text);
                        if ( text ~= newtext ) then
                                this:SetText(newtext);
                                return;
                        end
                end
        end
        lOrig_ChatEdit_OnTextChanged(this);
end

lOrig_ChatEdit_OnChar = ChatFrameEditBox:GetScript("OnChar");
function ChatEdit_OnChar()
        if ( LootLinkState.typelinks and LootLinkState.autotypelinks ) then
                local text = this:GetText();
                if ( string.find(text, "^/script") or string.find(text, "^/dump")) then
                else
                        LootLink_TypeLinks_AutoComplete(text);
                end
        elseif ( lOrig_ChatEdit_OnChar ) then
                lOrig_ChatEdit_OnChar(this);
        end
end
ChatFrameEditBox:SetScript("OnChar", ChatEdit_OnChar);

lOrig_ChatEdit_OnTabPressed = ChatEdit_OnTabPressed;
function ChatEdit_OnTabPressed()
        this.lastLength = nil;
        if ( LootLinkState.typelinks and LootLinkState.autotypelinks and this.lastItem ) then
                this:Insert("");
        else
                lOrig_ChatEdit_OnTabPressed();
        end
end

lOrig_ChatEdit_OnEnterPressed = ChatEdit_OnEnterPressed;
function ChatEdit_OnEnterPressed()
        if ( LootLinkState.typelinks and LootLinkState.autotypelinks and this.itemComplete ) then
                this:SetText(this:GetText().."]");
        else
                lOrig_ChatEdit_OnEnterPressed();
        end
        this.itemComplete = nil;
end