EXTVENDOR_VERSION = GetAddOnMetadata("ExtVendor", "Version");
EXTVENDOR_VERSION_ID = 10404;
EXTVENDOR_ITEMS_PER_SUBPAGE = MERCHANT_ITEMS_PER_PAGE;  -- transfer original page size to become "sub-pages"
EXTVENDOR_SUBPAGES_PER_PAGE = 2;                        -- number of sub-pages per page
MERCHANT_ITEMS_PER_PAGE = 20;                           -- overrides default value of base ui, default functions will handle page display accordingly
EXTVENDOR_HOOKS = {};
EXTVENDOR_DATA = {};
EXTVENDOR_SELECTED_QUALITY = 0;
EXTVENDOR_SPECIFIC_QUALITY = false;

local L = LibStub("AceLocale-3.0"):GetLocale("ExtVendor", true);

local EXTVENDOR_ARMOR_RANKS = {
    [L["ARMOR_CLOTH"]] = 1,
    [L["ARMOR_LEATHER"]] = 2,
    [L["ARMOR_MAIL"]] = 3,
    [L["ARMOR_PLATE"]] = 4,
};

local CLASS_WEAPON_PROFICIENCIES = {
    ["DEATHKNIGHT"] = { "1H_AXE", "1H_MACE", "1H_SWORD", "2H_AXE", "2H_MACE", "2H_SWORD", "POLEARM" },
    ["DRUID"]       = { "1H_MACE", "2H_MACE", "POLEARM", "STAFF", "DAGGER", "FIST" },
    ["HUNTER"]      = { "1H_AXE", "1H_SWORD", "2H_AXE", "2H_SWORD", "POLEARM", "STAFF", "DAGGER", "FIST", "BOW", "GUN", "CROSSBOW" },
    ["MAGE"]        = { "1H_SWORD", "STAFF", "DAGGER", "WAND" },
    ["MONK"]        = { "1H_AXE", "1H_MACE", "1H_SWORD", "POLEARM", "STAFF", "FIST" },
    ["PALADIN"]     = { "1H_AXE", "1H_MACE", "1H_SWORD", "2H_AXE", "2H_MACE", "2H_SWORD", "POLEARM" },
    ["PRIEST"]      = { "1H_MACE", "DAGGER", "STAFF" },
    ["ROGUE"]       = { "1H_AXE", "1H_MACE", "1H_SWORD", "DAGGER", "FIST" },
    ["SHAMAN"]      = { "1H_AXE", "1H_MACE", "2H_AXE", "2H_MACE", "DAGGER", "FIST", "STAFF" },
    ["WARLOCK"]     = { "1H_SWORD", "DAGGER", "STAFF" },
    ["WARRIOR"]     = { "1H_AXE", "1H_MACE", "1H_SWORD", "2H_AXE", "2H_MACE", "2H_SWORD", "DAGGER", "FIST", "POLEARM", "STAFF" },
};

--========================================
-- Initial load routine
--========================================
function ExtVendor_OnLoad(self)

    ExtVendor_RebuildMerchantFrame();

    ExtVendor_UpdateButtonPositions();

    EXTVENDOR_HOOKS["MerchantFrame_UpdateMerchantInfo"] = MerchantFrame_UpdateMerchantInfo;
    MerchantFrame_UpdateMerchantInfo = ExtVendor_UpdateMerchantInfo;
    EXTVENDOR_HOOKS["MerchantFrame_UpdateBuybackInfo"] = MerchantFrame_UpdateBuybackInfo;
    MerchantFrame_UpdateBuybackInfo = ExtVendor_UpdateBuybackInfo;

    MerchantFrame:HookScript("OnShow", ExtVendor_OnShow);
    MerchantFrame:HookScript("OnHide", ExtVendor_OnHide);

    self:RegisterEvent("ADDON_LOADED");

    SLASH_EXTVENDOR1 = "/evui";
    SlashCmdList["EXTVENDOR"] = ExtVendor_CommandHandler;

end

--========================================
-- Hooked merchant frame OnShow
--========================================
function ExtVendor_OnShow(self)

    MerchantFrameSearchBox:SetText("");
    if (EXTVENDOR_DATA['config']['stockfilter_defall']) then
        SetMerchantFilter(LE_LOOT_FILTER_ALL);
    end
    ExtVendor_SetMinimumQuality(0);

end

--========================================
-- Hooked merchant frame OnHide
--========================================
function ExtVendor_OnHide(self)

    CloseDropDownMenus();

end

--========================================
-- Event handler
--========================================
function ExtVendor_OnEvent(self, event, ...)
    
    if (event == "ADDON_LOADED") then
        local arg1 = ...;
        if (arg1 == "ExtVendor") then
            ExtVendor_Setup();
        end
    end

end

--========================================
-- Post-load setup
--========================================
function ExtVendor_Setup()

    local version = ExtVendor_CheckSetting("version", EXTVENDOR_VERSION_ID);

    EXTVENDOR_DATA['config']['version'] = EXTVENDOR_VERSION_ID;

    ExtVendor_CheckSetting("usable_items", false);
    ExtVendor_CheckSetting("hide_filtered", false);
    ExtVendor_CheckSetting("optimal_armor", false);
    ExtVendor_CheckSetting("show_suboptimal_armor", false);
    ExtVendor_CheckSetting("hide_known_recipes", false);
    ExtVendor_CheckSetting("stockfilter_defall", false);
    ExtVendor_CheckSetting("show_load_message", false);

    ExtVendor_CheckSetting("quickvendor_suboptimal", false);
    ExtVendor_CheckSetting("quickvendor_alreadyknown", false);
    ExtVendor_CheckSetting("quickvendor_unusable", false);
    ExtVendor_CheckSetting("quickvendor_whitegear", false);

    if (EXTVENDOR_DATA['config']['show_load_message']) then
        ExtVendor_Message(string.format(L["LOADED_MESSAGE"], EXTVENDOR_VERSION));
    end

end

--========================================
-- Check configuration setting, and
-- initialize with default value if not
-- present
--========================================
function ExtVendor_CheckSetting(field, default)

    if (not EXTVENDOR_DATA['config']) then
        EXTVENDOR_DATA['config'] = {};
    end
    if (EXTVENDOR_DATA['config'][field] == nil) then
        EXTVENDOR_DATA['config'][field] = default;
    end
    return EXTVENDOR_DATA['config'][field];
end

