vanilla-wow-addons – Rev 1

Subversion Repositories:
Rev:
local L = AceLibrary("AceLocale-2.0"):new("FuBar_GarbageFu")
local Tablet = AceLibrary("Tablet-2.0");
local dewdrop = AceLibrary("Dewdrop-2.0");
local compost = AceLibrary("Compost-2.0");
local abacus = AceLibrary("Abacus-2.0");
local PeriodicTable = PeriodicTableEmbed:GetInstance("1");

GarbageFu = AceLibrary("AceAddon-2.0"):new("FuBarPlugin-2.0", "AceEvent-2.0", "AceConsole-2.0", "AceDB-2.0", "AceDebug-2.0");
GarbageFu.version = "1.0." .. string.sub("$Revision: 10349 $", 12, -3);
GarbageFu.date = string.sub("$Date: 2006-09-06 11:24:19 -0700 (Wed, 06 Sep 2006) $", 8, 17);
GarbageFu.hasIcon = true;
GarbageFu.clickableTooltip = true;
GarbageFu.overrideMenu = true;
GarbageFu.printFrame = DEFAULT_CHAT_FRAME;
GarbageFu.debugFrame = DEFAULT_CHAT_FRAME;

GarbageFu:RegisterDB("GarbageFuDB");
GarbageFu:RegisterDefaults("profile", {
        moneyformat = 1,
        namelength = 32,
        ignoreammobag = true,
        ignoreherbbag = false,
        ignoresoulbag = false,
        ignoreenchbag = false,
        sellallbutton = true,
        sellonlygrey = true,
        pricetype = false,
        garbageprices = false,
        itemicon = false,
        version = "",
        textcolor = { r=0.2, g=0.8, b=1 };
        threshold = 0,
        dropsets = {},
        dropitem = {},
        keepsets = {},
        keepitem = {},
});

GarbageFu:RegisterDefaults("account", {
        overrideprices = {};
});

------------------------------------------------------------------------------------------------------
-- Simple Property functions
------------------------------------------------------------------------------------------------------

function GarbageFu:IsAutodrop()
        return self.db.profile.autodrop;
end

function GarbageFu:ToggleAutodrop()
        self.db.profile.autodrop = not self.db.profile.autodrop;
end

function GarbageFu:GetDropThreshold()
        return self.db.profile.threshold;
end

function GarbageFu:IsDropThreshold(value)
        return self.db.profile.threshold == tonumber(value);
end

function GarbageFu:SetDropThreshold(value)
        self.db.profile.threshold = tonumber(value);
        self:UpdateDisplay();
end

function GarbageFu:GetMoneyFormat(value)
        return self.db.profile.moneyformat;
end

function GarbageFu:SetMoneyFormat(value)
        self.db.profile.moneyformat = tonumber(value);
        self:UpdateDisplay();
end

function GarbageFu:GetNameLength(value)
        return self.db.profile.namelength;
end

function GarbageFu.SetNameLength(self,value)
        self.db.profile.namelength = value;
        self:UpdateText();
end

function GarbageFu:IsIgnoringAmmoBag()
        return self.db.profile.ignoreammobag;
end

function GarbageFu:ToggleIgnoreAmmoBag()
        self.db.profile.ignoreammobag = not self.db.profile.ignoreammobag;
        self:InitBagScan();
end

function GarbageFu:IsIgnoringHerbBag()
        return self.db.profile.ignoreherbbag;
end

function GarbageFu:ToggleIgnoreHerbBag()
        self.db.profile.ignoreherbbag = not self.db.profile.ignoreherbbag;
        self:InitBagScan();
end

function GarbageFu:IsIgnoringSoulBag()
        return self.db.profile.ignoresoulbag;
end

function GarbageFu:ToggleIgnoreSoulBag()
        self.db.profile.ignoresoulbag = not self.db.profile.ignoresoulbag;
        self:InitBagScan();
end

function GarbageFu:IsIgnoringEnchBag()
        return self.db.profile.ignoreenchbag;
end

function GarbageFu:ToggleIgnoreEnchBag()
        self.db.profile.ignoreenchbag = not self.db.profile.ignoreenchbag;
        self:InitBagScan();
end

function GarbageFu:IsSafeToDelete()
        return self.vars.safedelete;
end

function GarbageFu:SetSafeToDelete(value)
        self.vars.safedelete = value;
end

function GarbageFu:IsSellAllButton()
        return self.db.profile.sellallbutton;
end

function GarbageFu:ToggleSellAllButton()
        self.db.profile.sellallbutton = not self.db.profile.sellallbutton;
        if self.db.profile.sellallbutton then
                MerchantFrame.sellAllButton:Show();
        else
                MerchantFrame.sellAllButton:Hide();
        end
end

function GarbageFu:IsItemIcon()
        return self.db.profile.itemicon;
end

function GarbageFu:ToggleItemIcon()
        self.db.profile.itemicon = not self.db.profile.itemicon;
        self:UpdateText();
end
        
function GarbageFu:IsPriceType()
        return self.db.profile.pricetype;
end

function GarbageFu:TogglePriceType()
        self.db.profile.pricetype = not self.db.profile.pricetype;
        self:UpdateTooltip();
end
        
function GarbageFu:IsSellOnlyGrey()
        return self.db.profile.sellonlygrey;
end

function GarbageFu:ToggleSellOnlyGrey()
        self.db.profile.sellonlygrey = not self.db.profile.sellonlygrey;
end

function GarbageFu:IsUsingGarbagePrices()
        return self.db.profile.garbageprices;
end

function GarbageFu:ToggleUseGarbagePrices()
        self.db.profile.garbageprices = not self.db.profile.garbageprices;
        if ( self.db.profile.garbageprices ) then
                self.db.profile.garbageprices = self:LoadGarbagePrices();
        else
                self.vars.vendorprices = nil;
                self.vars.pricecache = self:Table(self.vars.pricecache);
        end
        self:InitBagScan();
end
        
------------------------------------------------------------------------------------------------------
-- A little more complicate property functions
------------------------------------------------------------------------------------------------------

function GarbageFu:IsDropItem(itemid)
        if (self.db.profile.dropitem[itemid]) then return true end
end

function GarbageFu:ToggleDropItem(itemid)
        if (self.db.profile.dropitem[itemid]) then
                self.db.profile.dropitem[itemid] = nil;
        else
                self.db.profile.dropitem[itemid] = true;
                self.db.profile.keepitem[itemid] = nil;
        end
        self:UpdateDisplay();   
end

function GarbageFu:IsKeepItem(itemid)
        if (self.db.profile.keepitem[itemid]) then return true end
end

function GarbageFu:ToggleKeepItem(itemid)
        if (self.db.profile.keepitem[itemid]) then
                self.db.profile.keepitem[itemid] = nil;
        else
                self.db.profile.keepitem[itemid] = true;
                self.db.profile.dropitem[itemid] = nil;
        end
        self:UpdateDisplay();   
end

------------------------------------------------------------------------------------------------------
-- Constant data
------------------------------------------------------------------------------------------------------

