vanilla-wow-addons – Rev 1

Subversion Repositories:
Rev:
-- ============================================================================
-- TTCraftAddict.lua
--
-- Copyright (c) Matthew Johnson.  All rights reserved.
--
-- This work may be freely adapted and distributed as long as this notice remains intact.
-- This work may NOT be (re)sold or included in any compilations that are (re)sold.
--
-- ============================================================================

-- ============================================================================
-- Object definitions.
-- ============================================================================
TTCA_Item =
        {
        Name = nil,
        Type = nil,
        Level = nil,
        Id = nil,
        ReagentId = nil,
        Texture = nil,
        NumAvailable = nil,
        NumRequired = nil,
        New = function (self)
                      obj = {};
                      setmetatable(obj, self);
                      self.__index = self;
                      return obj;
                  end,
        };

-- ============================================================================
-- Global variables.
-- ============================================================================
local Pre_TTCA_TradeSkillFrame_SetSelection = nil;
local Pre_TTCA_CraftFrame_SetSelection = nil;

-- The list of items in the build tree.
local vTTCA_BuildTree = nil;

-- The list of raw materials needed to build the selected item.
local vTTCA_MaterialList = nil;

-- The list of items need to be made to build the selected item.
local vTTCA_ConstructionList = nil;

-- The list of tools needed to build the selected item.
local vTTCA_ToolList = nil;

-- The currently active tab.
local vTTCA_ActiveTab = nil

-- Flag to determine if we are currently building the tree.
local vfTTCA_BuildingTree = false;

-- Flag to determine if we should watch for a bag update so we know when to
-- build the next item.
local vfTTCA_WatchForBagUpdate = false;

-- ============================================================================
-- Global defines.
-- ============================================================================
TTCA_LISTITEMS_VISIBLE = 15;
TTCA_LISTITEM_HEIGHT = 22;
TTCA_LISTITEM_WIDTH = 300;
TTCA_LISTITEM_INDENT = 15;

TTCA_TAB_BUILDTREE = 1;
TTCA_TAB_MATERIALS = 2;

local TTCA_TypeColor =
        {
        ["optimal"]     = { r = 1.00, g = 0.50, b = 0.25 },
        ["medium"]      = { r = 1.00, g = 1.00, b = 0.00 },
        ["easy"]        = { r = 0.25, g = 0.75, b = 0.25 },
        ["trivial"]     = { r = 0.50, g = 0.50, b = 0.50 },
        ["reagent"]     = { r = 0.50, g = 0.50, b = 0.50 },
        ["selected"]    = { r = 1.00, g = 1.00, b = 1.00 },
        ["unavailable"] = { r = 1.00, g = 0.00, b = 0.00 },
        ["header"]      = { r = 0.30, g = 0.30, b = 1.00 },
        };

-- ============================================================================
-- OnLoad
--
-- Called when this addon is first loaded.
-- ============================================================================
function TTCA_OnLoad()

        -- Register events
        this:RegisterEvent("BAG_UPDATE");
        this:RegisterEvent("CRAFT_CLOSE");
        this:RegisterEvent("CRAFT_SHOW");
        this:RegisterEvent("CRAFT_UPDATE");
        this:RegisterEvent("SPELLCAST_INTERRUPTED");
        this:RegisterEvent("SPELLCAST_STOP");
        this:RegisterEvent("TRADE_SKILL_CLOSE");
        this:RegisterEvent("TRADE_SKILL_SHOW");
        this:RegisterEvent("TRADE_SKILL_UPDATE");

        --TradeSkillFrame_LoadUI();

        -- Hook
        Pre_TTCA_TradeSkillFrame_SetSelection = TradeSkillFrame_SetSelection;
        TradeSkillFrame_SetSelection = TTCA_Override_TradeSkillFrame_OnSetSelection;

        Pre_TTCA_CraftFrame_SetSelection = CraftFrame_SetSelection;
        CraftFrame_SetSelection = TTCA_Override_CraftFrame_OnSetSelection;

end

-- ============================================================================
-- OnEvent
--
-- Called to handle the events registered for this addon.
-- ============================================================================
function TTCA_OnEvent()

        if (vfTTCA_BuildingTree) then

                if ((vfTTCA_WatchForBagUpdate) and (event == "BAG_UPDATE")) then

                        vfTTCA_WatchForBagUpdate = false;
                        TTCA_Update();
                        TTCA_BuildNextItem(vTTCA_ConstructionList);
                
                elseif (event == "SPELLCAST_INTERRUPTED") then

                        -- Building was interrupted, reset the building flag.
                        vfTTCA_BuildingTree = false;
                        vfTTCA_WatchForBagUpdate = false;

                elseif (event == "SPELLCAST_STOP") then

                        -- Finished building the current item, so build the next item in
                        -- the construction list when the current item appears in the
                        -- character's bag.
                        vfTTCA_WatchForBagUpdate = true;

                end

        end

        if ((event == "CRAFT_CLOSE") or (event == "TRADE_SKILL_CLOSE")) then
                TTCA_OnTradeSkillClose();

        elseif ((event == "CRAFT_SHOW") or (event == "TRADE_SKILL_SHOW")) then
                TTCA_OnTradeSkillShow();

        elseif ((event == "CRAFT_UPDATE") or (event == "TRADE_SKILL_UPDATE")) then
                TTCA_OnTradeSkillUpdate();

        end

end