--========================================
-- Rearrange item slot positions
--========================================
function ExtVendor_UpdateButtonPositions(isBuyBack)

    local btn;
    local vertSpacing;

    if (isBuyBack) then
        vertSpacing = -30;
        horizSpacing = 50;
    else
        vertSpacing = -16;
        horizSpacing = 12;
    end
    for i = 1, MERCHANT_ITEMS_PER_PAGE, 1 do
        btn = _G["MerchantItem" .. i];
        if (isBuyBack) then
            if (i > BUYBACK_ITEMS_PER_PAGE) then
                btn:Hide();
            else
                if (i == 1) then
                    btn:SetPoint("TOPLEFT", MerchantFrame, "TOPLEFT", 64, -105);
                else
                    if ((i % 3) == 1) then
                        btn:SetPoint("TOPLEFT", _G["MerchantItem" .. (i - 3)], "BOTTOMLEFT", 0, vertSpacing);
                    else
                        btn:SetPoint("TOPLEFT", _G["MerchantItem" .. (i - 1)], "TOPRIGHT", horizSpacing, 0);
                    end
                end
            end
        else
            btn:Show();
            if ((i % EXTVENDOR_ITEMS_PER_SUBPAGE) == 1) then
                if (i == 1) then
                    btn:SetPoint("TOPLEFT", MerchantFrame, "TOPLEFT", 24, -70);
                else
                    btn:SetPoint("TOPLEFT", _G["MerchantItem" .. (i - (EXTVENDOR_ITEMS_PER_SUBPAGE - 1))], "TOPRIGHT", 12, 0);
                end
            else
                if ((i % 2) == 1) then
                    btn:SetPoint("TOPLEFT", _G["MerchantItem" .. (i - 2)], "BOTTOMLEFT", 0, vertSpacing);
                else
                    btn:SetPoint("TOPLEFT", _G["MerchantItem" .. (i - 1)], "TOPRIGHT", horizSpacing, 0);
                end
            end
        end
    end

end