GarbageFu.vars = {};
GarbageFu.vars.moneyformats = {
        { name=L["Condensed"], ex=abacus:FormatMoneyCondensed(123456,true,true) },
        { name=L["Short"], ex=abacus:FormatMoneyShort(123456,true,true) },
        { name=L["Full"], ex=abacus:FormatMoneyFull(123456,true,true) },
        { name=L["Extended"], ex=abacus:FormatMoneyExtended(123456,true,true) },
}
GarbageFu.vars.sets = {
        {id="foodall", name=L["Food"], order=1, sub = { 
                {id="food", name=L["Normal Food"], order=1 },
                {id="foodbonus", name=L["Bonus Food"], order=2 },
                {id="foodstat", name=L["Stat Food"], order=3 },
                {id="foodbreadconjured", name=L["Conjured Bread"], order=4 },
                {id="foodraw", name=L["Raw Food"], order=5 },
                {id="foodclassbread", name=L["Bread"], order=6 },
                {id="foodclassfish", name=L["Fish"], order=7 },
                {id="foodclassmeat", name=L["Meat"], order=8 },
                {id="foodclasscheese", name=L["Cheese"], order=9 },
                {id="foodclassfruit", name=L["Fruit"], order=10 },
                {id="foodclassfungus", name=L["Fungus"], order=11 },
        }, },
        {id="waterall", name=L["Water"], order=2, sub = {
                {id="water", name=L["Normal Water"], order=1 },
                {id="waterperc", name=L["Percent Water"], order=2 },
                {id="waterconjured", name=L["Conjured Water"], order=3 },
        }, },
        {id="booze", name=L["Booze"], order=3 },
        {id="tradeskills", name=L["Tradeskill"], order=4, sub = {
                {id="tradeskillalchemy", name=L["Alchemy"], order=1 },
                {id="tradeskillblacksmithing", name=L["Blacksmithing"], order=2 },
                {id="tradeskillcooking", name=L["Cooking"], order=3 },
                {id="tradeskillenchanting", name=L["Enchanting"], order=4 },
                {id="tradeskillengineering", name=L["Engineering"], order=5 },
                {id="tradeskillfirstaid", name=L["First Aid"], order=6 },
                {id="tradeskillleatherworking", name=L["Leatherworking"], order=7 },
                {id="tradeskilltailoring", name=L["Tailoring"], order=8 },
                {id="tradeskillpoison", name=L["Poison"], order=9 },
                {id="tradeskillsmelting", name=L["Smelting"], order=10 },
        }, },
        {id="tradeskilltools", name=L["Tradeskill Tools"], order=5 },
        {id="gatherskill", name=L["Gathered"], order=6, sub = {
                {id="gatherskillfishing", name=L["Fishing"], order=1 },
                {id="gatherskilldisenchant", name=L["Disenchant"], order=2 },
                {id="gatherskillherbalism", name=L["Herbalism"], order=3 },
                {id="gatherskillmining", name=L["Mining"], order=4 },
                {id="gatherskillskinning", name=L["Skinning"], order=5 },
        }, },
        {id="ammo", name=L["Ammunition"], order=7 },
        {id="bandages", name=L["Bandages"], order=8 },
        {id="explosives", name=L["Explosives"], order=9 },
        {id="faire", name=L["Darkmoon Fair"], order=10 },
        {id="fireworks", name=L["Fireworks"], order=11 },
        {id="poisons", name=L["Poisons"], order=12 },
        {id="potionall", name=L["Potions"], order=13, sub = {
                {id="potionhealall", name=L["Heal Potions"], order=1 },
                {id="potionmanaall", name=L["Mana Potions"], order=2 },
                {id="potionrage", name=L["Rage Potions"], order=3 },
                {id="potioncure", name=L["Cure Potions"], order=4 },
                {id="potionbuff", name=L["Buff Potions"], order=5 },
        }, },
        {id="scrolls", name=L["Scrolls"], order=14 },
        {id="reagent", name=L["Reagents"], order=15, sub = {
                {id="reagentpaladin", name=L["Paladin"], order=1 },
                {id="reagentdruid", name=L["Druid"], order=2 },
                {id="reagentmage", name=L["Mage"], order=3 },
                {id="reagentpriest", name=L["Priest"], order=4 },
                {id="reagentrogue", name=L["Rogue"], order=5 },
                {id="reagentshaman", name=L["Shaman"], order=6 },
                {id="reagentwarlock", name=L["Warlock"], order=7 },
        }, },
        {id="weapontempenchants", name=L["Weapon Enchants"], order=16 },
        {id="mounts", name=L["Mounts"], order=17 },
        {id="minipetall", name=L["Mini Pets"], order=18 },
}

------------------------------------------------------------------------------------------------------
-- FuBar required functions
------------------------------------------------------------------------------------------------------

function GarbageFu:OnInitialize()
        -- Put stuff that only neeeds to be done once here
        self.vars.bags = self:Table();
        self.vars.items = self:Table();
        self.vars.colors = self:Table();
        self.vars.pricecache = self:Table();
        -- Get Item quality colors
        for i=0,6 do
                self.vars.colors[i] = self:Table();
                self.vars.colors[i].r, self.vars.colors[i].g, self.vars.colors[i].b, self.vars.colors[i].hex = GetItemQualityColor(i);
                self.vars.colors[i].desc = getglobal("ITEM_QUALITY".. i.. "_DESC");
        end
        -- Create Static dialog for Reset Settings
        StaticPopupDialogs["GARBAGEFU_RESET"] = {
        text = L["Are you sure you want to reset all your settings for GarbageFU?"],
        button1 = TEXT(ACCEPT),
        button2 = TEXT(CANCEL),
        OnAccept = function() GarbageFu.SettingsReset(self)     end,
          timeout = 0,
          whileDead = 1,
                hideOnEscape = 1
        };
        -- Sort Sets tables
        for i,s in self.vars.sets do
                if s.sub then table.sort(s.sub,function(a,b) return a.order<b.order end) end
        end
        table.sort(self.vars.sets,function(a,b) return a.order<b.order end)
        -- Create the SellAllButton
        self:MakeSellAllButton()
        if self:IsUsingGarbagePrices() then
                self:LoadGarbagePrices();
        end
        -- Store AddOn version. In case we need to check it in the future for upgrades.
        self.db.profile.version = self.version;
end

function GarbageFu:OnEnable()
        -- Stuff that needs to be done every time the mod is enabled
        self:SetSafeToDelete(false);
        if self:IsSellAllButton() then
                MerchantFrame.sellAllButton:Show();
        end
        self:InitBagScan();
        self:MakeSetsTables();
        self:OnEnteringWorld();
        self:SetSafeToDelete(true);
end

function GarbageFu:OnDisable()
        self:SetSafeToDelete(false);
        MerchantFrame.sellAllButton:Hide();
end

function GarbageFu:OnDataUpdate()
end

function GarbageFu:OnTextUpdate()
        self:DebugPrint("OnTextUpdate");
        local item = self:GetFirstDroppableItem();
        if item then
                local text = "";
                if ( string.len(item.name) > self:GetNameLength() ) then
                        text = self.vars.colors[item.qual].hex..string.sub(item.name, 1, self:GetNameLength()-2).."..".."|r";
                else
                        text = self.vars.colors[item.qual].hex..item.name.."|r";
                end
                if ( item.maxstack > 1 ) then
                        text = text.." |cffffff00x"..tostring(item.stack).."|r";
                end
                text = text.." "..self:GetMoneyString(item.totvalue);
                self:SetText(text);
                if self:IsItemIcon() then self:SetIcon(item.tex) else self:SetIcon(true) end
        else
                self:SetText("GarbageFu");
                self:SetIcon(true);
        end
        if self:IsThereItemsToSell() then
                MerchantFrame.sellAllButton:Enable();
        else
                MerchantFrame.sellAllButton:Disable();
        end
end

function GarbageFu:OnTooltipUpdate()
        self:DebugPrint("OnTooltipUpdate");
        local cat;
        if self:IsPriceType() then
                cat = Tablet:AddCategory('columns', 3)
        else
                cat = Tablet:AddCategory('columns', 2)
        end     
        for i,item in self.vars.items do
                if self:IsItemDroppable(item) then
                        local stacktext = "|r";
                        if ( item.maxstack > 1 ) then
                                stacktext = stacktext .. "|r |cffffff00("..tostring(item.stack).."/"..tostring(item.maxstack)..")|r";
                        end
                        local moneytext = self:GetMoneyString(item.totvalue)
                        cat:AddLine(
                                'text', self.vars.colors[item.qual].hex..item.name..stacktext, 
                                'text2', moneytext, 
                                'text3', item.pricetype, 
                                'hasCheck', true, 'checked', true, 'checkIcon', item.tex,
                                'func', 'OnClickItem', 'arg1', self, 'arg2', item
                        )
                end
        end
        if MerchantFrame:IsVisible() then
                Tablet:SetHint(L["Shift-Click to sell item\nControl-Click to add item to keep list"]);
        else
                Tablet:SetHint(L["Shift-Click to drop item\nControl-Click to add item to keep list"]);
        end
