local MAJOR_VERSION = "LibDropMenu-1.0"
local MINOR_VERSION = 1

local lib = LibStub:NewLibrary(MAJOR_VERSION, MINOR_VERSION)
if not lib then return end

if not BluePrint then function BluePrint() end end

local _G = _G
local type = _G.type
local pairs = _G.pairs
local tinsert = _G.tinsert
local sort = _G.sort
local unpack = _G.unpack
local GetCursorPosition = _G.GetCursorPosition
local GetScreenHeight = _G.GetScreenHeight
local GetScreenWidth = _G.GetScreenWidth

lib.menus = lib.menus or {}

local dummyText = CreateFrame("Frame")
dummyText = dummyText:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmallLeft")
dummyText:Hide()

local uiScale
local menuframe = {}
local menuType = { ["header"] = 1, ["group"] = 1, ["text"] = 1, ["range"] = 1, ["toggle"] = 1, ["execute"] = 1, ["color"] = 1, ["select"] = 1, ["input"] = 1, ["keybinding"] = 1, }
local clickFunctions = {
	toggle = function(self)
		lib:RunSetFunction(self.set, not lib:RunGetFunction(self.get))
		if lib:RunGetFunction(self.get) then
			self.icon:Show()
		else
			self.icon:Hide()
		end
	end,
	color = function(self)
		ColorPickerFrame.func = function()
			local r, g, b = ColorPickerFrame:GetColorRGB()
			self.expand:SetVertexColor(r, g, b)
			if self.hasAlpha then
				lib:RunSetFunction(self.set, r, g, b, 1 - OpacitySliderFrame:GetValue())
			else
				lib:RunSetFunction(self.set, r, g, b)
			end
		end
		ColorPickerFrame.hasOpacity = self.hasAlpha
		if self.hasAlpha then
			ColorPickerFrame.opacityFunc = ColorPickerFrame.func
			ColorPickerFrame.opacity = self.colors[4] and (1 - self.colors[4]) or 0
			self.previousValues = { r = self.colors[1], g = self.colors[2], b = self.colors[3], a = self.colors[4] }
		else
			ColorPickerFrame.opacityFunc = nil
			ColorPickerFrame.opacity = nil
			self.previousValues = { r = self.colors[1], g = self.colors[2], b = self.colors[3] }
		end
		ColorPickerFrame.cancelFunc = function()
			lib:RunSetFunction(self.set, self.previousValues.r, self.previousValues.g, self.previousValues.b, self.previousValues.a)
			self.previousValues = nil
		end
		ColorPickerFrame:SetColorRGB(self.colors[1], self.colors[2], self.colors[3])
		ShowUIPanel(ColorPickerFrame)
	end,
}

local function GetTextWidth(text)
	if text == nil or text == "" then
		return 0
	end
	dummyText:SetText(text)
	return dummyText:GetWidth() + 2
end


------- User Function -----------

function lib:RegisterMenu(name, menu)
	if type(name) == "string" and type(menu) == "table" and menu.type == "group" and type(menu.args) == "table" then
		lib.menus[name] = menu
	end
end

function lib:UnregisterMenu(name)
	if type(name) == "string" then
		lib.menus[name] = nil
	end
end

function lib:IsOpen(name)
	return name and self.OpenedMenu == name
end

function lib:OpenMenu(name, point, relative, relativePoint, xoffset, yoffset, minWidth, skipTitle)
	if type(name) == "string" and lib.menus[name] then
		self.skipTitle = skipTitle
		self.OpenedMenu = name
		self.minWidth = minWidth
		self:CreateMenu(name, self:GetList(self.menus[name].args, 1), 1)
		LibDropMenu1:ClearAllPoints()
		if point == "CURSOR" or point == "cursor" then
			uiScale = UIParent:GetScale()
			xoffset, yoffset = GetCursorPosition()
			xoffset = xoffset / uiScale
			yoffset = -(GetScreenHeight() * uiScale - yoffset) / uiScale
			point, relative, relativePoint = "TOPLEFT", UIParent, "TOPLEFT"
		end
		LibDropMenu1:SetPoint(point, relative, relativePoint, xoffset, yoffset)
	end
end

function lib:CloseMenu(level)
	if not(type(level) == "number" and level > 0) then
		level = 1
	end
	for i = level, #menuframe do
		menuframe[i]:Hide()
	end
	if level == 1 then
		if menuframe.input then
			menuframe.input:Hide()
		end
		if menuframe.range then
			menuframe.range:Hide()
		end
		if menuframe.select then
			menuframe.select:Hide()
		end
		self.OpenedMenu = nil
		self.minWidth = 1
	else
		if menuframe.input and menuframe.input.openedLevel == level then
			menuframe.input:Hide()
		end
		if menuframe.range and menuframe.range.openedLevel == level  then
			menuframe.range:Hide()
		end
		if menuframe.select and menuframe.select.openedLevel == level  then
			menuframe.select:Hide()
		end
	end
end

function lib:Reflash()
	if self.OpenedMenu then
		local menu = lib.menus[self.OpenedMenu].args
		for i = 1, #menuframe do
			if menuframe[i]:IsVisible() then
				if i > 1 then
					menu = menu[menuframe[i].linked].args
				end
				self:CreateMenu(menuframe[i].linked, lib:GetList(menu, i), i, true)
			else
				break
			end
		end
	end
end

-------- Library Function : Don't Run it! -----------

function lib:ExecuteFunction(func)
	if type(func) == "function" then
		func()
	elseif type(func) == "string" and self.OpenedMenu and lib.menus[self.OpenedMenu].handler and type(lib.menus[self.OpenedMenu].handler[func]) == "function" then
		lib.menus[self.OpenedMenu].handler[func](lib.menus[self.OpenedMenu].handler)
	end