-- ============================================================================
-- Override_TradeSkillFrame_OnSetSelection
--
-- This event occurs every time a selection is set within the built in craft
-- skill window.  This function is hooked into the built in function so that
-- this addon can catch the selection change events.
-- ============================================================================
function TTCA_Override_CraftFrame_OnSetSelection(id)

        Pre_TTCA_CraftFrame_SetSelection(id);

end

-- ============================================================================
-- Override_TradeSkillFrame_OnSetSelection
--
-- This event occurs every time a selection is set within the built in trade
-- skill window.  This function is hooked into the built in function so that
-- this addon can catch the selection change events.
-- ============================================================================
function TTCA_Override_TradeSkillFrame_OnSetSelection(id)

        Pre_TTCA_TradeSkillFrame_SetSelection(id);

        -- If the id is the currently selected item, don't bother reseting stuff.
        if ((vTTCA_BuildTree ~= nil) and (id ~= vTTCA_BuildTree[1].Id)) then

                -- Reset the build tree scroll frame.
                TTCA_Reset();

                -- Reset the quantity.
                TTCA_InputBox_Quantity:SetNumber(1);

        end

        -- The selection may have changed, so update the trade skill build tree.
        TTCA_Update();

end

-- ============================================================================
-- OnTradeSkillClose
--
-- This event occurs when the user closes the built in trade skill window.
-- ============================================================================
function TTCA_OnTradeSkillClose()

        -- Hide the trade skill build tree.
        TTCA_ShowBuildTree(false);

        -- Hide the trade skill show button.
        HideUIPanel(TTCA_Button_ShowBuildTree);

end

-- ============================================================================
-- OnTradeSkillShow
--
-- This event occurs when the users opens the built in trade skill window.
-- ============================================================================
function TTCA_OnTradeSkillShow()

        -- Show the trade skill tree expand button.
        TTCA_Button_ShowBuildTree:SetPoint("TOPLEFT", TTCA_GetTradeSkillOrCraftFrameName(), "TOPRIGHT", -37, -9);
        ShowUIPanel(TTCA_Button_ShowBuildTree);

        -- Reset the selected build tree item.
        TTCA_SetSelection(1);

        -- Reset the quantity.
        TTCA_InputBox_Quantity:SetNumber(1);

end

-- ============================================================================
-- OnTradeSkillUpdate
--
-- This event occurs every time the built in trade skill window fires an update
-- event.
-- ============================================================================
function TTCA_OnTradeSkillUpdate()

        -- Update the build tree frame.
        TTCA_Update();

end

-- ============================================================================
-- ShowBuildTree
--
-- Shows/Hides the trade skill build tree window.
-- ============================================================================
function TTCA_ShowBuildTree(show)

        -- If the active tab is not yet set, make it the first tab.
        if (vTTCA_ActiveTab == nil) then

                vTTCA_ActiveTab = TTCA_TAB_BUILDTREE;
                PanelTemplates_SelectTab(TTCA_Tab_BuildTree);

        end

        if (show) then

                -- Hide the expand button and show the skill tree frame.
                HideUIPanel(TTCA_Button_ShowBuildTree);
                ShowUIPanel(TTCA_MainFrame);
                TTCA_MainFrame:SetPoint("TOPLEFT", TTCA_GetTradeSkillOrCraftFrameName(), "TOPRIGHT", -35, -13);
                TTCA_Update();
                PlaySound("igCharacterInfoOpen");

        else

                -- Hide the trade skill expand frame and show the expand button.
                HideUIPanel(TTCA_MainFrame);
                ShowUIPanel(TTCA_Button_ShowBuildTree);
                PlaySound("igCharacterInfoClose");

        end

end

-- ============================================================================
-- Reset
--
-- Resets the scroll frame to its initial values.
-- ============================================================================
function TTCA_Reset()

        -- Reset the scroll frame.
        FauxScrollFrame_SetOffset(TTCA_List_ScrollFrame, 0);
        TTCA_List_ScrollFrameScrollBar:SetValue(0);

        -- Reset the buttons.
        TTCA_Button_CreateTree:Disable();
        TTCA_Button_CreateAllTree:Disable();

end

-- ============================================================================
-- Update
--
-- Calculate the build tree, required raw materials, tools, and a construction
-- list for the currently selected item.
-- ============================================================================
function TTCA_Update()

        -- If the build tree window isn't visible, don't bother updating.
        if (not TTCA_MainFrame:IsVisible()) then
                return;
        end

        -- Create the build tree.
        vTTCA_BuildTree = {};
        TTCA_CreateBuildTree(TTCA_GetSkillSelectionIndex(), vTTCA_BuildTree, nil, TTCA_InputBox_Quantity:GetNumber());

        -- Compute raw material requirements and the construction list for the currently
        -- selected item.
        vTTCA_MaterialList, vTTCA_ConstructionList, vTTCA_ToolList = TTCA_CreateMaterialConstructionToolLists(vTTCA_BuildTree, TTCA_GetSelection());

        -- Disable the create tree button.
        TTCA_Button_CreateTree:Disable();
        TTCA_Button_CreateAllTree:Disable();

        -- Update the list of items with the build tree or the material summary,
        -- depending on which tab is active.
        if (vTTCA_ActiveTab == TTCA_TAB_BUILDTREE) then

                TTCA_UpdateBuildTree(vTTCA_BuildTree);

                if (TTCA_IsItemBuildable(vTTCA_BuildTree, TTCA_GetSelection()) and (vTTCA_BuildTree[TTCA_GetSelection()].Type ~= "reagent")) then

                        TTCA_Button_CreateTree:Enable();
                        TTCA_Button_CreateAllTree:Enable();

                end

        elseif (vTTCA_ActiveTab == TTCA_TAB_MATERIALS) then

                TTCA_UpdateMaterials(vTTCA_MaterialList);

        end