end

function GarbageFu:OnMenuRequest(level, value, inTooltip, value2)
        if value then
                if value2 then
                        self:DebugPrint("OnMenuRequest level="..level.." value="..value.." value2="..value2);
                else
                        self:DebugPrint("OnMenuRequest level="..level.." value="..value);
                end
        else
                self:DebugPrint("OnMenuRequest level="..level);
        end
        if ( level == 1 ) then
                local item = self:GetFirstDroppableItem();
                if item then
                        local text = self.vars.colors[item.qual].hex..item.name.."|r";
                        if ( item.maxstack > 1 ) then
                                text = text.." |cffffff00x"..tostring(item.stack).."|r";
                        end
                        dewdrop:AddLine('text', text..' '..self:GetMoneyString(item.totvalue), 
                                'notClickable', true,   'checked', true, 'checkIcon', item.tex );
                        local text = MerchantFrame:IsVisible() and L["Sell this item"] or L["Drop this item"];
                        dewdrop:AddLine('text', text, 'arg1', self, 'func', 'DropFirstItem', 'tooltipTitle', text,
                                'tooltipText', L["Drops this item, or sells it if the vendor window is open"] );
                        if ( item.qual > 0 ) then
                                dewdrop:AddLine('text', L["Keep this item"], 'arg1', self, 'func', 'KeepFirstItem',
                                        'tooltipTitle', L["Keep this item"], 'tooltipText', L["Adds this item to the keep items list"]);
                        end
                        if MerchantFrame:IsVisible() and self:IsThereItemsToSell() then
                                local sellValue = self:GetSellValue();
                                GameTooltip:AddLine(L["Value:"].." "..self:GetMoneyString(sellValue));
                                if self:IsSellOnlyGrey() then
                                        dewdrop:AddLine('text', L["Sell all grey items"].." "..self:GetMoneyString(sellValue), 
                                                'arg1', self, 'func', 'SellAllItems',
                                                'tooltipTitle', L["Sell all grey items"], 'tooltipText', L["SellsAll TooltipText2"],
                                                'closeWhenClicked', true );
                                else
                                        dewdrop:AddLine('text', L["Sell all garbage items"].." "..self:GetMoneyString(sellValue), 
                                                'arg1', self, 'func', 'SellAllItems',
                                                'tooltipTitle', L["Sell all garbage items"], 'tooltipText', L["SellsAll TooltipText1"],
                                                'closeWhenClicked', true );
                                end
                        end
                        if ( item.qual > 0 or item.totvalue == 0 or self.db.account.overrideprices[item.id]) then
                                dewdrop:AddLine('text', L["Edit value for this item"], 'hasArrow', true,
                                        'hasEditBox', true, 'editBoxText', tostring(item.value),
                                        'editBoxArg1', self, 'editBoxFunc', GarbageFu.SetFirstItemValue, 
                                        'editBoxChangeArg1', self, 'editBoxChangeFunc', GarbageFu.ValidateValue,
                                        'tooltipTitle', L["Edit value for this item"], 'tooltipText', L["Value TooltipText"] );
                        end
                        dewdrop:AddLine();
                end
                dewdrop:AddLine('text', L["Drop"], 'hasArrow', true, 'value', 'drop' );
                dewdrop:AddLine('text', L["Keep"], 'hasArrow', true, 'value', 'keep' );
                dewdrop:AddLine('text', L["Edit item values"], 'hasArrow', true, 'value', 'itemvalue' );
                dewdrop:AddLine();
                dewdrop:AddLine('text', L["Options"], 'hasArrow', true, 'value', 'options' );
        elseif ( level == 2 ) then
                if ( value == 'drop' ) then
                        dewdrop:AddLine('text', L["Drop"], "isTitle", true );
                        dewdrop:AddLine('text', L["Drop Sets"], 'hasArrow', true, 'value', 'dropsets' );
                        dewdrop:AddLine('text', L["Drop Items"], 'hasArrow', true, 'value', 'dropitem' );
                elseif ( value == 'keep' ) then
                        dewdrop:AddLine('text', L["Keep"], "isTitle", true );
                        dewdrop:AddLine('text', L["Keep Sets"], 'hasArrow', true, 'value', 'keepsets' );
                        dewdrop:AddLine('text', L["Keep Items"], 'hasArrow', true, 'value', 'keepitem' );
                elseif ( value == 'itemvalue' ) then
                        -- Edit item values
                        dewdrop:AddLine('text', L["Edit item values"], "isTitle", true );
                        local tbl = self:GetCustomItemValueTable();
                        for _,i in tbl do
                                dewdrop:AddLine('text', self.vars.colors[i.qual].hex .. i.name, 
                                        'checked', self.db.account.overrideprices[i.id] ~= nil, 'hasArrow', true, 'hasEditBox', true,
                                        'editBoxText', tostring(i.value),
                                        'editBoxArg1', self, 'editBoxArg2', i.id, 'editBoxFunc', GarbageFu.SetItemValue, 
                                        'editBoxChangeArg1', self, 'editBoxChangeFunc', GarbageFu.ValidateValue,
                                        'tooltipTitle', L["Edit value for this item"], 'tooltipText', L["Value TooltipText"]);
                        end
                        --compost:Reclaim(tbl,2);
                        --tbl = nil;
                elseif ( value == 'options' ) then
                        dewdrop:AddLine('text', L["Drop Threshold"], 'hasArrow', true, 'value', 'threshold' );
                        dewdrop:AddLine('text', L["Money Format"], 'hasArrow', true, 'value', 'moneyformat' );
                        dewdrop:AddLine('text', L["Max Item Name Length"], 'hasArrow', true, 
                                'hasSlider', true, 'sliderMin', 5, 'sliderMax', 32, 'sliderStep', 1,
                                'sliderValue', self:GetNameLength(), 
                                'sliderFunc', GarbageFu.SetNameLength, 'sliderArg1', self );
                        dewdrop:AddLine('text', L["Sell All Button on Merchant Window"], 'checked', self:IsSellAllButton(), 
                                'arg1', self, 'func', 'ToggleSellAllButton',
                                'tooltipTitle', L["Sell All Button on Merchant Window"], 'tooltipText', L["SellAllButton TooltipText"]);
                        dewdrop:AddLine('text', L["Only Autosell Grey Items"], 'checked', self:IsSellOnlyGrey(), 
                                'arg1', self, 'func', 'ToggleSellOnlyGrey',
                                'tooltipTitle', L["Only Autosell Grey Items"], 'tooltipText', L["OnlyGrey TooltipText"]);
                        dewdrop:AddLine('text', L["Use Items Icon on Toolbar"], 'checked', self:IsItemIcon(), 
                                'arg1', self, 'func', 'ToggleItemIcon');
                        dewdrop:AddLine('text', L["Show Price Type in Tooltip"], 'checked', self:IsPriceType(), 
                                'arg1', self, 'func', 'TogglePriceType');
                        dewdrop:AddLine('text', L["Use GarbageFu Prices"], 'checked', self:IsUsingGarbagePrices(), 
                                'arg1', self, 'func', 'ToggleUseGarbagePrices',
                                'disabled', not self:IsGarbagePricesAvailable(),
                                'tooltipTitle', L["Use GarbageFu Prices"], 'tooltipText', L["GarbageFu_Prices ToltipText"]);
                        dewdrop:AddLine('text', L["Ignore Ammo Bags"], 'checked', self:IsIgnoringAmmoBag(), 
                                'arg1', self, 'func', 'ToggleIgnoreAmmoBag');
                        dewdrop:AddLine('text', L["Ignore Herb Bags"], 'checked', self:IsIgnoringHerbBag(), 
                                'arg1', self, 'func', 'ToggleIgnoreHerbBag');
                        dewdrop:AddLine('text', L["Ignore Soulshard Bags"], 'checked', self:IsIgnoringSoulBag(), 
                                'arg1', self, 'func', 'ToggleIgnoreSoulBag');
                        dewdrop:AddLine('text', L["Ignore Enchanting Bags"], 'checked', self:IsIgnoringEnchBag(), 
                                'arg1', self, 'func', 'ToggleIgnoreEnchBag');
                        --dewdrop:AddLine('text', L["Rescan"], 'arg1', self, 'func', 'InitBagScan' );
                        dewdrop:AddLine('text', L["Reset"], 'arg1', "GARBAGEFU_RESET", 'func', StaticPopup_Show, 'closeWhenClicked', true );
                        dewdrop:AddLine();
                        self:AddImpliedMenuOptions(2);
                end
        elseif ( level == 3 ) then
                if (value2 == 'drop') then
                        if ( value == 'dropsets' ) then
                                -- Drop - Sets
                                dewdrop:AddLine('text', L["Drop Sets"], "isTitle", true );
                                for n,i in self.vars.sets do
                                        local checked = self:IsDropSet(n);
                                        local checkIcon = "Interface\\Buttons\\UI-CheckBox-Check" 
                                        if self:IsChildDropSet(n) then
                                                checked = true;
                                                checkIcon = "Interface\\Buttons\\UI-CheckBox-Check-Disabled"
                                        end
                                        dewdrop:AddLine('text', i.name, 
                                                'checked', checked, 'checkIcon', checkIcon,
                                                'disabled', self:IsKeepSet(n),
                                                'hasArrow', i.sub ~= nil, 'value', n,
                                                'arg1', self, 'arg2', n, 'func', 'ToggleDropSets' );
                                end
                        elseif ( value == 'dropitem' ) then
                                -- Drop - Items
                                dewdrop:AddLine('text', L["Drop Items"], "isTitle", true );
                                local tbl = self:GetDropItemTable();
                                for _,i in tbl do
                                        dewdrop:AddLine('text', self.vars.colors[i.qual].hex .. i.name, 
                                                'checked', self:IsDropItem(i.id),
                                                'arg1', self, 'arg2', i.id, 'func', 'ToggleDropItem');
                                end
                                --compost:Reclaim(tbl,2);
                                --tbl = nil;
                        end
                elseif (value2 == 'keep') then 
                        if ( value == 'keepsets' ) then
                                -- Keep - Sets
                                dewdrop:AddLine('text', L["Keep Sets"], "isTitle", true );
                                for n,i in self.vars.sets do
                                        local checked = self:IsKeepSet(n);
                                        local checkIcon = "Interface\\Buttons\\UI-CheckBox-Check" 
                                        if self:IsChildKeepSet(n) then
                                                checked = true;
                                                checkIcon = "Interface\\Buttons\\UI-CheckBox-Check-Disabled"
                                        end
                                        dewdrop:AddLine('text', i.name, 
                                                'checked', checked, 'checkIcon', checkIcon,
                                                'disabled', self:IsDropSet(n),
                                                'hasArrow', i.sub ~= nil, 'value', n,
                                                'arg1', self, 'arg2', n, 'func', 'ToggleKeepSets' );
                                end
                        elseif ( value == 'keepitem' ) then
                                -- Keep - Items
                                dewdrop:AddLine('text', L["Keep Items"], "isTitle", true );
                                local tbl = self:GetKeepItemTable();
                                for _,i in tbl do
                                        dewdrop:AddLine('text', self.vars.colors[i.qual].hex .. i.name, 
                                                'checked', self:IsKeepItem(i.id),
                                                'arg1', self, 'arg2', i.id, 'func', 'ToggleKeepItem');
                                end
                                --compost:Reclaim(tbl,2);
                                --tbl = nil;
                        end
                elseif ( value2 == 'options' ) then
                        if ( value == 'threshold' ) then
                                dewdrop:AddLine('text', L["Drop Threshold"], "isTitle", true );
                                for a=0,6 do
                                        dewdrop:AddLine('text', self.vars.colors[a].hex..self.vars.colors[a].desc.."|r",
                                                'checked', a == self:GetDropThreshold(),
                                                'arg1', self, 'arg2', a, 'isRadio', true, 
                                                'func', 'SetDropThreshold' );
                                end
                        elseif ( value == 'moneyformat' ) then
                                dewdrop:AddLine('text', L["Money Format"], "isTitle", true );
                                for a,f in self.vars.moneyformats do
                                        dewdrop:AddLine('text', f.name.."\t"..f.ex,
                                                'checked', a == self:GetMoneyFormat(),
                                                'arg1', self, 'arg2', a, 'isRadio', true, 
                                                'func', 'SetMoneyFormat' );
                                end
                        else
                                self:AddImpliedMenuOptions(2);
                        end
                end
        elseif ( level == 4 ) then
                if ( value2 == 'dropsets' ) then
                        dewdrop:AddLine('text', L["Drop Set"].." "..self.vars.sets[value].name, "isTitle", true );
                        for n,i in self.vars.sets[value].sub do
                                dewdrop:AddLine('text', i.name, 
                                        'checked', self:IsDropSet(value,n),
                                        'disabled',self:IsKeepSet(value,n),
                                        'arg1', self, 'arg2', value, 'arg3', n, 'func', 'ToggleDropSets' );
                        end
                elseif ( value2 == 'keepsets' ) then
                        dewdrop:AddLine('text', L["Keep Set"].." "..self.vars.sets[value].name, "isTitle", true );
                        for n,i in self.vars.sets[value].sub do
                                dewdrop:AddLine('text', i.name, 
                                        'checked', self:IsKeepSet(value,n),
                                        'disabled', self:IsDropSet(value,n),
                                        'arg1', self, 'arg2', value, 'arg3', n, 'func', 'ToggleKeepSets' );
                        end
                end
        end
