local SHL = {}
if GetLocale() == "zhCN" then
SHL["SocketHelperTitle"] = "|cff33ff99[便捷]|r智能插孔"
SHL["ALWAYS_EMPTY"] = "总是显示空的宝石孔位"
SHL["ALWAYS_EMPTY_DESC"] = "在智能插孔中总是显示空的宝石孔位。"
SHL["AUTO_ACCEPT"] = "自动接受"
SHL["AUTO_ACCEPT_DESC"] = "自动接受插上宝石，无需点击应用。"
SHL["BAG_FULL_BUTTON"] = "隐藏移除按键。你的背包已满。清理出一些背包空间然后重新打开镶嵌界面。"
SHL["BAG_FULL_CHISEL"] = "你的背包已满，不要使用打孔器！"
SHL["Bek"] = "贝克"
SHL["Cor"] = "克尔"
SHL["DELETE_SET_CONFIRM"] = "删除%s？"
SHL["DELETION_BLOCKED"] = "删除物品被阻拦。此物品包含一个统御碎片，或者是一个统御碎片。"
SHL["DISENCHANT_MSG"] = "此物品包含一个统御碎片。不要分解它！"
SHL["Dyz"] = "迪兹"
SHL["ENABLE"] = "启用"
SHL["ENABLE_DESC"] = "启用角色界面下方的快速宝石孔位栏。"
SHL["ERROR_LOADING"] = "加载碎片套装失败。确保你目前装备的物品有足够的统御插槽。"
SHL["ERROR_TEXT"] = "发生错误。此物品已经包含一个统御碎片。"
SHL["EXTRA_SLOT"] = "碎片套装加载成功，但是你仍然有一个空的统御插槽。镶嵌碎片然后更新套装。"
SHL["Jas"] = "亚斯"
SHL["Kyr"] = "基尔"
SHL["NO"] = "不"
SHL["NOT_ENOUGH_GEMS"] = "|cFFFC0316购买%s个%s用于孔位："
SHL["NOT_SAVED"] = "没有保存"
SHL["O_QUICK_SLOT"] = "智能插孔"
SHL["Oth"] = "欧兹"
SHL["REMOVE"] = "移除"
SHL["REPLACE"] = "将%s替换为%s，位于："
SHL["REPLACE_TIP"] = "|cFFFC0316Shift|r：全部替换"
SHL["Rev"] = "雷弗"
SHL["SAVE_SET"] = "保存套装"
SHL["SHARD_BUYBACK"] = "已自动购回%s，因为它包含一个统御碎片。不用谢。"
SHL["SHARD_NOT_FOUND"] = "错误。无法在你的背包或装备中找到%s碎片。此套装无法加载。"
SHL["SOCKET_WINDOW"] = "宝石镶嵌窗口"
SHL["STAY_OPEN"] = "保持开启"
SHL["STAY_OPEN_DESC"] = "在关闭角色界面后，保持快速宝石孔位栏打开。"
SHL["Tel"] = "泰尔"
SHL["UPDATE_SET"] = "更新套装"
SHL["WHY"] = "你为什么会想尝试那样做？"
SHL["Zed"] = "泽德"
elseif GetLocale() == "zhTW" then
SHL["SocketHelperTitle"] = "|cff33ff99[便捷]|r智能插孔"
SHL["ALWAYS_EMPTY"] = "永遠顯示空的"
SHL["ALWAYS_EMPTY_DESC"] = "永遠在快速插槽條中顯示空白的寶石槽"
SHL["AUTO_ACCEPT"] = "自動接受"
SHL["AUTO_ACCEPT_DESC"] = "自動接受寶石，無需您單擊套用。"
SHL["BAG_FULL_BUTTON"] = "移除按鍵已隱藏。你的背包已滿。清理出一些背包空間然後重新打開鑲嵌界面。"
SHL["BAG_FULL_CHISEL"] = "你的背包已滿，不要使用靈火鑿子！"
SHL["Bek"] = "貝克"
SHL["Cor"] = "寇爾"
SHL["DELETE_SET_CONFIRM"] = "刪除 %s？"
SHL["DELETION_BLOCKED"] = "刪除物品已被阻擋。此物品包含一個統御裂片，或者是一個統御裂片。"
SHL["DISENCHANT_MSG"] = "此物品包含一個統御裂片。不要分解它！"
SHL["Dyz"] = "迪茲"
SHL["ENABLE"] = "啟用"
SHL["ENABLE_DESC"] = "在角色框架下啟用快速寶石插槽條。"
SHL["ERROR_LOADING"] = "無法載入裂片套裝。確保您當前裝備的物品有足夠的統御插槽。"
SHL["ERROR_TEXT"] = "發生錯誤。此物品已經包含一個統御裂片。"
SHL["EXTRA_SLOT"] = "套裝成功載入，但你仍然有一個空的統御插槽。插入裂片並更新套裝。"
SHL["Jas"] = "賈斯"
SHL["Kyr"] = "琪爾"
SHL["NO"] = "不"
SHL["NOT_ENOUGH_GEMS"] = "|cFFFC0316購買 %sx%s 也可以替換："
SHL["NOT_SAVED"] = "尚未儲存"
SHL["O_QUICK_SLOT"] = "快速插槽"
SHL["Oth"] = "歐斯"
SHL["REMOVE"] = "移除"
SHL["REPLACE"] = "將 %s 替換為 %s："
SHL["REPLACE_TIP"] = "|cFFFC0316Shift|r：全部替換"
SHL["Rev"] = "雷夫"
SHL["SAVE_SET"] = "儲存套裝"
SHL["SHARD_BUYBACK"] = "已自動買回%s，因為它包含一個統御裂片。不客氣！不用謝。"
SHL["SHARD_NOT_FOUND"] = "錯誤。在您的背包或裝備中找不到 %s 裂片。無法載入此套裝。"
SHL["SOCKET_WINDOW"] = "插槽視窗"
SHL["STAY_OPEN"] = "持續開啟"
SHL["STAY_OPEN_DESC"] = "關閉角色視窗後，保持快速插槽欄處於展開狀態。"
SHL["Tel"] = "特爾"
SHL["UPDATE_SET"] = "更新套裝"
SHL["WHY"] = "你為什麼會想嘗試那樣做？"
SHL["Zed"] = "杰德"
else
SHL["SocketHelperTitle"] = "|cff33ff99[Domination]|rSocketHelper"
SHL["ERROR_TEXT"] = "Something went wrong. There is already a shard in this item."
SHL["DISENCHANT_MSG"] = "This item has a Domination Shard in it. Do not disenchant it!"
SHL["BAG_FULL_CHISEL"] = "Your bags are full DO NOT use the chisel!"
SHL["BAG_FULL_BUTTON"] = "Remove button hidden. Your bags are full. Clear up some bag space and re-open the socket interface."
SHL["DELETION_BLOCKED"] = "Item deletion blocked. That item either contains a Domination Shard or is a Domination Shard."
SHL["SHARD_BUYBACK"] = "Automatically bought %s back because it has a Domination Shard in it. You're welcome."
SHL["WHY"] = "Why would you even try that?" --When you try to replace a shard with another shard
SHL["NO"] = "No."
SHL["REMOVE"] = "Remove"
SHL["SAVE_SET"] = "Save Set"
SHL["UPDATE_SET"] = "Update Set"
SHL["SHARD_NOT_FOUND"] = "Error. A %s shard could not be found anywhere in your bags or equipment. This set can not be loaded."
SHL["ERROR_LOADING"] = "Failed to load the shard set. Make sure your currently equipped items have enough domination sockets."
SHL["EXTRA_SLOT"] = "Set successfully loaded, but you still have an empty domination socket. Fill the socket(s) and update the set."
SHL["DELETE_SET_CONFIRM"] = "Delete %s?"
SHL["CONFIRM_REPLACE"] = "Are you sure you want to replace %s with %s?"
SHL["UNIQUE_WARNING"] = "WARNING: The gem you are replacing is unique. Auto accept disabled."
SHL["NOT_SAVED"] = "Not Saved" --ldb text when your set is not saved
--SHL["DUPLICATE_FOUND"] = "You own multiple versions of this item.\nThe macro will probably fail.\nOpen the socket window to remove the gem manually."
--SHL["MAUAL_MODE"] = "This macro function does not work if you have multiple versions of the same item in your bags. Using manual mode."

SHL["REPLACE"] = "Replace %s with %s in:"
SHL["REPLACE_TIP"] = "|cFFFC0316Shift|r: Replace All"
SHL["NOT_ENOUGH_GEMS"] = "|cFFFC0316Purchase %sx%s to also include:" --example: Missing [item Link]x3

SHL["LCLICK_REMOVE"] = "   |cFFFC0316Left-Click|r: Remove"
SHL["RCLICK_TRANSFER"] = "\n   |cFFFC0316Right-Click|r: Remove & Resocket"

--Below goes on shard buttons.
--ItemID of base item provided if translation is needed
SHL["Bek"] = "Bek" --187057
SHL["Jas"] = "Jas" --187059
SHL["Rev"] = "Rev" --187061
SHL["Dyz"] = "Dyz" --187073
SHL["Oth"] = "Oth" --187076
SHL["Zed"] = "Zed" --187079
SHL["Cor"] = "Cor" --187063
SHL["Kyr"] = "Kyr" --187065
SHL["Tel"] = "Tel" --187071

--Options
SHL["O_QUICK_SLOT"] = "Quick Slots"
SHL["ENABLE"] = "Enable"
SHL["ENABLE_DESC"] = "Enable the quick gem slots bar under the character frame."
SHL["ALWAYS_EMPTY"] = "Always Show Empty"
SHL["ALWAYS_EMPTY_DESC"] = "Always show empty gem slots in the quick slot bar."
SHL["STAY_OPEN"] = "Stay Open"
SHL["STAY_OPEN_DESC"] = "Keep the quick slot bar expanded after you close the character window."

SHL["SOCKET_WINDOW"] = "Socket Window"
SHL["AUTO_ACCEPT"] = "Auto Accept"
SHL["AUTO_ACCEPT_DESC"] = "Auto accept gems without requiring you to click apply."
end

DSH_ADDON = DSH_ADDON or LibStub("AceAddon-3.0"):NewAddon("DSH_ADDON", "AceConsole-3.0")
local DSH = DSH_ADDON

DSH.TITLE = SHL["SocketHelperTitle"]
DSH.VERSION = "v2.2.10"
DSH.AUTHOR = "Metriss - Stormrage"

local GetItemGem = GetItemGem
local GetSocketItemInfo = GetSocketItemInfo
local GetInventoryItemLink = GetInventoryItemLink
local GetItemInfoInstant = GetItemInfoInstant or C_Item.GetItemInfoInstant
--local GetContainerItemLink = C_Container.GetContainerItemLink
--local PickupContainerItem = C_Container.PickupContainerItem
--local GetContainerNumSlots = C_Container.GetContainerNumSlots
--local GetContainerItemID = C_Container.GetContainerItemID

local GetContainerItemLink = GetContainerItemLink or C_Container.GetContainerItemLink
local PickupContainerItem = PickupContainerItem or C_Container.PickupContainerItem
local GetContainerNumSlots = GetContainerNumSlots or C_Container.GetContainerNumSlots
local GetContainerItemID = GetContainerItemID or C_Container.GetContainerItemID
local SocketContainerItem = C_Container.SocketContainerItem
local GetContainerItemInfo = C_Container.GetContainerItemInfo
local SocketInventoryItem = SocketInventoryItem
local GetExistingSocketLink = GetExistingSocketLink
local GetItemIcon = GetItemIcon or C_Item.GetItemIconByID
local GetItemCount = GetItemCount or C_Item.GetItemCount
local GetItemInfo = GetItemInfo or C_Item.GetItemInfo
local GetNumSockets = GetNumSockets
--local GetSocketInfo = GetSocketInfo
local GetSocketTypes = GetSocketTypes

local UNIQUE_EQUIP_PATTERN = _G.ITEM_LIMIT_CATEGORY_MULTIPLE:gsub("%(%%d%)", "%%(%%d%%)"):gsub("%-", "%%-"):gsub('%%s', '(.+)')

local NUM_BAG_SLOTS = NUM_BAG_SLOTS

local pairs, next, select, type, unpack, ipairs, table, tContains = pairs, next, select, type, unpack, ipairs, table, tContains
local format, tostring, tonumber = string.format, tostring, tonumber

local SLOT_GEM_WRAP = 6
local SOCKET_GEM_WRAP = 9
local BUTTON_SIZE = 35.3
local BUTTON_PAD = 2
local SLOT_BUTTON_SIZE = 20
local SLOT_BUTTON_PAD = 2
local SET_BUTTON_HEIGHT = 20
local MAX_SLOT = 15

DSH.debug = false

local EF = CreateFrame('Frame') -- event handler frame
EF:RegisterEvent('ADDON_LOADED')
EF:SetScript('OnEvent', function(self, event, ...) self[event](self, ...) end)
--DSH.EF = EF
local updateThrottle = CreateFrame("Frame")

local function DSHPrint(text)
	print("|cFFFC0316DSH: |r" .. text)
end

--Format: [gem ID] = "GetSocketTypes(n)"
local GEM_TYPES_SPECIAL = {
    [204012] = "Primordial",
    [204010] = "Primordial",
    [204027] = "Primordial",
    [204001] = "Primordial",
    [204005] = "Primordial",
    [204013] = "Primordial",
    [204002] = "Primordial",
    [204011] = "Primordial",
    [204009] = "Primordial",
    [204019] = "Primordial",
    [204018] = "Primordial",
    [204006] = "Primordial",
    [204021] = "Primordial",
    [204025] = "Primordial",
    [204022] = "Primordial",
    [204008] = "Primordial",
    [204029] = "Primordial",
    [204003] = "Primordial",
    [204004] = "Primordial",
    [204007] = "Primordial",
    [204014] = "Primordial",
    [204000] = "Primordial",
    [204015] = "Primordial",
    [204020] = "Primordial",
    [204030] = "Primordial",

    [198298] = "Tinker", --Plane Displacer
    [198299] = "Tinker",
    [198300] = "Tinker",
    [225242] = "Tinker", --Refurbished

    [198289] = "Tinker", --Alarm-O-Turret
    [198290] = "Tinker",
    [198291] = "Tinker",
    [225241] = "Tinker", --Refurbished

    [201407] = "Tinker", --Arclight Vital Correctors
    [201408] = "Tinker",
    [201409] = "Tinker",

    [199188] = "Tinker", --Polarity Amplifier
    [199189] = "Tinker",
    [199190] = "Tinker",

    [198301] = "Tinker", --Supercollide-O-Tron
    [198302] = "Tinker",
    [198303] = "Tinker",
    [225243] = "Tinker", --Refurbished

    [205014] = "Tinker", --Shadowflame Rockets
    [205015] = "Tinker",
    [205016] = "Tinker",
    [225244] = "Tinker", --Refurbished

    [198295] = "Tinker", --Breath of Neltharion
    [198296] = "Tinker",
    [198297] = "Tinker",

    [198304] = "Tinker", --Grounded Circuitry
    [198305] = "Tinker",
    [198306] = "Tinker",

    --TWW Tinkers--
    [221904] = "Tinker", --Earthen Delivery Drill
    [221905] = "Tinker",
    [221906] = "Tinker",

    [221908] = "Tinker", --Heartseeking Health Injector
    [221909] = "Tinker",
    [221910] = "Tinker",

    [228642] = "SingingThunder",
    [228648] = "SingingThunder", 
    [228638] = "SingingThunder",
    [228634] = "SingingThunder",

    [228644] = "SingingSea",
    [228647] = "SingingSea", 
    [228639] = "SingingSea",
    [228636] = "SingingSea",
    
    [228643] = "SingingWind",
    [228646] = "SingingWind", 
    [228640] = "SingingWind",
    [228635] = "SingingWind",

}