end

-- ============================================================================
-- UpdateBuildTree
-- ============================================================================
function TTCA_UpdateBuildTree(buildTree)

        -- Find the total number of items in the build tree.
        local cBuildTreeItems = getn(buildTree);

        -- Find the scroll offset for the faux scroll frame.
        local iListItemOffset = FauxScrollFrame_GetOffset(TTCA_List_ScrollFrame);

        -- Update the scroll frame.
        FauxScrollFrame_Update(TTCA_List_ScrollFrame, cBuildTreeItems, TTCA_LISTITEMS_VISIBLE, TTCA_LISTITEM_HEIGHT, nil, nil, nil, TTCA_ListItem_HighlightFrame, 300, 300);

        -- Hide the highlight frame until we know where it is appearing.
        TTCA_ListItem_HighlightFrame:Hide();

        -- Loop through our visible list items and update each one.
        for i=1, TTCA_LISTITEMS_VISIBLE, 1 do

                local listItem = getglobal("TTCA_ListItem" .. i);
                local listItemIndent = getglobal("TTCA_ListItem" .. i .. "_Indent");
                local listItemTexture = getglobal("TTCA_ListItem" .. i .. "_Texture");
                local listItemTooltipTriggerFrame = getglobal("TTCA_ListItem" .. i .. "_TooltipTriggerFrame");

                local iBuildTreeIndex = i + iListItemOffset;

                if (iBuildTreeIndex <= cBuildTreeItems) then

                        local buildTreeItem = buildTree[iBuildTreeIndex];

                        if (buildTreeItem.Name ~= nil) then

                                -- Indent the item by its level.
                                listItemIndent:SetWidth(TTCA_LISTITEM_INDENT * (buildTreeItem.Level-1) + 1);

                                -- Set the texture.
                                listItemTexture:SetTexture(buildTreeItem.Texture);
                                listItemTexture:SetWidth(20);
                                listItemTexture:Show();

                                -- Set the name, number required, and number available.
                                local itemText = buildTreeItem.Name .. "  ";
                                local itemNumText = "(" .. buildTreeItem.NumAvailable .. "/" .. buildTreeItem.NumRequired .. ")";
                                local itemNumBuildableText = "";
                                local itemNumTextColor;

                                -- If this item is not a reagent, compute how many we can build based on the num available.
                                if (buildTreeItem.Type ~= "reagent") then
                                        itemNumBuildableText = "  [" .. floor(buildTreeItem.NumAvailable / buildTreeItem.NumRequired) .. "]";
                                end

                                -- If the number available is >= the number required, then color the text differently.
                                if (buildTreeItem.NumAvailable >= buildTreeItem.NumRequired) then
                                        itemNumTextColor = HIGHLIGHT_FONT_COLOR_CODE;
                                else
                                        itemNumTextColor = GRAY_FONT_COLOR_CODE;
                                end
                                listItem:SetText(itemText .. itemNumTextColor .. itemNumText .. itemNumBuildableText .. FONT_COLOR_CODE_CLOSE);

                                -- Set the color of the text.
                                local listItemColor = TTCA_TypeColor[buildTreeItem.Type];
                                listItem:SetTextColor(listItemColor.r, listItemColor.g, listItemColor.b);

                                -- Set the Id so we know which button gets selected on clicks.
                                listItem.Index = iBuildTreeIndex;

                                -- Set some values on the texture so we can show tooltips for each item.
                                listItemTooltipTriggerFrame.Id = buildTreeItem.Id;
                                listItemTooltipTriggerFrame.ReagentId = buildTreeItem.ReagentId;
                                listItemTooltipTriggerFrame:Show();

                                -- Set some values on the item so we can link it when shift-clicked.
                                listItem.Id = buildTreeItem.Id;
                                listItem.ReagentId = buildTreeItem.ReagentId;

                                -- Update the highlight frame.
                                if (TTCA_GetSelection() == iBuildTreeIndex) then

                                        -- Set the selected item's text color.
                                        local listItemTextColor = TTCA_TypeColor["selected"];
                                        listItem:SetTextColor(listItemTextColor.r, listItemTextColor.g, listItemTextColor.b);

                                        -- Set the color and location of the highlight frame.
                                        TTCA_ListItem_Highlight:SetVertexColor(listItemColor.r, listItemColor.g, listItemColor.b);
                                        TTCA_ListItem_HighlightFrame:SetPoint("TOPLEFT", "TTCA_ListItem" .. i, "TOPLEFT", -3, 0);
                                        TTCA_ListItem_HighlightFrame:Show();

                                end

                                -- Show the button.
                                listItem:Show();

                        end

                else

                        -- Hide the button since it is unused.
                        listItem:Hide();

                end

        end

end