end

function GarbageFu:OnClick(button)
        if IsShiftKeyDown() then
                self:DropFirstItem();
        end
        if IsControlKeyDown() then
                self:KeepFirstItem();
        end
end

------------------------------------------------------------------------------------------------------
-- Event functions
------------------------------------------------------------------------------------------------------

function GarbageFu:OnClickItem(item)
        if IsShiftKeyDown() then
                self:DropItem(item);
        end
        if IsControlKeyDown() then
                self:KeepItem(item);
        end
end

function GarbageFu:OnEnteringWorld()
        self:RegisterEvent("BAG_UPDATE","OnBagUpdate");
        self:RegisterEvent("MERCHANT_SHOW","OnMerchantOpen");
        self:RegisterEvent("MERCHANT_CLOSED","OnMerchantClose");
        self:RegisterEvent("PLAYER_LEAVING_WORLD","OnLeavingWorld");
        if self:IsEventRegistered("PLAYER_ENTERING_WORLD") then self:UnregisterEvent("PLAYER_ENTERING_WORLD") end
end

function GarbageFu:OnLeavingWorld()
        self:RegisterEvent("PLAYER_ENTERING_WORLD","OnEnteringWorld");
        if self:IsEventRegistered("BAG_UPDATE") then self:UnregisterEvent("BAG_UPDATE") end
        if self:IsEventRegistered("MERCHANT_SHOW") then self:UnregisterEvent("MERCHANT_SHOW") end
        if self:IsEventRegistered("MERCHANT_CLOSED") then self:UnregisterEvent("MERCHANT_CLOSED") end
        if self:IsEventRegistered("PLAYER_LEAVING_WORLD") then self:UnregisterEvent("PLAYER_LEAVING_WORLD") end
end

function GarbageFu:OnBagUpdate(bag)
--      local bag = arg1;
        self:DebugPrint("OnBagUpdate bag="..tostring(bag));
        if ( not bag or bag < 0 or bag > 4 ) then return end
        if ( self.vars.bags[bag].bagName ~= GetBagName(bag) ) then -- Houston, we got a problem
                self:DebugPrint("New or changed bag detected. Doing a full scan");
                self:InitBagScan(); -- New or changed bag detected. Do a full scan
                return
        end
        if self.vars.bags[bag].ignore then return end
        self:SetSafeToDelete(false);
        for slot=1,self.vars.bags[bag].numSlots do
                if not self.vars.bags[bag][slot] then self.vars.bags[bag][slot] = self:Table() end
                local link = GetContainerItemLink(bag, slot);
                local _, stack = GetContainerItemInfo(bag, slot);
                if ( link ~= self.vars.bags[bag][slot].link or stack ~= self.vars.bags[bag][slot].stack ) then
                        self.vars.bags[bag][slot].link = link;
                        self.vars.bags[bag][slot].stack = stack;
                        self:UpdateItem(bag,slot);
                        self:SortItems();
                        self:UpdateDisplay();
                end
        end
        self:SetSafeToDelete(true);