--========================================
-- Show merchant page
--========================================
function ExtVendor_UpdateMerchantInfo()
    EXTVENDOR_HOOKS["MerchantFrame_UpdateMerchantInfo"]();
    ExtVendor_UpdateButtonPositions();

    -- set title and portrait
	MerchantNameText:SetText(UnitName("NPC"));
	SetPortraitTexture(MerchantFramePortrait, "NPC");

    -- locals
    local totalMerchantItems = GetMerchantNumItems();
    local visibleMerchantItems = 0;
    local indexes = {};
    local search = string.trim(MerchantFrameSearchBox:GetText());
	local name, texture, price, quantity, numAvailable, isUsable, extendedCost, r, g, b, notOptimal;
    local link, quality, itemLevel, itemMinLevel, itemType, itemSubType, itemStackCount, itemEquipLoc, itemSellPrice;
    local isFiltered = false;
    local isBoP = false;
    local isKnown = false;
    local isDarkmoonReplica = false;
    local checkAlreadyKnown;
    local kc;

    -- **************************************************
    --  Pre-check filtering if hiding filtered items
    -- **************************************************
    if (EXTVENDOR_DATA['config']['hide_filtered']) then
        visibleMerchantItems = 0;
        for i = 1, totalMerchantItems, 1 do
		    name, texture, price, quantity, numAvailable, isUsable, extendedCost = GetMerchantItemInfo(i);
            if (name) then
                isFiltered = false;
                isDarkmoonReplica = false;
                link = GetMerchantItemLink(i);
                quality = 1;
                isKnown = false;
                isBoP = false;

                -- check if item is a darkmoon faire replica
                if ((not isBoP) and (string.sub(name, 1, string.len(L["REPLICA"]) + 1) == (L["REPLICA"] .. " "))) then
                    isDarkmoonReplica = true;
                end
                -- get info from item link
                if (link) then
                    isBoP, isKnown = ExtVendor_GetExtendedItemInfo(link);
                    _, _, quality, itemLevel, itemMinLevel, itemType, itemSubType, itemStackCount, itemEquipLoc, _, itemSellPrice = GetItemInfo(link);
                end
                -- filter known recipes
                if (EXTVENDOR_DATA['config']['hide_known_recipes'] and isKnown) then
                    isFiltered = true;
                end
                -- check search filter
                if (string.len(search) > 0) then
                    if (not string.find(string.lower(name), string.lower(search), 1, true)) then
                        isFiltered = true;
                    end
                end
                -- check quality filter
                if (EXTVENDOR_SELECTED_QUALITY > 0) then
                    if ((quality < EXTVENDOR_SELECTED_QUALITY) or ((quality > EXTVENDOR_SELECTED_QUALITY) and EXTVENDOR_SPECIFIC_QUALITY)) then
                        isFiltered = true;
                    end
                end
                -- check usability filter
                if (EXTVENDOR_DATA['config']['usable_items'] and (not isUsable) and (quality ~= 7) and (not isDarkmoonReplica)) then
                    isFiltered = true;
                end
                -- check optimal armor filter
                if (EXTVENDOR_DATA['config']['optimal_armor'] and (not EXTVENDOR_DATA['config']['show_suboptimal_armor'])) then
                    if ((quality ~= 7) and isUsable and (not isDarkmoonReplica)) then
                        if (not ExtVendor_IsOptimalArmor(itemType, itemSubType, itemEquipLoc)) then
                            isFiltered = true;
                        end
                    end
                end
                -- ***** add item to list if not filtered *****
                if (not isFiltered) then
                    table.insert(indexes, i);
                    visibleMerchantItems = visibleMerchantItems + 1;
                end
            end
        end
    else
        -- no item hiding, add all items to list
        visibleMerchantItems = totalMerchantItems;
        for i = 1, totalMerchantItems, 1 do
            table.insert(indexes, i);
        end
    end

    -- validate current page shown
    if (MerchantFrame.page > math.max(1, math.ceil(visibleMerchantItems / MERCHANT_ITEMS_PER_PAGE))) then
        MerchantFrame.page = math.max(1, math.ceil(visibleMerchantItems / MERCHANT_ITEMS_PER_PAGE));
    end

    -- Show correct page count based on number of items shown
	MerchantPageText:SetFormattedText(MERCHANT_PAGE_NUMBER, MerchantFrame.page, math.ceil(visibleMerchantItems / MERCHANT_ITEMS_PER_PAGE));

    -- **************************************************
    --  Display items on merchant page
    -- **************************************************
    for i = 1, MERCHANT_ITEMS_PER_PAGE, 1 do
        local index = ((MerchantFrame.page - 1) * MERCHANT_ITEMS_PER_PAGE) + i;
		local itemButton = _G["MerchantItem" .. i .. "ItemButton"];
        itemButton.link = nil;
		local merchantButton = _G["MerchantItem" .. i];
		local merchantMoney = _G["MerchantItem" .. i .. "MoneyFrame"];
		local merchantAltCurrency = _G["MerchantItem" .. i .. "AltCurrencyFrame"];
        if (index <= visibleMerchantItems) then
			name, texture, price, quantity, numAvailable, isUsable, extendedCost = GetMerchantItemInfo(indexes[index]);
            if (name ~= nil) then
			    _G["MerchantItem"..i.."Name"]:SetText(name);
			    SetItemButtonCount(itemButton, quantity);
			    SetItemButtonStock(itemButton, numAvailable);
			    SetItemButtonTexture(itemButton, texture);

                -- update item's currency info
			    if ( extendedCost and (price <= 0) ) then
				    itemButton.price = nil;
				    itemButton.extendedCost = true;
				    itemButton.link = GetMerchantItemLink(indexes[index]);
				    itemButton.texture = texture;
				    MerchantFrame_UpdateAltCurrency(indexes[index], i);
				    merchantAltCurrency:ClearAllPoints();
				    merchantAltCurrency:SetPoint("BOTTOMLEFT", "MerchantItem"..i.."NameFrame", "BOTTOMLEFT", 0, 31);
				    merchantMoney:Hide();
				    merchantAltCurrency:Show();
			    elseif ( extendedCost and (price > 0) ) then
				    itemButton.price = price;
				    itemButton.extendedCost = true;
				    itemButton.link = GetMerchantItemLink(indexes[index]);
				    itemButton.texture = texture;
				    MerchantFrame_UpdateAltCurrency(indexes[index], i);
				    MoneyFrame_Update(merchantMoney:GetName(), price);
				    merchantAltCurrency:ClearAllPoints();
				    merchantAltCurrency:SetPoint("LEFT", merchantMoney:GetName(), "RIGHT", -14, 0);
				    merchantAltCurrency:Show();
				    merchantMoney:Show();
			    else
				    itemButton.price = price;
				    itemButton.extendedCost = nil;
				    itemButton.link = GetMerchantItemLink(indexes[index]);
				    itemButton.texture = texture;
				    MoneyFrame_Update(merchantMoney:GetName(), price);
				    merchantAltCurrency:Hide();
				    merchantMoney:Show();
			    end

                isDarkmoonReplica = false;
                isBoP = false;
                isKnown = false;
                isFiltered = false;

                quality = 1;
                if (itemButton.link) then
                    isBoP, isKnown = ExtVendor_GetExtendedItemInfo(itemButton.link);
                    _, _, quality, itemLevel, itemMinLevel, itemType, itemSubType, itemStackCount, itemEquipLoc, _, itemSellPrice = GetItemInfo(itemButton.link);
                end

                -- set color
                r, g, b = GetItemQualityColor(quality);
                _G["MerchantItem" .. i .. "Name"]:SetTextColor(r, g, b);

                -- check if item is a darkmoon faire replica
                if ((not isBOP) and (string.sub(name, 1, string.len(L["REPLICA"]) + 1) == (L["REPLICA"] .. " "))) then
                    isDarkmoonReplica = true;
                end

                -- check filtering
                if (not EXTVENDOR_DATA['config']['hide_filtered']) then
                    -- check search filter
                    if (string.len(search) > 0) then
                        if (not string.find(string.lower(name), string.lower(search), 1, true)) then
                            isFiltered = true;
                        end
                    end
                    -- check usability filter
                    if (EXTVENDOR_DATA['config']['usable_items'] and (not isUsable) and (quality ~= 7)) then
                        isFiltered = true;
                    end
                    -- check quality filter
                    if (EXTVENDOR_SELECTED_QUALITY > 0) then
                        if ((quality < EXTVENDOR_SELECTED_QUALITY) or ((quality > EXTVENDOR_SELECTED_QUALITY) and EXTVENDOR_SPECIFIC_QUALITY)) then
                            isFiltered = true;
                        end
                    end
                    -- filter known recipes
                    if (EXTVENDOR_DATA['config']['hide_known_recipes'] and isKnown) then
                        isFiltered = true;
                    end
                end

                -- filter suboptimal armor
                if ((quality ~= 7) and isUsable and (not isDarkmoonReplica)) then
                    if (EXTVENDOR_DATA['config']['optimal_armor']) then
                        if (not ExtVendor_IsOptimalArmor(itemType, itemSubType, itemEquipLoc)) then
                            isFiltered = true;
                        end
                    end
                end
                ExtVendor_SearchDimItem(_G["MerchantItem" .. i], isFiltered);

			    itemButton.hasItem = true;
			    itemButton:SetID(indexes[index]);
			    itemButton:Show();
                local colorMult = 1.0;
                local detailColor = {};
                local slotColor = {};
                -- unavailable items (limited stock, bought out) are darkened
			    if ( numAvailable == 0 ) then
                    colorMult = 0.5;
                end
			    if ( not isUsable ) then
                    slotColor = {r = 1.0, g = 0, b = 0};
                    detailColor = {r = 1.0, g = 0, b = 0};
			    else
                    if (notOptimal) then
                        slotColor = {r = 0.25, g = 0.25, b = 0.25};
                        detailColor = {r = 0.5, g = 0, b = 0};
                    else
                        slotColor = {r = 1.0, g = 1.0, b = 1.0};
                        detailColor = {r = 0.5, g = 0.5, b = 0.5};
                    end
			    end
			    SetItemButtonNameFrameVertexColor(merchantButton, detailColor.r * colorMult, detailColor.g * colorMult, detailColor.b * colorMult);
			    SetItemButtonSlotVertexColor(merchantButton, slotColor.r * colorMult, slotColor.g * colorMult, slotColor.b * colorMult);
			    SetItemButtonTextureVertexColor(itemButton, slotColor.r * colorMult, slotColor.g * colorMult, slotColor.b * colorMult);
			    SetItemButtonNormalTextureVertexColor(itemButton, slotColor.r * colorMult, slotColor.g * colorMult, slotColor.b * colorMult);
            end
        else
			itemButton.price = nil;
			itemButton.hasItem = nil;
			itemButton:Hide();
			SetItemButtonNameFrameVertexColor(merchantButton, 0.5, 0.5, 0.5);
			SetItemButtonSlotVertexColor(merchantButton,0.4, 0.4, 0.4);
			_G["MerchantItem"..i.."Name"]:SetText("");
			_G["MerchantItem"..i.."MoneyFrame"]:Hide();
			_G["MerchantItem"..i.."AltCurrencyFrame"]:Hide();
            ExtVendor_SearchDimItem(_G["MerchantItem" .. i], false);
        end
    end

	MerchantFrame_UpdateRepairButtons();

	-- Handle vendor buy back item
	local buybackName, buybackTexture, buybackPrice, buybackQuantity, buybackNumAvailable, buybackIsUsable = GetBuybackItemInfo(GetNumBuybackItems());
	if ( buybackName ) then
		MerchantBuyBackItemName:SetText(buybackName);
		SetItemButtonCount(MerchantBuyBackItemItemButton, buybackQuantity);
		SetItemButtonStock(MerchantBuyBackItemItemButton, buybackNumAvailable);
		SetItemButtonTexture(MerchantBuyBackItemItemButton, buybackTexture);
		MerchantBuyBackItemMoneyFrame:Show();
		MoneyFrame_Update("MerchantBuyBackItemMoneyFrame", buybackPrice);
		MerchantBuyBackItem:Show();
	else
		MerchantBuyBackItemName:SetText("");
		MerchantBuyBackItemMoneyFrame:Hide();
		SetItemButtonTexture(MerchantBuyBackItemItemButton, "");
		SetItemButtonCount(MerchantBuyBackItemItemButton, 0);
		-- Hide the tooltip upon sale
		if ( GameTooltip:IsOwned(MerchantBuyBackItemItemButton) ) then
			GameTooltip:Hide();
		end
	end

	-- Handle paging buttons
	if ( visibleMerchantItems > MERCHANT_ITEMS_PER_PAGE ) then
		if ( MerchantFrame.page == 1 ) then
			MerchantPrevPageButton:Disable();
		else
			MerchantPrevPageButton:Enable();
		end
		if ( MerchantFrame.page == ceil(visibleMerchantItems / MERCHANT_ITEMS_PER_PAGE) or visibleMerchantItems == 0) then
			MerchantNextPageButton:Disable();
		else
			MerchantNextPageButton:Enable();
		end
		MerchantPageText:Show();
		MerchantPrevPageButton:Show();
		MerchantNextPageButton:Show();
	else
		MerchantPageText:Hide();
		MerchantPrevPageButton:Hide();
		MerchantNextPageButton:Hide();
	end

	-- Show all merchant related items
	MerchantBuyBackItem:Show();
	MerchantFrameBottomLeftBorder:Show();
	MerchantFrameBottomRightBorder:Show();

	-- Hide buyback related items
    for i = 13, MERCHANT_ITEMS_PER_PAGE, 1 do
	    _G["MerchantItem" .. i]:Show();
    end
	--BuybackFrameTopLeft:Hide();
	--BuybackFrameTopRight:Hide();
	--BuybackFrameBotLeft:Hide();
	--BuybackFrameBotRight:Hide();
    --BuybackFrameTopMid:Hide();
    --BuybackFrameBotMid:Hide();

    -- update text color for buyback slot
    local link = GetBuybackItemLink(GetNumBuybackItems());
    if (link) then
        local _, _, quality = GetItemInfo(link);
        local r, g, b = GetItemQualityColor(quality);
        MerchantBuyBackItemName:SetTextColor(r, g, b);
    end

    if (ExtVendor_GetQuickVendorList()) then
        ExtVendor_SetJunkButtonState(true);
    else
        ExtVendor_SetJunkButtonState(false);
    end