-- ============================================================================
-- UpdateMaterials
-- ============================================================================
function TTCA_UpdateMaterials(materialList)

        -- Find the total number of items in the material list.
        local cMaterialItems = getn(materialList);

        -- Find the scroll offset for the faux scroll frame.
        local iListItemOffset = FauxScrollFrame_GetOffset(TTCA_List_ScrollFrame);

        -- Update the scroll frame.
        FauxScrollFrame_Update(TTCA_List_ScrollFrame, cMaterialItems, TTCA_LISTITEMS_VISIBLE, TTCA_LISTITEM_HEIGHT, nil, nil, nil, TTCA_ListItem_HighlightFrame, 300, 300);

        -- Hide the highlight frame since it isn't used for this view.
        TTCA_ListItem_HighlightFrame:Hide();

        -- Loop through our material list.
        for i=1, TTCA_LISTITEMS_VISIBLE, 1 do

                local listItem = getglobal("TTCA_ListItem" .. i);
                local listItemIndent = getglobal("TTCA_ListItem" .. i .. "_Indent");
                local listItemTexture = getglobal("TTCA_ListItem" .. i .. "_Texture");
                local listItemTooltipTriggerFrame = getglobal("TTCA_ListItem" .. i .. "_TooltipTriggerFrame");

                local iMaterialListIndex = i + iListItemOffset;

                if (iMaterialListIndex <= cMaterialItems) then

                        local materialListItem = materialList[iMaterialListIndex];

                        if (materialListItem.Name ~= nil) then

                                -- Indent the item by its level.
                                listItemIndent:SetWidth(TTCA_LISTITEM_INDENT * (materialListItem.Level-1) + 1);

                                -- Set the texture, if one exists.
                                if (materialListItem.Texture) then
                                        listItemTexture:SetTexture(materialListItem.Texture);
                                        listItemTexture:SetWidth(20);
                                        listItemTexture:Show();
                                else
                                        listItemTexture:SetWidth(1);
                                        listItemTexture:Hide();
                                end

                                -- Set the name.
                                local wzName = materialListItem.Name;

                                -- Set the number required and available, if they exist.
                                if (materialListItem.NumAvailable and materialListItem.NumRequired) then
                                        wzName = wzName .. "  (" .. materialListItem.NumAvailable .. "/" .. materialListItem.NumRequired .. ")";
                                end

                                -- Set the text on the list item.
                                listItem:SetText(wzName);

                                -- Set the color of the text.
                                local listItemColor = TTCA_TypeColor["reagent"];

                                -- If the number required and available exists, then color the item if we have enough.
                                if (materialListItem.NumAvailable and materialListItem.NumRequired) then

                                        if (materialListItem.NumAvailable >= materialListItem.NumRequired) then
                                                listItemColor = TTCA_TypeColor["selected"];
                                        end

                                elseif (materialListItem.Type) then

                                        listItemColor = TTCA_TypeColor[materialListItem.Type];

                                end

                                listItem:SetTextColor(listItemColor.r, listItemColor.g, listItemColor.b);

                                -- Set some values on the texture so we can show tooltips for each item.  But if the
                                -- texture isn't set, don't bother doing this.
                                if (materialListItem.Texture) then

                                        listItemTooltipTriggerFrame.Id = materialListItem.Id;
                                        listItemTooltipTriggerFrame.ReagentId = materialListItem.ReagentId;
                                        listItemTooltipTriggerFrame:Show();

                                else

                                        listItemTooltipTriggerFrame:Hide();

                                end

                                -- Set some values on the item so we can link it when shift-clicked.
                                listItem.Id = materialListItem.Id;
                                listItem.ReagentId = materialListItem.ReagentId;

                                -- Show the button.
                                listItem:Show();

                        end

                else

                        -- Hide the button since it is unused.
                        listItem:Hide();

                end

        end

end

-- ============================================================================
-- GetSelection
--
-- Returns the item currently selected in the build tree tab.
-- ============================================================================
function TTCA_GetSelection()

        return TTCA_MainFrame.SelectionIndex;

end

-- ============================================================================
-- SetSelection
--
-- Makes the item at the given build tree index the currently selected item.
-- ============================================================================
function TTCA_SetSelection(index)

        TTCA_MainFrame.SelectionIndex = index;
        TTCA_Update();

end

-- ============================================================================
-- FindTradeSkillIdByName
--
-- Iterates through all of the player's trade skills and returns the index of
-- the first trade skill that matches the given name.  If no match is found
-- then nil is returned.
-- ============================================================================
function TTCA_FindTradeSkillIdByName(tradeSkillName)

        local cTradeSkills = TTCA_GetNumSkills();

        for i=1, cTradeSkills, 1 do

                local name = TTCA_GetSkillInfo(i);

                if (tradeSkillName == name) then
                        return i;
                end

        end

        return nil;

end