local SINGING_SOCKET = {
    ["SingingThunder"] = true,
    ["SingingWind"] = true,
    ["SingingSea"] = true,
}

--slotName = locale string for socket text
--Name = what GetSocketTypes(n) returns
--This is the order that the gem slots under the character frame show up in
local SLOT_INFO_ORDERED = {
    {Name = "Meta", Icon = 136257, slotName = EMPTY_SOCKET_META},
    {Name = "Prismatic", Icon = 458977, slotName = EMPTY_SOCKET_PRISMATIC},
    {Name = "Tinker", Icon = 2958630, slotName = EMPTY_SOCKET_TINKER},
    {Name = "Cogwheel", Icon = 407324, slotName = EMPTY_SOCKET_COGWHEEL},
    {Name = "Primordial", Icon = 4095404, slotName = EMPTY_SOCKET_PRIMORDIAL},
    {Name = "SingingThunder", Icon = 136259, slotName = EMPTY_SOCKET_SINGINGTHUNDER},
    {Name = "SingingSea", Icon = 136256, slotName = EMPTY_SOCKET_SINGINGSEA},
    {Name = "SingingWind", Icon = 136258, slotName = EMPTY_SOCKET_SINGINGWIND},
}

--creating an additional dictionary to quickly check if items have gem slots from tooltip scans.
local SLOT_INFO = {}
for i, slotInfo in pairs(SLOT_INFO_ORDERED) do
    SLOT_INFO[slotInfo.slotName] = slotInfo
end

local function dbpr(...)
	if not DSH.debug then return end
	print("|cFFF60404DSHDB:|r",...)
end

DSH.dbpr = dbpr

function EF:ADDON_LOADED(addon)
	if addon == "_ShiGuang" then
		--EF:UnregisterEvent("ADDON_LOADED")
		DSH:InitializeSettings(addon)
		EF:RegisterEvent("SOCKET_INFO_UPDATE")
		EF:RegisterEvent("ITEM_LOCKED")
		EF:RegisterEvent("ITEM_UNLOCKED")
		--EF:RegisterEvent("MERCHANT_UPDATE")
		--EF:RegisterEvent("DELETE_ITEM_CONFIRM")
		EF:RegisterEvent("PLAYER_ENTERING_WORLD")
		EF:RegisterEvent("UNIT_INVENTORY_CHANGED")
		EF:RegisterEvent("PLAYER_EQUIPMENT_CHANGED")
		--EF:RegisterEvent("PLAYER_REGEN_DISABLED")
		CharacterFrame:HookScript('OnShow', function() EF:CHARACTER_FRAME_SHOW() end)
		CharacterFrame:HookScript('OnHide', function() EF:CHARACTER_FRAME_HIDE_GBC() end)
		CharacterFrame:HookScript('OnEnter', function() EF:CHARACTER_FRAME_HIDE_GBC() end)

		--GameTooltip:HookScript("OnTooltipSetItem", function() DSH:ItemToolTip() end);

		--local name, _ = UnitName("player")
		--if name and name == "Zariuh" then
		--	DSH.debug = true
		--end

        --if name and name == "Metris" then
		--	DSH.debug = true
		--end

        --if DSH.VERSION == "v2.2.10" then
        --    DSH.debug = true
        --end

		DSH.gemButtons = {}
		DSH.slotButtons = {}
        DSH.uniqueInfo = {}
        DSH.UniqueEquipped = {}
    end
end

--used to get frame info which is the same in all localizations?
local slotNames = {"Head", "Neck", "Shoulder", "Shirt", "Chest",
				"Waist", "Legs", "Feet", "Wrist", "Hands", 
				"Finger0", "Finger1", "Trinket0", "Trinket1", "Back"}
				

function DSH:IsDominationShard(ID)
	local ID = tonumber(ID)
	if shardIDs[ID] then
		return true
	end
end

function DSH:GetBagFreeSpace()
	local totalFreeSlots = 0
	for b = 0, NUM_BAG_SLOTS do
		local freeSlots = GetContainerNumFreeSlots(b)
		totalFreeSlots = totalFreeSlots + freeSlots
	end
	return totalFreeSlots
end

function DSH:UseContainerItemByID(id, socketNum)
	for b = 0, NUM_BAG_SLOTS do
		for s = 1, GetContainerNumSlots(b) do
			local itemID = GetContainerItemID(b, s)
			if itemID == id then
				--dbpr("Picking up", b, s)
				PickupContainerItem(b, s);
                if socketNum then
                    ClickSocketButton(socketNum)
                    ClearCursor()
                end
				break
			end
		end
	end
end

local function updateGemButtonAnchors(isSlotButton)
	--todo - Make this only check the buttons that could be wrapped
	local rows = 1
	local col = 1
    local wrap = isSlotButton and SLOT_GEM_WRAP or SOCKET_GEM_WRAP

    --dbpr("Wrap", wrap)
	for i = 2, #DSH.gemButtons do
		if DSH.gemButtons[i]:IsShown() then
			if i <= wrap then col = i end
			if ((i-1) % wrap == 0) then
				rows = rows + 1
				DSH.gemButtons[i]:SetPoint("TOPLEFT", DSH.gemButtons[i-(wrap)], "BOTTOMLEFT", 0, -1*BUTTON_PAD)
			else
				DSH.gemButtons[i]:ClearAllPoints()
				DSH.gemButtons[i]:SetPoint("LEFT", DSH.gemButtons[i-1], "RIGHT", BUTTON_PAD, 0)
			end
		end
	end
	-- dbpr("COL: ", col)
	return col, rows
end