end

--========================================
-- Show buyback page
--========================================
function ExtVendor_UpdateBuybackInfo()
    EXTVENDOR_HOOKS["MerchantFrame_UpdateBuybackInfo"]();
    ExtVendor_UpdateButtonPositions(true);
    --BuybackFrameTopMid:Show();
    --BuybackFrameBotMid:Show();

    -- apply coloring
    local btn, link, quality, r, g, b;
    for i = 1, BUYBACK_ITEMS_PER_PAGE, 1 do
        btn = _G["MerchantItem" .. i];
        if (btn) then
            link = GetBuybackItemLink(i);
            if (link) then
                _, _, quality = GetItemInfo(link);
                r, g, b = GetItemQualityColor(quality);
                _G["MerchantItem" .. i .. "Name"]:SetTextColor(r, g, b);
            end
            ExtVendor_SearchDimItem(btn, false);
        end
    end
end

--========================================
-- Rebuilds the merchant frame into
-- the extended design
--========================================
function ExtVendor_RebuildMerchantFrame()

    -- set the new width of the frame
    MerchantFrame:SetWidth(690);

    -- create new item buttons as needed
    for i = 1, MERCHANT_ITEMS_PER_PAGE, 1 do
        if (not _G["MerchantItem" .. i]) then
            CreateFrame("Frame", "MerchantItem" .. i, MerchantFrame, "MerchantItemTemplate");
        end
    end

    -- Thank you Blizzard for making the frame dynamically resizable for me. :D

    -- retexture the border element around the repair/buyback spots on the merchant tab
    MerchantFrameBottomLeftBorder:SetTexture("Interface\\AddOns\\ExtVendor\\textures\\bottomborder");
    MerchantFrameBottomRightBorder:SetTexture("Interface\\AddOns\\ExtVendor\\textures\\bottomborder");

    -- alter the position of the buyback item slot on the merchant tab
    MerchantBuyBackItem:ClearAllPoints();
    MerchantBuyBackItem:SetPoint("TOPLEFT", MerchantItem10, "BOTTOMLEFT", -14, -20);

    -- move the next/previous page buttons
    MerchantPrevPageButton:ClearAllPoints();
    MerchantPrevPageButton:SetPoint("CENTER", MerchantFrame, "BOTTOM", 30, 55);
    MerchantPageText:ClearAllPoints();
    MerchantPageText:SetPoint("BOTTOM", MerchantFrame, "BOTTOM", 160, 50);
    MerchantNextPageButton:ClearAllPoints();
    MerchantNextPageButton:SetPoint("CENTER", MerchantFrame, "BOTTOM", 290, 55);

    -- currency insets
    MerchantExtraCurrencyInset:ClearAllPoints();
    MerchantExtraCurrencyInset:SetPoint("BOTTOMRIGHT", MerchantMoneyInset, "BOTTOMLEFT", 0, 0);
    MerchantExtraCurrencyInset:SetPoint("TOPLEFT", MerchantMoneyInset, "TOPLEFT", -165, 0);
    MerchantExtraCurrencyBg:ClearAllPoints();
    MerchantExtraCurrencyBg:SetPoint("TOPLEFT", MerchantExtraCurrencyInset, "TOPLEFT", 3, -2);
    MerchantExtraCurrencyBg:SetPoint("BOTTOMRIGHT", MerchantExtraCurrencyInset, "BOTTOMRIGHT", -3, 2);

    -- add the search box
    local editbox = CreateFrame("EditBox", "MerchantFrameSearchBox", MerchantFrame, "EV_SearchBoxTemplate");
    editbox:SetWidth(200);
    editbox:SetHeight(24);
    editbox:SetPoint("TOPRIGHT", MerchantFrame, "TOPRIGHT", -15, -30);
    editbox:SetAutoFocus(false);
    editbox:SetScript("OnTextChanged", ExtVendor_UpdateDisplay);
    editbox:SetMaxLetters(30);

    -- add quick-vendor button
    local junkBtn = CreateFrame("Button", "MerchantFrameSellJunkButton", MerchantFrame);
    junkBtn:SetWidth(32);
    junkBtn:SetHeight(32);
    junkBtn:SetPoint("TOPLEFT", MerchantFrame, "TOPLEFT", 70, -27);
    junkBtn.tooltip = L["QUICKVENDOR_BUTTON_TOOLTIP"];
    junkBtn:SetScript("OnClick", ExtVendor_StartQuickVendor);
    junkBtn:SetScript("OnEnter", ExtVendor_ShowButtonTooltip);
    junkBtn:SetScript("OnLeave", ExtVendor_HideButtonTooltip);
    junkBtn:SetPushedTexture("Interface\\Buttons\\UI-Quickslot-Depress");
    junkBtn:SetHighlightTexture("Interface\\Buttons\\ButtonHilight-Square", "ADD");
    junkBtnIcon = junkBtn:CreateTexture("MerchantFrameSellJunkButtonIcon", "BORDER");
    junkBtnIcon:SetTexture("Interface\\Icons\\Inv_Misc_Bag_10");
    junkBtnIcon:SetPoint("TOPLEFT", junkBtn, "TOPLEFT", 0, 0);
    junkBtnIcon:SetPoint("BOTTOMRIGHT", junkBtn, "BOTTOMRIGHT", 0, 0);

    -- filter button
    local filterBtn = CreateFrame("Button", "MerchantFrameFilterButton", MerchantFrame, "UIMenuButtonStretchTemplate");
    filterBtn:SetText(FILTER);
    filterBtn:SetPoint("RIGHT", MerchantFrameSearchBox, "LEFT", -30, 0);
    filterBtn:SetWidth(80);
    filterBtn:SetHeight(22);
    filterBtn:SetScript("OnClick", ExtVendor_DisplayFilterDropDown);
    MerchantFrameFilterButtonRightArrow:Show();

    -- hide the new stock filter dropdown
    MerchantFrameLootFilter:Hide();

    -- filter options dropdown
    local filterDropdown = CreateFrame("Frame", "MerchantFrameFilterDropDown", UIParent, "UIDropDownMenuTemplate");

    -- create a new tooltip object for handling item tooltips in the background
    evTooltip = CreateFrame("GameTooltip", "ExtVendorHiddenTooltip", UIParent, "GameTooltipTemplate");