-- ============================================================================
-- CreateMaterialConstructionToolLists
--
-- Generates a list of raw materials required in order to create the selected
-- item.  If no item is selected, it creates a summary for the first item.
-- ============================================================================
function TTCA_CreateMaterialConstructionToolLists(buildTree, index)

        if ((index <= 0) or (index > getn(buildTree))) then
                index = 1;
        end

        local materialList = {};
        local constructionList = {};
        local toolList = {};
        local finalizedMaterialList = {};

        TTCA_CreateMaterialConstructionToolListsSub(materialList, constructionList, toolList, buildTree, index, buildTree[index].NumRequired);

        -- Sort the material list alphabetically.
        materialList = TTCA_Sort(materialList);

        -- Sort the tool list alphabetically.
        toolList = TTCA_Sort(toolList);

        -- Construct the final version of the material list.
        -- Format: selected item, tools, materials.

        -- Add the selected item as the first item in the material list.
        local selectedItem = TTCA_Item:New();
        selectedItem.Name = buildTree[index].Name;
        selectedItem.Type = buildTree[index].Type;
        selectedItem.Texture = buildTree[index].Texture;
        selectedItem.Id = buildTree[index].Id;
        selectedItem.ReagentId = buildTree[index].ReagentId;
        selectedItem.Level = 1;
        tinsert(finalizedMaterialList, selectedItem);

        -- Add the tools to the material list.
        if (getn(toolList) > 0) then

                -- Add a tool header if there is at least one tool.
                local toolHeaderItem = TTCA_Item:New();
                toolHeaderItem.Name = TTCA_TEXT_REQUIREDTOOLS;
                toolHeaderItem.Type = "header";
                toolHeaderItem.Level = 2;
                tinsert(finalizedMaterialList, toolHeaderItem);

                -- Now add the tools.
                for i=1, getn(toolList), 1 do

                        local toolItem = TTCA_Item:New()
                        toolItem.Name = toolList[i].Name;
                        toolItem.Type = toolList[i].Type;
                        toolItem.Level = 3;
                        tinsert(finalizedMaterialList, toolItem);

                end

        end

        -- Add the materials to the material list.
        if (getn(materialList) > 0) then

                -- Add a material header if there is at least on material.
                local materialHeaderItem = TTCA_Item:New();
                materialHeaderItem.Name = TTCA_TEXT_REQUIREDMATERIALS;
                materialHeaderItem.Type = "header";
                materialHeaderItem.Level = 2;
                tinsert(finalizedMaterialList, materialHeaderItem);

                -- Now add the materials.
                for i=1, getn(materialList), 1 do
                
                        local materialItem = materialList[i];
                        materialItem.Level = 3;
                        tinsert(finalizedMaterialList, materialItem);

                end

        end

        return finalizedMaterialList, constructionList, toolList;

end

-- ============================================================================
-- CreateMaterialConstructionToolListsSub
-- ============================================================================
function TTCA_CreateMaterialConstructionToolListsSub(materialList, constructionList, toolList, buildTree, index, iNumRequired)

        local currentItem = buildTree[index];

        -- If this item is a reagent, add it to the summary.
        if (currentItem.Type == "reagent") then

                if (currentItem.Name == nil) then
                        return;
                end

                -- Initialize this reagent information if it hasn't been in the summary
                -- before.
                if (materialList[currentItem.Name] == nil) then
                        materialList[currentItem.Name] = {};
                        materialList[currentItem.Name].NumRequired = 0;
                end

                -- Add this reagent's information to the summary.
                materialList[currentItem.Name].Name = currentItem.Name;
                materialList[currentItem.Name].Type = currentItem.Type;
                materialList[currentItem.Name].NumAvailable = currentItem.NumAvailable;
                materialList[currentItem.Name].NumRequired = materialList[currentItem.Name].NumRequired + currentItem.NumRequired;
                materialList[currentItem.Name].Texture = currentItem.Texture;
                materialList[currentItem.Name].Id = currentItem.Id;
                materialList[currentItem.Name].ReagentId = currentItem.ReagentId;

        else

                -- This is not a reagent, so add it to the construction list.
                tinsert(constructionList, currentItem);

                -- Get the tools required for this item.
                TTCA_AppendToolsToList(toolList, GetTradeSkillTools(currentItem.Id));

                -- This is not a reagent, so recurse over its reagents.
                local cBuildItems = getn(buildTree);

                for i=index+1, cBuildItems, 1 do

                        if (i <= cBuildItems) then

                                if (currentItem.Level + 1 == buildTree[i].Level)  then

                                        TTCA_CreateMaterialConstructionToolListsSub(materialList, constructionList, toolList, buildTree, i, iNumRequired);

                                elseif (currentItem.Level == buildTree[i].Level) then

                                        return;

                                end

                        end

                end

        end

end

-- ============================================================================
-- AppendToolsToList
-- ============================================================================
function TTCA_AppendToolsToList(list, ...)

        if (not list) then
                return;
        end

        -- Loop over the tools and add each one to the tool list.
        for i=1, arg.n, 2 do

                local toolDetails = {};

                if (arg[i+1] == 1) then
                        toolDetails.Type = "selected";
                else
                        toolDetails.Type = "unavailable";
                end

                toolDetails.Name = arg[i];
                list[toolDetails.Name] = toolDetails;

        end

end