local function createGemButtonContainer()

    if DSH.GBC then return end
	-- local frame = CreateFrame("Frame", nil, DSH.DC or DSH.SBC,  "BackdropTemplate")
	local frame = CreateFrame("Frame", nil, CharacterFrame,  "BackdropTemplate")

	--frame:SetPoint("TOPLEFT", ItemSocketingFrame, "BOTTOMLEFT", 0, -2)
	
	-- frame:SetSize(ItemSocketingFrame and ItemSocketingFrame:GetWidth() or (9*(BUTTON_SIZE+BUTTON_PAD)), BUTTON_SIZE+(BUTTON_PAD*2))
	frame:SetBackdrop({bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", tile = true, tileSize = 16, edgeFile = [[Interface\ButtoPLTrader:NS\WHITE8X8]], edgeSize = 1 * EF:GetEdgeScale()})
	frame:SetBackdropColor(0, 0, 0, .75)
	frame:SetBackdropBorderColor(0, 0, 0, .9)

    hooksecurefunc(frame, "Show", function() EF:RegisterEvent("BAG_UPDATE_DELAYED") end)
    hooksecurefunc(frame, "Hide", function() EF:UnregisterEvent("BAG_UPDATE_DELAYED") end)

    --frame:SetScript("OnShow", function() dbpr("SHOW") end)
    --frame:SetScript("OnHide", function() dbpr("HIDE") end)
    DSH.GBC = frame
end

--local function isPrimordialGem(gemID)
--    if PRIMORDIAL_GEMS[gemID] then return true end
--end

--local function isTinkerGem(gemID)
--    if TINKERS[gemID] then return true end
--end

local function socketingItemIsMultiSocket()
    if ItemSocketingSocket2 and ItemSocketingSocket2:IsShown() then return true end
end

local function getCurrentSocketingTypes(isSlotButton)
    
    if isSlotButton and DSH.SBC.curSlotBtn then
        return {DSH.SBC.curSlotBtn.slotType} 
    end
    
    local socketTypes = {}

    if SINGING_SOCKET[GetSocketTypes(1)] then
        --if it's a singing socket don't show gems for sockets that are filled
        --(since you can remove them)
        --quick hack fix
        for i = 1, GetNumSockets() do
            if not GetExistingSocketLink(i) then
                table.insert(socketTypes, GetSocketTypes(i))
                dbpr("Inserting", GetSocketTypes(i))
            end
        end
    else
        for i = 1, GetNumSockets() do
            table.insert(socketTypes, GetSocketTypes(i))
        end
    end

    --local socketType = GetSocketTypes(1)
    if socketTypes then return socketTypes end

    --return prismatic as a fallback
    --(why did I do this?)
    return {SLOT_INFO[EMPTY_SOCKET_PRISMATIC].Name}

end

local function currentItemAlreadyGemmed(isSlotButton)

    if not isSlotButton and (GetExistingSocketLink(1) or GetExistingSocketLink(2) or GetExistingSocketLink(3)) then
        return true
    end

    if isSlotButton and DSH.SBC.curSlotBtn and DSH.SBC.curSlotBtn.gemID then
        return true
    end
    
end

local function shouldShowGem(isSlotButton, gemID, gemLink, socketTypes)

    if GEM_TYPES_SPECIAL[gemID] then
        --if GEM_TYPES_SPECIAL[gemID] ~= socketType then return end
        --dbpr(socketTypes, GEM_TYPES_SPECIAL[gemID])
        if not tContains(socketTypes, GEM_TYPES_SPECIAL[gemID]) then return end
    else
        if socketTypes[1] ~= "Prismatic" then return end
    end

    --if socketType ~= "Prismatic" and GEM_TYPES_SPECIAL[gemID] then return end

    --show the gem if its attached to the socket frame and the gem isn't the same as what's already socketed
    --always show the gem if there are multiple sockets (fix later to check all sockets?)
    if (not isSlotButton and ((GetExistingSocketLink(1) ~= gemLink) or socketingItemIsMultiSocket())) then
        return true
    end

    --show if its a slot button and the gem isn't the same as the gem that's already there
    if isSlotButton and DSH.SBC.curSlotBtn.gemID ~= gemID then
        return true
    end

    return

end


local function getGemToggleState(isSlotButton, gemLink, gemID)
    local isUnique, uniqueType = DSH:IsGemUnique(gemLink, gemID)

    if isSlotButton and isUnique and DSH.UniqueEquipped[uniqueType] and (DSH.SBC.curSlotBtn and (uniqueType ~= DSH.SBC.curSlotBtn.uniqueType)) then return end
    return true
end

local function allGemSlotsFilled()
    if ItemSocketingFrame and ItemSocketingFrame:IsVisible() then
        for i = 1, 3 do
            if _G["ItemSocketingSocket"..i] and _G["ItemSocketingSocket"..i]:IsVisible() then
                dbpr(i, "is visible")
                if not GetExistingSocketLink(i) then return end
            end
        end
        return true
        
    end
end


function DSH:UpdateGemButtons(isSlotButton)

    if DSH:IsRemix() then return end

	if not (ItemSocketingFrame and ItemSocketingFrame:IsShown())
	and not (isSlotButton and CharacterFrame and CharacterFrame:IsShown()) then
		return
	end
	
	local buttonCount = 1
    local socketTypes = getCurrentSocketingTypes(isSlotButton)
    --dbpr(socketType)
	--DSH.GBC.isDomination = isDomination
	DSH.GBC.isSlotContainer = isSlotButton

    if socketTypes[1] == "Tinker" and currentItemAlreadyGemmed(isSlotButton) then
        --Reail: Tinker ALready Gemmed, add the remove item
        dbpr("Tinker Already Gemmed")
        DSH:UpdateGemButton("remove", nil, 202087)
        buttonCount = 2

        local itemName = GetSocketItemInfo()

        if isSlotButton then
            DSH.gemButtons["remove"]:SetAttribute("macrotext", "/use item:202087\n/script HideUIPanel(ItemSocketingFrame)\n/click StaticPopup1Button1\n/use "..DSH.SBC.curSlotBtn.slot)
        else
            if DSH.lockedSocketSlot and itemName then
                --item in bag
                DSH.gemButtons["remove"]:SetAttribute("macrotext", "/use item:202087\n/script HideUIPanel(ItemSocketingFrame)\n/click StaticPopup1Button1\n/use "..itemName.."\n/script C_Container.SocketContainerItem("..DSH.lockedSocketBag..", "..DSH.lockedSocketSlot..")")
            else
                --item equipped
                DSH.gemButtons["remove"]:SetAttribute("macrotext", "/use item:202087\n/click StaticPopup1Button1\n/use "..DSH.lockedSocketBag.."\n/script SocketInventoryItem("..DSH.lockedSocketBag..")")
            end
        end

        DSH:ToggleButton(DSH.gemButtons["remove"], GetItemCount(202087) > 0 and true or false)
    elseif SINGING_SOCKET[socketTypes[1]] and isSlotButton and currentItemAlreadyGemmed(isSlotButton) then

        dbpr("Singing Slot Socketed")
        DSH:UpdateGemButton("remove", nil, 1059115, nil, true)
        buttonCount = 2

        --DSH.SBC.curSlotBtn.socketNum
        DSH.gemButtons["remove"]:SetAttribute("macrotext", "/script SocketInventoryItem("..DSH.SBC.curSlotBtn.slot..")\n"
                                                        .."/use "..DSH.SBC.curSlotBtn.slot.."\n"
                                                        .."/click ItemSocketingSocket"..DSH.SBC.curSlotBtn.socketNum.."\n"
                                                        .."/script HideUIPanel(ItemSocketingFrame)")

    else
        --We actually need to show gems to put in the item

        for gemID, gemInfo in pairs(DSH.gemsInBags) do
            if shouldShowGem(isSlotButton, gemID, gemInfo.itemLink, socketTypes) then

                DSH:UpdateGemButton(buttonCount, gemInfo.itemLink, gemInfo.itemID, gemInfo.quality)

                --need to wait for gem to load before checking if its unique
                local item = Item:CreateFromItemID(gemInfo.itemID)
                local button = DSH.gemButtons[buttonCount]
                item:ContinueOnItemLoad(function() DSH:ToggleButton(button, getGemToggleState(isSlotButton, item:GetItemLink(), gemID)) end)

                buttonCount = buttonCount + 1
            end
        end
    end
		
	if isSlotButton then
		if buttonCount == 1 then
            --hide the gem button container if no gems found
			DSH.GBC:Hide()
			DSH.SBC.curSlotBtn = nil
		else
			DSH.GBC:Show()
			DSH.GBC:SetParent(DSH.SBC.curSlotBtn)
			DSH.GBC:ClearAllPoints()
			local col, rows = updateGemButtonAnchors(isSlotButton)
			DSH.GBC:SetSize((col) * (BUTTON_SIZE+BUTTON_PAD) + BUTTON_PAD, rows * (BUTTON_SIZE+BUTTON_PAD) + BUTTON_PAD)
			DSH.GBC:SetPoint("TOP", DSH.SBC.curSlotBtn, "BOTTOM", 0, -2)
		end
	else
		DSH.GBC:Show()
		DSH.GBC:SetParent(ItemSocketingFrame)
		DSH.GBC:ClearAllPoints()
		local col, rows = updateGemButtonAnchors(isSlotButton)
		DSH.GBC:SetSize(ItemSocketingFrame and ItemSocketingFrame:GetWidth() or (9*(BUTTON_SIZE+BUTTON_PAD)), rows * (BUTTON_SIZE+BUTTON_PAD) + BUTTON_PAD)
		DSH.GBC:SetPoint("TOPLEFT", ItemSocketingFrame, "BOTTOMLEFT", 0, -2)
	end

end

local function getMatchingSlotButtons(gemLink)
	local slotMatches = {}
	for i, slotButton in pairs(DSH.slotButtons) do
		if slotButton:IsVisible() then
			-- dbpr(gemLink, slotButton.gemLink)
			if gemLink == slotButton.gemLink then
				table.insert(slotMatches, slotButton)
			end
			-- dbpr(slotButton.gemLink)
		end
	end
	return slotMatches
end

local function getSlotLocalizedName(slot)
    --local slotName = slotLocalizedNames[slotBtn.slot]
    local slotName = strupper(slotNames[slot]) .. "SLOT"
    if slot >= 11 and slot <= 14 then slotName = slotName .. "_UNIQUE" end
    slotName = _G[slotName]
    return slotName

end

function EF:MODIFIER_STATE_CHANGED(key, down)
	if not DSH.GBC.curGemBtn or not DSH.GBC.isSlotContainer or DSH.SBC.curSlotBtn.unique or (DSH.SBC.curSlotBtn and not DSH.SBC.curSlotBtn.gemID) then
		DSH:ToggleInfoTooltip(false, "")
		return
	end
	if key == "LSHIFT" then
		if down == 1 then
			-- dbpr(DSH.curSlotBtn.gemLink)

			local slotMatches = getMatchingSlotButtons(DSH.SBC.curSlotBtn.gemLink)
			--get item link again, the old item link shows up as [] sometimes
			local newItemLink = select(2, GetItemInfo(DSH.GBC.curGemBtn.itemLink)) or "[]"
			local tipText = format(SHL["REPLACE"], DSH.SBC.curSlotBtn.gemLink, newItemLink) --get link again, old item link doesn't load sometimes
			local newGemCount = GetItemCount(DSH.GBC.curGemBtn.ID)
			
			DSH:UpdateCurSlotGlow(slotMatches)
			
			for i, slotBtn in pairs(slotMatches) do
				tipText = tipText .. "\n"..i..": "..slotBtn.itemLink.. " ("..getSlotLocalizedName(slotBtn.slot)..")"

				if i == newGemCount and i ~= #slotMatches then
					tipText = tipText .. "\n\n"..format(SHL["NOT_ENOUGH_GEMS"], newItemLink, #slotMatches - newGemCount)
					
				end
			end
			
			DSH:ToggleInfoTooltip(true, tipText, DSH.GBC)
		else
			DSH:UpdateCurSlotGlow({DSH.SBC.curSlotBtn})
			DSH:ToggleInfoTooltip(true, format(SHL["REPLACE_TIP"], DSH.SBC.curSlotBtn.gemLink or ""), DSH.GBC)
		end
	end
end

local function replaceAllSlotGemMatches(gemID, isRetry)
	local slotMatches = getMatchingSlotButtons(DSH.SBC.curSlotBtn.gemLink)
	
	for i, slotBtn in pairs(slotMatches) do
		--dbpr(i)
        --dbpr("NExt", slotMatches[i+1].slot)
        --dbpr("THIS SLOT:", slotBtn.slot, "NEXT SLOT:", slotMatches[i+1].slot)

		SocketInventoryItem(slotBtn.slot)
		if not GetExistingSocketLink(1) and not isRetry then 
			dbpr("ERROR: NOT EXISTING SOCKET, RETRYING")
			C_Timer.After(0.1, function()
				CloseSocketInfo()
				replaceAllSlotGemMatches(gemID, true)
			end)
			break
		end
		
		local oldID = select(1, GetItemInfoInstant(GetExistingSocketLink(slotBtn.socketNum)))
		dbpr("OLD:", oldID, "REPLACE WITH:", gemID)
		
		if oldID ~= gemID then
            --dbpr("PUTTING INTO SOCKET NUM", slotBtn.socketNum)
			DSH:UseContainerItemByID(gemID, slotBtn.socketNum)
			
            dbpr("NEW SOCKET LINK IN SOCKET", slotBtn.socketNum, GetNewSocketLink(slotBtn.socketNum))
			local newID = GetNewSocketLink(slotBtn.socketNum) and select(1, GetItemInfoInstant(GetNewSocketLink(slotBtn.socketNum))) or nil
			dbpr("NEW:", newID)

			if not isRetry and (not newID or (newID and (newID ~= gemID))) then
				dbpr("ERROR: NEW GEM NOT IN, RETRYING")
				C_Timer.After(0.1, function()
					CloseSocketInfo()
					replaceAllSlotGemMatches(gemID, true)
				end)
				break
			end
            
            AcceptSockets()

            if slotMatches[i+1] and slotMatches[i+1].slot == slotBtn.slot then
                --API doesn't let you put multiple gems in the same item at once??
                CloseSocketInfo()
            end

		end
	end
	
	CloseSocketInfo()
end

local function confirmReplace(ID, newGem, oldGem, quality, slot, socketNum)

    StaticPopupDialogs["REPLACE_UNIQUE_GEM"] = {
        text = string.format(REPLACE_ENCHANT, oldGem, newGem),
        button1 = ACCEPT,--"Yes",
        button2 = CANCEL,--"No",
        OnAccept = function()
            DSH:GemButtonPress(ID, newGem, quality, slot, socketNum, true)
        end,
        timeout = 0,
        whileDead = true,
        hideOnEscape = true,
        preferredIndex = 3, 
    }

    StaticPopup_Show("REPLACE_UNIQUE_GEM");

end

function DSH:GemButtonPress(ID, itemName, quality, slot, socketNum, force, frame)

	if DSH.GBC.isSlotContainer or force then

        --if the gem you're replacing is unique, make sure
        if not force and DSH.SBC.curSlotBtn.unique then

            local oldGem = DSH.SBC.curSlotBtn.gemLink
            confirmReplace(ID, select(2, GetItemInfo(ID)), oldGem, quality, DSH.SBC.curSlotBtn.slot, DSH.SBC.curSlotBtn.socketNum)

            return
        else

            if IsShiftKeyDown() then
                replaceAllSlotGemMatches(ID)
                return
            end
            SocketInventoryItem(slot or DSH.SBC.curSlotBtn.slot)
        end
	end

    --dbpr("WOULD EXECUTE PUT", itemName, "into slot", slot, "socket num", socketNum)

    --quick fix for 11.0.7 singing gems
    if not DSH.GBC.isSlotContainer and SINGING_SOCKET[GEM_TYPES_SPECIAL[ID]] then
        for i = 1, GetNumSockets() do
            if not GetExistingSocketLink(i) then
                if GetSocketTypes(i) == GEM_TYPES_SPECIAL[ID] then
                    DSH:UseContainerItemByID(ID, i)
                    AcceptSockets()
                    --CloseSocketInfo()
                end
            end
        end
        return
    end

	local newGemInfo = GetNewSocketInfo(1)

	--GetNewSocketInfo for some reason returns a string of variables?? Maybe I'm dumb
	--I don't remember why I did this
	if not newGemInfo or (newGemInfo and not string.match(newGemInfo, itemName)) then
		
		if DSH.GBC.isSlotContainer then

            DSH:UseContainerItemByID(ID, socketNum or DSH.SBC.curSlotBtn.socketNum)
			AcceptSockets()
			CloseSocketInfo()
		else
            --If there are two gem sockets pickup the gem instead of putting it in
            if socketingItemIsMultiSocket() then
                --dbpr("TWO GEMS")
                DSH:UseContainerItemByID(ID, nil)
            else
                DSH:UseContainerItemByID(ID, 1)
                if DSH.db.profile.socketwindow.autoaccept then
                    local existingGemLink = GetExistingSocketLink(1)
                    local isUnique, uniqueType = DSH:IsGemUnique(existingGemLink, select(1, existingGemLink))

                    if isUnique then
                        DSHPrint(SHL["UNIQUE_WARNING"])
                    else
                        AcceptSockets()
                    end
                end
            end
		end
		
	end
end


--function DSH:CheckOverlayRemoveButton(frame, isEnter)

--    --bad naming, override = reused quality texture for gem texture, which means a gem can be removed.
--    if not frame.override then return end

--    DSH:UpdateGemButton("remove", nil, 1379201, nil, true)
--    DSH.gemButtons["remove"]:ClearAllPoints()
--    DSH.gemButtons["remove"]:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -2, 2)
--    DSH.gemButtons["remove"]:SetSize(15, 15)

--    dbpr("MANUAL CHECK", frame.bag, frame.slot)
--    DSH.gemButtons["remove"]:SetAttribute("macrotext", "/cast "..castName.."\n/script C_Container.SocketContainerItem("..frame.bag..","..frame.slot..")\n/click ItemSocketingSocket1\n/script HideUIPanel(ItemSocketingFrame)")

--end

function DSH:CreateGemButtonFont()

    if DSH.GemButtonFont then return end

    local font = CreateFont("DSHGemButtonFont")
	font:CopyFontObject("GameFontNormal");
	font:SetFont("Fonts\\FRIZQT__.TTF", 13, "OUTLINE")
	font:SetTextColor(1, 1, 1, 1.0);

    DSH.GemButtonFont = font

    --return font
end

local function createGemButton(i)
	--shard button container
	if not DSH.GBC then return end

	if i == "remove" then
		DSH.gemButtons[i] = CreateFrame ("button", nil, DSH.GBC, "SecureActionButtonTemplate")
        local frame = DSH.gemButtons[i]
        frame:RegisterForClicks("AnyUp", "AnyDown")
		frame:SetAttribute("type", "macro")
        frame:SetFrameStrata("HIGH")
        frame:SetScript("OnEnter", function()
            DSH.GBC.curGemBtn = frame
            if DSH.GBC.isSlotContainer then
                local warningText = ""
                DSH:ToggleInfoTooltip(true, CALENDAR_VIEW_EVENT_REMOVE.. warningText, DSH.GBC)
            end    
        end)
        frame:SetScript("OnLeave", function()
            DSH.GBC.curGemBtn = nil
            DSH:ToggleInfoTooltip(false, "")
        end)
        --CALENDAR_VIEW_EVENT_REMOVE 
        --DSH.gemButtons[i]:SetAttribute("macrotext", "/laugh") -- text for macro on left click
    else
		DSH.gemButtons[i] = CreateFrame("button", nil, DSH.GBC)
	end
	
	local frame = DSH.gemButtons[i]
	
	if i == 1 or i == "remove" then
		frame:SetPoint("TOPLEFT", DSH.GBC, "TOPLEFT", BUTTON_PAD, -1*BUTTON_PAD)
	else
		frame:SetPoint("LEFT", DSH.gemButtons[i-1], "RIGHT", BUTTON_PAD, 0)
	end
	frame:SetWidth(BUTTON_SIZE)
	frame:SetHeight(BUTTON_SIZE)

	frame:SetNormalFontObject("GameFontNormal")
	
	if type(i) == "number" then
		frame:SetScript("OnEnter", function()
			DSH:ToggleItemTooltip(true, frame)
			DSH.GBC.curGemBtn = frame;
			EF:RegisterEvent("MODIFIER_STATE_CHANGED")
			EF:MODIFIER_STATE_CHANGED("LSHIFT", IsShiftKeyDown() and 1 or 0)
            --DSH:CheckOverlayRemoveButton(frame, true)
		end)
		frame:SetScript("OnLeave", function()
            DSH:UpdateCurSlotGlow({DSH.SBC and DSH.SBC.curSlotBtn});
            DSH:ToggleInfoTooltip(false, "");
            DSH:ToggleItemTooltip(false);
            EF:UnregisterEvent("MODIFIER_STATE_CHANGED");
            DSH.GBC.curGemBtn = nil;
            --DSH:CheckOverlayRemoveButton(frame)
        end)
		frame:SetScript("OnClick", function() DSH:GemButtonPress(frame.ID, frame.itemName, frame.quality, nil, nil, nil, frame) end)
	end
	
	--Todo Fix this font stuff, it's wrong

	--local font = CreateFont("DSHGemButtonFont")
	--font:CopyFontObject("GameFontNormal");
	--font:SetFont("Fonts\\FRIZQT__.TTF", 13, "OUTLINE")
	--font:SetTextColor(1, 1, 1, 1.0);

    DSH:CreateGemButtonFont()

	frame:SetNormalFontObject(DSH.GemButtonFont)
	
	
    frame.qualityTex = frame:CreateTexture(nil, "overlay")
    frame.qualityTex:SetTexture("Interface\\Professions\\ProfessionsQualityIcons")
    --frame.qualityTex:SetSize(16, 16)
    --frame.qualityTex:SetAllPoints()
    frame.qualityTex:SetPoint("TOPLEFT", -3, 3);
    frame.qualityTex:SetSize(30, 30)

	frame.tex = frame:CreateTexture()

end

local function updateButtonTextLocation(button, pos1, pos2)
	local fontStr = button:GetFontString()
	if fontStr then
		fontStr:ClearAllPoints()
		fontStr:SetPoint(pos1, button, pos2)
	end

end

local QUALITY_COORDS = {{x=74,y=40}, {x=38,y=70}, {x=39,y=101}, {x=74, y=70}, {x=75, y=101}}
local function updateGemButtonQualityTexture(gemButton, quality)

    if not quality or quality == ""  then
        gemButton.qualityTex:Hide()
        gemButton.quality = nil
        return
    end

    gemButton.qualityTex:Show()

    local left = QUALITY_COORDS[quality].x;
    local top = QUALITY_COORDS[quality].y;
    --gemButton.qualityTex:SetPoint("TOPLEFT", -3, 3);
    gemButton.quality = quality
    gemButton.qualityTex:SetTexture("Interface\\Professions\\ProfessionsQualityIcons")
    gemButton.qualityTex:SetTexCoord(left/128, (left+28)/128, top/128, (top+28)/128);
end



function DSH:UpdateGemButton(i, itemLink, itemID, quality, isSpell, _, bag, slot)
	
	if not DSH.gemButtons[i] then
		createGemButton(i)
	end

    --if it's a spell just pass the icon as itemID, messy but w/e
	local itemIcon = isSpell and itemID or GetItemIcon(itemID)

    --dbpr("ITEM ICON:", itemIcon)
	local gemButton = DSH.gemButtons[i]
	
	gemButton.tex:SetTexture(itemIcon)
	gemButton.tex:SetTexCoord(0.08, 0.92, 0.08, 0.92)
	gemButton.tex:SetAllPoints()

    updateGemButtonQualityTexture(gemButton, tonumber(quality), _)
	
	gemButton:SetNormalTexture(gemButton.tex)
	
	gemButton.itemLink = itemLink
	gemButton.ID = itemID
    gemButton.quality = quality
    --gemButton.override = qualityOverrideItemID
    gemButton.bag = bag
    gemButton.slot = slot
	--gemButton.itemName = select(1, GetItemInfo(itemID)) --this doesn't return the name somtimes?
	if itemLink then
		gemButton.itemName = string.match(itemLink, "%[(.*)%]")
	else
		gemButton.itemName = ""
	end

    if isSpell then
        gemButton:SetText("")
    else
        gemButton:SetText(GetItemCount(itemID))
    end
    updateButtonTextLocation(gemButton, "BOTTOMRIGHT", "BOTTOMRIGHT")
	--end
	
	gemButton:Show()
end

function DSH:IsRemix()
    if DSH.remixChecked then return DSH.isRemix end

    DSH.remixChecked = true

    AuraUtil.ForEachAura("player", "HELPFUL", nil, function(name, icon, _, _, _, _, _, _, _, spellId)
        if spellId == 424143 then
            DSH.isRemix = true
            DSH.remixGemType = {}
            --DSH.remixGemSearch = {PRISMATIC_GEM}
            dbpr("MOP Remix: True")
            return
        end
    end)
    
    return DSH.isRemix

end


local function hideGemButtons()
	for k, gemButton in pairs(DSH.gemButtons) do
		gemButton:Hide()
		gemButton.itemLink = nil
		gemButton.ID = nil
	end
end

local function addGemToTable(name, itemLink, itemID, quality)
	if itemLink and itemID then
		DSH.gemsInBags[name] = {itemLink = itemLink, itemID = itemID, quality = quality}
	end
end

local function getGemQuality(gemLink)
    local quality = tonumber(string.match(gemLink, "-Tier(%d):"))
    return quality
end

function DSH:UpdateGemsInBags()

	if not DSH.checkForGems then return end

	DSH.checkForGems = false --only checks for new gems if your bags have changed
	DSH.gemsInBags = {}

	for b = 0, NUM_BAG_SLOTS do
		for s = 1, GetContainerNumSlots(b) do
			local itemLink = GetContainerItemLink(b, s)
			if itemLink then
				local itemID, type, subtype, _, icon, itemTypeID, itemSubTypeID = GetItemInfoInstant(itemLink)

                --if type == "Item Enhancement" then dbpr(itemLink, type, itemTypeID, subtype, itemSubTypeID) end
                if itemTypeID == 3 then --3 = gem
                    --dbpr(itemLink, itemTypeID, subtype, itemSubTypeID)
                    addGemToTable(itemID, itemLink, itemID, getGemQuality(itemLink))
				end
			end
		end
	end
end

--local function curSlotItemStillHasGem()
--	local itemLink = GetInventoryItemLink("player", DSH.SBC.curSlotBtn.slot)
--	local gemLink = DSH:GetGemID(itemLink)
--	return gemLink
--end

function DSH:InitGemButtons(isSlotButton)

	createGemButtonContainer()
	
	hideGemButtons()

    DSH:UpdateGemsInBags()

	if not DSH.gemsInBags then return end --and not isRemove then return end
	
	DSH:UpdateGemButtons(isSlotButton)
	--DSH:UpdateSetContainer()
end


function DSH:ToggleItemTooltip(show, frame)

    --if not frame then return end

	if show then
		if frame.itemLink then
			GameTooltip:SetOwner(frame, "ANCHOR_NONE")
			GameTooltip:SetPoint("BOTTOMLEFT", frame, "TOPRIGHT", 0, 0)
			GameTooltip:SetHyperlink(frame.gemLink and frame.gemLink or frame.itemLink)
			GameTooltip:Show()
		end
	else
		GameTooltip:Hide()
	end
end


--In case another tab gets added. Maybe another addon adds a character tab with proper name?
local function getLastCharTab()
	local frame
	local i = 3
	repeat
		frame = _G["CharacterFrameTab" .. i]
		i = i + 1
	until not _G["CharacterFrameTab" .. i]
	return frame
end

local function slotExtendClick(frame, clickType)
	if clickType == "LeftButton" then
		DSH.db.char.quickslots.extended = not DSH.db.char.quickslots.extended
		frame:SetText(DSH.db.char.quickslots.extended and "<" or ">")
		DSH:UpdateSlotButtons()
	else
		--InterfaceOptionsFrame_OpenToCategory(GetAddOnMetadata(addonName, "Title"))
		--InterfaceOptionsFrame_OpenToCategory(GetAddOnMetadata(addonName, "Title"))
        Settings.OpenToCategory(SHL["SocketHelperTitle"])
        Settings.OpenToCategory(SHL["SocketHelperTitle"])
	end
end

local function createSlotButtonContainer()
	local frame = CreateFrame("Button", nil, PaperDollFrame, "BackdropTemplate")
	frame:SetPoint("LEFT", getLastCharTab(), "RIGHT", 2, 0)
	-- frame.bg = CreateFrame("Frame", nil, CharacterMainHandSlot, "BackdropTemplate")
	DSH:FormatFrame(frame, true)
	frame:SetSize(SLOT_BUTTON_SIZE, 20)
	frame:SetText(DSH.db.char.quickslots.extended and "<" or ">")
	frame:SetScript("OnClick", slotExtendClick)
	frame:RegisterForClicks("LeftButtonDown", "RightButtonDown")
	frame:SetScript("OnEnter", function()
		-- if not DSH.db.char.quickslots.extended then slotExtendClick(frame, "LeftButton") end
		EF:CHARACTER_FRAME_HIDE_GBC()
	end)

	return frame
end

local function slotButtonEnter(frame)
	DSH:UpdateCurSlotGlow({frame}, true)
	DSH:ToggleItemTooltip(true, frame)
	if (ItemSocketingFrame and ItemSocketingFrame:IsShown()) then return end
	
	DSH.SBC.curSlotBtn = frame

	-- DSH.SBC.bg:SetPoint("CENTER", frame, "CENTER")
	DSH:InitGemButtons(true)
end

local function getExtraPad(isBagStart)
    if isBagStart then return SLOT_BUTTON_PAD + SLOT_BUTTON_SIZE end
    return 0
end

local function updateEmptySlotButton(slot, i, slotType, showLink, socketNum, slotTex, isBagStart, gemID, bagItemID, bag)
	DSH:CreateSlotButton(i)
	--local isPrimordial = ((slotType == SLOT_INFO[EMPTY_SOCKET_PRIMORDIAL].Name) and true)
    --todo -- change this so all this info is held in a single table
	--dbpr("s", s, "i", i)
    DSH.slotButtons[i].slot = slot
    DSH.slotButtons[i].bag = bag
	DSH.slotButtons[i].slotType = slotType
	DSH.slotButtons[i].gemLink = nil
	DSH.slotButtons[i].gemID = gemID
    DSH.slotButtons[i].unique = nil
    DSH.slotButtons[i].uniqueType = nil
	DSH.slotButtons[i].itemLink = showLink
    DSH.slotButtons[i].socketNum = socketNum

    DSH.slotButtons[i].bagItemID = bagItemID
    --dbpr("bagItemID", bagItemID)

    if i == 1 then
        DSH.slotButtons[i]:SetPoint("LEFT", DSH.SBC, "RIGHT", SLOT_BUTTON_PAD + getExtraPad(isBagStart), 0)
    else
        DSH.slotButtons[i]:SetPoint("LEFT", DSH.slotButtons[i-1], "RIGHT", SLOT_BUTTON_PAD + getExtraPad(isBagStart), 0)
    end
    --458977
    --136264
    --dbpr("slotType", slotType)
	DSH.slotButtons[i].tex:SetTexture(slotTex or 458977)
	--DSH.slotButtons[i].tex:SetAtlas((SLOT_INFO[slotType] and SLOT_INFO[slotType].Atlas) and SLOT_INFO[slotType].Atlas or "")
    DSH.slotButtons[i].tex:SetAllPoints()
    updateGemButtonQualityTexture(DSH.slotButtons[i], nil)

    DSH.slotButtons[i]:Show()
end


function DSH:CreateSlotButton(i)

    if DSH.slotButtons[i] then return end
	--slot button container
	--DSH.SBC = DSH.SBC or createSlotButtonContainer()

	DSH.slotButtons[i] = CreateFrame ("button", "DSH_SlotBtn_"..i, DSH.SBC, "AutoCastOverlayTemplate")
	--dbpr(DSH.slotButtons[i].Shine)

    --for k, v in pairs(DSH.slotButtons[i]) do
    --    dbpr(k, v)
    --end
    --dbpr("-------------------")

	local frame = DSH.slotButtons[i]

    if type(i) == "number" then
        frame:SetScript("OnEnter", slotButtonEnter)
        frame:SetScript("OnLeave", function()
            DSH:ToggleItemTooltip(false)
        end)
        frame:SetScript("OnClick", function()
            if frame.bag then
                SocketContainerItem(frame.bag, frame.slot)
            else
                SocketInventoryItem(frame.slot)
            end
            DSH:UpdateCurSlotGlow()
        end)
    end

	frame:SetWidth(SLOT_BUTTON_SIZE)
	frame:SetHeight(SLOT_BUTTON_SIZE)

    --frame.AutoCastOverlay = CreateFrame ("button", "", frame, "AutoCastOverlayTemplate")
    --frame.AutoCastOverlay:SetAllPoints()
    --frame.AutoCastOverlay.Corners:Hide()

    --for k, v in pairs(frame.AutoCastOverlay) do
    
    --    dbpr(k, v)
    --end

    --frame.Shine:ClearAllPoints()
    --frame.Shine:SetPoint("CENTER", frame, "CENTER")
    --frame.Shine:SetAllPoints()
    --frame.Shine:SetAllPoints()
    --frame.Shine:SetSize(20, 20)

    frame.Corners:SetDrawLayer("OVERLAY")
    frame.Corners:Hide()
	
	frame:SetNormalFontObject("GameFontNormal")
	
	DSH:CreateGemButtonFont()

	frame:SetNormalFontObject(DSH.GemButtonFont)
	
	frame.tex = frame:CreateTexture()
    frame.qualityTex = frame:CreateTexture(nil, "overlay")
    frame.qualityTex:SetSize(16, 16)
    frame.qualityTex:SetPoint("TOPLEFT", 0, 0);
    frame.qualityTex:SetTexture("Interface\\Professions\\ProfessionsQualityIcons")

    --frame.qualityTex:SetAllPoints()
    --frame.slotTex = frame:CreateTexture()
    --frame.slotTex:SetTexture("Interface\\AddOns\\DominationSocketHelper\\media\\images\\slot_icons.tga")

end


function DSH:UpdateCurSlotGlow(btnTable, force)

    for _, btn in pairs(DSH.slotButtons) do
        --AutoCastOverlay_AutoCastStop(btn)
        btn:ShowAutoCastEnabled(false)
        btn.Corners:Hide()
    end
	
    local glowSlots = {}
	if btnTable then
        if (not DSH.GBC or DSH.GBC.isSlotContainer or force) then
            for k, btn in pairs(btnTable) do
                if btn and btn.slot then
                    --if btn contains bag information, it's not equipped. No glow
                    if not btn.bag then
                        ActionButton_ShowOverlayGlow(_G["Character"..slotNames[btn.slot].."Slot"])
                    end
                    --AutoCastOverlay_AutoCastStart(btn)
                    btn:ShowAutoCastEnabled(true)
                    btn.Corners:Show()
                    glowSlots[btn.slot] = true
                end
            end
        end
    end

    for i = 1, MAX_SLOT do
        if not glowSlots[i] then
            ActionButton_HideOverlayGlow(_G["Character"..slotNames[i].."Slot"])
        end
	end
end

local function createSlotLocations()
	local slotLocations = {}
	for s = 1, MAX_SLOT do
		slotLocations[s] = ItemLocation:CreateFromEquipmentSlot(s);
	end
	return slotLocations
end

--function DSH:IsGemUnique(gemLink, gemID)

--    --do this so you don't have to scan the tooltip every time
--    --probably a better way to do this idk
--    if DSH.uniqueInfo[gemID] then
--        return DSH.uniqueInfo[gemID] == "Unique" and true or false
--    end

--    dbpr("SCAN TOOLTIP")

--    DSH.scantip = DSH.scantip or CreateFrame("GameTooltip", "DSHScanningTooltip", nil, "GameTooltipTemplate")
--    DSH.scantip:SetOwner(UIParent, "ANCHOR_NONE")
--    DSH.scantip:SetHyperlink(gemLink)
--    local text = tostring(_G["DSHScanningTooltipTextLeft3"]:GetText())
--    local isUnique = text:match(UNIQUE_EQUIP_PATTERN)

--    --can't do true and false because checking if the key exists wouldn't work?
--    DSH.uniqueInfo[gemID] = isUnique and 'Unique' or 'Not Unique'
--    return isUnique and true or false
--end

function DSH:IsGemUnique(gemLink, gemID)

    if not gemLink or not gemID then return end

    if DSH.uniqueInfo[gemID] ~= nil then
        return DSH.uniqueInfo[gemID].isUnique, DSH.uniqueInfo[gemID].uniqueType
    end

    DSH.scantip = DSH.scantip or CreateFrame("GameTooltip", "DSHScanningTooltip", nil, "GameTooltipTemplate")
    DSH.scantip:SetOwner(UIParent, "ANCHOR_NONE")
    DSH.scantip:SetHyperlink(gemLink)
    local text = tostring(_G["DSHScanningTooltipTextLeft3"]:GetText())

    local uniqueType = text:match(UNIQUE_EQUIP_PATTERN)

    --dbpr(gemLink, "is uniqueType", uniqueType, "text:", text)

    DSH.uniqueInfo[gemID] = {isUnique = uniqueType and true or false, uniqueType = uniqueType}
    return DSH.uniqueInfo[gemID].isUnique, uniqueType
end


local function updateGemmedSlotButton(i, gemLink, itemLink, gemID)
	-- dbpr(i)
	if itemLink == DSH.slotButtons[i].itemLink then --make sure the item is the same
		DSH.slotButtons[i].gemLink = gemLink
		DSH.slotButtons[i].gemID = gemID
        DSH.slotButtons[i].unique, DSH.slotButtons[i].uniqueType = DSH:IsGemUnique(gemLink, gemID)
        DSH.slotButtons[i].itemLink = DSH.slotButtons[i].isDomination and gemLink or itemLink
		DSH.slotButtons[i].tex:SetTexture(select(5, GetItemInfoInstant(gemLink)))
		DSH.slotButtons[i].tex:SetAllPoints()

        if DSH.slotButtons[i].unique then
            DSH.UniqueEquipped[DSH.uniqueInfo[gemID].uniqueType] = true
        end

        local gemQuality = getGemQuality(gemLink)
        DSH.slotButtons[i].gemQuality = gemQuality
        updateGemButtonQualityTexture(DSH.slotButtons[i], gemQuality)

        --DSH.slotButtons[i].tex:SetPoint("LEFT", DSH.slotButtons[i], "RIGHT")
        --DSH.slotButtons[i].tex:SetSize(10, 10)
    end
end

--local function getTexCoords(slot)
--    local width = 544
--    local left = (slot * 32)/1024
--    local right = ((slot+1) * 32)/1024
--    print(left, right)
--    return left, right, 0, 1
--end

local function hideButtons(btnTable, start)
	for i = start, #btnTable do
		btnTable[i]:Hide()
	end
end

function DSH:RemoveGemsFromItemLink(itemLink)
    --Don't really know how else to do this, can't get it to work with gsub
    if not itemLink then return end

    local item, itemID, enchantID, gem1, gem2, gem3, gem4, extra = strsplit(":", itemLink, 8)
    --dbpr(extra)
    --dbpr("enchantid", enchantID, "gem1", gem1, "gem2", gem2, "gem3", gem3, "gem4", gem4)
    return item .. ":" .. itemID .. ":" .. enchantID .. ":::::" .. extra, {tonumber(gem1), tonumber(gem2), tonumber(gem3), tonumber(gem4)}
end

function DSH:GetGemSlotInfo(itemLink)
    --DSH.scantip = DSH.scantip or CreateFrame("GameTooltip", "MyScanningTooltip", nil, "GameTooltipTemplate")
    --scantip:SetOwner(UIParent, "ANCHOR_NONE")

    --DSH:GetGemID(itemLink, 1) why was this here?
    
    --in Dragonflight GetItemStats doesn't return gem slots added from JC items, so need to scan tooltip now

    local gemSlotInfo = {}
    local slotCount = 0

    local removedGemsLink, removedGems = DSH:RemoveGemsFromItemLink(itemLink)

    DSH.scantip = DSH.scantip or CreateFrame("GameTooltip", "DSHScanningTooltip", nil, "GameTooltipTemplate")
    DSH.scantip:SetOwner(UIParent, "ANCHOR_NONE")
    DSH.scantip:SetHyperlink(removedGemsLink)
    for i=2, DSH.scantip:NumLines() do   -- can skip first line since it's just the item name.. or not. up to you
        local text = _G["DSHScanningTooltipTextLeft"..i]:GetText()

        if SLOT_INFO[text] then
            slotCount = slotCount + 1
            table.insert(gemSlotInfo, {SLOT_INFO[text].Name, removedGems[slotCount], SLOT_INFO[text].Icon})
        end
    end

    return gemSlotInfo
end

local function getSlotGemInfo(s, gemID, itemLink)
	if gemID then
		local item = Item:CreateFromItemID(gemID)
		item:ContinueOnItemLoad(function() updateGemmedSlotButton(s, item:GetItemLink(), itemLink, gemID) end)
	end
end

local function shouldShowSlot(slotType, gemID)
    if not slotType then return end
    if slotType == SLOT_INFO[EMPTY_SOCKET_PRIMORDIAL].Name and gemID then return end
    if DSH.db.char.quickslots.extended or (not gemID and not DSH.db.char.quickslots.extended and DSH.db.profile.quickslots.alwaysempty) then return true end
end


local function itemLoaded(itemsFound, itemLink, s)
	DSH.totalLoaded = DSH.totalLoaded + 1
	table.insert(DSH.loadedItems, {itemLink = itemLink, slot = s})
	
	--loads all items before continuing.
	if DSH.totalLoaded ~= itemsFound then return end

    dbpr("All Items Loaded")

	local quickSlotInfo = {}
	local buttonCount = 1
    local gemSlotFound
    local hiddenCount = 0

	for k, itemInfo in pairs(DSH.loadedItems) do
		local itemLink = itemInfo.itemLink
		local s = itemInfo.slot
		local slotTex, isDomination
		
		--local hasSlot, slotType, gemID = getGemSlotInfo(itemLink)
        local gemSlotInfo = DSH:GetGemSlotInfo(itemLink)

        for socketNum, slotInfo in pairs(gemSlotInfo) do
            local slotType, gemID, slotTex = unpack(slotInfo)
            --dbpr("TEX", slotTex)
            --dbpr(socketNum, slotType, gemID)
            if slotType then gemSlotFound = true end

            if DSH:IsRemix() or shouldShowSlot(slotType, gemID) then
                if not quickSlotInfo[slotType] then quickSlotInfo[slotType] = {} end
                if not quickSlotInfo[slotType][s] then quickSlotInfo[slotType][s] = {} end
                quickSlotInfo[slotType][s][socketNum] = {slotType = slotType, gemID = gemID, itemLink = itemLink, slotTex = slotTex}	
            else
                hiddenCount = hiddenCount + 1    
            end

        end
	end

    if DSH:IsRemix() then

        DSH.socketInfo = quickSlotInfo
        DSH.updating = false
	
        if DSH.updateQueued then
            DSH.updateQueued = false
            -- dbpr("update queued redoing")
            DSH:UpdateSlotButtons()
        end

        return
    end
	
    if not DSH.db.char.quickslots.extended then
        DSH.SBC:SetText((hiddenCount > 0 and hiddenCount or "") .. ">")
    end

	local buttonCount = 1


    for i, slotInfo in ipairs(SLOT_INFO_ORDERED) do
        dbpr(i, slotInfo.Name)
        if quickSlotInfo[slotInfo.Name] then


            for slot, slotGemInfo in DSH:PairsByKeys(quickSlotInfo[slotInfo.Name]) do
                for socketNum, slotInfo in pairs(slotGemInfo) do
                --print(slot, slotGemInfo)
                    --dbpr("HERE", slotType)
                    updateEmptySlotButton(slot, buttonCount, slotInfo.slotType, slotInfo.itemLink, socketNum, slotInfo.slotTex)
                    getSlotGemInfo(buttonCount, slotInfo.gemID, slotInfo.itemLink)
                    buttonCount = buttonCount + 1
                end
            end
        end
	end


    --for slotType, slotTypeInfo in DSH:PairsByKeys(quickSlotInfo) do

    --    dbpr(slotType)
    --end

	--for slotType, slotTypeInfo in DSH:PairsByKeys(quickSlotInfo) do

    --    --dbpr(slotType)

	--	for slot, slotGemInfo in DSH:PairsByKeys(slotTypeInfo) do
    --        for socketNum, slotInfo in pairs(slotGemInfo) do
    --        --print(slot, slotGemInfo)
	--		    --dbpr("HERE", slotType)
    --            updateEmptySlotButton(slot, buttonCount, slotInfo.slotType, slotInfo.itemLink, socketNum, slotInfo.slotTex)
    --            getSlotGemInfo(buttonCount, slotInfo.gemID, slotInfo.itemLink)
    --            buttonCount = buttonCount + 1
    --        end
	--	end
	--end

    --for i, slotInfo in pairs(SLOT_INFO_ORDERED) do

    --    if quickSlotInfo[slotInfo.Name] then
    --    --dbpr(slotType)

    --        for slot, slotGemInfo in DSH:PairsByKeys(slotTypeInfo) do
    --            for socketNum, slotInfo in pairs(slotGemInfo) do
    --            --print(slot, slotGemInfo)
    --                --dbpr("HERE", slotType)
    --                updateEmptySlotButton(slot, buttonCount, slotInfo.slotType, slotInfo.itemLink, socketNum, slotInfo.slotTex)
    --                getSlotGemInfo(buttonCount, slotInfo.gemID, slotInfo.itemLink)
    --                buttonCount = buttonCount + 1
    --            end
    --        end
    --    end
	--end

	hideButtons(DSH.slotButtons, buttonCount)

	if DSH.SBC then
        if buttonCount == 1 and not gemSlotFound then
            DSH.SBC:Hide() 
        else
            DSH.SBC:Show()
        end
    end

	DSH:UpdateCurSlotGlow()

	DSH.updating = false
	
	if DSH.updateQueued then
		DSH.updateQueued = false
		-- dbpr("update queued redoing")
		DSH:UpdateSlotButtons()
	end
end

function DSH:UpdateSlotButtons()
    --dbpr("UPDATESLOTBUTONS")

    DSH.SBC = DSH.SBC or createSlotButtonContainer()

	if not (CharacterFrame and CharacterFrame:IsShown()) then return end

	if not DSH:IsRemix() and not DSH.db.profile.quickslots.enable then
		if DSH.SBC then DSH.SBC:Hide() end
		return
	end

	if DSH.updating then DSH.updateQueued = true return end
	
	local slotsFound = {}
    DSH.socketInfo = {} --breaks something?
	DSH.loadedItems = {}
	DSH.totalLoaded = 0
	DSH.slotLocations = DSH.slotLocations or createSlotLocations()
	
	--Find slots that need to be checked
	for s = 1, MAX_SLOT do
		if C_Item.DoesItemExist(DSH.slotLocations[s]) then
			table.insert(slotsFound, s)
		end
	end
	
	if #slotsFound == 0 then
		hideButtons(DSH.slotButtons, 1)
	else
		DSH.updating = true
	end
	
	dbpr("Updating Slot Buttons")

    DSH.UniqueEquipped = {}

	--Queue up all items to be loaded if they aren't
	for i, s in pairs(slotsFound) do
		local item = Item:CreateFromItemLocation(DSH.slotLocations[s])
		item:ContinueOnItemLoad(function() itemLoaded(#slotsFound, item:GetItemLink(), s) end)
	end
end

function EF:CHARACTER_FRAME_SHOW()
	--DSH.SBC = DSH.SBC or createSlotButtonContainer()
	
	--delay update because it breaks sometimes if you don't
		--nevermind I did some convoluted nonsense to make it work
	-- C_Timer.After(0.1, function() DSH:UpdateSlotButtons() end)
	DSH:UpdateSlotButtons()
	slotInfo = ItemLocation:CreateFromEquipmentSlot(1)
end

--Hides the gem container/set container when you mouseover char if they are anchored there
function EF:CHARACTER_FRAME_HIDE_GBC()
	if DSH.GBC and DSH.GBC.isSlotContainer then DSH.GBC:Hide() end
	if DSH.SBC then
        DSH.SBC.curSlotBtn = nil
        if DSH.SBC.bagSlotExtended then
            DSH.SBC.bagSlotExtended = false
            DSH:UpdateSlotButtons()
        end
    end
	DSH:UpdateCurSlotGlow()
	
	if not CharacterFrame:IsVisible() and not DSH.db.profile.quickslots.stayopen then
		DSH.db.char.quickslots.extended = false
		DSH.SBC:SetText(">")
	end
	
end

local function getCorrectFrameScale(frame)
	while frame:GetParent() do
		if frame:GetScale() ~= 1 then
			return frame:GetScale() * UIParent:GetScale()
		end
		frame = frame:GetParent()
	end
	return UIParent:GetScale()
end


function EF:PLAYER_ENTERING_WORLD()
	--Hook IsDisenchantable so you can't DE items with shard in them from addons
	--Only Tested with Molinari
	--local lib = LibStub:GetLibrary('LibProcessable', true)
	--if lib then
	--	local function blockDisenchant()
	--		DSH:ItemToolTip(true)
	--	end
	--	hooksecurefunc(lib, "IsDisenchantable", blockDisenchant)
	--end

	EF:UNIT_INVENTORY_CHANGED('player') --force set to be created for ldb
	EF:UnregisterEvent("PLAYER_ENTERING_WORLD")
end

function DSH:ToggleButton(button, state)
	button:SetEnabled(state)
	button.tex:SetDesaturated((state and nil) or (not state and 1))
end

function EF:SOCKET_INFO_CLOSE()
	dbpr("SOCKET_INFO_CLOSE")
	EF:UnregisterEvent('CHAT_MSG_LOOT')
	EF:UnregisterEvent('SOCKET_INFO_CLOSE')
	if DSH.Input then DSH.Input:SetText("") end
end


function DSH:PairsByKeys(t)
    local a = {}
    for n in pairs(t) do table.insert(a, n) end
    table.sort(a,  function(a,b) return b>a end)
    local i = 0 
    local iter = function () 
        i = i + 1
        if a[i] == nil then return nil
        else return a[i], t[a[i]]
        end
     end
     return iter
end

local function socketInfoDelayedUpdate()
	EF:SetScript("OnUpdate", nil)
	DSH.checkForGems = true

	if ItemSocketingFrame:IsShown() then
		EF:RegisterEvent('SOCKET_INFO_CLOSE')
        DSH:InitGemButtons()
		--end
	end
end

--Throttles updates from changing equip/gems to once per frame
--Need inventory changed to properly update when only changing gems
--Need equip change to properly update when changing equipment with gems
local invChanged, equipChanged
updateThrottle:SetScript("OnUpdate", function()
	if equipChanged then
		equipChanged = false
		if not invChanged then --stops from doing 2 updates in one frame
			DSH:UpdateSlotButtons()
		end
	end
	
	if invChanged then
		invChanged = false
		DSH.checkForGems = true

		--DSH.currentSet = DSH:GetCurrentShardSet()
		--DSH:UpdateLDBText(getFirstSetMatch())
		--DSH:UpdateSetButtons()

		if CharacterFrame and CharacterFrame:IsVisible() then
			DSH:UpdateSlotButtons()
			--brute force updating if the quick slot is still open

			if DSH.SBC and DSH.SBC.curSlotBtn and DSH.SBC.curSlotBtn:IsVisible() then
				DSH:UpdateCurSlotGlow({DSH.SBC.curSlotBtn})
			else
				DSH:UpdateCurSlotGlow()--hide slot glow
			end
		end

		if DSH.GBC and DSH.GBC:IsVisible() then
            --dbpr("IS BAG BUTTON?", DSH.SBC.curSlotBtn)
			DSH:InitGemButtons(DSH.GBC.isSlotContainer)
		end
	end
end)

function DSH:RemixBagUpdate()
    invChanged = true
end

function EF:UNIT_INVENTORY_CHANGED(unit)
	if unit == 'player' then
		invChanged = true
	end
end

function EF:BAG_UPDATE_DELAYED(unit)
	invChanged = true
end

function EF:PLAYER_EQUIPMENT_CHANGED(slot)
	equipChanged = true
end


function EF:SOCKET_INFO_UPDATE()
	if not DSH.lockedSocketOpen then
		DSH.lockedSocketOpen = true
		--dbpr("changing locked bag to", DSH.lockedBag)
		DSH.lockedSocketBag = DSH.lockedBag
		DSH.lockedSocketSlot = DSH.lockedSlot
	end

	EF:SetScript("OnUpdate", function() socketInfoDelayedUpdate() end)
end

function DSH:GetItemFromBagSlot(bag, slot)
	local itemLink
	if slot then --in bag
		itemLink = GetContainerItemLink(bag, slot)
	 else --equipped
		itemLink = GetInventoryItemLink("player", bag)
	end
	return itemLink
end

function EF:ITEM_LOCKED(bag, slot)
	
	DSH.lockedBag = bag
	DSH.lockedSlot = slot
	DSH.lockedItem = DSH:GetItemFromBagSlot(bag, slot)
	
    --dbpr("LOCKED ITEM", DSH.lockedItem)
	--Check if you're dragging a shard with a domination window open
	--if slot and ItemSocketingFrame and ItemSocketingFrame:IsShown() and DSH.shardInSocket then
	--	local itemLink = GetContainerItemLink(bag, slot)
	--	local _, itemID = strsplit(":", itemLink)
	--	if DSH:IsDominationShard(itemID) then
	--		DSH:BlockFrameToggle(true)
	--	end
	--end
end

function EF:ITEM_UNLOCKED(bag, slot)
	--opened socket item changed
	--DSH:BlockFrameToggle(false)
	--dbpr("UNLOCKED BAG:", bag, "slot:", slot)
	if bag == DSH.lockedSocketBag and slot == DSH.lockedSocketSlot then
		DSH.lockedSocketOpen = false
	end

end

function EF:GetEdgeScale()
	local yRes = select(2,GetPhysicalScreenSize())
	local uiScale = UIParent:GetScale()
	local scale = 768/yRes/uiScale
	return scale
end

local function createButtonFont()
	local font = CreateFont("DSHButtonFont")
	font:CopyFontObject("GameFontNormal");
	font:SetTextColor(1, 1, 1, 1.0);
	return font
end

function DSH:FormatFrame(frame, isButton)
	
	frame:SetBackdrop ({edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1 * EF:GetEdgeScale(), bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true})
	-- frame:SetBackdropColor (.2, .2, .2, 1)
	-- frame:SetBackdropBorderColor (0.3, 0.3, 0.3)
	frame:SetBackdropColor (.1, .1, .1, 1)
	frame:SetBackdropBorderColor (0, 0, 0)

	if isButton then
		DSH.ButtonFont = DSH.ButtonFont or createButtonFont()
		frame:SetNormalFontObject(DSH.ButtonFont)
	end

end

local function createInfoTooltip()
	-- local scale = PLTrader.db.profile.general.scale
	-- local f = PLT:CreateBackgroundFrame(MAIN_FRAME_WIDTH*scale, MAIN_FRAME_HEIGHT*scale)
	local f = CreateFrame("Frame", nil, DSH.SBC, "BackdropTemplate")
	DSH:FormatFrame(f, false)
	f.text = f:CreateFontString(nil, "overlay", "GameFontNormal")
	-- f.text:SetFont(PLT_FONT, 10)
	f.text:SetPoint("TOPLEFT", f, "TOPLEFT", 5, -5)
	-- f.text:SetWidth((MAIN_FRAME_WIDTH*scale)-10)
	f.text:SetTextColor (1, 1, 1, 1)
	f.text:SetJustifyH("LEFT")
	return f
end

function DSH:ToggleInfoTooltip(show, text, frame)
	-- if not frame then return end
	
	if show then-- and (DSHrader.db.profile.general.tooltips or frame) then
	
		DSH.infoTooltip = DSH.infoTooltip or createInfoTooltip()
		DSH.infoTooltip.text:SetText(text)
		
		local textHeight = DSH.infoTooltip.text:GetStringHeight();
		local textWidth = DSH.infoTooltip.text:GetStringWidth();
		DSH.infoTooltip:SetHeight(textHeight + 10)
		DSH.infoTooltip:SetWidth(textWidth + 10)
        local yOff = DSH.GBC and (DSH.GBC.curGemBtn:GetTop() - DSH.GBC:GetTop()) or 0

		DSH.infoTooltip:SetPoint("TOPLEFT", frame, "TOPRIGHT", 2, yOff)
		DSH.infoTooltip:Show()
	
	else
		if DSH.infoTooltip then DSH.infoTooltip:Hide() end
	end	
end


function DSH:GetGemID(itemLink, slotCount)
	if not itemLink or not slotCount then return end
    --local splitItemLink = {strsplit(":", itemLink)}
	local gemID = select(slotCount + 3, strsplit(":", itemLink)) 

    if gemID then
		return tonumber(gemID)
	end
end

function DSH:InitializeSettings(event, addon)
   	self.defaults = {
		char = {
			quickslots = {
				extended = true,
			},
			sets = {
				--version = VERSION,
				--debugging = false,
				--xofs = (GetScreenWidth()/2)-(MAIN_FRAME_WIDTH/2),
				--yofs = (GetScreenHeight()/2)-(MAIN_FRAME_HEIGHT/2),
			},
		},
		profile = {
			quickslots = {
				enable = true,
				alwaysempty = true,
				stayopen = true,
			},
			socketwindow = {
				enable = true,
				autoaccept = false,
			},
		},
	}
		
	self.db = LibStub("AceDB-3.0"):New("DSHDB", self.defaults)
end

--"|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_1:0|t"

-- DSH:RegisterChatCommand("dsh", function(msg, editbox)
	-- local words = {}
	-- for word in msg:gmatch("[^ ]+") do 
		-- table.insert(words, word)
	-- end
	
	-- if #words == 0 or words[1] == "open" then
	
	-- else
	
	-- end
-- end)

--------------------------------------------------------------
--Removed Domination Functions, Don't Exist in 10.0----------
-------------------------------------------------------------

--function DSH:ToggleDomContainer(show)
--	-- DSH.DC = DSH.DC or CreateFrame("Frame", nil, ItemSocketingFrame)
--	if show then
--		DSH.DC = DSH.DC or CreateFrame("Frame", nil, ItemSocketingFrame)
--		DSH.DC:Show()
--	elseif DSH.DC then
--		DSH.DC:Hide()
--	end
--end

--function DSH:CreateSetContainer()
--	if DSH.SetC then return end
--	local frame = CreateFrame("Frame", nil, DSH.LDBButton or DSH.GBC,  "BackdropTemplate")
--	frame:SetBackdrop({bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", tile = true, tileSize = 16, edgeFile = [[Interface\ButtoPLTrader:NS\WHITE8X8]], edgeSize = 1 * EF:GetEdgeScale()})
--	frame:SetBackdropColor(0, 0, 0, .75)
--	frame:SetBackdropBorderColor(0, 0, 0, .9)
--	frame:SetFrameStrata("HIGH")

--	DSH.SetC = frame
--end


--function DSH:UpdateSetButtons()
--	if InCombatLockdown() or not DSH.SetC then return end
--	if not DSH.SetC:IsShown() then return end

--	-- dbpr("update set buttons")
	
--	if not DSH.setButtons then DSH.setButtons = {} end
	
--	hideSetButtons()
	
--	local width = 0
--	--DSH.currentSet = DSH:GetCurrentShardSet()
--	local i = 1
--	local matchName

--	for setName, setInfo in DSH:PairsByKeys(DSH.db.char.sets) do
--		local match = false
--		if setInfo.key == DSH.currentSet.key then
--			match = true
--			matchName = setName
--		end
--		UpdateSetButton(i, setName, match)
		
--		if DSH.setButtons[i]:GetWidth() > width then width = DSH.setButtons[i]:GetWidth() end
		
--		i = i + 1
--	end
	
--	--DSH:UpdateLDBText(matchName)
--	DSH.SetC.setButtonWidth = width
	
--	--this not great
--	if DSH.RemoveSetButton and DSH.RemoveSetButton:IsShown() and DSH.RemoveSetButton:GetWidth() > width then
--		width = DSH.RemoveSetButton:GetWidth() - SET_BUTTON_HEIGHT
--	end
	
--	--update bg size
--	local height = (i - 1) * SET_BUTTON_HEIGHT + 10
--	local bgWidth = width + SET_BUTTON_HEIGHT*2 + 10
	
--	DSH.SetC:SetSize(bgWidth, height)
	
--	for _, button in pairs(DSH.setButtons) do
--		button:SetWidth(width)
--	end

--	if next(DSH.db.char.sets) then
--		DSH.SetC:Show()
--	else
--		DSH.SetC:Hide()
--	end
	
--end

--function DSH:UpdateSetContainer()
--	--dbpr("update")
--	if InCombatLockdown() then return end
--	-- if not DSH.SetC then return end
--	if DSH.LDBOpen then
--		DSH:CreateSetContainer()
--		DSH.SetC:SetParent(DSH.LDBButton)
--		DSH.SetC:ClearAllPoints()
--		local _, y = DSH.LDBButton:GetCenter()
--		if y > GetScreenHeight()/2 then
--			DSH.SetC:SetPoint("TOPLEFT", DSH.LDBButton, "BOTTOMLEFT", -5, 0)
--		else
--			DSH.SetC:SetPoint("BOTTOMLEFT", DSH.LDBButton, "TOPLEFT", -5, 0)
--		end
--		DSH.SetC:SetFrameStrata("HIGH")
--		DSH:UpdateSetRemoveButtonPosition()
--		DSH.SetC:Show()
--		DSH:UpdateSetButtons()
--	else
--		--if the gem button container isn't domination then don't show sets
--		if DSH.GBC and not DSH.GBC.isDomination then 
--			if DSH.SetC then DSH.SetC:Hide() end
--			return
--		end
--		DSH:CreateSetContainer()
--		DSH.SetC:SetParent(DSH.GBC)
--		DSH.SetC:ClearAllPoints()
--		-- if DSH.GBC and DSH.GBC:IsShown() then
--		if ItemSocketingFrame and ItemSocketingFrame:IsShown() then
--			DSH.SetC:SetPoint("TOPLEFT", DSH.GBC, "BOTTOMLEFT", 0, -2)
--		else
--			DSH.SetC:SetPoint("TOPLEFT", DSH.GBC, "TOPRIGHT", 2, 0)
--		end
		
--		DSH.SetC:Show()
--		DSH:UpdateSetRemoveButtonPosition()
--		DSH:UpdateSetButtons()
--	end
--end

--function DSH:CreateSetCreateButtons()
--	if DSH.Input then return end
	
--	DSH.Input = CreateFrame("EditBox", "", DSH.DC, "BackdropTemplate");
--	DSH.Input:SetPoint("TOPRIGHT", ItemSocketingSocket1, "TOPLEFT", -5, 0)
--	DSH.Input:SetFrameStrata("DIALOG")
--	DSH.Input:SetFont("Fonts\\FRIZQT__.TTF", 12)
--	DSH.Input:SetCursorPosition(1)
--	DSH.Input:SetTextInsets(4, 0, 0, 4)
--	DSH.Input:SetScript("OnTextChanged", function() DSH:InputTextChanged() end)
	
--	DSH:FormatFrame(DSH.Input)
--	DSH.Input:SetBackdropColor (.25, .25, .25, 1)
--	--DSH.Input:SetBackdropBorderColor (.5, .5, .5)

--	DSH.Input:SetSize(120, 19)
--	DSH.Input:SetJustifyH("CENTER")
--	DSH.Input:SetJustifyV("CENTER")
--	DSH.Input:SetMultiLine(false)
--	DSH.Input:SetAutoFocus(false)
--	DSH.Input:SetMaxLetters(15)

--	DSH.Save = CreateFrame("Button", "", DSH.Input, "BackdropTemplate")
--	DSH:FormatFrame(DSH.Save, true)
	
--	DSH.Save:SetPoint("TOP", DSH.Input, "BOTTOM" , 0, -2)
--	DSH.Save:SetWidth(DSH.Input:GetWidth())
--	DSH.Save:SetHeight(DSH.Input:GetHeight())
--	DSH.Save:SetScript("OnClick", function() DSH:SaveButtonPress() end)
--	DSH.Save:SetText(SHL["SAVE_SET"])
	
--end


--local function socketInfoDelayedUpdate()
--	EF:SetScript("OnUpdate", nil)
--	--DSH.shardInSocket = false
--	DSH.checkForGems = true
	
--	--if DSH.Delete then DSH.Delete:Hide() end

--	--Kinda a mess down here but w/e
--	if ItemSocketingFrame:IsShown() then
--		EF:RegisterEvent('SOCKET_INFO_CLOSE')
--		--if GetSocketTypes(1) == "Domination" then
--		--	DSH:ToggleDomContainer(true)
--		--	DSH:CreateSetCreateButtons()

--		--	local gemLink = GetExistingSocketLink(1)
			
--		--	DSH:InitGemButtons(true)

--		--	if gemLink then
--		--		local _, gemID = strsplit(":", gemLink)

--		--		DSH.BlockFrame = DSH.BlockFrame or DSH:CreateBlockFrame(ItemSocketingSocket1)
--		--		if DSH:IsDominationShard(gemID) then
--		--			DSH.shardInSocket = true
--		--			if DSH:GetBagFreeSpace() > 0 then
--		--				DSH:ToggleRemoveButton(true)
--		--			else--removing shard when bag is full deletes it?
--		--				DSHPrint(SHL["BAG_FULL_BUTTON"])
--		--				DSH:ToggleRemoveButton(false)
--		--			end
--		--		end
--		--	else
--		--		DSH:ToggleRemoveButton(false)
--		--	end
			
--		--else
--        --DSH:ToggleDomContainer(false)
--        DSH:InitGemButtons(false)
--		--end
--	end
--end

--function DSH:BlockFrameToggle(show)
--	if not DSH.BlockFrame then return end
--	if show then
--		DSH.BlockFrame:Show()
--	else
--		DSH.BlockFrame:Hide()
--	end
--end

--function DSH:CreateBlockFrame(parent)
--	local frame = CreateFrame("button", nil, parent, "BackdropTemplate")
--	frame:SetSize(40, 40)
--	frame:SetBackdrop({bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", tile = true, tileSize = 16, edgeFile = [[Interface\ButtoPLTrader:NS\WHITE8X8]], edgeSize = 2})
--	frame:SetBackdropColor(0, 0, 0, 1)
--	frame:SetBackdropBorderColor(0, 0, 0, 1)
--	frame:SetPoint("CENTER", 0, 0)
--	frame:SetFrameStrata("HIGH")

--	local font = CreateFont("DSHButtonFont")
--	font:CopyFontObject("GameFontNormal");
--	font:SetTextColor(1, 1, 1, 1.0);
--	frame:SetNormalFontObject(font)
	
--	frame:SetText(SHL["NO"])
--	frame:SetScript("OnClick", function() DSHPrint(SHL["WHY"]) end)
--	frame:Hide()
--	return frame
--end

--local function getSocketedShardSlotOrItem(shard)
	
--	--If the item is in your bag you have to use it by item ID
--	if DSH.removeShards[shard] == "Bag" then
--		for b = 0, NUM_BAG_SLOTS do
--			for s = 1, GetContainerNumSlots(b) do
--				local itemLink = select(7, GetContainerItemInfo(b, s))
--				if itemLink then
--					local gemID = DSH:GetGemID(itemLink)
--					if shardIDs[gemID] and shardIDs[gemID] == shard then
--						--dbpr("at",b,s)
--						return GetContainerItemID(b, s)
--					end

--				end
--			end
--		end
--	else
--		for s = 1, 10 do
--			local itemLink = GetInventoryItemLink("player", s)
--			local gemID = DSH:GetGemID(itemLink)
--			if DSH:IsDominationShard(gemID) and shardIDs[gemID] == shard then
--				return s
--			end
--		end
--	end

--end

--local function setRemoveButtonMacro(shard)
--	local slotOrItem = getSocketedShardSlotOrItem(shard)
--	if not slotOrItem then
--		DSHPrint(string.format(SHL["SHARD_NOT_FOUND"], shard))
--		DSH.RemoveSetButton:Hide()
--		DSH.loadingSet = nil
--		return
--	end
	
--	local macroText
--	if DSH.removeShards[shard] == "Bag" then
--		macroText = "/use item:187532\n/use item:"..slotOrItem
--	else
--		macroText = "/use item:187532\n/use "..slotOrItem
--	end
	
--	DSH.RemoveSetButton:SetAttribute("macrotext", macroText)
--end

--function DSH:UpdateSetRemoveButtonPosition()
--	if not DSH.RemoveSetButton then return end
	
--	DSH.RemoveSetButton:SetPoint("LEFT", DSH.loadingButton, "LEFT")
--	DSH.RemoveSetButton:SetFrameStrata("FULLSCREEN_DIALOG")
	
--	if DSH.RemoveSetButton:GetWidth() > DSH.SetC.setButtonWidth then
--		DSH.SetC:SetWidth((DSH.RemoveSetButton:GetWidth() - SET_BUTTON_HEIGHT)+ SET_BUTTON_HEIGHT*2 + 10)
--	else
--		DSH.SetC:SetWidth(DSH.SetC.setButtonWidth + SET_BUTTON_HEIGHT*2 + 10)
--	end
	
--end

--function DSH:UpdateRemoveGemButton()
--	--DSH.RemoveSetButton = DSH.RemoveSetButton or DSH:CreateRemoveButton(DSH.SetC)
--	if InCombatLockdown() then return end

--	if not DSH.RemoveSetButton then
--		DSH.RemoveSetButton = DSH:CreateRemoveButton(DSH.SetC)
--		if not DSH.RemoveSetButton then return end
--		DSH.RemoveSetButton:SetBackdropColor (0, 0, 0, 1)
--		DSH.RemoveSetButton:SetFrameStrata("DIALOG")
--		DSH.RemoveSetButton:SetHeight(SET_BUTTON_HEIGHT)
--		DSH.RemoveSetButton:SetScript("OnEnter", function()
--			if DSH.Delete then DSH.Delete:Hide() end
--			--EF:RegisterEvent("UNIT_SPELLCAST_SENT")
--			-- EF:RegisterEvent("CHAT_MSG_LOOT")
--		end)
--		DSH.RemoveSetButton:SetScript("OnLeave", function()
--			if DSH.Delete then DSH.Delete:Hide() end
--			--EF:UnregisterEvent("UNIT_SPELLCAST_SENT")
--		end)
--	end
	
--	if DSH.removeShards and next(DSH.removeShards) ~= nil then
--		if DSH:GetBagFreeSpace() > 1 then
--			if not DSH.RemoveSetButton:IsVisible() then
--				DSH.RemoveSetButton:Show()
--				DSH.RemoveSetButton:Enable()
--			end
--			DSH.RemoveSetButton.text = SHL["REMOVE"].." ["..SHL[next(DSH.removeShards)].."]"
			
--			local color = DSH.loadColor
--			if DSH.RemoveSetButton:IsEnabled() then
--				DSH.RemoveSetButton:SetText(color.. DSH.RemoveSetButton.text)
--				DSH.RemoveSetButton:SetWidth(DSH.RemoveSetButton:GetTextWidth() + 10)
--				DSH:UpdateSetRemoveButtonPosition()
--			end
			
--			setRemoveButtonMacro(next(DSH.removeShards))
--		else
--			DSHPrint(SHL["BAG_FULL_BUTTON"])
--			DSH.loadingSet = nil
--			DSH.RemoveSetButton:Hide()
--		end
--	else
--		DSH.RemoveSetButton:Hide()
--		DSH.ErrorCount = 0
--		DSH:LoadSelectedSet()
--	end
--end

--function DSH:SetButtonPress(button)
--	if DSH.loadingSet and DSH.loadingSet == button.setName then return end
--	if DSH.RemoveSetButton and not DSH.RemoveSetButton:IsEnabled() then return end
	
--	DSH.removeShards = {}
--	DSH:UpdateGemsInBags()
	
--	DSH.loadingSet = button.setName
--	DSH.loadingButton = button
	
--	local shardsInSet = getShardsFromKey(DSH.db.char.sets[button.setName].key)

--	for _, shard in pairs(shardsInSet) do
--		if not DSH.gemsInBags[shard] and not tContains(DSH.currentSet.shards, shard) then
--			DSH.removeShards[shard] = "Bag"
--		end
--	end
	
--	for _, shard in pairs(DSH.currentSet.shards) do
--		if not tContains(shardsInSet, shard) then
--			DSH.removeShards[shard] = "Char"
--		end
--	end

--	EF:RegisterEvent('CHAT_MSG_LOOT')
--	DSH:UpdateRemoveGemButton()
--end

--function DSH:SetButtonMouseover(enter, button)
--	if enter then
	
--		DSH.Delete:Show()
--		DSH.Delete.setName = button.setName
--		DSH.Delete:SetPoint("LEFT", button, "RIGHT", 2, 0)
		
--		if DSH.currentSet.key ~= DSH.db.char.sets[button.setName].key then
--			button:SetText("|cFFFBFF9D"..button.setName)
--		end

--	else --exit
		
--		if DSH.currentSet.key ~= DSH.db.char.sets[button.setName].key then
--			button:SetText(button.setName)
--		end

--	end

--end

--function DSH:DeleteSet(deleteName)
--	if deleteName then
--		DSH.db.char.sets[deleteName] = nil
--		--DSH:UpdateLDBText(getFirstSetMatch())
--		DSH:UpdateSetButtons()
--		DSH.Delete:Hide()
--	end
--end

--local function hideSetButtons()
--	for _, button in pairs(DSH.setButtons) do
--		button:Hide()
--	end
--end

--local function createSetDeleteButton()
--	local frame = CreateFrame("Button", nil, DSH.SetC)
--	frame:SetNormalTexture("Interface\\Buttons\\UI-GroupLoot-Pass-Up")
--    frame:SetHighlightTexture("Interface\\Buttons\\UI-GroupLoot-Pass-Highlight")
--    frame:SetPushedTexture("Interface\\Buttons\\UI-GroupLoot-Pass-Down")
--	frame:SetSize(SET_BUTTON_HEIGHT, SET_BUTTON_HEIGHT)
--	frame:Hide()
--	frame:SetScript("OnClick", function()
--		local deleteName = frame.setName
--		StaticPopupDialogs["DELETE_SHARD_SET"] = {
--			text = string.format(SHL["DELETE_SET_CONFIRM"], deleteName),
--			button1 = ACCEPT,--"Yes",
--			button2 = CANCEL,--"No",
--			OnAccept = function()
--				DSH:DeleteSet(deleteName)
--			end,
--			timeout = 0,
--			whileDead = true,
--			hideOnEscape = true,
--			preferredIndex = 3, 
--		}

--		StaticPopup_Show("DELETE_SHARD_SET");
	
--	end)
--	--frame:SetPoint("LEFT", button, "RIGHT", 2, 0)
--	return frame
--end

--local function createSetButton(i)
--	DSH.Delete = DSH.Delete or createSetDeleteButton()
--	local button = CreateFrame("Button", nil, i == 1 and DSH.SetC or DSH.setButtons[i-1], "BackdropTemplate")

--	button:SetPoint("TOPLEFT", DSH.SetC, "TOPLEFT", 5 + SET_BUTTON_HEIGHT, -1*((SET_BUTTON_HEIGHT*(i-1))+5))
	
--	button:SetHeight(SET_BUTTON_HEIGHT)
--	DSH:FormatFrame(button, true)

--	local icon = button:CreateTexture(nil, 'ARTWORK')
--	icon:SetPoint('RIGHT', button, "LEFT", -3, 0)
--	icon:SetSize(.9*SET_BUTTON_HEIGHT, .9*SET_BUTTON_HEIGHT)
--	icon:SetTexCoord(0.08, 0.92, 0.08, 0.92)
--	button.Icon = icon

--	button:SetBackdropColor (0, 0, 0, 0)
--	button:SetBackdropBorderColor (0, 0, 0, 0)
--	button:SetScript("OnClick", function() DSH:SetButtonPress(button) end)
--	button:SetScript("OnEnter", function() DSH:SetButtonMouseover(true, button) end)
--	button:SetScript("OnLeave", function() DSH:SetButtonMouseover(false, button) end)
--	button:SetText(" ")
--	local fontStr = button:GetFontString()
--	fontStr:SetPoint("LEFT", button, "LEFT")
--	return button
--end

--DSH.loadColor = "|cFFF6A504"

--local function UpdateSetButton(i, name, match)
--	DSH.setButtons[i] = DSH.setButtons[i] or createSetButton(i)
	
--	local button = DSH.setButtons[i]
	
--	if match then
--		button:SetText("|cFF5DFF01"..name)
--	else
--		button:SetText(name)
--	end

--	local texture = setIcons[DSH.db.char.sets[name].icon]
	
--	if texture then
--		button.Icon:SetDesaturated(false)
--	else
--		button.Icon:SetDesaturated(true)
--		texture = "Interface\\Icons\\inv_misc_gem_variety_02"
--	end
	
--	button.Icon:SetTexture(texture)
	
--	button:SetWidth(button:GetTextWidth())
--	button.setName = name
--	button:Show()
--end

--[[
function EF:BAG_UPDATE_DELAYED()
	if GetItemCount(EF.WaitingID) == 0 then return end
	shardRemovalSuccessful()
	EF:UnregisterEvent("BAG_UPDATE_DELAYED")
end

function DSH:InputTextChanged()
	if DSH.Input:GetText() == "" then
		DSH.Save:SetText("|cFF393c3d"..SHL["SAVE_SET"])
		DSH.Save:Disable()
	elseif DSH.db.char.sets[DSH.Input:GetText()] then
		DSH.Save:Enable()
		DSH.Save:SetText(SHL["UPDATE_SET"])
	else
		DSH.Save:Enable()
		DSH.Save:SetText(SHL["SAVE_SET"])
	end
end

function DSH:GetCurrentShardSet()
	local setShards = {shards = {}, key = {}}
	for s = 1, 10 do
		local itemLink = GetInventoryItemLink("player", s)
		local gemID = DSH:GetGemID(itemLink)
		if DSH:IsDominationShard(gemID) then
			local gemName = shardIDs[tonumber(gemID)]
			table.insert(setShards.shards, gemName)
		end
	end
	--Convert equipped shards into unique key for easy comparison later
	local key = ""
	
	for _, name in pairs(shardOrder) do
		if tContains(setShards.shards, name) then
			key = key .. "1"
		else
			key = key .. "0"
		end
	end
	
	setShards.key = tonumber(key, 2)
	return setShards
end

function DSH:OpenFirstDomSocketItem()
	for s = 1, 10 do
		SocketInventoryItem(s)
		if GetSocketTypes(1) == "Domination" then
			--DSH:UpdateSetButtons()
			return
		end
	end
	CloseSocketInfo();
end

local function numberToBinStr(x)
	ret=""
	while x~=1 and x~=0 do
		ret=tostring(x%2)..ret
		x=math.modf(x/2)
	end
	ret=tostring(x)..ret
	
	while #ret < 9 do
		ret = "0"..ret
	end
	
	return ret
end

local function getShardsFromKey(key)
	local gemString = numberToBinStr(key)
	local shardsInSet = {}
	for i = 1, #gemString do
		if string.sub(gemString, i, i) == "1" then
			table.insert(shardsInSet, shardOrder[i])
		end
	end
	return shardsInSet
end

--Disable the remove button while the chisel is on CD
function EF:UNIT_SPELLCAST_SENT(unit, _, _, spellID)
	if unit ~= "player" then return end
	--Chisel cast sent
	if spellID == 358498 then
		if DSH.RemoveSetButton then
			EF:RegisterEvent("CHAT_MSG_LOOT")
			--Take the current text "Remove [shard]" and makes it grey, disable button"
			DSH.RemoveSetButton:SetText("|cFF6C6C6C"..DSH.RemoveSetButton.text)
			DSH.RemoveSetButton:Disable()
			C_Timer.After(1.3, function()
				--You can't change secure buttons in combat so exit if in combat,
				if InCombatLockdown() then return end
				if DSH.RemoveSetButton:IsVisible() then
					DSH.RemoveSetButton:SetText(DSH.loadColor..DSH.RemoveSetButton.text)
					DSH.RemoveSetButton:SetWidth(DSH.RemoveSetButton:GetTextWidth() + 10)
					DSH:UpdateSetRemoveButtonPosition()
				end
				DSH.RemoveSetButton:Enable()
			end)
		end
	end
end


local	SelfItemPattern = _G.LOOT_ITEM_PUSHED_SELF 
		SelfItemPattern = SelfItemPattern:gsub('%%s', '(.+)')

local function shardRemovalSuccessful()
	DSH.checkForGems = true
	if DSH.GBC and DSH.GBC:IsShown() and DSH.GBC.isDomination then
		DSH:InitGemButtons(true, DSH.GBC.isSlotContainer)
	else
		--call this to update LDB Text
		-- DSH:UpdateSetContainer()
	end
	dbpr("REMOVE_SUCCESS")
	EF:UnregisterEvent('CHAT_MSG_LOOT')
	DSH:UpdateRemoveGemButton()
end

function EF:CHAT_MSG_LOOT(...)
	dbpr("chat_msg_loot")
	--Searching chat to make sure the removal was successful. Overkill?
	local message, _, _, _, looter = ...;
	local lootedItem = message:match(SelfItemPattern)
	if not lootedItem then return end
	local _, itemID = strsplit(":", lootedItem)
	itemID = tonumber(itemID)
	if shardIDs[itemID] then
]]--
		--if DSH.removeShards and DSH.removeShards[shardIDs[itemID]] then
		--	DSH.removeShards[shardIDs[itemID]] = nil
		--end
--[[		
		if GetItemCount(itemID) == 0 then
			EF.WaitingID = itemID
			EF:RegisterEvent("BAG_UPDATE_DELAYED")
		else
			--dbpr("msg was delayed")
			shardRemovalSuccessful()
		end
		
	end
end


function DSH:LoadSelectedSet()
	if not DSH.loadingSet then return end
	dbpr("SET LOADED")
	EF:UnregisterEvent("CHAT_MSG_LOOT")
	
	if DSH.currentSet.key == DSH.db.char.sets[DSH.loadingSet].key then
		return
	end
	
	DSH:UpdateGemsInBags()

	local missing = {}
	local shardsInSet = getShardsFromKey(DSH.db.char.sets[DSH.loadingSet].key)
	local socketStartedOpen
	if ItemSocketingFrame and ItemSocketingFrame:IsShown() then socketStartedOpen = true end
	
	for _, shard in pairs(shardsInSet) do
		if not tContains(DSH.currentSet.shards, shard) then
			table.insert(missing, shard)
		end
	end
	
	local extraSlot = false
	
	--LUA has no continue so this looks like shit
	for s = 1, 10 do
		SocketInventoryItem(s)
		local socketingItem = GetSocketItemInfo()
		local inventoryItem = GetInventoryItemLink("player", s)
		
		if inventoryItem then
			local itemName = select(1, GetItemInfo(inventoryItem))

			if itemName == socketingItem then
				if GetSocketTypes(1) == "Domination" and not GetExistingSocketLink(1) and not DSH:GetGemID(inventoryItem) then
					local nextGem = missing[1]
					if nextGem then
						if not DSH.gemsInBags[nextGem] then
							dbpr("Something went wrong?")
							DSH.loadingSet = nil
							return
						end
						
						DSH:UseContainerItemByID(DSH.gemsInBags[nextGem].itemID)
						
						if not GetNewSocketInfo(1) then
							DSH.ErrorCount = DSH.ErrorCount + 1
							if DSH.ErrorCount < 2 then
								dbpr("ERROR #", DSH.ErrorCount, ". Trying Again")
								CloseSocketInfo()
								C_Timer.After(0.1, function()
									CloseSocketInfo()
									DSH:LoadSelectedSet()
								end)
							else
								DSH.loadingSet = nil
								dbpr("Failed...")
							end
							return
						end

						AcceptSockets()
						table.remove(missing, 1)
					
					else
						extraSlot = true
					end
				end
			end
		end
	end
	
	if next(missing) then 
		DSHPrint(SHL["ERROR_LOADING"])
	elseif extraSlot then
		DSHPrint(SHL["EXTRA_SLOT"])
	end
	
	CloseSocketInfo();
	
	if socketStartedOpen then 
		DSH:OpenFirstDomSocketItem()
		dbpr("started open")
	end
	
	C_Timer.After(1, function() DSH.loadingSet = nil end)
end

local function getIconNumFromShardString(shardString)
	for i = 3, 9, 3 do
		if string.sub(shardString, i-2, i) == "111" then
			return (i/3)
		end
	end
	return 0
end

function DSH:SaveButtonPress()
	if DSH.Input:GetText() ~= "" then
		local setName = DSH.Input:GetText()
		
		local shardString = numberToBinStr(DSH.currentSet.key)
		
		local icon = getIconNumFromShardString(shardString)
		
		DSH.db.char.sets[setName] = {key = DSH.currentSet.key, icon = icon}
		DSH.Input:SetText("")
	end
	-- if not DSH.SetC then DSH:CreateSetContainer() end
	DSH:UpdateSetContainer()
	DSH:UpdateSetButtons()
end

--If the remove button is being used when combat starts hide it and cancel set load
--enable it in case it was disabled from being used (the timer that usually renables can't reenable it in combat)
function EF:PLAYER_REGEN_DISABLED()
	if DSH.RemoveSetButton then
		DSH.RemoveSetButton:Enable()
		DSH.RemoveSetButton:Hide()
		DSH.loadingSet = nil
	end
	if DSH.LDBOpen then
		DSH.LDBOpen = false
		DSH.SetC:Hide()
		DSH:UpdateSetContainer()
	end
	
end


local function getFirstSetMatch()
	for setName, setInfo in DSH:PairsByKeys(DSH.db.char.sets) do
		local match = false
		if setInfo.key == DSH.currentSet.key then
			return setName
		end
	end
end

function DSH:ItemToolTip(addonDisenchanting)
	local _, itemLink = GameTooltip:GetItem()
	if not itemLink then return end
	if DSH.ItemBlockFrame then DSH.ItemBlockFrame:Hide() end
	if (SpellIsTargeting() and SpellCanTargetItem()) or addonDisenchanting then
	
		if IsCurrentSpell(13262) or addonDisenchanting then --Disenchanting
			local gemID = DSH:GetGemID(itemLink)
			if gemID and DSH:IsDominationShard(gemID) then
				--PlaySound(8959)
				showItemBlockFrame()
				GameTooltip:SetText("|cffFF0000"..SHL["DISENCHANT_MSG"]);
				DSHPrint(SHL["DISENCHANT_MSG"])
			end
		elseif IsCurrentItem(187532) then --Using chisel

			if DSH:GetBagFreeSpace() < 1 then
				--PlaySound(8959)
				showItemBlockFrame()
				GameTooltip:SetText("|cffFF0000"..SHL["BAG_FULL_CHISEL"]);
				DSHPrint(SHL["BAG_FULL_CHISEL"])
			end
		end
	end
end

local function showItemBlockFrame()
	EF:RegisterEvent("CURRENT_SPELL_CAST_CHANGED")
	DSH.ItemBlockFrame = DSH.ItemBlockFrame or DSH.CreateBlockFrame(EF)
	local itemButton = GetMouseFocus()
	DSH.ItemBlockFrame:SetPoint("CENTER", itemButton, "CENTER", 0, 0)
	
	--Trying to fix problem with adibags scale
	local scale = getCorrectFrameScale(itemButton)

	DSH.ItemBlockFrame:SetSize(itemButton:GetWidth()* scale, itemButton:GetHeight()*scale)
	DSH.ItemBlockFrame:Show()
	DSH.ItemBlockFrame:SetFrameStrata("FULLSCREEN_DIALOG")
end

function EF:CURRENT_SPELL_CAST_CHANGED(cancelled)
	if cancelled then
		EF:UnregisterEvent("CURRENT_SPELL_CAST_CHANGED")
		if DSH.ItemBlockFrame then DSH.ItemBlockFrame:Hide() end
	end
end

local setIcons = {1003591, 1392550, 457655}
local shardOrder = {"Bek", "Jas", "Rev", "Cor", "Kyr", "Tel", "Dyz", "Oth", "Zed"}
local shardIDs = {
	--Blood
		[187057] = "Bek", [187284] = "Bek", [187293] = "Bek", [187302] = "Bek", [187312] = "Bek",
		[187059] = "Jas", [187285] = "Jas", [187294] = "Jas", [187303] = "Jas", [187313] = "Jas",
		[187061] = "Rev", [187286] = "Rev", [187295] = "Rev", [187304] = "Rev", [187314] = "Rev",
	--Unholy
		[187073] = "Dyz", [187290] = "Dyz", [187299] = "Dyz", [187308] = "Dyz", [187318] = "Dyz",
		[187076] = "Oth", [187291] = "Oth", [187300] = "Oth", [187309] = "Oth", [187319] = "Oth",
		[187079] = "Zed", [187292] = "Zed", [187301] = "Zed", [187310] = "Zed", [187320] = "Zed",
	--Frost
		[187063] = "Cor", [187287] = "Cor", [187296] = "Cor", [187305] = "Cor", [187315] = "Cor",
		[187065] = "Kyr", [187288] = "Kyr", [187297] = "Kyr", [187306] = "Kyr", [187316] = "Kyr",
		[187071] = "Tel", [187289] = "Tel", [187298] = "Tel", [187307] = "Tel", [187317] = "Tel",
}

function DSH:ToggleRemoveButton(show)
	
	DSH.RemoveButton = DSH.RemoveButton or DSH:CreateRemoveButton(DSH.DC)
	DSH.RemoveButton:SetPoint("LEFT", ItemSocketingSocket1, "RIGHT" , 5, 0)
	
	local itemName = GetSocketItemInfo()
	
	if DSH.lockedSocketSlot and itemName then
		--item in bag
		DSH.RemoveButton:SetAttribute("macrotext", "/use item:187532\n/script HideUIPanel(ItemSocketingFrame)\n/use "..itemName.."\n/script SocketContainerItem("..DSH.lockedSocketBag..", "..DSH.lockedSocketSlot..")")
	else
		--item equipped
		DSH.RemoveButton:SetAttribute("macrotext", "/use item:187532\n/script HideUIPanel(ItemSocketingFrame)\n/use "..DSH.lockedSocketBag.."\n/script SocketInventoryItem("..DSH.lockedSocketBag..")")
	end
	
	if show then
		DSH.RemoveButton:Show()
	else
		DSH.RemoveButton:Hide()
	end

end

function DSH:CreateRemoveButton(parent)
	if InCombatLockdown() then return end

	local button = CreateFrame("Button", nil, parent, "SecureActionButtonTemplate, BackdropTemplate");
	--button:SetPoint("LEFT", ItemSocketingSocket1, "RIGHT" , 5, 0)
	button:SetNormalFontObject("GameFontNormal")
	button:SetHeight(40)
	
	DSH:FormatFrame(button, true)

	button:SetText(SHL["REMOVE"])
	button:SetWidth(button:GetTextWidth() + 10)
	
	button:SetAttribute("type", "macro")
	button:Hide()
	return button
end

function EF:DELETE_ITEM_CONFIRM()
	local gemID = DSH:GetGemID(DSH.lockedItem)
	local _, itemID = strsplit(":", DSH.lockedItem)
	if (gemID and DSH:IsDominationShard(gemID)) or (itemID and DSH:IsDominationShard(itemID)) then
		ClearCursor()
		DSHPrint(SHL["DELETION_BLOCKED"])
	end
end

]]--


local ADDON_TITLE = DSH.TITLE --GetAddOnMetadata(addonName, "Title")
local config = LibStub("AceConfig-3.0")
local dialog = LibStub("AceConfigDialog-3.0")

local function createconfig()
	local generalOptions
	
	local options = {}
	options.type = "group"
	options.name = "DSH"
	options.args = {}
	
	local function get(info) --don't feel like fixing this to be more dynamic atm
		local ns,opt = string.split(".", info.arg)	

		local val = DSH.db.profile[ns][opt]

		if type(val) == "table" then
			return unpack(val)
		else
			return val
		end

	end

	local function set(info, arg1, arg2, arg3, arg4)
		local ns,opt,opt2 = string.split(".", info.arg)
		if arg2 then
			local entry = DSH.db.profile[ns][opt]
			entry[1] = arg1
			entry[2] = arg2
			entry[3] = arg3
			entry[4] = arg4
		else
			DSH.db.profile[ns][opt] = arg1
			DSH:UpdateSlotButtons()
		end
	
	end

	
	generalOptions = {
		type = "group",
		order = 1,
		--inline = true,
		name = format("|cFFAAD372%s|r %s", DSH.TITLE, DSH.VERSION),
		childGroups = "tab",
		get = get,
		set = set,
		args = {
			quickslots = {
				order = 1,
				type = "group",
				inline = true,
				name = SHL["O_QUICK_SLOT"],
				childGroups = "tab",
				args = {
					enable = {
						order = 2,
						width = .75,
						type = "toggle",
						name = SHL["ENABLE"],
						desc = SHL["ENABLE_DESC"],
						arg = "quickslots.enable",
					},
					emptyonly = {
						order = 3,
						type = "toggle",
						name = SHL["ALWAYS_EMPTY"],
						desc = SHL["ALWAYS_EMPTY_DESC"],
						arg = "quickslots.alwaysempty",
					},
					stayopen = {
						order = 4,
						type = "toggle",
						name = SHL["STAY_OPEN"],
						desc = SHL["STAY_OPEN_DESC"],
						arg = "quickslots.stayopen",
					},						
				},
			},
			socketwindow = {
				order = 2,
				type = "group",
				inline = true,
				name = SHL["SOCKET_WINDOW"],
				childGroups = "tab",
				args = {
					auto = {
						order = 2,
						type = "toggle",
						name = SHL["AUTO_ACCEPT"],
						desc = SHL["AUTO_ACCEPT_DESC"],
						arg = "socketwindow.autoaccept",
					},			
				},
			},
		},
	}
	
	return generalOptions
end

local function createBlizzOptions()
	local options = createconfig()

	-- General Options
	config:RegisterOptionsTable("DSH", options)
	--local blizzPanel = config:AddToBlizOptions("PLT-General", options.args.general.name, "PLT")
	local blizzPanel = dialog:AddToBlizOptions("DSH", ADDON_TITLE)
	
	return blizzPanel

end

local addonLoaded = CreateFrame("FRAME") -- Variables
 addonLoaded:RegisterEvent("ADDON_LOADED") --RegisterAddonLoad
 addonLoaded:SetScript("OnEvent",function(self, event, arg1)
	if arg1 == "_ShiGuang" then
		createBlizzOptions()
	end
end)