end

------------------------------------------------------------------------------------------------------
-- Core functions
------------------------------------------------------------------------------------------------------

function GarbageFu:InitBagScan()
        self:SetSafeToDelete(false);
        self.vars.items = self:Table(self.vars.items,3);
        for bag=0,4 do
                self.vars.bags[bag] = self:Table(self.vars.bags[bag],3);
                self.vars.bags[bag].numSlots = GetContainerNumSlots(bag);
                self.vars.bags[bag].bagName = GetBagName(bag);
                self.vars.bags[bag].ignore = self:IgnoreBag(bag);
                if not self.vars.bags[bag].ignore then
                        for slot=1,self.vars.bags[bag].numSlots do
                                self.vars.bags[bag][slot] = self:Table(self.vars.bags[bag][slot],2);
                                self.vars.bags[bag][slot].link = GetContainerItemLink(bag, slot)
                                _, self.vars.bags[bag][slot].stack = GetContainerItemInfo(bag, slot)
                                self:UpdateItem(bag,slot);
                        end
                end
        end
        self:SortItems();
        self:UpdateDisplay();
        self:SetSafeToDelete(true);
end

function GarbageFu:UpdateItem(bag, slot)
        local itemidx = nil;
        local item = nil;
        for a,i in self.vars.items do
                if( i.bag == bag and i.slot == slot ) then
                        itemidx = a;
                        item = i;
                        break;
                end
        end
        if ( self.vars.bags[bag][slot].link ) then
                if ( not item ) then item = self:Table() end
                item.bag = bag;
                item.slot = slot;
                item.stack = self.vars.bags[bag][slot].stack;
                if ( item.link ~= self.vars.bags[bag][slot].link ) then
                        item.link = self.vars.bags[bag][slot].link;
                        item.id, item.code, item.name, item.color = self:GetItemId(item.link);
                        if ( item.id ) then
                                _, _, item.qual, _, _, _, item.maxstack, _, item.tex = GetItemInfo(item.id);
                                if not item.qual then --- Item not in local cache
                                        item.qual = self:GetItemQualFromColor(item.color);
                                        item.tex = "Interface\\Icons\\INV_Misc_QuestionMark.blp";
                                        item.maxstack = 0;
                                        item.notseen = true;
                                else
                                        item.notseen = nil;
                                end
                                self:GetItemValue(item);
                        else
                                item.notseen = true;
                        end
                else -- Just new stack size, recalc totvalue
                        if ( item.value ) then
                                item.totvalue = item.value * item.stack;
                        end
                end             
                if ( not itemidx ) then
                        table.insert(self.vars.items, item);
                end             
        elseif ( itemidx ) then
                compost:Reclaim(table.remove(self.vars.items, itemidx),2);
        end
end

local function ItemSortFunc(item1, item2)
        if item1.notseen then return false end
        if item2.notseen then return true end
        if ( item1.totvalue < item2.totvalue ) then return true end
        if ( item1.totvalue == item2.totvalue ) then
                if ( item1.id < item2.id ) then return true end
        end
        return false
end

function GarbageFu:SortItems()
        table.sort(self.vars.items, ItemSortFunc);
end

function GarbageFu:GetItemId(link)
  if link then
                local _, _, color, code, id, name = string.find(link, "|cff(%x%x%x%x%x%x)|Hitem:((%d+):%d+:%d+:%d+)|h%[(.+)%]|h|r")
                return tonumber(id), code, name, color
  end
end

function GarbageFu:GetItemQualFromColor(color)
        for i,c in self.vars.colors do
                if ( color == string.sub(c.hex,5) ) then return i end
        end
end


-- Check if an item is elegiable for drop
function GarbageFu:IsItemDroppable(item)
        if (item.notseen) then self:UpdateItem(item.bag,item.slot) end -- Not seen item. Try again.
        if (item.notseen) then return false end -- Still not seen.
        if (item.totvalue == 0 and item.qual > 0) then return false end -- All items above poor quality without price is not dropped for saftey reasons
        if (self.db.profile.keepitem[item.id]) then return false end
        if (self.db.profile.dropitem[item.id]) then return true end
        if self:IsItemInKeepSets(item.id) then return false end
        if self:IsItemInDropSets(item.id) then return true end
        if (item.qual <= self:GetDropThreshold()) then return true end
        return false
end

function GarbageFu:GetFirstDroppableItem()
        for i,item in self.vars.items do
                if self:IsItemDroppable(item) then
                        return item
                end
        end
end

------------------------------------------------------------------------------------------------------
-- Drop down menu functions
------------------------------------------------------------------------------------------------------

function GarbageFu:GetDropItemTable()
        self.vars.temptbl = self:Table(self.vars.temptbl);
--      local tbl = {};
        local tbl2 = self:Table();
        -- Start with itemid's listed as drop
        for i,_ in self.db.profile.dropitem do
                local item = self:Table();
                item.id = i;
                item.name,_,item.qual = GetItemInfo(i);
                if item.name then
                        table.insert(self.vars.temptbl,item);
                        tbl2[i] = true;
                else
                        compost:Reclaim(item,1);
                        item = nil;
                end
        end
        -- Add items in inventory with a qual over drop threshold or in keep sets
        for _,i in self.vars.items do
                if ( not i.notseen and not tbl2[i.id] and i.totvalue > 0 and
                                 (i.qual>self:GetDropThreshold() or self:IsItemInKeepSets(i.id)) and
                                 not self:IsItemInDropSets(i.id)) then
                        local item = self:Table();
                        item.id = i.id;
                        item.name = i.name;
                        item.qual = i.qual;
                        table.insert(self.vars.temptbl,item);
                        tbl2[i.id] = true;
                end
        end
        compost:Reclaim(tbl2,2);
        tbl2 = nil;
        table.sort(self.vars.temptbl,function(a,b) return a.name<b.name end);
        return self.vars.temptbl
end

function GarbageFu:GetKeepItemTable()
        self.vars.temptbl = self:Table(self.vars.temptbl);
        local tbl2 = self:Table();
        -- Start with itemid's listed as keep
        for i,_ in self.db.profile.keepitem do
                local item = self:Table();
                item.id = i;
                item.name,_,item.qual = GetItemInfo(i);
                if item.name then
                        table.insert(self.vars.temptbl,item);
                        tbl2[i] = true;
                else
                        compost:Reclaim(item,1);
                        item = nil;
                end
        end
        -- Add items in inventory with a qual eq or below drop threshold or in drop sets. Not poor quality items
        for _,i in self.vars.items do
                if ( not i.notseen and i.totvalue > 0 and i.qual > 0 and not tbl2[i.id] and 
                                 (i.qual<=self:GetDropThreshold() or self:IsItemInDropSets(i.id)) and
                                 not self:IsItemInKeepSets(i.id)) then
                        local item = self:Table();
                        item.id = i.id;
                        item.name = i.name;
                        item.qual = i.qual;
                        table.insert(self.vars.temptbl,item);
                        tbl2[i.id] = true;
                end
        end
        compost:Reclaim(tbl2,2);
        tbl2 = nil;
        table.sort(self.vars.temptbl,function(a,b) return a.name<b.name end);
        return self.vars.temptbl
end