end

function lib:RunSetFunction(func, ...)
	if type(func) == "function" then
		func(...)
	elseif type(func) == "string" and self.OpenedMenu and lib.menus[self.OpenedMenu].handler and type(lib.menus[self.OpenedMenu].handler[func]) == "function" then
		lib.menus[self.OpenedMenu].handler[func](lib.menus[self.OpenedMenu].handler, ...)
	end
end

function lib:RunGetFunction(func)
	if type(func) == "function" then
		return func()
	elseif type(func) == "string" and self.OpenedMenu and lib.menus[self.OpenedMenu].handler and type(lib.menus[self.OpenedMenu].handler[func]) == "function" then
		return lib.menus[self.OpenedMenu].handler[func](lib.menus[self.OpenedMenu].handler)
	else
		return func
	end
end

function lib:GetList(menu, level)
	local list = {}
	for p, v in pairs(menu) do
		if menuType[v.type or "*"] then
			if not((type(v.hidden) == "function" and v.hidden()) or v.hidden) then
				v.elementName = p
				tinsert(list, v)
			end
		end
	end
	sort(list, function(a, b)
		if a.order and b.order then
			if a.order > 0 and b.order > 0 then
				return a.order < b.order
			elseif a.order < 0 and b.order < 0 then
				return a.order < b.order
			elseif a.order > 0 then
				return true
			else
				return false
			end
		elseif a.order then
			if a.order > 0 then
				return false
			else
				return true
			end
		elseif b.order then
			if b.order > 0 then
				return false
			else
				return true
			end
		end
	end)
	if level == 1 then
		tinsert(list, {
			type = "execute",
			name = "닫기",
			desc = "현재 메뉴를 닫습니다.",
			func = function()
				lib:CloseMenu(1)
			end,
		})
	end
	return list
end

local DisableButton = function(button)
	button.skipEnter = true
	button:SetScript("OnClick", nil)
	button:SetScript("PostClick", nil)
	button.text:SetTextColor(GRAY_FONT_COLOR.r, GRAY_FONT_COLOR.g, GRAY_FONT_COLOR.b)
	SetDesaturation(button.icon, true)
	SetDesaturation(button.expand, true)
end

local EnableButton = function(button)
	button.skipEnter = nil
	SetDesaturation(button.icon)
	SetDesaturation(button.expand)
end