end

--========================================
-- Performs additional updates to main
-- display - fades items for searching
-- and applies quality colors to names
--========================================
function ExtVendor_UpdateDisplay()

    if (MerchantFrame.selectedTab == 1) then
        ExtVendor_UpdateMerchantInfo();
    elseif (MerchantFrame.selectedTab == 2) then
        ExtVendor_UpdateBuybackInfo();
    end

    CloseDropDownMenus();

end

--========================================
-- Dims or shows an item frame
--========================================
function ExtVendor_SearchDimItem(itemFrame, isDimmed)

    if (not itemFrame) then return; end

    local alpha;

    if (isDimmed) then
        alpha = 0.2;
    else
        alpha = 1;
    end
    itemFrame:SetAlpha(alpha);

    local btn = _G[itemFrame:GetName() .. "ItemButton"];
    if (isDimmed) then
        btn:Disable();
    else
        btn:Enable();
    end

end

--========================================
-- Gets a list of junk items in the
-- player's bags
--========================================
function ExtVendor_GetQuickVendorList()

    local junk = {};
    local count, name, quality, price, maxStack;
    local isBoP, isKnown, reqClasses;

    for bag = 0, 4, 1 do
        if (GetContainerNumSlots(bag)) then
            for slot = 1, GetContainerNumSlots(bag), 1 do
                local isJunk = false;
                _, count = GetContainerItemInfo(bag, slot);
                link = GetContainerItemLink(bag, slot);
                if (link and count) then
                    isBoP, isKnown, reqClasses = ExtVendor_GetExtendedItemInfo(link);
                    name, _, quality, _, _, itemType, itemSubType, maxStack, itemEquipLoc, _, price = GetItemInfo(link);

                    -- make sure the item has a vendor price
                    if (price > 0) then

                        local isJunk, reason = ExtVendor_IsItemQuickVendor(quality, isBoP, isKnown, itemType, itemSubType, itemEquipLoc, reqClasses);

                        -- if the item meets requirements, add it to the list
                        if (isJunk) then
                            table.insert(junk, {name = name, quality = quality, count = count, maxStack = maxStack, stackPrice = count * price, reason = reason});
                        end
                    end
                end
            end
        end
    end

    table.sort(junk, function(a, b) return (a.stackPrice < b.stackPrice); end);

    if (table.maxn(junk) > 0) then
        return junk;
    end
    return nil;

end

--========================================
-- Show confirmation for selling all
-- junk items
--========================================
function ExtVendor_StartQuickVendor(self)

    local junk = ExtVendor_GetQuickVendorList();

    if (junk) then
        ExtVendor_ShowJunkPopup(junk);
    end

end

--========================================
-- Returns whether an item should
-- quick-vendor based on quality, type,
-- if it is already known, soulbound
--========================================
function ExtVendor_IsItemQuickVendor(quality, isSoulbound, alreadyKnown, type, subType, equipSlot, requiredClasses)
    -- NEVER quick-vendor legendary or heirloom items. EVER. Ever. ever.
    if (quality > 4) then
        return false;
    end
    -- *** Poor (grey) items ***
    if (quality == 0) then
        return true, L["QUICKVENDOR_REASON_POORQUALITY"];
    end
    -- *** Common (white) gear ***
    if (EXTVENDOR_DATA['config']['quickvendor_whitegear']) then
        if (quality == 1) then
            if (not ExtVendor_IsBlacklisted("WHITE_GEAR", name)) then
                if (type == "Armor") then
                    if ((subType == L["ARMOR_CLOTH"]) or (subType == L["ARMOR_LEATHER"]) or (subType == L["ARMOR_MAIL"]) or (subType == L["ARMOR_PLATE"])) then
                        if ((equipSlot ~= "INVTYPE_TABARD") and (equipSlot ~= "INVTYPE_SHIRT")) then
                            return true, L["QUICKVENDOR_REASON_WHITEGEAR"];
                        end
                    end
                elseif (type == "Weapon") then
                    if ((subType == L["WEAPON_1H_AXE"]) or (subType == L["WEAPON_1H_MACE"]) or (subType == L["WEAPON_1H_SWORD"]) or (subType == L["WEAPON_2H_AXE"])
                    or (subType == L["WEAPON_2H_MACE"]) or (subType == L["WEAPON_2H_SWORD"]) or (subType == L["WEAPON_POLEARM"]) or (subType == L["WEAPON_DAGGER"])
                    or (subType == L["WEAPON_FIST"]) or (subType == L["WEAPON_STAFF"]) or (subType == L["WEAPON_WAND"]) or (subType == L["WEAPON_BOW"])
                    or (subType == L["WEAPON_GUN"]) or (subType == L["WEAPON_CROSSBOW"])) then
                        return true, L["QUICKVENDOR_REASON_WHITEGEAR"];
                    end
                end
            end
        end
    end
    -- Soulbound stuff
    if (isSoulbound) then
        -- *** "Already Known" ***
        if (EXTVENDOR_DATA['config']['quickvendor_alreadyknown']) then
            if (alreadyKnown) then
                return true, L["QUICKVENDOR_REASON_ALREADYKNOWN"];
            end
        end
        -- *** Unusable (class-restricted, unusable armor/weapon types) ***
        if (EXTVENDOR_DATA['config']['quickvendor_unusable']) then
            if (not ExtVendor_ClassIsAllowed(UnitClass("player"), requiredClasses)) then
                return true, L["QUICKVENDOR_REASON_CLASSRESTRICTED"];
            end
            if (not ExtVendor_IsUsableArmorType(type, subType, equipSlot)) then
                return true, L["QUICKVENDOR_REASON_UNUSABLEARMOR"];
            end
            if (not ExtVendor_IsUsableWeaponType(type, subType, equipSlot)) then
                return true, L["QUICKVENDOR_REASON_UNUSABLEWEAPON"];
            end
        end
        -- *** Sub-optimal armor ***
        if (EXTVENDOR_DATA['config']['quickvendor_suboptimal']) then
            if (not ExtVendor_IsOptimalArmor(type, subType, equipSlot)) then
                return true, L["QUICKVENDOR_REASON_SUBOPTIMAL"];
            end
        end
    end
    -- nothing matched = do not quickvendor
    return false;