function GarbageFu:GetCustomItemValueTable()
        self.vars.temptbl = self:Table(self.vars.temptbl);
        local tbl2 = self:Table();
        -- Start with itemid's with a custom price
        for i,v in self.db.account.overrideprices do
                local item = self:Table();
                item.id = i;
                item.value = v;
                item.name,_,item.qual = GetItemInfo(i);
                if item.name then
                        table.insert(self.vars.temptbl,item);
                        tbl2[i] = true;
                else
                        compost:Reclaim(item,1);
                        item = nil;
                end
        end
        -- Add items in inventory. Not poor quality items that already have a price
        for _,i in self.vars.items do
                if ( not i.notseen and not tbl2[i.id] and (i.qual > 0 or i.totvalue == 0)) then
                        local item = self:Table();
                        item.id = i.id;
                        item.name = i.name;
                        item.qual = i.qual;
                        item.value = i.value;
                        if not item.value then item.value = 0 end
                        table.insert(self.vars.temptbl,item);
                        tbl2[i.id] = true;
                end
        end
        compost:Reclaim(tbl2,2);
        tbl2 = nil;
        table.sort(self.vars.temptbl,function(a,b) return a.name<b.name end);
        return self.vars.temptbl
end

function GarbageFu:KeepFirstItem()
        local item = self:GetFirstDroppableItem();
        if item then
                self:KeepItem(item);
        end
end

function GarbageFu:KeepItem(item)
        if item then
                self:ToggleKeepItem(item.id);
                self:MyPrint(L["Adding %s to keep item list"], item.link );
        end
end

function GarbageFu:DropFirstItem()
        local item = self:GetFirstDroppableItem();
        if item then
                self:DropItem(item);
        end
end

function GarbageFu:DropItem(item)
        if not item or not item.bag or not item.slot then return end
        if not self:IsSafeToDelete() then return end
        if self:IsItemDroppable(item) then
                if (MerchantFrame:IsVisible()) then
                        self:MyPrint(L["Selling x%s %s worth %s"], item.stack, item.name, self:GetMoneyString(item.totvalue) );
                        UseContainerItem(item.bag, item.slot)
                        self:SetSafeToDelete(false);
                else
                        self:MyPrint(L["Dropping x%s %s worth %s"], item.stack, item.name, self:GetMoneyString(item.totvalue) );
                        PickupContainerItem(item.bag, item.slot)
                        DeleteCursorItem();
                        self:SetSafeToDelete(false);
                end
        end
end

function GarbageFu:SettingsReset()
        self:ResetDB('profile');
        self:ResetDB('account');
        self.vars.dropsets = self:Table(self.vars.dropsets,1);
        self.vars.keepsets = self:Table(self.vars.keepsets,1);
        self:UpdateDisplay();
end

function GarbageFu:GetMoneyString(value)
        if ( value == 0 ) then
                return "|cffffcc00??|r"; -- No price
        elseif ( self.db.profile.moneyformat == 1 ) then
                return abacus:FormatMoneyCondensed(value,true,true)
        elseif ( self.db.profile.moneyformat == 2 ) then
                return abacus:FormatMoneyShort(value,true,true)
        elseif ( self.db.profile.moneyformat == 3 ) then
                return abacus:FormatMoneyFull(value,true,true)
        else
                return abacus:FormatMoneyExtended(value,true,true)
        end
end

------------------------------------------------------------------------------------------------------
-- Sets functions
------------------------------------------------------------------------------------------------------

function GarbageFu:IsItemInSet(itemId,set)
        local val,set = PeriodicTable:ItemInSet(itemId,set);
        if set then return true end
end

-- Check if item is in one of the sets selected for drop.
function GarbageFu:IsItemInDropSets(itemid)
        local val,set = PeriodicTable:ItemInSet(itemid,self.vars.dropsets);
        if set then return true end
end

-- Check if item is in one of the sets selected for keep.
function GarbageFu:IsItemInKeepSets(itemid)
        local val,set = PeriodicTable:ItemInSet(itemid,self.vars.keepsets);
        if set then return true end
end

function GarbageFu:IsDropSet(set,subset)
        local setid = self.vars.sets[set].id;
        if subset then
                local subsetid = self.vars.sets[set].sub[subset].id;
                return self.db.profile.dropsets[setid] or self.db.profile.dropsets[subsetid];
        end
        return self.db.profile.dropsets[setid];
end

function GarbageFu:IsChildDropSet(set)
        if self.vars.sets[set].sub then
                for _,s in self.vars.sets[set].sub do
                        if self.db.profile.dropsets[s.id] then
                                return true;
                        end
                end
        end
end

-- Converts self.db.profile.dropsets into self.vars.dropsets
-- Needed because self.db.profile.dropsets is index by name and self.vars.dropsets needs to be indexed by number with names as string.
function GarbageFu:MakeSetsTables()
        self.vars.dropsets = self:Table(self.vars.dropsets,1);
        for n,_ in self.db.profile.dropsets do
                table.insert(self.vars.dropsets,n);
        end
        self.vars.keepsets = self:Table(self.vars.keepsets,1);
        for n,_ in self.db.profile.keepsets do
                table.insert(self.vars.keepsets,n);
        end
end

function GarbageFu:ToggleDropSets(set,subset)
        self:DebugPrint("ToggleDropSets(set) set="..tostring(set).." subset="..tostring(subset));
        local setid = self.vars.sets[set].id;
        if subset then                                                                                                                                  -- Clicked a child
                local subsetid = self.vars.sets[set].sub[subset].id;
                if self.db.profile.dropsets[setid] then                         -- Check if parent is set
                        self.db.profile.dropsets[setid] = nil;                                  -- Clear parent
                        for _,s in self.vars.sets[set].sub do
                                self.db.profile.dropsets[s.id] = true;                          -- Set all children
                        end
                end
                if self.db.profile.dropsets[subsetid] then              -- Reverse child
                        self.db.profile.dropsets[subsetid] = nil;
                else    
                        self.db.profile.dropsets[subsetid] = true;
                end
                local allset = true;
                for _,s in self.vars.sets[set].sub do
                        if not self.db.profile.dropsets[s.id] then      -- Check if all children are set
                                allset = false;
                                break;
                        end
                end
                if allset then                                                                                                                          -- All children are set
                        self.db.profile.dropsets[setid] = true;                                 -- set parent
                        for i,s in self.vars.sets[set].sub do
                                self.db.profile.dropsets[s.id] = nil;                           -- And clear all children
                        end
                end
        else                                                                                                                                                                            -- Clicked a parent or a single
                if self.db.profile.dropsets[setid] then                         -- Reverse it
                        self.db.profile.dropsets[setid] = nil;
                else
                        self.db.profile.dropsets[setid] = true;
                        if self.vars.sets[set].sub then                                                 -- If it is a parent
                                for _,s in self.vars.sets[set].sub do                                                   -- Clear all children for both drop and keep
                                        self.db.profile.dropsets[s.id] = nil;
                                        self.db.profile.keepsets[s.id] = nil;
                                end
                        end
                end
        end
        self:MakeSetsTables();
        self:UpdateDisplay();
end

function GarbageFu:IsKeepSet(set,subset)
        local setid = self.vars.sets[set].id;
        if subset then
                local subsetid = self.vars.sets[set].sub[subset].id;
                return self.db.profile.keepsets[setid] or self.db.profile.keepsets[subsetid];
        end
        return self.db.profile.keepsets[setid];
end

function GarbageFu:IsChildKeepSet(set)
        if self.vars.sets[set].sub then
                for _,s in self.vars.sets[set].sub do
                        if self.db.profile.keepsets[s.id] then
                                return true;
                        end
                end
        end
end