function lib:CreateMenu(name, list, level, donot)
	if not menuframe[level] then
		menuframe[level] = CreateFrame("Frame", "LibDropMenu"..level, UIParent)
		menuframe[level]:Hide()
		menuframe[level]:SetFrameStrata("DIALOG")
		menuframe[level]:SetFrameLevel(level)
		menuframe[level]:SetClampedToScreen(true)
		menuframe[level]:SetBackdrop(GameTooltip:GetBackdrop())
		menuframe[level]:SetBackdropColor(GameTooltip:GetBackdropColor())
		menuframe[level].selecter = menuframe[level]:CreateTexture(nil, "OVERLAY")
		menuframe[level].selecter:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight")
		menuframe[level].selecter:SetBlendMode("ADD")
		menuframe[level].selecter:SetAlpha(0.7)
		menuframe[level].element = {}
		menuframe[level]:SetScript("OnShow", function(self) self.selecter:Hide() end)
	end
	menuframe[level].name = name
	menuframe[level]:SetID(level)
	if level == 1 and lib.menus[lib.OpenedMenu].name and not self.skipTitle then
		tinsert(list, 1, {
			type = "title",
			name = lib.menus[lib.OpenedMenu].name,
		})
	end
	local num = #list
	local maxWidth = 1
	local setMinWidth = nil
	for i = 1, num do
		lib:CreateButton(level, i)
		maxWidth = max(maxWidth, GetTextWidth(list[i].name or list[i].guiName))
		menuframe[level].element[i].text:SetText(list[i].name or list[i].guiName)
		menuframe[level].element[i].type = list[i].type
		menuframe[level].element[i].get = list[i].get
		menuframe[level].element[i].set = list[i].set
		menuframe[level].element[i].min = list[i].min
		menuframe[level].element[i].max = list[i].max
		menuframe[level].element[i].step = list[i].step
		menuframe[level].element[i].hasAlpha = list[i].hasAlpha
		menuframe[level].element[i].elementName = list[i].elementName
		menuframe[level].element[i].desc = list[i].desc
		menuframe[level].element[i].validate = list[i].validate
		menuframe[level].element[i].values = list[i].values
		if menuframe[level].element[i].type == "header" then
			menuframe[level].element[i].type = "title"
		end
		if list[i].usage and list[i].usage ~= "" then
			if list[i].desc then
				menuframe[level].element[i].desc = menuframe[level].element[i].desc.."\n\n사용법:"..list[i].usage
			end
		end
		if list[i].type == "title" then
			menuframe[level].element[i].text:SetTextColor(1, 0.82, 0)
		else
			menuframe[level].element[i].text:SetTextColor(1, 1, 1)
		end
		if list[i].type == "group" then
			setMinWidth = true
			menuframe[level].element[i].expand:SetTexture("Interface\\ChatFrame\\ChatFrameExpandArrow")
			menuframe[level].element[i].expand:SetVertexColor(1, 1, 1)
			menuframe[level].element[i].expand:Show()
			menuframe[level].element[i].swatchBg:Hide()
			menuframe[level].element[i].colors = nil
		elseif list[i].type == "color" then
			setMinWidth = true
			if type(list[i].get) ~= "function" then
				list[i].get = function() return 1, 1, 1, 1 end
			end
			menuframe[level].element[i].colors = { lib:RunGetFunction(list[i].get) }
			menuframe[level].element[i].swatchBg:Show()
			menuframe[level].element[i].expand:SetTexture("Interface\\ChatFrame\\ChatFrameColorSwatch")
			menuframe[level].element[i].expand:SetVertexColor(menuframe[level].element[i].colors[1], menuframe[level].element[i].colors[2], menuframe[level].element[i].colors[3])
			menuframe[level].element[i].expand:Show()
		else
			menuframe[level].element[i].colors = nil
			menuframe[level].element[i].expand:Hide()
			menuframe[level].element[i].swatchBg:Hide()
		end
		if list[i].type == "toggle" then
			menuframe[level].element[i].icon:SetTexture("Interface\\Buttons\\UI-CheckBox-Check")
			if lib:RunGetFunction(list[i].get) then
				menuframe[level].element[i].icon:Show()
			else
				menuframe[level].element[i].icon:Hide()
			end
		elseif list[i].icon then
			menuframe[level].element[i].icon:SetTexture(list[i].icon)
			menuframe[level].element[i].icon:Show()
		else
			menuframe[level].element[i].icon:Hide()
		end
		if list[i].type == "input" or (list[i].type == "text" and not list[i].validate) then
			menuframe[level].element[i].expand:SetTexture("Interface\\ChatFrame\\ChatFrameExpandArrow")
			menuframe[level].element[i].expand:Show()
			menuframe[level].element[i].spopen = "input"
			self:CreateInputFrame()
		elseif list[i].type == "keybinding" or (list[i].type == "text" and list[i].validate == "keybinding") then
			menuframe[level].element[i].expand:SetTexture("Interface\\ChatFrame\\ChatFrameExpandArrow")
			menuframe[level].element[i].expand:Show()
			menuframe[level].element[i].spopen = "keybinding"
			self:CreateKeybindingFrame()
		elseif list[i].type == "select" or list[i].type == "text" then
			menuframe[level].element[i].expand:SetTexture("Interface\\ChatFrame\\ChatFrameExpandArrow")
			menuframe[level].element[i].expand:Show()
			menuframe[level].element[i].spopen = "select"
			self:CreateSelectFrame()
			menuframe.select.table = list[i].type == "select" and "values" or "validate"
		elseif list[i].type == "range" then
			menuframe[level].element[i].expand:SetTexture("Interface\\ChatFrame\\ChatFrameExpandArrow")
			menuframe[level].element[i].expand:Show()
			menuframe[level].element[i].spopen = "range"
			self:CreateRangeFrame()
		else
			menuframe[level].element[i].spopen = nil
		end
		if list[i].type == "execute" then
			menuframe[level].element[i]:SetScript("OnClick", function() lib:ExecuteFunction(list[i].func) end)
		else
			menuframe[level].element[i]:SetScript("OnClick", clickFunctions[list[i].type])
		end
		if menuframe[level].element[i]:GetScript("OnClick") then
			menuframe[level].element[i]:SetScript("PostClick", function() lib:Reflash() end)
		else
			menuframe[level].element[i]:SetScript("PostClick", nil)
		end
		if (type(list[i].disabled) == "function" and list[i].disabled()) or list[i].disabled then

			DisableButton(menuframe[level].element[i])
		else
			EnableButton(menuframe[level].element[i])
		end
		menuframe[level].element[i]:Show()
	end
	for i = num + 1, #menuframe[level].element do
		menuframe[level].element[i]:Hide()
	end
	if self.minWidth and setMinWidth then
		maxWidth = max(maxWidth, self.minWidth)
	end
	for i = 1, num do
		menuframe[level].element[i].text:SetWidth(maxWidth)
		menuframe[level].element[i]:SetWidth(maxWidth + 36)
	end
	menuframe[level]:SetWidth(maxWidth + 56)
	menuframe[level]:SetHeight(18 * num + 20)
	menuframe[level]:Show()
	if not donot then
		menuframe[level].selecter:Hide()
		self:CloseMenu(level + 1)
	end
	return menuframe[level]
end

function lib:SetMenuPosition(button, level)
	local m = _G["LibDropMenu"..level]
	m:ClearAllPoints()
	m:SetPoint("TOPLEFT", button, "TOPRIGHT", 8, 10)
	if GetScreenWidth() == m:GetRight() then
		m:ClearAllPoints()
		m:SetPoint("TOPRIGHT", button, "TOPLEFT", -8, 10)
	end
end

local fv

local function GetFlootingValue(n)
	n = n..""
	fv = n:find("%.") and (n:len() - n:find("%.")) or tonumber(select(2, n:match("(%d+)e%-(%d+)")) or "")
	if fv then
		return "%."..fv.."f"
	else
		return "%d"
	end
end