end

--========================================
-- Performs quick-vendor
--========================================
function ExtVendor_ConfirmQuickVendor()
    local link, count, name, color, quality, price, maxStack, quantity;
    local totalPrice = 0;
    local itemsOnLine = 0;
    local numItemsSold = 0;
    local itemsSold = "";
    local soldPref = L["SOLD"];

    if (not MerchantFrame:IsShown()) then return; end

    for bag = 0, 4, 1 do
        if (GetContainerNumSlots(bag)) then
            for slot = 1, GetContainerNumSlots(bag), 1 do
                _, count = GetContainerItemInfo(bag, slot);
                link = GetContainerItemLink(bag, slot);
                if (link and count) then
                    name, _, quality, _, _, itemType, itemSubType, maxStack, itemEquipLoc, _, price = GetItemInfo(link);
                    local isBoP, isKnown, reqClasses = ExtVendor_GetExtendedItemInfo(link);

                    if (price > 0) then
                        if (ExtVendor_IsItemQuickVendor(quality, isBoP, isKnown, itemType, itemSubType, itemEquipLoc, reqClasses)) then
                            PickupContainerItem(bag, slot);
                            PickupMerchantItem(0);
                            _, _, _, color = GetItemQualityColor(quality);
                            if (itemsOnLine > 0) then
                                itemsSold = itemsSold .. ", ";
                            end
                            if (maxStack > 1) then
                                quantity = "x" .. count;
                            else
                                quantity = "";
                            end
                            itemsSold = itemsSold .. "|c" .. color .. "[" .. name .. "]|r" .. quantity;
                            itemsOnLine = itemsOnLine + 1;
                            if (itemsOnLine == 12) then
                                DEFAULT_CHAT_FRAME:AddMessage(soldPref .. " " .. itemsSold, ChatTypeInfo["SYSTEM"].r, ChatTypeInfo["SYSTEM"].g, ChatTypeInfo["SYSTEM"].b, GetChatTypeIndex("SYSTEM"));
                                soldPref = "    ";
                                itemsSold = "";
                                itemsOnLine = 0;
                            end
                            totalPrice = totalPrice + (price * count);
                            numItemsSold = numItemsSold + 1;
                        end
                    end
                end
            end
        end
    end

    if (itemsOnLine > 0) then
        DEFAULT_CHAT_FRAME:AddMessage(soldPref .. " " .. itemsSold, ChatTypeInfo["SYSTEM"].r, ChatTypeInfo["SYSTEM"].g, ChatTypeInfo["SYSTEM"].b, GetChatTypeIndex("SYSTEM"));
    end
    if (numItemsSold > 0) then
        DEFAULT_CHAT_FRAME:AddMessage(string.format(L["JUNK_MONEY_EARNED"], "|cffffffff" .. ExtVendor_FormatMoneyString(totalPrice)), ChatTypeInfo["SYSTEM"].r, ChatTypeInfo["SYSTEM"].g, ChatTypeInfo["SYSTEM"].b, GetChatTypeIndex("SYSTEM"));
    end
end

--========================================
-- Show button tooltips
--========================================
function ExtVendor_ShowButtonTooltip(self)

    if (self.tooltip) then
        GameTooltip:SetOwner(self, "ANCHOR_RIGHT");
        GameTooltip:SetText(self.tooltip);
        GameTooltip:Show();
    end

end

--========================================
-- Hide button tooltips
--========================================
function ExtVendor_HideButtonTooltip(self)

    if (GameTooltip:GetOwner() == self) then
        GameTooltip:Hide();
    end

end

--========================================
-- Enable/disable the sell junk button
--========================================
function ExtVendor_SetJunkButtonState(state)
    if (state) then
        MerchantFrameSellJunkButton:Enable();
        MerchantFrameSellJunkButtonIcon:SetDesaturated(false);
    else
        MerchantFrameSellJunkButton:Disable();
        MerchantFrameSellJunkButtonIcon:SetDesaturated(true);
    end
end

--========================================
-- Gold/silver/copper money formatting
--========================================
function ExtVendor_FormatMoneyString(value, trailing)

    value = tonumber(value) or 0;

    local gold = math.floor(value / 10000);
    local silver = math.floor(value / 100) % 100;
    local copper = value % 100;

    local disp = "";

    if (gold > 0) then
        disp = disp .. format(GOLD_AMOUNT_TEXTURE, gold, 0, 0) .. " ";
    end
    if ((silver > 0) or (trailing and (gold > 0))) then
        disp = disp .. format(SILVER_AMOUNT_TEXTURE, silver, 0, 0) .. " ";
    end
    if ((copper > 0) or (trailing and ((gold > 0) or (silver > 0)))) then
        disp = disp .. format(COPPER_AMOUNT_TEXTURE, copper, 0, 0);
    end

    return disp;

end

--========================================
-- Initialize the item quality dropdown
--========================================
function ExtVendor_InitQualityFilter()

    local info = {};
    local color;
    for i = 0, 7, 1 do
        -- skip common, legendary and artifact qualities for now
        if ((i ~= 1) and (i ~= 5) and (i ~= 6)) then
            if (i == 0) then
                info.text = ALL;
            else
                _, _, _, color = GetItemQualityColor(i);
                info.text = "|c" .. color .. _G["ITEM_QUALITY" .. i .. "_DESC"];
            end
            info.value = i;
            info.checked = nil;
            info.func = ExtVendor_SelectFilterQuality;
            UIDropDownMenu_AddButton(info);
        end
    end