-- ============================================================================
-- CreateBuildTree
-- ============================================================================
function TTCA_CreateBuildTree(tradeSkillId, buildTree, level, numRequired)

        if ((tradeSkillId <= 0) or (buildTree == nil)) then
                return nil;
        end

        if (level == nil) then
                level = 1;
        end

        if (numRequired == nil) then
                numRequired = 1;
        end

        -- Create a new trade skill item.
        local tradeSkillItem = TTCA_Item:New();
        tradeSkillItem.Name, tradeSkillItem.Type, tradeSkillItem.NumAvailable = TTCA_GetSkillInfo(tradeSkillId);
        tradeSkillItem.Level = level;
        tradeSkillItem.Id = tradeSkillId;
        tradeSkillItem.Texture = TTCA_GetSkillIcon(tradeSkillId);
        tradeSkillItem.NumRequired = numRequired;

        -- Add this item to the trade skill tree.
        tinsert(buildTree, tradeSkillItem);

        -- Create the reagent information for this trade skill.
        local cReagents = TTCA_GetSkillNumReagents(tradeSkillId);

        for i=1, cReagents, 1 do

                -- Get the reagent name.
                local reagentName = TTCA_GetSkillReagentInfo(tradeSkillId, i);

                -- Figure out if the reagent is craftable as a trade skill item.
                local iReagentTradeSkillId = TTCA_FindTradeSkillIdByName(reagentName);

                if (not (iReagentTradeSkillId == nil)) then

                        -- Figure out how many are required.
                        local name, texture, reagentNumRequired = TTCA_GetSkillReagentInfo(tradeSkillId, i);

                        -- The reagent is a craftable item, so create a trade skill item for it.
                        local tradeSkillReagentItem = TTCA_CreateBuildTree(iReagentTradeSkillId, buildTree, level + 1, reagentNumRequired * tradeSkillItem.NumRequired);

                        -- Initialize some other reagent information.
                        tradeSkillReagentItem.Name, tradeSkillReagentItem.Texture, tradeSkillReagentItem.NumRequired, tradeSkillReagentItem.NumAvailable = TTCA_GetSkillReagentInfo(tradeSkillId, i);

                        -- Modify the number required by the quantity entered.
                        tradeSkillReagentItem.NumRequired = tradeSkillReagentItem.NumRequired * tradeSkillItem.NumRequired;

                else

                        -- The reagent is not a craftable item, so get the reagent info for it.

                        -- Create a new trade skill item for this reagent.
                        local tradeSkillReagentItem = TTCA_Item:New();
                        tradeSkillReagentItem.Name, tradeSkillReagentItem.Texture, tradeSkillReagentItem.NumRequired, tradeSkillReagentItem.NumAvailable = TTCA_GetSkillReagentInfo(tradeSkillId, i);
                        tradeSkillReagentItem.Level = level + 1;
                        tradeSkillReagentItem.Id = tradeSkillId;
                        tradeSkillReagentItem.ReagentId = i;
                        tradeSkillReagentItem.Type = "reagent";

                        -- Sometimes NumAvailable can be nil.  Not yet sure why or how, but let's add some code for when it does.
                        if (tradeSkillReagentItem.NumAvailable == nil) then

                                DEFAULT_CHAT_FRAME:AddMessage("Congrats!  You've encountered a bug that I'm trying to track down.  Please go to www.wowinterface.com and send a message to twig314159 with the following information:");
                                DEFAULT_CHAT_FRAME:AddMessage("ItemName = " .. tradeSkillItem.Name);
                                DEFAULT_CHAT_FRAME:AddMessage("ReagentName = " .. tradeSkillReagentItem.Name);
                                DEFAULT_CHAT_FRAME:AddMessage("ReagentNumAvailable = " .. tradeSkillReagentItem.NumAvailable);

                                -- Fix up NumAvailable if it does happen to be nil.
                                tradeSkillReagentItem.NumAvailable = 0;

                        end

                        -- Modify the number required by the quantity entered.
                        tradeSkillReagentItem.NumRequired = tradeSkillReagentItem.NumRequired * tradeSkillItem.NumRequired;

                        -- Add this item to the trade skill tree.
                        tinsert(buildTree, tradeSkillReagentItem);

                end

        end

        return tradeSkillItem;

end

-- ============================================================================
-- ListItem_OnClick
-- ============================================================================
function TTCA_ListItem_OnClick(button)

        if (button == "LeftButton") then

                local fShift = IsShiftKeyDown();
                local fAlt = IsAltKeyDown();

                -- If the user shift-clicked the item, insert a link to it.
                if (fShift and not fAlt) then

                        if (ChatFrameEditBox:IsVisible()) then
                                ChatFrameEditBox:Insert(TTCA_GetItemLink(this.Id, this.ReagentId));
                        end

                -- If the user alt-clicked the item, link the entire dependency tree.
                -- If the build tree is active, then link all dependencies of the alt-clicked item.
                -- If the materials tree is active, then link only the materials listed in that view.
                elseif (fAlt and not fShift) then

                        -- If the chat edit box is not visible, just bail so we don't waste time
                        -- iterating only to do nothing.
                        if (not ChatFrameEditBox:IsVisible()) then
                                return;
                        end

                        -- The chat box is visible, so figure out who/what we're chatting with
                        -- so we can send each item as a seperate message and make sure it goes
                        -- to the right destination.
                        ChatEdit_ParseText(ChatFrameEditBox, 0);
                        local chatType = ChatFrameEditBox.chatType;
                        local chatLanguage = ChatFrameEditBox.language;
                        local chatTarget = nil;

                        -- Set the chat target.
                        if (chatType == "WHISPER") then
                                chatTarget = ChatFrameEditBox.tellTarget;
                        elseif (chatType == "CHANNEL") then
                                chatTarget = ChatFrameEditBox.channelTarget;
                        end

                        -- Link the dependency tree.
                        if (vTTCA_ActiveTab == TTCA_TAB_BUILDTREE) then

                                local cBuildItems = getn(vTTCA_BuildTree);

                                -- Loop through the build tree so we can link each item that this item depends on.
                                for i=this.Index, cBuildItems, 1 do

                                        local buildTreeItem = vTTCA_BuildTree[i];

                                        if (buildTreeItem ~= nil) then

                                                local text = "";

                                                -- Generate a nice little header first.
                                                if (i == this.Index) then

                                                        text = TTCA_TEXT_MATERIALSFOR .. TTCA_GetItemLink(buildTreeItem.Id, buildTreeItem.ReagentId);

                                                else

                                                        -- If we arrive back at the same level we started, then we're done!
                                                        if (buildTreeItem.Level == vTTCA_BuildTree[this.Index].Level) then
                                                                return;
                                                        end

                                                        -- Otherwise, ident the item and dump it to the chat window.
                                                        for n=vTTCA_BuildTree[this.Index].Level, buildTreeItem.Level, 1 do
                                                                text = text .. "  ";
                                                        end

                                                        text = text .. TTCA_GetItemLink(buildTreeItem.Id, buildTreeItem.ReagentId) .. " x" .. buildTreeItem.NumRequired;

                                                end
                                                
                                                if (text ~= nil) then

                                                        -- Now send the text and add it to the chat history.
                                                        SendChatMessage(text, chatType, chatLanguage, chatTarget);
                                                        ChatFrameEditBox:AddHistoryLine(text);

                                                end

                                        end

                                end                             

                        -- Link all the raw materials.
                        elseif (vTTCA_ActiveTab == TTCA_TAB_MATERIALS) then

                                -- Find the total number of materials.
                                local cMaterialItems = getn(vTTCA_MaterialList);

                                -- Loop through the material list so we can link each one.
                                for i=1, cMaterialItems, 1 do

                                        local materialListItem = vTTCA_MaterialList[i];

                                        if (materialListItem.Name ~= nil) then

                                                local text = nil;

                                                -- Generate a nice little header first.
                                                if (i == 1) then
                                                        text = TTCA_TEXT_MATERIALSFOR .. TTCA_GetItemLink(materialListItem.Id, materialListItem.ReagentId);
                                                end

                                                -- We want to link only reagents (not headers, tools, etc).
                                                if (materialListItem.Type == "reagent") then

                                                        text = "  " .. TTCA_GetItemLink(materialListItem.Id, materialListItem.ReagentId) .. " x" .. materialListItem.NumRequired;

                                                end

                                                if (text ~= nil) then

                                                        -- Now send the text and add it to the chat history.
                                                        SendChatMessage(text, chatType, chatLanguage, chatTarget);
                                                        ChatFrameEditBox:AddHistoryLine(text);

                                                end

                                        end

                                end

                        end                     

                -- Otherwise, just make the item the currently selected item.
                else

                        if (vTTCA_ActiveTab == TTCA_TAB_BUILDTREE) then

                                TTCA_SetSelection(this.Index);
                                TTCA_Update();

                        end

                end

        end