local buttonOnEnter = function(self)
	local parent = self:GetParent()
	if self.skipEnter then
		lib:CloseMenu(parent:GetID() + 1)
	else
		if self.type ~= "title" then
			parent.selecter:SetAllPoints(self)
			parent.selecter:Show()
		end
		if self.desc then
			GameTooltip:SetOwner(self, "ANCHOR_LEFT")
			GameTooltip:ClearLines()
			GameTooltip:AddLine(self.desc)
			GameTooltip:Show()
		end
		lib:CloseMenu(parent:GetID() + 1)
		if menuframe.input then
			menuframe.input.openedLevel = nil
			menuframe.input:Hide()
		end
		if menuframe.range then
			menuframe.range.openedLevel = nil
			menuframe.range:Hide()
		end
		if menuframe.select then
			menuframe.select.openedLevel = nil
			menuframe.select:Hide()
		end
		if self.type == "group" and self.elementName then
			if parent:GetID() == 1 then
				lib:CreateMenu(self.elementName, lib:GetList(lib.menus[lib.OpenedMenu].args[self.elementName].args, 2), 2)
				lib:SetMenuPosition(self, 2)
				lib:CloseMenu(3)
				LibDropMenu2.linked = self.elementName
			elseif parent:GetID() == 2 then
				lib:CreateMenu(self.elementName, lib:GetList(lib.menus[lib.OpenedMenu].args[LibDropMenu2.linked].args[self.elementName].args, 3), 3)
				lib:SetMenuPosition(self, 3)
				lib:CloseMenu(4)
				LibDropMenu3.linked = self.elementName
			elseif parent:GetID() == 3 then
				lib:CreateMenu(self.elementName, lib:GetList(lib.menus[lib.OpenedMenu].args[LibDropMenu2.linked].args[LibDropMenu3.linked].args[self.elementName].args, 4), 4)
				lib:SetMenuPosition(self, 4)
				lib:CloseMenu(5)
				LibDropMenu4.linked = self.elementName
			elseif parent:GetID() == 4 then
				lib:CreateMenu(self.elementName, lib:GetList(lib.menus[lib.OpenedMenu].args[LibDropMenu2.linked].args[LibDropMenu3.linked].args[LibDropMenu4.linked].args[self.elementName].args, 5), 5)
				lib:SetMenuPosition(self, 5)
				lib:CloseMenu(6)
				LibDropMenu5.linked = self.elementName
			elseif parent:GetID() == 5 then
				lib:CreateMenu(self.elementName, lib:GetList(lib.menus[lib.OpenedMenu].args[LibDropMenu2.linked].args[LibDropMenu3.linked].args[LibDropMenu4.linked].args[LibDropMenu5.linked].args[self.elementName].args, 6), 6)
				lib:SetMenuPosition(self, 6)
				lib:CloseMenu(7)
				LibDropMenu6.linked = self.elementName
			end
		elseif self.spopen == "input" then
			menuframe.input.openedLevel = parent:GetID()
			menuframe.input.editBox:SetText(lib:RunGetFunction(self.get))
			menuframe.input.editBox:SetScript("OnEnterPressed", function()
				lib:RunSetFunction(self.set, menuframe.input.editBox:GetText())
				menuframe.input:Hide()
				menuframe.input.openedLevel = nil
			end)
			menuframe.input:Show()
			lib:SetMenuPosition(self, self.spopen)
		elseif self.spopen == "range" then
			menuframe.range.openedLevel = parent:GetID()
			menuframe.range.prevValue = lib:RunGetFunction(self.get) or 0
			menuframe.range.slider.minValue, menuframe.range.slider.maxValue = type(self.min) == "function" and self.min() or self.min or 0, type(self.max) == "function" and self.max() or self.max or 1
			menuframe.range.slider.step = lib:RunGetFunction(self.step) or 0.01
			menuframe.range.slider.fv = GetFlootingValue(menuframe.range.slider.step)
			menuframe.range.slider:SetMinMaxValues(0, 1)
			menuframe.range.slider:SetValueStep(0.000000001)
			if menuframe.range.prevValue > menuframe.range.slider.maxValue then
				menuframe.range.prevValue = menuframe.range.slider.maxValue
			end
			if menuframe.range.prevValue < menuframe.range.slider.minValue then
				menuframe.range.prevValue = menuframe.range.slider.minValue
			end
			if menuframe.range.slider.maxValue <= menuframe.range.slider.minValue then
				menuframe.range.prevValue = 0
			end
			menuframe.range.slider.value = menuframe.range.prevValue
			menuframe.range.slider:SetValue(1 - (menuframe.range.slider.value - menuframe.range.slider.minValue) / (menuframe.range.slider.maxValue - menuframe.range.slider.minValue))
			menuframe.range.bottomText:SetText(menuframe.range.slider.minValue)
			menuframe.range.topText:SetText(menuframe.range.slider.maxValue)
			menuframe.range.editBox:SetText((menuframe.range.slider.fv):format(menuframe.range.slider.value))
			menuframe.range.slider:SetScript("OnValueChanged", function()
				menuframe.range.slider.value = floor(((1 - menuframe.range.slider:GetValue()) * (menuframe.range.slider.maxValue - menuframe.range.slider.minValue)) / menuframe.range.slider.step + 0.5) * menuframe.range.slider.step + menuframe.range.slider.minValue
				if menuframe.range.slider.value > menuframe.range.slider.maxValue then
					menuframe.range.slider.value = menuframe.range.slider.maxValue
				elseif menuframe.range.slider.value < menuframe.range.slider.minValue then
					menuframe.range.slider.value = menuframe.range.slider.minValue
				end
				menuframe.range.editBox:SetText((menuframe.range.slider.fv):format(menuframe.range.slider.value))
				menuframe.range.editBox:ClearFocus()
				lib:RunSetFunction(self.set, tonumber(menuframe.range.editBox:GetNumber()))
			end)
			menuframe.range.editBox:SetScript("OnEnterPressed", function()
				menuframe.range.slider.value = tonumber(menuframe.range.editBox:GetNumber()) or menuframe.range.slider.value
				if menuframe.range.slider.value > menuframe.range.slider.maxValue then
					menuframe.range.slider.value = menuframe.range.slider.maxValue
				end
				if menuframe.range.slider.value < menuframe.range.slider.minValue then
					menuframe.range.slider.value = menuframe.range.slider.minValue
				end
				if menuframe.range.slider.maxValue <= menuframe.range.slider.minValue then
					menuframe.range.slider:SetValue(0)
				else
					menuframe.range.slider:SetValue(1 - (menuframe.range.slider.value - menuframe.range.slider.minValue) / (menuframe.range.slider.maxValue - menuframe.range.slider.minValue))
				end
				menuframe.range.editBox:SetText((menuframe.range.slider.fv):format(menuframe.range.slider.value))
				menuframe.range.editBox:ClearFocus()
				lib:RunSetFunction(self.set, tonumber(menuframe.range.editBox:GetNumber()))
			end)
			menuframe.range:Show()
			menuframe.range.editBox:ClearFocus()
			lib:SetMenuPosition(self, self.spopen)
		elseif self.spopen == "select" then
			menuframe.select.openedLevel = parent:GetID()
			menuframe.select:Show()
			lib.selectElement = self
			lib:SetSelectFrame()
			lib:SetMenuPosition(self, self.spopen)
		end
	end
