vanilla-wow-addons – Rev 1

Subversion Repositories:
Rev:
------------------------------------------------------
-- AutoCraft.lua
-- Originally based on BetterTradeSkill by Matt Perry (http://somewhere.fscked.org)
------------------------------------------------------
FAC_VERSION = "11200.1";
------------------------------------------------------

FAC_Config = {};
FAC_Config.AutoIterate = false;

local P = {};
AutoCraft = P;

P.OrderedQueue = {};
P.QueuedReagents = {};
P.ManuallyPaused = false;
P.SkillCache = {};
P.ReagentCache = {};
P.NoOverride = true;
P.TradeSkillsSeen = {};

local MAX_QUEUE_BUTTONS = 10;
local CACHE_TIME = 5;   -- We cache the returns from our hooked GetTradeSkillInfo because it can be called many times a second without actually having any relevant conditions be different. This keeps us from repeatedly recalculating it when not needed.
local QUEUE_ADVANCE_DELAY = 1; -- In 1.8 we seem to have problems if we try to advance the queue too soon after finishing an item (if the item we just finished is needed to craft the next item in the queue, WoW thinks we don't have it yet and thus can't craft the next item). May have something to do with lag. So, we wait this many seconds before trying to advance the queue.
local TRADESKILL_UPDATE_TIME = 5;

function P.GetTradeSkillInfo(index)
        if (P.SkillCache[index] and (GetTime() - P.SkillCache[index].time < CACHE_TIME)) then
                local info = P.SkillCache[index].info;
                return info.skillName, info.difficulty, info.numAvailable, info.isExpanded, info.itemName;
        end
                
        local skillName, difficulty, numAvailable, isExpanded = P.Orig_GetTradeSkillInfo(index);

        if (skillName == nil or difficulty == "header") then
                -- something calls us a lot when the TradeSkillFrame is first shown, and Orig_GetTradeSkillInfo returns bogus at that time.
                -- just pass along bogus returns so we don't get bogged down in calculation or cache checks;
                -- we'll get called again and be able to use the right info soon enough.
                return skillName, difficulty, numAvailable, isExpanded, nil;
        end
        
        -- Adjustment #1: recalculate numAvailable based on our altered GetTradeSkillReagentInfo (normally its value is calculated by the client)
        local reagentTally = {};
        for reagentIndex = 1, GetTradeSkillNumReagents(index) do
                local reagentName, _, reagentCount, playerReagentCount = P.GetTradeSkillReagentInfo(index, reagentIndex);
                local completeItemsWorth = math.max(math.floor(playerReagentCount / reagentCount), 0); 
                table.insert(reagentTally, completeItemsWorth); -- how many could we make if this were the only reagent in question?
        end
        numAvailable = (math.min(unpack(reagentTally)) or 0); -- this is how many we can really make if we include already-queued reagents
        
        -- Adjustment #2 (TODO): how many of this item could we make if we were to make/buy the reagents right now?
        
        -- For testing
        --numAvailable = math.random(5);
        
        local itemName = P.GetTradeItemName(index);
        if (skillName ~= nil) then
                P.SkillCache[index] = { time=GetTime(), info={ skillName=skillName, difficulty=difficulty, numAvailable=numAvailable, isExpanded=isExpanded, itemName=itemName } };
        end
        
        return skillName, difficulty, numAvailable, isExpanded, itemName;
end

function P.GetTradeSkillReagentInfo(skillIndex, reagentIndex)
        local index = skillIndex.."."..reagentIndex;
        if (P.ReagentCache[index] and (GetTime() - P.ReagentCache[index].time < CACHE_TIME)) then
                local info = P.ReagentCache[index].info;
                return info.reagentName, info.reagentTexture, info.reagentCount, info.playerReagentCount;
        end

        local reagentName, reagentTexture, reagentCount, playerReagentCount = P.Orig_GetTradeSkillReagentInfo(skillIndex, reagentIndex);
        
        if (reagentName == nil or reagentTexture == nil) then
                -- something calls us a lot when the TradeSkillFrame is first shown, and Orig_GetTradeSkillReagentInfo returns bogus then.
                -- just pass along bogus returns so we don't get bogged down in calculation or cache checks;
                -- we'll get called again and be able to use the right info soon enough.
                return reagentName, reagentTexture, reagentCount, playerReagentCount;
        end
        
        -- Adjustment #1: if you've queued some of this item, add them to effective "inventory" for determining what you can make from it.
        playerReagentCount = playerReagentCount + P.NumOfItemInQueue(reagentName);

        -- Adjustment #2: if your queue contains items that use this reagent, subtract the amount that will be used from your "inventory".
        for _, queueEntry in P.OrderedQueue do
                local perItemCount = queueEntry.reagentsPerItem[reagentName];
                if (perItemCount ~= nil) then
                        playerReagentCount = playerReagentCount - ((queueEntry.numQueued - queueEntry.numProduced) * perItemCount);
                end
        end
        playerReagentCount = math.max(playerReagentCount, 0);
        
        if (reagentName ~= nil) then
                P.ReagentCache[index] = { time=GetTime(), info={ reagentName=reagentName, reagentTexture=reagentTexture, reagentCount=reagentCount,  playerReagentCount=playerReagentCount } };
        end

        return reagentName, reagentTexture, reagentCount, playerReagentCount;
end

function P.GetTradeSkillTools(skillIndex)
        local toolInfo = {P.Orig_GetTradeSkillTools(skillIndex)};
        local newToolInfo = {};
        for index = 1, table.getn(toolInfo) do
                local item = toolInfo[index];
                if (item and type(item) == "string") then
                        table.insert(newToolInfo, item);
                        if (toolInfo[index+1] == 1 or P.NumOfItemInQueue(item) > 0) then
                                table.insert(newToolInfo, 1);
                        end
                end
        end
        return unpack(newToolInfo);
end

function P.TradeSkillFrame_Update()
        P.Orig_TradeSkillFrame_Update();

        local skillName = GetTradeSkillLine();
        if (P.TradeSkillsSeen[skillName] == nil) then
                P.TradeSkillsSeen[skillName] = GetTime();
                --GFWUtils.Print("Disabling TradeSkillFrame overrides.");
                P.NoOverride = true;
        end
        if (P.NoOverride or FAC_Config.NoOverride) then return; end
        
        local numTradeSkills = GetNumTradeSkills();
        local skillOffset = FauxScrollFrame_GetOffset(TradeSkillListScrollFrame);
        for i=1, TRADE_SKILLS_DISPLAYED, 1 do
                local skillIndex = i + skillOffset;
                local skillName, skillType, numAvailable, isExpanded = P.GetTradeSkillInfo(skillIndex);
                local skillButton = getglobal("TradeSkillSkill"..i);
                if ( skillIndex <= numTradeSkills and skillName ) then  
                        if ( numAvailable == 0 ) then
                                skillButton:SetText(" "..skillName);
                        else
                                skillButton:SetText(" "..skillName.." ["..numAvailable.."]");
                        end
                end
        end
end

function P.TradeSkillFrame_SetSelection(id)
        P.Orig_TradeSkillFrame_SetSelection(id);
        
        local skillName = GetTradeSkillLine();
        if (P.TradeSkillsSeen[skillName] == nil) then
                P.TradeSkillsSeen[skillName] = GetTime();
                --GFWUtils.Print("Disabling TradeSkillFrame overrides.");
                P.NoOverride = true;
        end
        if (P.NoOverride or FAC_Config.NoOverride) then return; end
        
        local creatable = 1;
        local numReagents = GetTradeSkillNumReagents(id);
        for i=1, numReagents, 1 do
                local reagentName, reagentTexture, reagentCount, playerReagentCount = P.GetTradeSkillReagentInfo(id, i);
                local reagent = getglobal("TradeSkillReagent"..i)
                local name = getglobal("TradeSkillReagent"..i.."Name");
                local count = getglobal("TradeSkillReagent"..i.."Count");
                if ( reagentName and reagentTexture ) then
                        if ( playerReagentCount < reagentCount ) then
                                SetItemButtonTextureVertexColor(reagent, 0.5, 0.5, 0.5);
                                name:SetTextColor(GRAY_FONT_COLOR.r, GRAY_FONT_COLOR.g, GRAY_FONT_COLOR.b);
                                creatable = nil;
                        else
                                SetItemButtonTextureVertexColor(reagent, 1.0, 1.0, 1.0);
                                name:SetTextColor(HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b);
                        end
                        if ( playerReagentCount >= 100 ) then
                                playerReagentCount = "*";
                        end
                        count:SetText(playerReagentCount.." /"..reagentCount);
                end
        end
        local spellFocus = BuildColoredListString(P.GetTradeSkillTools(id));
        if ( spellFocus ) then
                TradeSkillRequirementText:SetText(spellFocus);
        end
        
        if (creatable) then
                AutoCraftQueue:Enable();
                AutoCraftQueueAll:Enable();
        else
                AutoCraftQueue:Disable();
                AutoCraftQueueAll:Disable();
        end
end

function P.Load()
        if (not P.Loaded) then
                -- Attach our UI elements to the TradeSkillFrame now that it's loaded.
                AutoCraftBackground:SetParent(TradeSkillFrame);
                AutoCraftBackground:SetPoint("TOPLEFT", 0, 0);  
                AutoCraftBackground:SetHeight(TradeSkillFrame:GetHeight() + 48); 
                        -- reset this in case someone (say, EnhancedTradeSkill) resized TradeSkillFrame on us
                AutoCraftBackground:Show();     
                AutoCraftQueue:SetParent(TradeSkillFrame);      
                AutoCraftQueue:SetPoint("TOPLEFT", TradeSkillCreateButton, "TOPLEFT", 0, 0);    
                AutoCraftQueue:Show();  
                AutoCraftQueueAll:SetParent(TradeSkillFrame);   
                AutoCraftQueueAll:SetPoint("TOPLEFT", TradeSkillCreateAllButton, "TOPLEFT", 0, 0);      
                AutoCraftQueueAll:Show();       
                AutoCraftBuy:SetParent(TradeSkillFrame);        
                AutoCraftBuy:SetPoint("TOPLEFT", TradeSkillCancelButton, "TOPLEFT", 0, 0);      
                AutoCraftBuy:Show();    
                AutoCraftQueueEverything:SetParent(TradeSkillFrame);    
                AutoCraftQueueEverything:Show();        
                AutoCraftRunQueue:SetParent(TradeSkillFrame);   
                AutoCraftRunQueue:Show();       
                AutoCraftClear:SetParent(TradeSkillFrame);      
                AutoCraftClear:Show();  
                AutoCraftRunAutomatically:SetParent(TradeSkillFrame);   
                AutoCraftRunAutomatically:Show();       
                AutoCraftRunAutomaticallyText:SetText(FAC_RUN_AUTOMATICALLY_CHECKBUTTON);
                AutoCraftRunAutomatically:SetChecked(FAC_Config.AutoIterate);

                -- Hide the old Create buttons so I can put my buttons in  their place.  
                -- Not sure if this will always work, but it does at the moment.
                TradeSkillCreateButton:Hide();
                TradeSkillCreateAllButton:Hide();
                TradeSkillCancelButton:Hide();

                -- we don't actually hook these anymore, but we keep the "aliases" around to be clearly readable when we're calling originals.
                P.Orig_GetTradeSkillTools = GetTradeSkillTools;
                P.Orig_GetTradeSkillReagentInfo = GetTradeSkillReagentInfo;
                P.Orig_GetTradeSkillInfo = GetTradeSkillInfo;

                -- we do hook these, however.
                P.Orig_TradeSkillFrame_SetSelection = TradeSkillFrame_SetSelection;
                TradeSkillFrame_SetSelection = P.TradeSkillFrame_SetSelection;
                P.Orig_TradeSkillFrame_Update = TradeSkillFrame_Update;
                TradeSkillFrame_Update = P.TradeSkillFrame_Update;
                
                P.Loaded = true;
                GFWUtils.Print("Fizzwidget AutoCraft "..FAC_VERSION.." initialized!");
        end
end

function P.NumOfItemInQueue(itemName)
        for _, queueEntry in P.OrderedQueue do
                if (queueEntry.item == itemName) then
                        local numPerCraft = (queueEntry.numPerCraft or 0);
                        local numQueued = (queueEntry.numQueued or 0);
                        if (numPerCraft > 1) then
                                return numQueued * numPerCraft;
                        else
                                return numQueued;
                        end
                end
        end
        return 0;
end

function P.OnUpdate(elapsed)
        if (P.NoOverride and P.Loaded and TradeSkillFrame and TradeSkillFrame:IsVisible()) then
                local skillName = GetTradeSkillLine();
                if (P.TradeSkillsSeen[skillName] and (GetTime() - P.TradeSkillsSeen[skillName] > TRADESKILL_UPDATE_TIME)) then
                        P.NoOverride = false;
                        --GFWUtils.Print("Enabling TradeSkillFrame overrides.");
                        TradeSkillFrame_SetSelection(GetTradeSkillSelectionIndex());
                        TradeSkillFrame_Update();
                end
        end
        if (not CastingBarFrame:IsVisible()) then
                P.CastingOtherSpell = false;
                P.CraftingQueuedItem = false;
        end
        if (P.AdvanceQueueTime ~= nil and GetTime() >= P.AdvanceQueueTime) then
                P.Iterate();
                P.AdvanceQueueTime = nil;
        end
end

function P.OnLoad()
        -- we do most of our loading once the TradeSkillFrame loads (which is on-demand now).

        this:RegisterEvent("ADDON_LOADED");
        this:RegisterEvent("CHAT_MSG_SPELL_TRADESKILLS");
        this:RegisterEvent("MERCHANT_SHOW");
        this:RegisterEvent("MERCHANT_CLOSED");
        this:RegisterEvent("TRADE_SKILL_SHOW");
        this:RegisterEvent("TRADE_SKILL_CLOSE");
        this:RegisterEvent("TRADE_SKILL_UPDATE");
        this:RegisterEvent("SPELLCAST_INTERRUPTED");
        this:RegisterEvent("SPELLCAST_FAILED");
        this:RegisterEvent("SPELLCAST_START");
        this:RegisterEvent("SPELLCAST_STOP");

        if ( IsAddOnLoaded("Blizzard_TradeSkillUI") ) then
        --      P.Load();
        end

end

function P.OnEvent(event, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9)

        if (event == "ADDON_LOADED" and (arg1 == "Blizzard_TradeSkillUI" or IsAddOnLoaded("Blizzard_TradeSkillUI"))) then
                P.Load();
        end
        if (not P.Loaded) then 
                -- all the other events we care about only have relevance once we're fully in action and attached to the TradeSkill UI
                return; 
        end

        --DevTools_Dump({event=event, arg1=arg1, arg2=arg2, arg3=arg3, arg4=arg4, arg5=arg5, arg6=arg6, arg7=arg7, arg8=arg8, arg9=arg9});
        if (event == "CHAT_MSG_SPELL_TRADESKILLS") then
                if (FAC_CreateItemPattern == nil) then
                        FAC_CreateItemPattern = GFWUtils.FormatToPattern(TRADESKILL_LOG_FIRSTPERSON);
                end
                for item in string.gfind(arg1, FAC_CreateItemPattern) do
                        P.HandleCreated(item);
                        return;
                end
        elseif (event == "TRADE_SKILL_CLOSE" or event == "MERCHANT_SHOW" or event == "MERCHANT_CLOSED") then
                P.UpdateDisplay();
        elseif (event == "TRADE_SKILL_SHOW") then
                P.UpdateDisplay();
                AutoCraftRunAutomaticallyText:SetText(FAC_RUN_AUTOMATICALLY_CHECKBUTTON);
                AutoCraftRunAutomatically:SetChecked(FAC_Config.AutoIterate);
                -- if we've just switched to the tradeskill window needed for the head of the queue, start iterating again
                if (not P.ManuallyPaused and P.OrderedQueue[1] ~= nil and P.OrderedQueue[1].skill == GetTradeSkillLine()) then
                        P.Iterate();
                end
        elseif (event == "TRADE_SKILL_UPDATE") then
                P.UpdateDisplay();
        elseif (event == "SPELLCAST_FAILED" or event == "SPELLCAST_INTERRUPTED") then
                P.CastingOtherSpell = false;
                P.CraftingQueuedItem = false;
                if (P.Unqueueing or P.CastingOtherSpell) then
                        P.Unqueueing = false; 
                        P.Iterate();
                        return;
                end
                P.UpdateDisplay();
        elseif (event == "SPELLCAST_STOP") then
                if (P.CastingOtherSpell) then
                        P.CastingOtherSpell = false;
                        P.Iterate();
                        P.UpdateDisplay();
                end
        elseif (event == "SPELLCAST_START") then
                if (P.OrderedQueue[1] and CastingBarText:GetText() == P.OrderedQueue[1].recipe) then
                        P.CraftingQueuedItem = true;
                        P.CastingOtherSpell = false;
                else
                        P.CraftingQueuedItem = false;
                        P.CastingOtherSpell = true;
                end
                P.UpdateDisplay();
        end
end

function P.Queue_OnClick()
        local id = tonumber(this:GetID());
        if (P.OrderedQueue[id]) then
                table.remove(P.OrderedQueue, id);
                if (id == 1 and P.CraftingQueuedItem) then
                        P.Unqueueing = true;
                        SpellStopCasting();
                end
                TradeSkillFrame_SetSelection(GetTradeSkillSelectionIndex());
                TradeSkillFrame_Update();       
                P.UpdateDisplay();
                P.Queue_UpdateTooltip(id);
        end
end

function P.Queue_OnEvent()
        P.Queue_UpdateTooltip(tonumber(this:GetID()));
end

function P.Queue_UpdateTooltip(id)
        local button = getglobal("QueuedSkillIcon"..id);
        if (button:IsVisible()) then
                GameTooltip:SetOwner(button, "ANCHOR_TOPLEFT");
                if ( id == 1 and P.CraftingQueuedItem) then
                        GameTooltip:SetText(NORMAL_FONT_COLOR_CODE..FAC_CREATING..FONT_COLOR_CODE_CLOSE, 1.0, 1.0, 1.0);
                else
                        GameTooltip:SetText(NORMAL_FONT_COLOR_CODE..FAC_QUEUED..FONT_COLOR_CODE_CLOSE, 1.0, 1.0, 1.0);
                end
                if (id == MAX_QUEUE_BUTTONS and table.getn(P.OrderedQueue) > MAX_QUEUE_BUTTONS) then
                        GameTooltip:AppendText(string.format(FAC_ADDITIONAL_ENTRIES, table.getn(P.OrderedQueue) - MAX_QUEUE_BUTTONS + 1));
                        for index = MAX_QUEUE_BUTTONS, table.getn(P.OrderedQueue) do
                                local num = P.OrderedQueue[index].numQueued - P.OrderedQueue[index].numProduced;
                                GameTooltip:AppendText("\n"..num .."x ".. P.OrderedQueue[index].item);
                        end
                        GameTooltip:AddLine(string.format(FAC_CLICK_TO_CANCEL_FORMAT, P.OrderedQueue[MAX_QUEUE_BUTTONS].item));
                else
                        local num = P.OrderedQueue[id].numQueued - P.OrderedQueue[id].numProduced;
                        GameTooltip:AppendText(num .."x ".. P.OrderedQueue[id].item);
                        GameTooltip:AddLine(FAC_CLICK_TO_CANCEL);
                end
                GameTooltip:Show();
        end
end

function P.QueueEverything()
        local numSkills = GetNumTradeSkills();
        local startIndex, stopIndex, increment;
        if (IsAltKeyDown()) then
                startIndex = 1;
                stopIndex = numSkills;
                increment = 1;
        else
                startIndex = numSkills;
                stopIndex = 1;
                increment = -1;
        end
        for index = startIndex, stopIndex, increment do
                local recipeName, _, numAvailable, _, itemName = P.GetTradeSkillInfo(index);
                if (numAvailable > 0) then
                        P.AddToQueue(recipeName, itemName, index, numAvailable);
                end
        end
        TradeSkillFrame_SetSelection(GetTradeSkillSelectionIndex());
        TradeSkillFrame_Update();       
        if (table.getn(P.OrderedQueue) == 0) then
                GFWUtils.Print(FAC_NOTHING_TO_QUEUE);
        end
end

-- TODO: option-click for "buy all" equivalent to "queue all"
function P.BuyReagents(amount)
        local index = GetTradeSkillSelectionIndex();
        
        --if (IsAltKeyDown()) then
        --      amount = TradeSkillFrame.numAvailable;
        --end
        
        for reagentIndex = 1, GetTradeSkillNumReagents(index) do
                local reagentName, _, reagentCount, playerReagentCount = P.GetTradeSkillReagentInfo(index, reagentIndex);
                local amountNeeded = (reagentCount * amount) - playerReagentCount;
                
                for merchantIndex = 1, GetMerchantNumItems() do
                        local name, _, price, numPerBuy, numAvailable, _ = GetMerchantItemInfo(merchantIndex);
                        if (name == reagentName) then
                                if (numAvailable >= 0 and (numAvailable * numPerBuy) < amountNeeded) then
                                        amountNeeded = numAvailable * numPerBuy;
                                end
                                local amountToBuy = math.ceil(amountNeeded / numPerBuy);
                                if (amountToBuy > 0) then
                                        if (numPerBuy > 1) then
                                                GFWUtils.Print(string.format(FAC_BUYING_SETS_FORMAT, amountToBuy, name));
                                                while (amountToBuy > 0 ) do
                                                        local amountBuyingNow = 1; -- dumb workaround for the API not telling us max stack size for things that come in multi-packs
                                                        BuyMerchantItem(merchantIndex, amountBuyingNow);
                                                        amountToBuy = amountToBuy - amountBuyingNow;
                                                end
                                        else
                                                while (amountToBuy > 0 ) do
                                                        local maxPerBuy = GetMerchantItemMaxStack(merchantIndex);
                                                        local amountBuyingNow = math.min(amountToBuy, maxPerBuy);
                                                        GFWUtils.Print(string.format(FAC_BUYING_FORMAT, amountBuyingNow, name));
                                                        BuyMerchantItem(merchantIndex, amountBuyingNow);
                                                        amountToBuy = amountToBuy - amountBuyingNow;
                                                end
                                        end
                                elseif (numAvailable == 0) then
                                        GFWUtils.Print(string.format(FAC_NOT_AVAILABLE_FORMAT, name));
                                else
                                        GFWUtils.Print(string.format(FAC_ALREADY_ENOUGH_FORMAT, name, amount, P.GetTradeItemName(index)));
                                end
                                break;
                        end
                end
        end
end

function P.Queue(amount)
        local index = GetTradeSkillSelectionIndex();
        local recipeName, _, numAvailable, _, itemName = P.GetTradeSkillInfo(index);

        if (amount > numAvailable) then amount = numAvailable; end

        if (amount > 0) then
                P.AddToQueue(recipeName, itemName, index, amount);
        end
        TradeSkillFrame_SetSelection(GetTradeSkillSelectionIndex());
        TradeSkillFrame_Update();
        P.UpdateDisplay();
end

function P.AddToQueue(recipeName, item, index, amount)

        local alreadyQueued = false;
        GFWUtils.Print(FAC_QUEUEING .. GFWUtils.Hilite(amount .. " x ") .. GetTradeSkillItemLink(index));
        for _, queueEntry in P.OrderedQueue do
                if (item == queueEntry.item) then
                        queueEntry.numQueued = queueEntry.numQueued + amount;
                        alreadyQueued = true;
                end
        end
        if (not alreadyQueued) then
                local reagentsPerItem = {};
                for reagentIndex = 1, GetTradeSkillNumReagents(index) do
                        local reagentName, _, reagentCount, _ = P.GetTradeSkillReagentInfo(index, reagentIndex);
                        reagentsPerItem[reagentName] = reagentCount;
                end
                local minMade, maxMade = GetTradeSkillNumMade(index);
                local numPerCraft = math.min((minMade or 0), (maxMade or 0)); 
                -- assume recipes that make a variable number of items will always produce the minimum, so we can't end up over-queueing.

                table.insert(P.OrderedQueue, {recipe=recipeName, item=item, numQueued=amount, numProduced=0, icon=GetTradeSkillIcon(index), skill=GetTradeSkillLine(), reagentsPerItem=reagentsPerItem, numPerCraft=numPerCraft});
        end

        TradeSkillFrame_SetSelection(GetTradeSkillSelectionIndex());
        TradeSkillFrame_Update();
        P.UpdateDisplay();
        if (FAC_Config.AutoIterate) then
                P.ManuallyPaused = false;
                P.Iterate();
        else
                P.ManuallyPaused = true;
        end
        P.UpdateDisplay();
end

function P.UpdateDisplay()
        
        -- we clear these out in case relevant inventory has (actually or effectively) changed.
        P.SkillCache = {};
        P.ReagentCache = {};

        if (MerchantFrame:IsVisible()) then
                AutoCraftBuy:Enable();
        else
                AutoCraftBuy:Disable();
        end
        
        if (table.getn(P.OrderedQueue) > 0) then
                if (P.CraftingQueuedItem) then
                        QueueMessageText:Hide();
                        QueueMessageText:SetText("");
                        AutoCraftRunQueue:SetText(FAC_PAUSE_QUEUE_BUTTON);
                else
                        if (not P.ManuallyPaused and P.OrderedQueue[1] ~= nil and P.OrderedQueue[1].skill ~= GetTradeSkillLine()) then
                                QueueMessageText:SetText(FAC_QUEUE_PAUSED..": "..string.format(FAC_SWITCH_WINDOW_FORMAT, P.OrderedQueue[1].skill));
                        elseif (P.CastingOtherSpell) then
                                if (CastingBarText:GetText() ~= nil) then
                                        QueueMessageText:SetText(FAC_QUEUE_PAUSED..": "..string.format(FAC_WAITING_SPELL_FORMAT, CastingBarText:GetText()));
                                end
                        else
                                QueueMessageText:SetText(FAC_QUEUE_PAUSED);
                        end
                        QueueMessageText:Show();
                        AutoCraftRunQueue:SetText(FAC_RUN_QUEUE_BUTTON);
                end

                AutoCraftRunQueue:Enable();
                AutoCraftClear:Enable();
        else
                QueueMessageText:Hide();
                QueueMessageText:SetText("");
                AutoCraftRunQueue:SetText(FAC_RUN_QUEUE_BUTTON);
                AutoCraftRunQueue:Disable();
                AutoCraftClear:Disable();
        end
        
        for i = 1, MAX_QUEUE_BUTTONS do
                button = getglobal("QueuedSkillIcon"..i);
                currentItem = P.OrderedQueue[i];
                if (currentItem == nil) then
                        button:Hide();
                else
                        if (i == MAX_QUEUE_BUTTONS and table.getn(P.OrderedQueue) > MAX_QUEUE_BUTTONS) then
                                SetItemButtonTexture(button, "Interface\\Icons\\INV_Misc_QuestionMark");
                                SetItemButtonCount(button, (table.getn(P.OrderedQueue) - MAX_QUEUE_BUTTONS - 1));
                        else
                                SetItemButtonTexture(button, currentItem.icon);
                                SetItemButtonCount(button, (currentItem.numQueued - currentItem.numProduced));
                        end
                        button:Show();
                end
        end
end

function P.StartStop()
        if (P.CraftingQueuedItem == true) then
                P.ManuallyPaused = true;
                SpellStopCasting();
                P.UpdateDisplay();
        else
                P.ManuallyPaused = false;
                P.Iterate();
                P.UpdateDisplay();
        end
end

function P.Clear()
        if (table.getn(P.OrderedQueue) > 0) then
                if (P.CraftingQueuedItem and not P.ManuallyPaused) then
                        SpellStopCasting();
                end
                P.OrderedQueue = {};
                TradeSkillFrame_SetSelection(GetTradeSkillSelectionIndex());
                TradeSkillFrame_Update();       
                P.UpdateDisplay();
        end
end

function P.Iterate()    
        if (P.CraftingQueuedItem or P.CastingOtherSpell) then return; end
        if (table.getn(P.OrderedQueue) > 0) then
                local index = P.GetTradeRecipeIndex(P.OrderedQueue[1].recipe);
                if (index >= 0) then
                        if (P.OrderedQueue[1].skill == GetTradeSkillLine()) then
                                DoTradeSkill(index, (P.OrderedQueue[1].numQueued - P.OrderedQueue[1].numProduced));
                                P.UpdateDisplay();
                                return;
                        end
                end
                P.ManuallyPaused = false;
                P.UpdateDisplay();
        end
end

function P.HandleCreated(item)
        if (P.OrderedQueue[1] and item == P.OrderedQueue[1].item) then
                P.OrderedQueue[1].numProduced = P.OrderedQueue[1].numProduced + 1;
                if (P.OrderedQueue[1].numProduced >= P.OrderedQueue[1].numQueued) then
                        P.CraftingQueuedItem = false; -- this will get set back to true when we get the SPELLCAST_START event
                        table.remove(P.OrderedQueue, 1);
                end
                TradeSkillFrame_SetSelection(GetTradeSkillSelectionIndex());
                TradeSkillFrame_Update();       
                P.UpdateDisplay();
                --P.Iterate();
                if (table.getn(P.OrderedQueue) > 0) then
                        P.AdvanceQueueTime = GetTime() + QUEUE_ADVANCE_DELAY;
                end
        end
end

function P.GetTradeItemName(index)
        local linktext = GetTradeSkillItemLink(index);
        if (linktext) then
                local _,_,item = string.find(linktext, "^.*%[(.*)%].*$");
                return item;
        else
                local item, difficulty, amount = P.Orig_GetTradeSkillInfo(index);
                return item;
        end
end

-- find the trade skill index based on the skill name (recipe name), as indices are subject to change.
function P.GetTradeRecipeIndex(recipeName)
        local numSkills = GetNumTradeSkills();
        for index = 1, numSkills do
                local skillName = P.Orig_GetTradeSkillInfo(index);
                if (skillName == recipeName) then
                        return index;
                end
        end
        return -1;
end