end

-- ============================================================================
-- ListItem_OnEnter
-- ============================================================================
function TTCA_ListItem_OnEnter()

        if (this.Id) then

                GameTooltip:SetOwner(this, "ANCHOR_UPPERLEFT");

                if (TTCA_IsCraftFrameVisible()) then
                        GameTooltip:SetCraftItem(this.Id, this.ReagentId);
                else
                        GameTooltip:SetTradeSkillItem(this.Id, this.ReagentId);
                end

        end

end

-- ============================================================================
-- ListItem_OnLeave
-- ============================================================================
function TTCA_ListItem_OnLeave()

        GameTooltip:Hide();

end

-- ============================================================================
-- CreateTree_OnClick
-- ============================================================================
function TTCA_CreateTree_OnClick(createAll)

        -- Set the building tree flag.
        vfTTCA_BuildingTree = true;

        -- Set the flag that says we're trying to build as many as we can.
        vfTTCA_BuildingAll = createAll;

        TTCA_BuildNextItem(vTTCA_ConstructionList);

end

-- ============================================================================
-- Tab_OnClick
-- ============================================================================
function TTCA_Tab_OnClick(activateTab)

        if (vTTCA_ActiveTab ~= activateTab) then

                -- Reset the scroll frame.
                TTCA_Reset();

                -- Deselect all of the tabs.
                PanelTemplates_DeselectTab(TTCA_Tab_BuildTree);
                PanelTemplates_DeselectTab(TTCA_Tab_Materials);

                -- Select the tab the user clicked.
                vTTCA_ActiveTab = activateTab;
                PanelTemplates_SelectTab(this);

                -- Update the frame list.
                TTCA_Update();

        end

end

-- ============================================================================
-- IncrementQuantity_OnClick
-- ============================================================================
function TTCA_IncrementQuantity_OnClick(delta)

        local iNewNumber = TTCA_InputBox_Quantity:GetNumber() + delta;

        if ((iNewNumber > 0) and (iNewNumber < 100)) then

                TTCA_InputBox_Quantity:SetNumber(iNewNumber);

                -- Since the number has changed, we need to update our build tree.
                TTCA_Update();

        end

end

-- ============================================================================
-- BuildNextItem
-- ============================================================================
function TTCA_BuildNextItem(constructionList)

        local cItems = getn(constructionList);

        -- Loop through each item in the list and build it if we have all the
        -- reagents available.
        for i=1, cItems, 1 do

                local currentItem = constructionList[i];

                local tradeSkillDetails = {};
                tradeSkillDetails.Name, tradeSkillDetails.Type, tradeSkillDetails.NumAvailable = TTCA_GetSkillInfo(currentItem.Id);

                if ((tradeSkillDetails.NumAvailable > 0) and (currentItem.NumAvailable <= currentItem.NumRequired) and (i==1)) then

                        -- If we built the first item in the list and not building as many as
                        -- we can, then we're done!
                        if (not vfTTCA_BuildingAll) then
                                vfTTCA_BuildingTree = false;
                        end

                        TTCA_DoSkill(currentItem.Id, 1);

                        -- This means we built our intended target.  So, decrement the quantity by 1.
                        -- If it would be zero, then we really are done.  If not, keep building.
                        if (TTCA_InputBox_Quantity:GetNumber() - 1 > 0) then

                                TTCA_IncrementQuantity_OnClick(-1);
                                vfTTCA_BuildingTree = true;

                        end

                        return;

                elseif ((tradeSkillDetails.NumAvailable > 0) and (currentItem.NumAvailable < currentItem.NumRequired)) then

                        TTCA_DoSkill(currentItem.Id, 1);

                        return;

                end

        end

        -- If we made it here, then we went through the whole build list and found
        -- nothing to build, so stop trying.
        vfTTCA_BuildingTree = false;
        vfTTCA_BuildingAll = false;