end

local buttonOnLeave = function(self)
	local parent = self:GetParent()
	GameTooltip:Hide()
	if not ((menuframe[parent:GetID() + 1] and menuframe[parent:GetID() + 1]:IsVisible()) or (menuframe.input and menuframe.input:IsVisible()) or (menuframe.range and menuframe.range:IsVisible()) or (menuframe.select and menuframe.select:IsVisible())) then
		parent.selecter:Hide()
	end
end

function lib:CreateButton(level, id)
	if not menuframe[level].element[id] then
		menuframe[level].element[id] = CreateFrame("Button", menuframe[level]:GetName().."_"..id, menuframe[level])
		menuframe[level].element[id]:SetHeight(18)
		menuframe[level].element[id].icon = menuframe[level].element[id]:CreateTexture(nil, "ARTWORK")
		menuframe[level].element[id].icon:SetWidth(16)
		menuframe[level].element[id].icon:SetHeight(16)
		menuframe[level].element[id].icon:SetTexCoord(0.07, 0.93, 0.07, 0.93)
		menuframe[level].element[id].icon:SetPoint("LEFT", menuframe[level].element[id], "LEFT", 0, 0)
		menuframe[level].element[id].text = menuframe[level].element[id]:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmallLeft")
		menuframe[level].element[id].text:SetPoint("LEFT", menuframe[level].element[id], "LEFT", 18, 0)
		menuframe[level].element[id].expand = menuframe[level].element[id]:CreateTexture(nil, "OVERLAY")
		menuframe[level].element[id].expand:SetWidth(16)
		menuframe[level].element[id].expand:SetHeight(16)
		menuframe[level].element[id].expand:SetTexture("Interface\\ChatFrame\\ChatFrameExpandArrow")
		menuframe[level].element[id].expand:SetPoint("LEFT", menuframe[level].element[id].text, "RIGHT", 2, 0)
		menuframe[level].element[id].swatchBg = menuframe[level].element[id]:CreateTexture(nil, "BACKGROUND")
		menuframe[level].element[id].swatchBg:SetWidth(14)
		menuframe[level].element[id].swatchBg:SetHeight(14)
		menuframe[level].element[id].swatchBg:SetTexture(1, 1, 1)
		menuframe[level].element[id].swatchBg:SetPoint("CENTER", menuframe[level].element[id].expand, "CENTER", 0, 0)
		menuframe[level].element[id]:SetScript("OnEnter", buttonOnEnter)
		menuframe[level].element[id]:SetScript("OnLeave", buttonOnLeave)
		menuframe[level].element[id]:SetID(id)
		menuframe[level].element[id]:ClearAllPoints()
		if id == 1 then
			menuframe[level].element[id]:SetPoint("TOPLEFT", menuframe[level], "TOPLEFT", 10, -10)
		else
			menuframe[level].element[id]:SetPoint("TOPLEFT", menuframe[level].element[id - 1], "BOTTOMLEFT", 0, 0)
		end
	end
	return menuframe[level].element[id]
end

function lib:CreateInputFrame()
	if not menuframe.input then
		menuframe.input = CreateFrame("Frame", "LibDropMenuinput", UIParent)
		menuframe.input:SetFrameStrata("DIALOG")
		menuframe.input:SetToplevel(true)
		menuframe.input:SetClampedToScreen(true)
		menuframe.input:SetBackdrop(GameTooltip:GetBackdrop())
		menuframe.input:SetBackdropColor(GameTooltip:GetBackdropColor())
		menuframe.input:SetWidth(200)
		menuframe.input:SetHeight(40)
		menuframe.input.editBox = CreateFrame("EditBox", nil, menuframe.input)
		menuframe.input.editBox:SetFontObject("ChatFontNormal")
		menuframe.input.editBox:SetWidth(160)
		menuframe.input.editBox:SetHeight(13)
		menuframe.input.editBox:SetPoint("CENTER", menuframe.input, "CENTER", 0, 0)
		menuframe.input.editBox:SetScript("OnEscapePressed", function()
			menuframe.input.editBox:ClearFocus()
			menuframe.input:Hide()
			menuframe.input.openedLevel = nil
		end)
		local left = menuframe.input.editBox:CreateTexture(nil, "BACKGROUND")
		left:SetTexture("Interface\\ChatFrame\\UI-ChatInputBorder-Left")
		left:SetTexCoord(0, 0.390625, 0, 1)
		left:SetWidth(100)
		left:SetHeight(32)
		left:SetPoint("LEFT", menuframe.input.editBox, "LEFT", -10, 0)
		local right = menuframe.input.editBox:CreateTexture(nil, "BACKGROUND")
		right:SetTexture("Interface\\ChatFrame\\UI-ChatInputBorder-Right")
		right:SetTexCoord(0.609375, 1, 0, 1)
		right:SetWidth(100)
		right:SetHeight(32)
		right:SetPoint("RIGHT", menuframe.input.editBox, "RIGHT", 10, 0)
		menuframe.input:SetScript("OnHide", function()
			menuframe.input.editBox:ClearFocus()
		end)
		menuframe.input:Hide()
	end