function GarbageFu:ToggleKeepSets(set,subset)
        self:DebugPrint("ToggleKeepSets(set) set="..tostring(set).." subset="..tostring(subset));
        local setid = self.vars.sets[set].id;
        if subset then                                                                                                                                  -- Clicked a child
                local subsetid = self.vars.sets[set].sub[subset].id;
                if self.db.profile.keepsets[setid] then                         -- Check if parent is set
                        self.db.profile.keepsets[setid] = nil;                                  -- Clear parent
                        for _,s in self.vars.sets[set].sub do
                                self.db.profile.keepsets[s.id] = true;                          -- Set all children
                        end
                end
                if self.db.profile.keepsets[subsetid] then              -- Reverse child
                        self.db.profile.keepsets[subsetid] = nil;
                else    
                        self.db.profile.keepsets[subsetid] = true;
                end
                local allset = true;
                for _,s in self.vars.sets[set].sub do
                        if not self.db.profile.keepsets[s.id] then      -- Check if all children are set
                                allset = false;
                                break;
                        end
                end
                if allset then                                                                                                                          -- All children are set
                        self.db.profile.keepsets[setid] = true;                                 -- set parent
                        for i,s in self.vars.sets[set].sub do
                                self.db.profile.keepsets[s.id] = nil;                           -- And clear all children
                        end
                end
        else                                                                                                                                                                            -- Clicked a parent or a single
                if self.db.profile.keepsets[setid] then                         -- Reverse it
                        self.db.profile.keepsets[setid] = nil;
                else
                        self.db.profile.keepsets[setid] = true;
                        if self.vars.sets[set].sub then                                                 -- If it is a parent
                                for _,s in self.vars.sets[set].sub do                                   -- Clear all children for both drop and keep
                                        self.db.profile.keepsets[s.id] = nil;
                                        self.db.profile.dropsets[s.id] = nil;
                                end
                        end
                end
        end
        self:MakeSetsTables();
        self:UpdateDisplay();
end

------------------------------------------------------------------------------------------------------
-- Special bag functions
------------------------------------------------------------------------------------------------------

function GarbageFu:GetBagId(bag)
        local slotId = ContainerIDToInventoryID(bag);
        local bagLink = GetInventoryItemLink("player",slotId);
        local bagId = self:GetItemId(bagLink);
        return bagId;
end

function GarbageFu:IsBagAmmoBag(bagId)
        return self:IsItemInSet(bagId, {"bagsammo","bagsquiver"});
end

function GarbageFu:IsBagHerbBag(bagId)
        return self:IsItemInSet(bagId, "bagsherb");
end

function GarbageFu:IsBagSoulBag(bagId)
        return self:IsItemInSet(bagId, "bagssoul");
end

function GarbageFu:IsBagEnchBag(bagId)
        return self:IsItemInSet(bagId, "bagsench");
end

function GarbageFu:IgnoreBag(bag)
        local bagId = self:GetBagId(bag);
        if ( self:IsIgnoringAmmoBag() and self:IsBagAmmoBag(bagId) ) then return true end
        if ( self:IsIgnoringHerbBag() and self:IsBagHerbBag(bagId) ) then return true end
        if ( self:IsIgnoringSoulBag() and self:IsBagSoulBag(bagId) ) then return true end
        if ( self:IsIgnoringEnchBag() and self:IsBagEnchBag(bagId) ) then return true end
end

------------------------------------------------------------------------------------------------------
-- Sell Items functions
------------------------------------------------------------------------------------------------------

function GarbageFu:IsItemSellable(item)
        if ( item.qual == 0 or ( not self:IsSellOnlyGrey() and self:IsItemDroppable(item))) then
                return true;
        end
        return false;
end

function GarbageFu:IsThereItemsToSell()
        for _,item in pairs(self.vars.items) do
                if self:IsItemSellable(item) then
                        return true;
                end
        end
        return false;
end

function GarbageFu:GetSellItems()
        local sellItems = self:Table();
        local sellValue = 0;
        for _,item in pairs(self.vars.items) do
                if self:IsItemSellable(item) then
                        table.insert(sellItems,item);
                        sellValue = sellValue + item.totvalue;
                end
        end
        return sellItems, sellValue;
end

function GarbageFu:GetSellValue()
        local sellValue = 0;
        for _,item in pairs(self.vars.items) do
                if self:IsItemSellable(item) then
                        sellValue = sellValue + item.totvalue;
                end
        end
        return sellValue;
end

function GarbageFu:SellAllItems()
        if ( not self:IsEventScheduled("SellItem") ) then
                if self.vars.sellitems then compost:Reclaim(self.vars.sellitems) end
                self.vars.sellitems, self.vars.sellvalue = self:GetSellItems();
                if self.vars.sellitems[1] then
                        self:ScheduleRepeatingEvent("SellItem", self.SellItemEvent, 0.5, self);
                end
        end
end

function GarbageFu:SellItemEvent()
        local item = self.vars.sellitems[1];
        if ( item and MerchantFrame:IsVisible() ) then  
                if self:IsSafeToDelete() then
                        self:SetSafeToDelete(false);
                        self:MyPrint(L["Selling x%s %s worth %s"], item.stack, item.name, self:GetMoneyString(item.totvalue) );
                        UseContainerItem(item.bag, item.slot)
                        table.remove(self.vars.sellitems,1);
                end
        else -- No more items or Merchant frame closed
                if ( not item ) then
                        self:MyPrint(L["All items sold."]);
                        MerchantFrame.sellAllButton:Disable();
                end
                self:CancelScheduledEvent("SellItem");
                return
        end
end

function GarbageFu:OnMerchantOpen()
        if self:IsThereItemsToSell() then
                MerchantFrame.sellAllButton:Enable();
        else
                MerchantFrame.sellAllButton:Disable();
        end
end

function GarbageFu:OnMerchantClose()
        if ( self:IsEventScheduled("SellItem") ) then
                self:CancelScheduledEvent("SellItem");
        end
end

local buttonSize = 32
function GarbageFu:MakeSellAllButton()
        if not MerchantFrame.sellAllButton then
                local sellAllButton = CreateFrame("Button", "GarbageFu_SellItemButton", MerchantFrame)
                MerchantFrame.sellAllButton = sellAllButton
                sellAllButton:SetWidth(buttonSize)
                sellAllButton:SetHeight(buttonSize)
                local texture = sellAllButton:CreateTexture()
                texture:SetWidth(buttonSize * 1.6458333)
                texture:SetHeight(buttonSize * 1.6458333)
                texture:SetPoint("CENTER", sellAllButton, "CENTER")
                texture:SetTexture("Interface\\Buttons\\UI-Quickslot2")
                sellAllButton:SetNormalTexture(texture)
                local texture = sellAllButton:CreateTexture(nil, "BACKGROUND")
                texture:SetTexture("Interface\\AddOns\\FuBar_GarbageFu\\icon.tga")
                texture:SetAllPoints(sellAllButton)
                sellAllButton:SetPushedTexture("Interface\\Buttons\\UI-Quickslot-Depress")
                local texture = sellAllButton:CreateTexture()
                texture:SetTexture("Interface\\Buttons\\ButtonHilight-Square")
                texture:SetAllPoints(sellAllButton)
                sellAllButton:SetHighlightTexture(texture)
                local texture = sellAllButton:CreateTexture()
                texture:SetTexture("Interface\\AddOns\\FuBar_GarbageFu\\icon.tga")
                texture:SetDesaturated(true)
                texture:SetAllPoints(sellAllButton)
                sellAllButton:SetDisabledTexture(texture)
                sellAllButton:SetPoint("TOPRIGHT", MerchantFrame, "TOPRIGHT", -44, -38)
                sellAllButton:SetScript("OnClick", function()
                        self:SellAllItems()
                end)
                sellAllButton:SetScript("OnEnter", function()
                        self:SellAllButtonOnEnter(this)
                end)
                sellAllButton:SetScript("OnLeave", function()
                        GameTooltip:Hide()
                end)
                local texture = sellAllButton:CreateTexture(nil, "ARTWORK")
                texture:SetTexture(0, 0, 0, 0.5)
                texture:SetAllPoints(sellAllButton)
                local button_Enable = sellAllButton.Enable
                function sellAllButton:Enable()
                        button_Enable(self)
                        texture:Hide()
                end
                local button_Disable = sellAllButton.Disable
                function sellAllButton:Disable()
                        button_Disable(self)
                        texture:Show()
                end
                MerchantFrame.sellAllButton:Disable()
        end     
end