end

--========================================
-- Handler for selecting an item quality
--========================================
function ExtVendor_SelectFilterQuality(self)

    UIDropDownMenu_SetSelectedValue(MerchantFrameQualityFilter, self.value);
    EXTVENDOR_SELECTED_QUALITY = self.value;

    ExtVendor_UpdateDisplay();

end

--========================================
-- Show the filter options dropdown menu
--========================================
function ExtVendor_DisplayFilterDropDown(self)

    local className = UnitClass("player");
    local currFilter = GetMerchantFilter();
    stockFilters = { { text = className, checked = ((currFilter ~= LE_LOOT_FILTER_BOE) and (currFilter ~= LE_LOOT_FILTER_ALL)), func = function() ExtVendor_SetStockFilter(LE_LOOT_FILTER_CLASS); end } };
    local numSpecs = GetNumSpecializations();
    for i = 1, numSpecs do
        local _, name, _, icon = GetSpecializationInfo(i);
        table.insert(stockFilters, { text = name, checked = (currFilter == LE_LOOT_FILTER_SPEC1 + i - 1), leftPadding = 10, func = function() ExtVendor_SetStockFilter(LE_LOOT_FILTER_SPEC1 + i - 1); end });
    end
    table.insert(stockFilters, { text = ALL_SPECS, checked = (currFilter == LE_LOOT_FILTER_CLASS), leftPadding = 10, func = function() ExtVendor_SetStockFilter(LE_LOOT_FILTER_CLASS); end });
    table.insert(stockFilters, { text = ITEM_BIND_ON_EQUIP, checked = (currFilter == LE_LOOT_FILTER_BOE), func = function(self) ExtVendor_SetStockFilter(LE_LOOT_FILTER_BOE); end });
    table.insert(stockFilters, { text = ALL, checked = (currFilter == LE_LOOT_FILTER_ALL), func = function() ExtVendor_SetStockFilter(LE_LOOT_FILTER_ALL); end });

    local menu = {
        { text = L["HIDE_UNUSABLE"], checked = EXTVENDOR_DATA['config']['usable_items'], func = function() ExtVendor_ToggleSetting("usable_items"); ExtVendor_UpdateDisplay(); end },
        { text = L["HIDE_FILTERED"], checked = EXTVENDOR_DATA['config']['hide_filtered'], func = function() ExtVendor_ToggleSetting("hide_filtered"); ExtVendor_UpdateDisplay(); end },
        { text = L["HIDE_KNOWN_RECIPES"], checked = EXTVENDOR_DATA['config']['hide_known_recipes'], func = function() ExtVendor_ToggleSetting("hide_known_recipes"); ExtVendor_UpdateDisplay(); end },
        { text = L["FILTER_SUBOPTIMAL"], checked = EXTVENDOR_DATA['config']['optimal_armor'], func = function() ExtVendor_ToggleSetting("optimal_armor"); ExtVendor_UpdateDisplay(); end },
        { text = L["QUALITY_FILTER_MINIMUM"], hasArrow = true, notCheckable = true,
            menuList = {
                { text = ALL, checked = (EXTVENDOR_SELECTED_QUALITY == 0), func = function() ExtVendor_SetMinimumQuality(0); end },
                { text = ITEM_QUALITY_COLORS[2].hex .. ITEM_QUALITY2_DESC, checked = (EXTVENDOR_SELECTED_QUALITY == 2), func = function() ExtVendor_SetMinimumQuality(2); end },
                { text = ITEM_QUALITY_COLORS[3].hex .. ITEM_QUALITY3_DESC, checked = (EXTVENDOR_SELECTED_QUALITY == 3), func = function() ExtVendor_SetMinimumQuality(3); end },
                { text = ITEM_QUALITY_COLORS[4].hex .. ITEM_QUALITY4_DESC, checked = (EXTVENDOR_SELECTED_QUALITY == 4), func = function() ExtVendor_SetMinimumQuality(4); end },
                { text = ITEM_QUALITY_COLORS[7].hex .. ITEM_QUALITY7_DESC, checked = (EXTVENDOR_SELECTED_QUALITY == 7), func = function() ExtVendor_SetMinimumQuality(7); end },
            },
        },
        { text = L["QUALITY_FILTER_SPECIFIC"], hasArrow = true, notCheckable = true,
            menuList = {
                { text = ALL, checked = (EXTVENDOR_SELECTED_QUALITY == 0), func = function() ExtVendor_SetMinimumQuality(0); end },
                { text = ITEM_QUALITY_COLORS[1].hex .. ITEM_QUALITY1_DESC, checked = (EXTVENDOR_SELECTED_QUALITY == 1), func = function() ExtVendor_SetSpecificQuality(1); end },
                { text = ITEM_QUALITY_COLORS[2].hex .. ITEM_QUALITY2_DESC, checked = (EXTVENDOR_SELECTED_QUALITY == 2), func = function() ExtVendor_SetSpecificQuality(2); end },
                { text = ITEM_QUALITY_COLORS[3].hex .. ITEM_QUALITY3_DESC, checked = (EXTVENDOR_SELECTED_QUALITY == 3), func = function() ExtVendor_SetSpecificQuality(3); end },
                { text = ITEM_QUALITY_COLORS[4].hex .. ITEM_QUALITY4_DESC, checked = (EXTVENDOR_SELECTED_QUALITY == 4), func = function() ExtVendor_SetSpecificQuality(4); end },
                { text = ITEM_QUALITY_COLORS[7].hex .. ITEM_QUALITY7_DESC, checked = (EXTVENDOR_SELECTED_QUALITY == 7), func = function() ExtVendor_SetSpecificQuality(7); end },
            },
        },
        { text = L["STOCK_FILTER"], hasArrow = true, notCheckable = true, menuList = stockFilters },
    };
    EasyMenu(menu, MerchantFrameFilterDropDown, self, 0, 0, "MENU", 1);
end

--========================================
-- Sets the 'stock' filter and updates
-- the vendor display
--========================================
function ExtVendor_SetStockFilter(index)
    SetMerchantFilter(index);
    ExtVendor_UpdateDisplay();
end

--========================================
-- Determine if a piece of armor is the
-- best type for the player's class
-- (cloth/leather/mail/plate)
--========================================
function ExtVendor_IsOptimalArmor(type, subType, slot)
    if (type == L["ARMOR"]) then
        if (slot == "INVTYPE_CLOAK") then
            return true;
        end
        if ((subType == L["ARMOR_CLOTH"]) or (subType == L["ARMOR_LEATHER"]) or (subType == L["ARMOR_MAIL"]) or (subType == L["ARMOR_PLATE"])) then
            if (subType ~= ExtVendor_GetOptimalArmorType()) then
                return false;
            end
        end
    end
    return true;