end

function lib:CreateRangeFrame()
	if not menuframe.range then
		menuframe.range = CreateFrame("Frame", "LibDropMenurange", UIParent)
		menuframe.range:EnableMouse(true)
		menuframe.range:EnableMouseWheel(true)
		menuframe.range:SetFrameStrata("DIALOG")
		menuframe.range:SetToplevel(true)
		menuframe.range:SetClampedToScreen(true)
		menuframe.range:SetBackdrop(GameTooltip:GetBackdrop())
		menuframe.range:SetBackdropColor(GameTooltip:GetBackdropColor())
		menuframe.range:SetWidth(105)
		menuframe.range:SetHeight(170)
		menuframe.range.slider = CreateFrame("Slider", nil, menuframe.range)
		menuframe.range.slider:SetOrientation("VERTICAL")
		menuframe.range.slider:SetMinMaxValues(0, 1)
		menuframe.range.slider:SetValueStep(0.01)
		menuframe.range.slider:SetValue(0.5)
		menuframe.range.slider:SetWidth(16)
		menuframe.range.slider:SetHeight(128)
		menuframe.range.slider:SetPoint("LEFT", menuframe.range, "LEFT", 15, 0)
		menuframe.range.slider:SetBackdrop(OpacitySliderFrame:GetBackdrop())
		menuframe.range.slider:SetThumbTexture((OpacitySliderFrame:GetThumbTexture()):GetTexture())
		menuframe.range.topText = menuframe.range.slider:CreateFontString(nil, "ARTWORK")
		menuframe.range.topText:SetFontObject("GameFontGreenSmall")
		menuframe.range.topText:SetPoint("BOTTOM", menuframe.range.slider, "TOP")
		menuframe.range.bottomText = menuframe.range.slider:CreateFontString(nil, "ARTWORK")
		menuframe.range.bottomText:SetFontObject("GameFontGreenSmall")
		menuframe.range.bottomText:SetPoint("TOP", menuframe.range.slider, "BOTTOM")
		menuframe.range.editBox = CreateFrame("EditBox", nil, menuframe.range)
		menuframe.range.editBox:ClearFocus()
		menuframe.range.editBox:SetAutoFocus(false)
		menuframe.range.editBox:SetFontObject("ChatFontNormal")
		menuframe.range.editBox:SetWidth(44)
		menuframe.range.editBox:SetHeight(13)
		menuframe.range.editBox:SetPoint("LEFT", menuframe.range.slider, "RIGHT", 12, 0)
		menuframe.range.editBox:SetJustifyH("CENTER")
		menuframe.range.editBox:SetScript("OnEscapePressed", function()
			menuframe.range.editBox:ClearFocus()
			menuframe.range:Hide()
			menuframe.range.openedLevel = nil
			lib:RunSetFunction(self.set, self.prevValue)
		end)
		local left = menuframe.range.editBox:CreateTexture(nil, "BACKGROUND")
		left:SetTexture("Interface\\ChatFrame\\UI-ChatInputBorder-Left")
		left:SetTexCoord(0, 0.125, 0, 1)
		left:SetWidth(32)
		left:SetHeight(32)
		left:SetPoint("LEFT", menuframe.range.editBox, "LEFT", -10, 0)
		local right = menuframe.range.editBox:CreateTexture(nil, "BACKGROUND")
		right:SetTexture("Interface\\ChatFrame\\UI-ChatInputBorder-Right")
		right:SetTexCoord(0.875, 1, 0, 1)
		right:SetHeight(32)
		right:SetWidth(32)
		right:SetPoint("RIGHT", menuframe.range.editBox, "RIGHT", 10, 0)
		menuframe.range:SetScript("OnHide", function()
			menuframe.range.editBox:ClearFocus()
		end)
		menuframe.range:SetScript("OnMouseWheel", function(self, wheel)
			self.editBox:ClearFocus()
			if wheel > 0 then
				self.slider.value = self.slider.value + self.slider.step
				if self.slider.value > self.slider.maxValue then
					self.slider.value = self.slider.maxValue
				end
			else
				self.slider.value = self.slider.value - self.slider.step
				if self.slider.value < self.slider.minValue then
					self.slider.value = self.slider.minValue
				end
			end
			self.slider:SetValue(1 - (self.slider.value - self.slider.minValue) / (self.slider.maxValue - self.slider.minValue))
		end)
		menuframe.range:Hide()
	end
end

function lib:CreateSelectFrame()
	if not menuframe.select then
		menuframe.select = CreateFrame("Frame", "LibDropMenuselect", UIParent)
		menuframe.select:SetFrameStrata("DIALOG")
		menuframe.select:SetToplevel(true)
		menuframe.select:SetClampedToScreen(true)
		menuframe.select:SetBackdrop(GameTooltip:GetBackdrop())
		menuframe.select:SetBackdropColor(GameTooltip:GetBackdropColor())
		menuframe.select.selecter = menuframe.select:CreateTexture(nil, "OVERLAY")
		menuframe.select.selecter:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight")
		menuframe.select.selecter:SetBlendMode("ADD")
		menuframe.select.selecter:SetAlpha(0.7)
		menuframe.select:SetScript("OnHide", function() lib.selectElement = nil end)
	end
end