end

-- ============================================================================
-- IsItemBuildable
-- ============================================================================
function TTCA_IsItemBuildable(buildTree, index, iNumRequired)

        if (iNumRequired == nil) then
                iNumRequired = 1;
        end

        local currentItem = buildTree[index];

        if (currentItem == nil) then
                return false;
        end

        -- If this item is a reagent, report that it is buildable as long as we
        -- have enough.
        if (currentItem.Type == "reagent") then

                if (currentItem.NumAvailable >= currentItem.NumRequired) then

                        return true;

                end

        else

                -- This item is not a regeant, so only report it is buildable if all of
                -- its children are buildable or we already have enough of them.
                local fIsBuildable = true;

                if (currentItem.NumAvailable >= currentItem.NumRequired) then

                        return fIsBuildable;

                end

                local cBuildItems = getn(buildTree);

                for i=index+1, cBuildItems, 1 do

                        if (i <= cBuildItems) then

                                if (currentItem.Level + 1 == buildTree[i].Level) then

                                        if (not TTCA_IsItemBuildable(buildTree, i, currentItem.NumRequired)) then

                                                fIsBuildable = false;

                                        end

                                elseif (currentItem.Level == buildTree[i].Level) then

                                        return fIsBuildable;

                                end

                        end

                end

                return fIsBuildable;

        end

        return false;

end

-- ============================================================================
-- Sort
-- ============================================================================
function TTCA_Sort(dictionary)

        local sortedList = {};

        -- Convert out dictionary list into an array.
        for key, value in dictionary do
                tinsert(sortedList, value);
        end

        -- Sort the array by the .Name property.
        sort(sortedList, function(a, b) return a.Name < b.Name end);

        return sortedList;

end

-- ============================================================================
-- LinkItem
--
-- Returns a link to the given item.  This link can then be used in a chat box.
-- ============================================================================
function TTCA_GetItemLink(itemId, reagentId)

        if (itemId and reagentId == nil) then
                return TTCA_GetSkillItemLink(itemId);
        elseif (itemId and reagentId) then
                return TTCA_GetSkillReagentItemLink(itemId, reagentId);
        end

        return nil;

end

function TTCA_IsCraftFrameVisible()

        return (CraftFrame ~= nil and CraftFrame:IsVisible());

end

function TTCA_GetTradeSkillOrCraftFrameName()

        if (TradeSkillFrame ~= nil and TradeSkillFrame:IsVisible()) then
                return TradeSkillFrame:GetName();
        elseif (CraftFrame ~= nil and CraftFrame:IsVisible()) then
                return CraftFrame:GetName();
        else
                return nil;
        end

end

function TTCA_GetSkillSelectionIndex()

        if (TTCA_IsCraftFrameVisible()) then
                return GetCraftSelectionIndex();
        else
                return GetTradeSkillSelectionIndex();
        end

end

function TTCA_GetNumSkills()

        if (TTCA_IsCraftFrameVisible()) then
                return GetNumCrafts();
        else
                return GetNumTradeSkills();
        end

end

function TTCA_GetSkillInfo(i)

        local name, subName, type, numAvailable, isExpanded;

        if (TTCA_IsCraftFrameVisible()) then
                name, subName, type, numAvailable, isExpanded = GetCraftInfo(i);
        else
                name, type, numAvailable, isExpanded = GetTradeSkillInfo(i);
        end

        return name, type, numAvailable, isExpanded;

end

function TTCA_GetSkillIcon(i)

        if (TTCA_IsCraftFrameVisible()) then
                return GetCraftIcon(i);
        else
                return GetTradeSkillIcon(i);
        end

end

function TTCA_GetSkillNumReagents(i)

        if (TTCA_IsCraftFrameVisible()) then
                return GetCraftNumReagents(i);
        else
                return GetTradeSkillNumReagents(i);
        end

end

function TTCA_GetSkillReagentInfo(index, reagentIndex)

        if (TTCA_IsCraftFrameVisible()) then
                return GetCraftReagentInfo(index, reagentIndex);
        else
                return GetTradeSkillReagentInfo(index, reagentIndex);
        end

end

function TTCA_DoSkill(i, n)

        if (TTCA_IsCraftFrameVisible()) then
                return DoCraft(i);
        else
                return DoTradeSkill(i, n);
        end

end

function TTCA_GetSkillItemLink(itemId)

        if (TTCA_IsCraftFrameVisible()) then
                return GetCraftItemLink(itemId);
        else
                return GetTradeSkillItemLink(itemId);
        end

end

function TTCA_GetSkillReagentItemLink(itemId, reagentId)

        if (TTCA_IsCraftFrameVisible()) then
                return GetCraftReagentItemLink(itemId, reagentId);
        else
                return GetTradeSkillReagentItemLink(itemId, reagentId);
        end

end