end

--========================================
-- Returns the optimal armor type for the
-- player's class (factors in level for
-- hunters, shamans, paladins and
-- warriors), as well as the highest
-- armor type the class can ever wear
-- (regardless of level)
--========================================
function ExtVendor_GetOptimalArmorType()

    local _, cls = UnitClass("player");
    local lvl = UnitLevel("player");

    local optArmor, maxArmor;

    if ((cls == "MAGE") or (cls == "WARLOCK") or (cls == "PRIEST")) then
        optArmor = L["ARMOR_CLOTH"];
        maxArmor = L["ARMOR_CLOTH"];
    elseif ((cls == "ROGUE") or (cls == "DRUID") or (cls == "MONK")) then
        optArmor = L["ARMOR_LEATHER"];
        maxArmor = L["ARMOR_LEATHER"];
    elseif ((cls == "HUNTER") or (cls == "SHAMAN")) then
        if (lvl >= 40) then
            optArmor = L["ARMOR_MAIL"];
        else
            optArmor = L["ARMOR_LEATHER"];
        end
        maxArmor = L["ARMOR_MAIL"];
    elseif ((cls == "PALADIN") or (cls == "WARRIOR") or (cls == "DEATHKNIGHT")) then
        if (lvl >= 40) then
            optArmor = L["ARMOR_PLATE"];
        else
            optArmor = L["ARMOR_MAIL"];
        end
        maxArmor = L["ARMOR_PLATE"];
    end
    return optArmor, maxArmor;
end

--========================================
-- Returns whether or not the character's
-- class can EVER wear armor of the given
-- type (e.g. mages can NEVER wear
-- leather or higher, shamans can NEVER
-- wear plate, etc.)
--========================================
function ExtVendor_IsUsableArmorType(type, subType, slot)
    local _, maxArmor = ExtVendor_GetOptimalArmorType();

    if ((type == L["ARMOR"]) and (slot ~= "INVTYPE_CLOAK")) then
        if ((subType == L["ARMOR_CLOTH"]) or (subType == L["ARMOR_LEATHER"]) or (subType == L["ARMOR_MAIL"]) or (subType == L["ARMOR_PLATE"])) then
            if (EXTVENDOR_ARMOR_RANKS[subType] > EXTVENDOR_ARMOR_RANKS[maxArmor]) then
                return false;
            end
        end
    end
    return true;
end

--========================================
-- Returns whether or not the character's
-- class can use the given weapon type
-- based on class proficiencies
--========================================
function ExtVendor_IsUsableWeaponType(type, subType, slot)
    if (type == L["WEAPON"]) then
        local _, cls = UnitClass("player");
        for index, wt in pairs(CLASS_WEAPON_PROFICIENCIES[cls]) do
            if (L["WEAPON_" .. wt] == subType) then
                return true;
            end
        end
        return false;
    end
    return true;
end

--========================================
-- Toggles a boolean config setting
--========================================
function ExtVendor_ToggleSetting(name)
    if (EXTVENDOR_DATA['config'][name]) then
        EXTVENDOR_DATA['config'][name] = false;
    else
        EXTVENDOR_DATA['config'][name] = true;
    end
end

--========================================
-- Sets the minimum quality filter
--========================================
function ExtVendor_SetMinimumQuality(quality)
    EXTVENDOR_SELECTED_QUALITY = math.max(0, math.min(7, quality));
    EXTVENDOR_SPECIFIC_QUALITY = false;
    ExtVendor_UpdateDisplay();
end

--========================================
-- Sets the specific quality filter
--========================================
function ExtVendor_SetSpecificQuality(quality)
    EXTVENDOR_SELECTED_QUALITY = math.max(0, math.min(7, quality));
    EXTVENDOR_SPECIFIC_QUALITY = true;
    ExtVendor_UpdateDisplay();
end

--========================================
-- Output message to chat frame
--========================================
function ExtVendor_Message(msg)
    DEFAULT_CHAT_FRAME:AddMessage("|cffffff00<" .. L["ADDON_TITLE"] .. ">|r " .. msg);
end

--========================================
-- Slash command handler
--========================================
function ExtVendor_CommandHandler(cmd)

    InterfaceOptionsFrame_OpenToCategory(ExtVendorConfigContainer);

end

--========================================
-- Retrieve additional item info via the
-- item's tooltip
--========================================
function ExtVendor_GetExtendedItemInfo(link)

    -- set up return values
    local isBoP = false
    local isKnown = false;
    local classes = {};

    -- generate item tooltip in hidden tooltip object
    ExtVendorHiddenTooltip:SetOwner(UIParent, "ANCHOR_LEFT");
    ExtVendorHiddenTooltip:SetHyperlink(link);

    for cl = 2, ExtVendorHiddenTooltip:NumLines(), 1 do
        local checkLine = _G["ExtVendorHiddenTooltipTextLeft" .. cl]:GetText();
        if (checkLine) then

            -- check if item binds when picked up
            if (cl <= 3) then
                if (checkLine == ITEM_BIND_ON_PICKUP) then
                    isBoP = true;
                end
            end

            -- check for "Already Known"
            if (cl <= 4) then
                if (checkLine == ITEM_SPELL_KNOWN) then
                    isKnown = true;
                end
            end

            -- check for "Classes: xxx"
            local checkClasses = ExtVendor_GetRequiredClasses(checkLine);
            if (checkClasses) then
                classes = checkClasses;
            end

        end
    end

    ExtVendorHiddenTooltip:Hide();

    return isBoP, isKnown, classes;
end

--========================================
-- Returns a list of required classes
-- based on the "Classes:" line of an
-- item tooltip
--========================================
function ExtVendor_GetRequiredClasses(tooltipString)
    if (string.find(tooltipString, "Classes:")) then
        local out = {};
        for w in string.gmatch(tooltipString, "Classes:([(%s*)(%a+)(,*)]+)") do
            for x in string.gmatch(w, "(%a+)") do
                table.insert(out, x);
            end
        end
        return out;
    end
    return nil;
end

--========================================
-- Returns whether or not the specified
-- class is in the given list of classes
--========================================
function ExtVendor_ClassIsAllowed(class, classes)
    if (table.maxn(classes) > 0) then
        for index, name in pairs(classes) do
            if (class == name) then
                return true;
            end
        end
        return false;
    end
    return true;
end

--========================================
-- Returns whether or not the specified
-- item name is blacklisted under the
-- given category
--========================================
function ExtVendor_IsBlacklisted(category, itemName)

    if (EXTVENDOR_QUICKVENDOR_BLACKLIST[category]) then
        for idx, name in pairs(EXTVENDOR_QUICKVENDOR_BLACKLIST[category]) do
            if (name == itemName) then
                return true;
            end
        end
    end

    return false;

end