function lib:SetSelectFrame()
	local element = lib.selectElement
	menuframe.select.menu = menuframe.select.menu or {}
	local mn
	local menu = lib:RunGetFunction(lib.selectElement[menuframe.select.table])
	if not menu[1] then
		mn = {}
		for p, v in pairs(menu) do
			mn[v] = p
		end
		menu = {}
		for p in pairs(mn) do
			tinsert(menu, p)
		end
		sort(menu)
	end
	local maxWidth = 1
	local value = lib:RunGetFunction(lib.selectElement.get)
	for i = 1, #menu do
		if not menuframe.select.menu[i] then
			menuframe.select.menu[i] = CreateFrame("Button", nil, menuframe.select)
			menuframe.select.menu[i]:SetHeight(18)
			menuframe.select.menu[i].icon = menuframe.select.menu[i]:CreateTexture(nil, "ARTWORK")
			menuframe.select.menu[i].icon:SetWidth(16)
			menuframe.select.menu[i].icon:SetHeight(16)
			menuframe.select.menu[i].icon:SetTexture("Interface\\Buttons\\UI-RadioButton")
			menuframe.select.menu[i].icon:SetTexCoord(0, 0.25, 0, 1)
			menuframe.select.menu[i].icon:SetPoint("LEFT", menuframe.select.menu[i], "LEFT", 0, 0)
			menuframe.select.menu[i].checked = menuframe.select.menu[i]:CreateTexture(nil, "ARTWORK")
			menuframe.select.menu[i].checked:SetWidth(16)
			menuframe.select.menu[i].checked:SetHeight(16)
			menuframe.select.menu[i].checked:SetTexture("Interface\\Buttons\\UI-RadioButton")
			menuframe.select.menu[i].checked:SetTexCoord(0.25, 0.5, 0, 1)
			menuframe.select.menu[i].checked:SetAllPoints(menuframe.select.menu[i].icon)
			menuframe.select.menu[i].text = menuframe.select.menu[i]:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmallLeft")
			menuframe.select.menu[i].text:SetPoint("LEFT", menuframe.select.menu[i].icon, "RIGHT", 2, 0)
			menuframe.select.menu[i]:SetID(i)
			if i == 1 then
				menuframe.select.menu[i]:SetScript("OnEnter", function(button)
					menuframe.select.selecter:ClearAllPoints()
					menuframe.select.selecter:SetAllPoints(button)
					menuframe.select.selecter:Show()
				end)
				menuframe.select.menu[i]:SetScript("OnLeave", function(button)
					menuframe.select.selecter:Hide()
				end)
				menuframe.select.menu[i]:SetScript("OnClick", function(button)
					if button.id then
						lib:RunSetFunction(lib.selectElement.set, button.id)
					else
						lib:RunSetFunction(lib.selectElement.set, button.text:GetText())
					end
					lib:SetSelectFrame()
				end)
				menuframe.select.menu[i]:SetPoint("TOPLEFT", menuframe.select, "TOPLEFT", 10, -10)
			else
				menuframe.select.menu[i]:SetScript("OnEnter", menuframe.select.menu[1]:GetScript("OnEnter"))
				menuframe.select.menu[i]:SetScript("OnLeave", menuframe.select.menu[1]:GetScript("OnLeave"))
				menuframe.select.menu[i]:SetScript("OnClick", menuframe.select.menu[1]:GetScript("OnClick"))
				menuframe.select.menu[i]:SetPoint("TOPLEFT", menuframe.select.menu[i - 1], "BOTTOMLEFT", 0, 0)
			end
		end
		if mn then
			menuframe.select.menu[i].id = mn[menu[i]]
		else
			menuframe.select.menu[i].id = nil
		end
		if mn and value and menuframe.select.menu[i].id == value then
			menuframe.select.menu[i].checked:Show()
		elseif not mn and menu[i] == value then
			menuframe.select.menu[i].checked:Show()
		else
			menuframe.select.menu[i].checked:Hide()
		end
		maxWidth = max(maxWidth, GetTextWidth(menu[i]))
		menuframe.select.menu[i].text:SetText(menu[i])
		menuframe.select.menu[i]:Show()
	end
	for i = #menu + 1, #menuframe.select.menu do
		menuframe.select.menu[i].id = nil
		menuframe.select.menu[i]:Hide()
	end
	for i = 1, #menu do
		menuframe.select.menu[i].text:SetWidth(maxWidth)
		menuframe.select.menu[i]:SetWidth(maxWidth + 20)
	end
	menuframe.select:SetWidth(maxWidth + 40)
	menuframe.select:SetHeight(18 * #menu + 20)
end

function lib:CreateKeybindingFrame()
	if not menuframe.keybinding then
	end
end
--[[
----------- Test Code

LDM_SELECT1 = "s6"
LDM_SELECT2 = "선택2"


local v1 = true
local v2 = false
local v3 = "선택2"
local v31 = "s6"
local v4 = "텍스트"
local v5 = 50

lib:RegisterMenu("TEST", {
	type = "group",
	name = "TEST",
	args = {
		testToggle = {
			type = "toggle",
			name = "토글 테스트",
			desc = "토글 테스트입니다",
			set = function(v) v1 = v BluePrint(v) end,
			get = function() return v1 end,
			order = 2,
		},
		bg = {
			type = "color",
			name = "색상 및 투명도",
			desc = "프레임의 색상 및 투명도를 설정합니다.",
			get = function() return 0, 1, 0.2, 0.75 end,
			set = function(r, g, b, a)
				BluePrint(format("SetColor: %.2f, %.2f, %.2f, %.2f", r, g, b, a or -1))
			end,
			order = 70,
			hasAlpha = true,
		},
		sss = {
			type = "text",
			name = "선택 테스트(특수)",
			desc = "프레임 선택 테스트",
			get = function() return LDM_SELECT1 end,
			set = function(v) LDM_SELECT1 = v BluePrint(v) end,
			order = 3,
			validate = { ["s1"] = "Select 1", ["s2"] = "Select 2", ["s3"] = "Select 3", ["s4"] = "Select 4", ["s5"] = "Select 5", ["s6"] = "Select 6" },
		},
		ssss = {
			type = "text",
			name = "선택 테스트(일반)",
			desc = "프레임 선택 테스트",
			get = function() return LDM_SELECT2 end,
			set = function(v) LDM_SELECT2 = v BluePrint(v) end,
			order = 4,
			validate = { "선택1", "선택2", "선택3", "선택4", "선택5", "선택6" },
		},
		range = {
			type = "range",
			name = "범위 테스트",
			desc = "범위 테스트중",
			get = function() return v5 end,
			set = function(v) v5 = v BluePrint("Set: "..v) end,
			min = 20, max = 100, step = 0.01,
		},
		editBox = {
			type = "text",
			name = "에디트 테스트",
			desc = "에디트 테스트입니다.",
			get = function() return v4 end,
			set = function(v) v4 = v BluePrint(v) end,
			order = 9,
		},
		testGroup = {
			type = "group",
			name = "테스트 그룹",
			desc = "테스트 그룹입니다.",
			icon = "Interface\\Icons\\Temp",
			order = 1,
			args = {
				testExecute = {
					type = "execute",
					name = "실행 테스트",
					desc = "실행 테스트입니다.",
					func = function() BluePrint("테스트 OK") end,
					order = 1,
				},
				editBox = {
					type = "text",
					name = "에디트 테스트",
					desc = "에디트 테스트입니다.",
					get = function() return v4 end,
					set = function(v) v4 = v BluePrint(v) end,
					order = 9,
				},
				testGroup = {
					type = "group",
					name = "그룹 테스트1",
					desc = "그룹 테스트 중입니다.",
					disabled = true,
					icon = "Interface\\Icons\\Temp",
					args = {
						test = {
							type = "toggle",
							name = "토글 테스트",
							get = function() return true end,
							set = nil,
						},
					},
				},
				testGroup2 = {
					type = "group",
					name = "그룹 테스트2",
					desc = "그룹 테스트 중입니다.",
					args = {
						test = {
							type = "toggle",
							name = "토글 테스트",
							get = function() return true end,
							set = nil,
						},
						testGroup = {
							type = "group",
							name = "그룹 테스트1",
							desc = "그룹 테스트 중입니다.",
							args = {
								test = {
									type = "toggle",
									name = "토글 테스트",
									get = function() return true end,
									set = nil,
								},
								testGroup = {
									type = "group",
									name = "그룹 테스트1",
									desc = "그룹 테스트 중입니다.",
									args = {
										test = {
											type = "toggle",
											name = "토글 테스트",
											get = function() return true end,
											set = nil,
										},
									},
								},
								testGroup2 = {
									type = "group",
									name = "그룹 테스트2",
									desc = "그룹 테스트 중입니다.",
									args = {
										test = {
											type = "toggle",
											name = "토글 테스트",
											get = function() return true end,
											set = nil,
										},

										testGroup = {
											type = "group",
											name = "그룹 테스트1",
											desc = "그룹 테스트 중입니다.",
											args = {
												test = {
													type = "toggle",
													name = "토글 테스트",
													get = function() return true end,
													set = nil,
												},
											},
										},
										testGroup2 = {
											type = "group",
											name = "그룹 테스트2",
											desc = "그룹 테스트 중입니다.",
											args = {
												test = {
													type = "toggle",
													name = "토글 테스트",
													get = function() return true end,
													set = nil,
												},
											},
										},
										testGroup3 = {
											type = "group",
											name = "그룹 테스트3",
											desc = "그룹 테스트 중입니다.",
											args = {
												test = {
													type = "toggle",
													name = "토글 테스트",
													get = function() return true end,
													set = nil,
												},
												testGroup3 = {
													type = "group",
													name = "그룹 테스트3",
													desc = "그룹 테스트 중입니다.",
													args = {
														test = {
															type = "toggle",
															name = "토글 테스트",
															get = function() return true end,
															set = nil,
														},
													},
												},
											},
										},
									},
								},
								testGroup3 = {
									type = "group",
									name = "그룹 테스트3",
									desc = "그룹 테스트 중입니다.",
									args = {
										test = {
											type = "toggle",
											name = "토글 테스트",
											get = function() return true end,
											set = nil,
										},
									},
								},
							},
						},
						testGroup2 = {
							type = "group",
							name = "그룹 테스트2",
							desc = "그룹 테스트 중입니다.",
							args = {
								test = {
									type = "toggle",
									name = "토글 테스트",
									get = function() return true end,
									set = nil,
								},
							},
						},
						testGroup3 = {
							type = "group",
							name = "그룹 테스트3",
							desc = "그룹 테스트 중입니다.",
							args = {
								test = {
									type = "toggle",
									name = "토글 테스트",
									get = function() return true end,
									set = nil,
								},
							},
						},
					},
				},
				testGroup3 = {
					type = "group",
					name = "그룹 테스트3",
					desc = "그룹 테스트 중입니다.",
					args = {
						test = {
							type = "toggle",
							name = "토글 테스트",
							get = function() return true end,
							set = nil,
						},
					},
				},
			}
		},
	}
})
lib:OpenMenu("TEST", "CENTER", UIParent, "CENTER")

]]
--[[
lib:RegisterMenu("BlueItemInfo2", BlueItemInfo2.options)
lib:OpenMenu("BlueItemInfo2", "CENTER", UIParent, "CENTER")
]]