function GarbageFu:SellAllButtonOnEnter(button)
        GameTooltip:SetOwner(button, "ANCHOR_RIGHT")
        if self:IsSellOnlyGrey() then
                GameTooltip:SetText(L["Sell all grey items"]);
                GameTooltip:AddLine(L["SellsAll TooltipText2"]);
        else
                GameTooltip:SetText(L["Sell all garbage items"]);
                GameTooltip:AddLine(L["SellsAll TooltipText1"]);
        end
        local sellValue = self:GetSellValue();
        GameTooltip:AddLine(L["Value:"].." "..self:GetMoneyString(sellValue));
        GameTooltip:Show()
end

------------------------------------------------------------------------------------------------------
-- Item Price functions
------------------------------------------------------------------------------------------------------

-- Get item value. Doh!
function GarbageFu:GetItemValue(item)
        if item.id and item.qual and item.code then
                item.value, item.pricetype = self:GetUserOverridePrice(item.id)
                if not item.value then item.value, item.pricetype = self:GetKCIVendorPrice(item.id) end
                if not item.value then item.value, item.pricetype = self:GetInformantVendorPrice(item) end
                if not item.value then item.value, item.pricetype = self:GetWoWEconVendorPrice(item) end
                if not item.value then item.value, item.pricetype = self:GetItemSyncVendorPrice(item.link) end
                if not item.value then item.value, item.pricetype = self:GetPriceMasterVendorPrice(item.id) end
                if not item.value then item.value, item.pricetype = self:GetLudwigSellValueVendorPrice(item.id) end
                if not item.value then item.value, item.pricetype = self:GetLocalVendorPrice(item.id) end
        end
        
        if item.value then
                item.totvalue = item.value * item.stack;
        else
                item.value = 0; -- No item value know
                item.totvalue = 0; -- No item value know
        end             
end

-- Price Types. First char: U = User, V = Vendor, A = Auction
--              Second char: Addon the price was gotten from.
--                                              K = KC_Items, W = WoWEcon, I = Informant, P = PriceMaster, L = Ludwig, T = Local table

-- Get item price from the User Override table
function GarbageFu:GetUserOverridePrice(itemid)
        if self.db.account.overrideprices[itemid] then
                return self.db.account.overrideprices[itemid], "|cffff7700UO|r"
        end
end

-- KC_Items
function GarbageFu:GetKCIVendorPrice(itemid)
        local kv, vs
        if not KC_ItemsDB then return end
        kv = KC_ItemsDB[itemid]
        if kv then _, _, vs = string.find(kv, ",(%d+),%d+") end
        if (vs and vs ~= "0") then return tonumber(vs), "|cff0077ffVK|r" end
end

-- Auctioneer/Informant
function GarbageFu:GetInformantVendorPrice(item)
        if not Informant then return end
        local itemData = Informant.GetItem(item.id)
        if (itemData and itemData['buy'] and itemData['buy'] ~= 0) then return itemData['sell'], "|cff0077ffVI" end
end

-- WoWEcon
function GarbageFu:GetWoWEconVendorPrice(item)
        if not WOWEcon_Enabled then return end
        local price =   WOWEcon_GetVendorPrice_ByLink(item.link);
        if price and price > 0 then return price, "|cff0077ffVW|r" end
end

-- PriceMaster doesn't have a price asking function, so we have to tap directly in to it's data. Ugly!
function GarbageFu:GetPriceMasterVendorPrice(itemid)
        if ( CompletePrices and CompletePrices[itemid] and CompletePrices[itemid].p ) then
                return CompletePrices[itemid].p, "|cff0077ffVP|r" 
        end
end

-- Ludwig_SellValue
function GarbageFu:GetLudwigSellValueVendorPrice(itemid)
        local items = tostring(itemid)
        if ( Ludwig_SellValues and Ludwig_SellValues[items] ) then
                return Ludwig_SellValues[items], "|cff0077ffVL|r"
        end
end

function GarbageFu:GetItemSyncVendorPrice(itemlink)
        if (not ISync) then return end;
                local price = tonumber(ISync:FetchDB(ISync:GetCoreID(itemlink), "price"));
        if price and price > 0 then return price, "|cff0077ffVS|r" end
end

-- Look it up in our own price table
function GarbageFu:GetLocalVendorPrice(itemid)
        if not self.vars.vendorprices then return end

        if self.vars.pricecache[itemid] then return self.vars.pricecache[itemid], "|cff0077ffVT|r" end

        local _, _, price = string.find(self.vars.vendorprices, " "..itemid..":(%d+)")
        if price then
                self.vars.pricecache[itemid] = tonumber(price)
                return self.vars.pricecache[itemid], "|cff0077ffVT|r"
        end
end

-- Check that we got a numeric value from the editbox.
function GarbageFu.ValidateValue(self, value)
        self:DebugPrint( "ValidateValue value="..tostring(value));
        local res = ""
        for s in string.gfind(value, "(%d*).-(%d*)") do
            res=res..s    
        end
        return res
end

-- Stores a new User Override price for the top item.
function GarbageFu.SetFirstItemValue(self, value)
        local item = self:GetFirstDroppableItem();
        if item then
                GarbageFu.SetItemValue(self, item.id, value);
        end
end

-- Stores a new User Override price for an item, or removing it.
function GarbageFu.SetItemValue(self, itemid, value)
        value = tonumber(value);
        if itemid then
                self:DebugPrint( "SetItemValue value="..tostring(value).."itemid="..tostring(itemid));
                if ( not value or value == 0 ) then
                        if self.db.account.overrideprices[itemid] then
                                self.db.account.overrideprices[itemid] = nil;
                                local name = GetItemInfo(itemid);
                                self:MyPrint(L["Removing custom price for %s."], name);
                        end
                else
                        self.db.account.overrideprices[itemid] = value;
                                local name = GetItemInfo(itemid);
                        self:MyPrint(L["Setting price for %s to %s."], name, self:GetMoneyString(value));
                end
                self:UpdateItemValue(itemid)
                self:SortItems();
                self:UpdateDisplay();
        end
end

-- Updates all occurances of itemid in the inventory with a new price.
function GarbageFu:UpdateItemValue(itemid)
        for _, item in self.vars.items do
                if ( item.id == itemid ) then
                        self:GetItemValue(item);
                end
        end
end

-- Load our own price table.
function GarbageFu:LoadGarbagePrices(db)
        if not IsAddOnLoaded("FuBar_GarbageFu_Prices") then
                local res,reason = LoadAddOn("FuBar_GarbageFu_Prices");
        end
        if GarbageFu_PriceDb then               
                self.vars.vendorprices = GarbageFu_PriceDb;
                return true;
        elseif GarbageFu_PriceTbl then
                self.vars.vendorprices = "";
                self.vars.pricecache = GarbageFu_PriceTbl;
        else
                self:MyPrint(L["Can't load FuBar_GarbageFu_Price. Check that it exists and are enabled"]);
                return;
        end
end

function GarbageFu:IsGarbagePricesAvailable()
        local name, title, notes, enabled, loadable, reason = GetAddOnInfo("FuBar_GarbageFu_Prices");
        return not reason;
end

------------------------------------------------------------------------------------------------------
-- Compost interface
------------------------------------------------------------------------------------------------------

-- Method that will get a table from compost. If a table is passed in it will be erased and return.
-- If a depth is specified then any subtables will be reclaimed by compost before erasing the table and returning it.
function GarbageFu:Table(t,depth)
        if t then
                if type(t) ~= "table" then 
                        error("GarbageFu:Table called with a none table as input");
                        return
                end
                if depth and depth > 0 then
                        for i in pairs(t) do
                                if type(t[i]) == "table" then 
                                        compost:Reclaim(t[i], depth - 1 );
                                end
                        end
                end
                return compost:Erase(t);
        else
                return compost:GetTable();
        end
end

------------------------------------------------------------------------------------------------------
-- Chat output functions
------------------------------------------------------------------------------------------------------

function GarbageFu:DebugPrint(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10)
        if self.CustomDebug then
                self:CustomDebug(1, 0.5, 0, nil, nil, nil, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10);
        end
end

function GarbageFu:MyPrint(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10)
        self:CustomPrint(self.db.profile.textcolor.r, self.db.profile.textcolor.g, self.db.profile.textcolor.b, nil, nil, nil, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10);
end