local AddName, Ns = ...
local L = Ns.L

local newTicker, rowWidget
local gttFont, gttFontSize, gttFontFlags
local settingsOffsetY, dropListRowHeight = -15, 18
local DropListMaxVisibleRows = 20 -- max visible rows in a droplist before scrollbar appears
local checkBoxHeight, radioSize, defaultLabelOsW, MainMenuButtonHeight = 30, 14, 40, 21 
Ns.TopTabHeight = 22
local topTabColor = { r=0.298, g=0.298, b=0.298, a=0}
local greenColor = { r=0.443, g=1, b=0.788 }
local redColor = { r=1, g=0, b=0.658 }
local orangeColor = { r=1, g=0.741, b=0.313 }

local anchorLableHeight = 10

Ns.WidgetDisplayFormat = "%s %s"

local opPoints = {
	LEFT = { point="RIGHT", x=-5, y=0, use="x", },
	RIGHT = { point="LEFT", x=5, y=0, use="x" },
	CENTER = { point="CENTER", use="xy" },
}

local TooltipOffsets = {
	Default = { point="RIGHT", topoint="LEFT", x=100, y=24 },
	CheckBox = { point="LEFT", topoint="RIGHT", x=-2, y=4 },
    EditBox = { point="LEFT", topoint="RIGHT", x=6, y=1 },
	Slider = { point="LEFT", topoint="RIGHT", x=-55, y=16 },
}

local Mixins, WidgetPool = { scripts={}, functions={} }, {}

local W = {}
Ns.W = W
Ns.Mixins = Mixins

local function ColorBorders(self, r, g, b, a)
    for i=1, 4 do
        self.Borders[i]:SetColorTexture(r, g, b, a and a or 1)
    end
end
Ns.ColorBorders = ColorBorders

local function CreateBorder(frame, size)
    local f = frame
    local scale = f:GetEffectiveScale()
    local pixel = 1 / scale
    local t = pixel * (size or 1)
    f.SetBorderColor = f.SetBorderColor or ColorBorders
    f.Borders = f.Borders or {}
    local offset = t / 2
    for i=1, 4 do
        if not f.Borders[i] then
            f.Borders[i] = f:CreateLine(nil, "BORDER", nil, 0)
        end
        local b = f.Borders[i]
        b:SetThickness(t)
        b:SetColorTexture(0, 0, 0, 1)
        if i==1 then
            b:SetStartPoint("TOPLEFT", -offset, 0)
            b:SetEndPoint("TOPRIGHT", offset, 0)
        elseif i==2 then
            b:SetStartPoint("TOPRIGHT", 0, offset)
            b:SetEndPoint("BOTTOMRIGHT", 0, -offset)
        elseif i==3 then
            b:SetStartPoint("BOTTOMRIGHT", offset, 0)
            b:SetEndPoint("BOTTOMLEFT", -offset, 0)
        else
            b:SetStartPoint("BOTTOMLEFT", 0, -offset)
            b:SetEndPoint("TOPLEFT", 0, offset)
        end
    end
end
Ns.CreateBorder = CreateBorder

local WidgetBG = {
	bgFile="Interface/BUTTONS/WHITE8X8",
	--edgeFile="Interface/Tooltips/UI-Tooltip-Border",
	tile=false,
	edgeSize=3,
	tileSize=0,
	insets={ left=1, right=1, top=1, bottom=1, },
}

local EditBoxBG = {
	bgFile="Interface/BUTTONS/WHITE8X8",
	--edgeFile="Interface/BUTTONS/WHITE8X8",
	tile=false,
	edgeSize=1,
	tileSize=0,
	insets={ left=1, right=1, top=1, bottom=1 },
}

local RadioBG = {
	bgFile="Interface/BUTTONS/WHITE8X8",
	--edgeFile="Interface/Tooltips/UI-Tooltip-Border", 
	tile=false,
	edgeSize=1,
	tileSize=0,
	insets={ left=1, right=4, top=1, bottom=9, },
}

local SliderBG = {
	bgFile="Interface/BUTTONS/WHITE8X8",
	--edgeFile="Interface/Tooltips/UI-Tooltip-Border",
	tile=false,
	edgeSize=4,
	tileSize=0,
	insets={ left=1, right=1, top=1, bottom=1, },
}

local DragBG = {
	bgFile="Interface/BUTTONS/WHITE8X8",
	edgeFile="Interface/Tooltips/UI-Tooltip-Border",
	tile=false,
	edgeSize=7,
	tileSize=0,
	insets={ left=0, right=0, top=0, bottom=0, },
}

----------------------------------
--			Fonts Init			--
----------------------------------
local LSM = LibStub("LibSharedMedia-3.0")
Ns.LSM = LSM
LSM:Register("font", "Accidental Presidency", [[Interface\Addons\SinStats\fonts\Accidental Presidency.ttf]])
LSM:Register("font", "Oswald", [[Interface\Addons\SinStats\fonts\Oswald-Regular.otf]])
LSM:Register("font", "FORCED SQUARE", [[Interface\Addons\SinStats\fonts\FORCED SQUARE.ttf]])
LSM:Register("font", "Bazooka", [[Interface\Addons\SinStats\fonts\Bazooka.ttf]])
LSM:Register("font", "DorisPP", [[Interface\Addons\SinStats\fonts\DORISPP.ttf]])
LSM:Register("font", "Enigmatic", [[Interface\Addons\SinStats\fonts\Enigma__2.ttf]])
LSM:Register("font", "Liberation Sans (U)", [[Interface\Addons\SinStats\fonts\LiberationSans-Regular.ttf]])
LSM:Register("font", "White Rabbit", [[Interface\Addons\SinStats\fonts\WHITRABT.ttf]])
LSM:Register("font", "Monofonto", [[Interface\Addons\SinStats\fonts\MONOFONT.ttf]])
LSM:Register("font", "FSEX300 (U)", [[Interface\Addons\SinStats\fonts\FSEX300.ttf]])
LSM:Register("font", "PT Sans", [[Interface\Addons\SinStats\fonts\PTSansNarrow.ttf]])
LSM:Register("font", "GoogleSans-Medium", [[Interface\AddOns\SinStats\fonts\GoogleSans-Regular.ttf]])
LSM:Register("font", "GoogleSans-Medium2", [[Interface\AddOns\SinStats\fonts\GoogleSans-Medium.ttf]])
LSM:Register("font", "GSans", [[Interface\Addons\SinStats\fonts\GoogleSans-Medium.ttf]])

--Ns.ConfigDefaultFont = Ns.LSM:Fetch("font", "GSans")
Ns.ConfigDefaultFont = Ns.LSM:Fetch("font", "GoogleSans-Medium")
Ns.ConfigDefaultFont2 = Ns.LSM:Fetch("font", "GoogleSans-Medium2")

if GetLocale() == 'zhCN' or GetLocale() == 'zhTW' or GetLocale() == 'koKR' then
    Ns.ConfigDefaultFont = Ns.LSM:Fetch("font", Ns.LSM:GetDefault("font"))
	Ns.ConfigDefaultFont2 = Ns.LSM:Fetch("font", Ns.LSM:GetDefault("font"))
end

----------------------------------------------
--			Droplist Source Tables			--
----------------------------------------------
local ListData = {}
ListData.FontFlags = {
	{ text=L["None"], value="" },
	{ text=L["Thin"], value="OUTLINE", },
	{ text=L["Thick"], value="THICKOUTLINE" },
	{ text=L["Monochrome"], value="MONOCHROME" },
	{ text=L["Thin Monochrome"], value="OUTLINE, MONOCHROME" },
	{ text=L["Thick Monochrome"], value="THICKOUTLINE, MONOCHROME" },
}
ListData.TextDataSeparator = {
	{ text=L[":"], value=":", },
	{ text=L["-"], value=" -", },
	{ text=L["|"], value=" | ", },
	{ text=L["/"], value=" /", },
	{ text=L[">"], value=" >", },
	{ text=L["<"], value=" <", },
	{ text=L["None"], value="", },
}
ListData.Alignments = {
	{ text=L["Left"], value="TOPLEFT", },
	{ text=L["Right"], value="TOPRIGHT", },
	{ text=L["Center"], value="TOP", },
}
ListData.IconAlignments = {
	{ text=L["Left"], value="TOPLEFT", },
	{ text=L["Right"], value="TOPRIGHT", },
}
ListData.Rows = {
	{ text=1, value=1, },
	{ text=2, value=2, },
	{ text=3, value=3, },
	{ text=4, value=4, },
	{ text=5, value=5, },
}
ListData.Strata = {
	{ text=L["Lowest"], value="BACKGROUND", },
	{ text=L["Low"], value="LOW", },
	{ text=L["Medium"], value="MEDIUM", },
	{ text=L["High"], value="HIGH", },
	{ text=L["Highest"], value="TOOLTIP", },
}

ListData.Decimals = {
	{ text=0, value=0, },
	{ text=1, value=1, },
	{ text=2, value=2, },
	{ text=3, value=3, },
}

ListData.Fonts = {}
for k, v in ipairs(Ns.LSM:List("font")) do
	tinsert(ListData.Fonts, { text=v, value=v, })
end

ListData.BarTextures = {}
for k, v in ipairs(Ns.LSM:List("statusbar")) do
	tinsert(ListData.BarTextures, { text=v, value=v, })
end

ListData.BarMaxMode = {
	{ text=L["BarMaxCap"], value=1, },
	{ text=L["Custom"], value=2, },
}

ListData.JourneyBarMode = {
	{ text=L["NextRank"], value=1, },
	{ text=L["MaxRank"], value=2, },
}

ListData.SpeedBar = {
	{ text=L["Auto"], value=1, },
	{ text=L["Steady Flying"], value=2, },
	{ text=L["Skyriding"], value=3, },
	{ text=L["On Foot"], value=4, },
	{ text=L["Custom"], value=5, },
	{ text=L["Mounted"], value=6, },
}

ListData.Profiles = {}
local function UpdateProfiles()
	wipe(ListData.Profiles)
	for k, v in pairs(SinStatsDB.profiles) do
		tinsert(ListData.Profiles, { text=k, value=k })
	end
	sort(ListData.Profiles, function(a, b)
		return a.text < b.text
	end)
end

--------------------------------------
--			Local Functions			--
--------------------------------------
local TipFormat = "%s"
local function AdjustCheckTip(self, text)
--	self:SetWidth(self:GetWidth() + self.Label:GetWidth())
	self.Tip:SetText(format(TipFormat, L[text.."Tip"]))
	self.Tip:SetTextColor(orangeColor.r, orangeColor.g, orangeColor.b)
end

local function AdjustTopTabText(self, text)
	self:SetWidth(self.Label:GetWidth() + defaultLabelOsW)
end

local function AddTipText(self)
	self.Tip = self:CreateFontString(nil, "OVERLAY")
	self.Tip:SetPoint("BOTTOMLEFT", self, "TOPLEFT", 0, Ns.TooltipYOffset) -- CHECKBOX TIP TEXT
	self.Tip:SetFont(Ns.ConfigDefaultFont2, Ns.ConfigDefaultFontSize -4) -- TIP TEXT SIZE
	self.Tip:SetJustifyH("LEFT")
	self.Tip:SetJustifyV("TOP")
end

local function AddLabel(self)
	self.Label = self:CreateFontString(nil, "OVERLAY")
	Mixin(self.Label, Mixins.functions.Labels)
	Mixin(self, Mixins.functions.HasFontString)
	self:SetFont(Ns.ConfigDefaultFont, Ns.ConfigDefaultFontSize -4)
end

local function AnchorWidget(self, location, x, y, notrelative) -- location = where the lab el will be located in relation to the parent
	location = strupper(location)
	local anchor = (notrelative and location) or opPoints[location] or location  -- because the anchor point will actually be the opposite of the location (topoint)
	self:ClearAllPoints()
	self:SetPoint(anchor.point or location, self:GetParent(), location, x or anchor.x, y or anchor.y)
end

local function CancelTicker(self, leaveshown)
	self:Cancel()
	if not leaveshown then
		rowWidget:Hide()
	end
	rowWidget = nil
	newTicker = nil
end

local function GetLabelText(text, myinformation, noicons)
	if noicons then
		return strtrim(L[text])
	else
		return format(Ns.WidgetDisplayFormat, Ns:GetSpellIcon(myinformation), strtrim(L[text]))
	end
end

local function KillPage(page)
	if Ns.ConfigFrame[page] then
		Ns:KillWidget(Ns.ConfigFrame[page])
		Ns.ConfigFrame[page] = nil
	end
end

local function OnEnterFunc(self)
	if newTicker then
		if rowWidget and rowWidget == self.List then
			CancelTicker(newTicker, true)
		else
			CancelTicker(newTicker)
		end
	end
end

local function OnLeaveFunc(self)
	if not self:IsMouseOver() and not self.List:IsMouseOver() and self.List:IsShown() then
		rowWidget = self.List
		if newTicker then
			CancelTicker(newTicker)
		end
		newTicker = C_Timer.NewTicker(1, CancelTicker)
	end
end

local function PlayOnTab()
	PlaySound(SOUNDKIT.IG_QUEST_LOG_OPEN, "Ambience")
end

local function PlayOnCheck()
	PlaySound(SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_ON, "Ambience")
end

local function SetAnchorText(self, text, anchorcenter)
	if #self.CurrentWidgets == 0 or strtrim(text) == "" then
		return
	end
	self:SetAnchor()
end

function Ns.InitCustomColors(self, stat, settings, myinformation)
	if self:GetChecked() then
		if not settings.ColorName then
			settings.ColorName = {}
			if Ns.Profile.StatTextColor then
				Ns.CopyTable(Ns.Profile.StatTextColor, settings.ColorName)
			else
				Ns.CopyTable(myinformation.ColorName.default, settings.ColorName)
			end
			settings.ColorValue = {}
			if Ns.Profile.DataColor then
				Ns.CopyTable(Ns.Profile.DataColor, settings.ColorValue)
			else
				Ns.CopyTable(myinformation.ColorValue.default, settings.ColorValue)
			end
			local parent = self:GetParent():GetParent()
			if not parent.CurrentWidgets then
				parent = self:GetParent():GetParent():GetParent():GetParent()
			end
			for k, v in pairs(parent.CurrentWidgets) do
				if v.MyInformation and (v.MyInformation.stat == "ColorName" or  v.MyInformation.stat == "ColorValue") then
					v:ColorInit()
				end
			end
		end
	end
end

function Ns.OnBarMaxModeChange(self, key, settings, myinformation)
	local anchor = self.AnchorFrame
	if not anchor or not anchor.CurrentWidgets then return end
	local isCustom = settings[key] == 2
	for k, v in ipairs(anchor.CurrentWidgets) do
		if v.MyInformation and v.MyInformation.stat == "BarMaxCustom" then
			v:SetEnabled(isCustom)
			break
		end
	end
end

--------------------------------------
--			Label Functions			--
--------------------------------------
Mixins.functions.Labels = {}

function Mixins.functions.Labels.Anchor(self, location, x, y, notrelative)
	AnchorWidget(self, location, x, y, notrelative)
end

------------------------------------------
--	Parents with Label Functions		--
------------------------------------------
Mixins.functions.HasFontString = {}
function Mixins.functions.HasFontString.AnchorLabel(self, location, x, y, notrelative)
	AnchorWidget(self.Label, location, x, y, notrelative)
end

function Mixins.functions.HasFontString.GetLabel(self)
	return self.Label:GetText()
end

function Mixins.functions.HasFontString.GetLabelHeight(self)
	return math.ceil(self.Label:GetHeight())
end

function Mixins.functions.HasFontString.SetFont(self, font, size, flags)
	local text = self.Label
	local setFont = font or Ns.ConfigDefaultFont
	local setSize = size or Ns.ConfigDefaultFontSize
	text:SetFont(setFont, setSize)
end

function Mixins.functions.HasFontString.SetFontSize(self, size)
	local f, s = self.Label:GetFont()
	if not f then
		self.Label:SetFont(Ns.ConfigDefaultFont, Ns.ConfigDefaultFontSize -4, "")
		f = Ns.ConfigDefaultFont
	end
	self.Label:SetFont(f, size)
end

function Mixins.functions.HasFontString.SetJustifyH(self, justify)
	self.Label:SetJustifyH(justify)
end

function Mixins.functions.HasFontString.SetJustifyV(self, justify)
	self.Label:SetJustifyV(justify)
end

function Mixins.functions.HasFontString.SetTextColor(self, r, g, b, a)
	self.Label:SetTextColor(r, g, b, a)
end

function Mixins.functions.HasFontString.SetLabel(self, text)
	self.Label:SetText(text)
end

------------------------------------------
--			Widgets and Mixins			--
------------------------------------------
function W.AnchorScroll(parent)
	local f = CreateFrame("ScrollFrame", nil, parent)
	f:SetClipsChildren(true)

	f.ScrollChild = CreateFrame("Frame", nil, f)
	f.ScrollChild:SetSize(180, 500)
	f.ScrollChild:SetPoint("TOPLEFT", f)
	f:SetScrollChild(f.ScrollChild)

	local arrowSize = 11 -- width and height of arrow buttons
	local scrollStep = 40
	local arrowXOffset = 14
	local thumbXOffset = 0

	local function CreateArrowButton(parentFrame, texture)
		local btn = CreateFrame("Button", nil, parentFrame)
		btn:SetSize(arrowSize, arrowSize)
		btn.Icon = btn:CreateTexture(nil, "ARTWORK")
		btn.Icon:SetAllPoints()
		btn.Icon:SetTexture(Ns.TexturePath .. texture)
		btn.Icon:SetVertexColor(greenColor.r, greenColor.g, greenColor.b, 0.7)
		btn:SetScript("OnEnter", function(self) self.Icon:SetVertexColor(greenColor.r, greenColor.g, greenColor.b, 1) end)
		btn:SetScript("OnLeave", function(self) self.Icon:SetVertexColor(greenColor.r, greenColor.g, greenColor.b, 0.7) end)
		return btn
	end

	-- Up arrow
	local upBtn = CreateArrowButton(parent, "ArrowUp")
	upBtn:SetPoint("TOPRIGHT", f, "TOPRIGHT", arrowXOffset, 0)
	upBtn:Hide()
	f.UpArrow = upBtn

	-- Down arrow
	local downBtn = CreateArrowButton(parent, "ArrowDown")
	downBtn:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", arrowXOffset, 0)
	downBtn:Hide()
	f.DownArrow = downBtn

	-- scrollbar
	local bar = CreateFrame("Frame", nil, parent, BackdropTemplateMixin and "BackdropTemplate")
	bar:SetWidth(8)
	bar:SetPoint("TOP", upBtn, "BOTTOM", thumbXOffset, -1)
	bar:SetPoint("BOTTOM", downBtn, "TOP", thumbXOffset, 1)
	bar:SetBackdrop({bgFile = "Interface/BUTTONS/WHITE8X8"})
	bar:SetBackdropColor(0.1, 0.1, 0.1, 0.4)
	bar:Hide()
	f.ScrollBar = bar

	local capH = 4
	local thumb = CreateFrame("Frame", nil, bar)
	thumb:SetWidth(8)
	local topCap = thumb:CreateTexture(nil, "ARTWORK")
	topCap:SetPoint("TOPLEFT")
	topCap:SetPoint("TOPRIGHT")
	topCap:SetHeight(capH)
	topCap:SetTexture("Interface/CHARACTERFRAME/TempPortraitAlphaMask")
	topCap:SetTexCoord(0, 1, 0, 0.5)
	topCap:SetVertexColor(0.388, 0.380, 0.388, 1)
	local midTex = thumb:CreateTexture(nil, "ARTWORK")
	midTex:SetPoint("TOPLEFT", thumb, "TOPLEFT", thumbXOffset, -capH)
	midTex:SetPoint("BOTTOMRIGHT", thumb, "BOTTOMRIGHT", thumbXOffset, capH)
	midTex:SetTexture("Interface/AddOns/SinStats/Textures/Scrollbar")
	midTex:SetVertexColor(1, 1, 1, 0.5)
	local botCap = thumb:CreateTexture(nil, "ARTWORK")
	botCap:SetPoint("BOTTOMLEFT")
	botCap:SetPoint("BOTTOMRIGHT")
	botCap:SetHeight(capH)
	botCap:SetTexture("Interface/CHARACTERFRAME/TempPortraitAlphaMask")
	botCap:SetTexCoord(0, 1, 0.5, 1)
	botCap:SetVertexColor(1, 1, 1, 0.5)
	thumb:SetPoint("TOP", 0, 0)
	thumb:SetHeight(20)
	thumb:EnableMouse(true)
	bar.Thumb = thumb

	local function UpdateScrollBar()
		local range = f:GetVerticalScrollRange()
		if range <= 0 then
			bar:Hide()
			upBtn:Hide()
			downBtn:Hide()
			return
		end
		bar:Show()
		upBtn:Show()
		downBtn:Show()
		local trackH = bar:GetHeight()
		local ratio = f:GetHeight() / (f:GetHeight() + range)
		local thumbH = math.max(20, trackH * ratio)
		thumb:SetHeight(thumbH)
		local travel = trackH - thumbH
		local scrollPos = f:GetVerticalScroll()
		local offset = travel > 0 and (scrollPos / range) * travel or 0
		thumb:ClearAllPoints()
		thumb:SetPoint("TOP", 0, -offset)
	end
	f.UpdateScrollBar = UpdateScrollBar

	-- Mouse wheel scrolling
	f:EnableMouseWheel(true)
	f:SetScript("OnMouseWheel", function(self, delta)
		local range = self:GetVerticalScrollRange()
		if range <= 0 then return end
		local current = self:GetVerticalScroll()
		local newScroll = math.max(0, math.min(range, current - delta * scrollStep))
		self:SetVerticalScroll(newScroll)
		UpdateScrollBar()
	end)

	f:SetScript("OnScrollRangeChanged", function()
		UpdateScrollBar()
	end)

	upBtn:SetScript("OnClick", function()
		local range = f:GetVerticalScrollRange()
		if range <= 0 then return end
		f:SetVerticalScroll(math.max(0, f:GetVerticalScroll() - scrollStep))
		UpdateScrollBar()
	end)
	downBtn:SetScript("OnClick", function()
		local range = f:GetVerticalScrollRange()
		if range <= 0 then return end
		f:SetVerticalScroll(math.min(range, f:GetVerticalScroll() + scrollStep))
		UpdateScrollBar()
	end)

	local function ThumbOnUpdate(self)
		local _, cursorY = GetCursorPosition()
		local scale = self:GetEffectiveScale()
		cursorY = cursorY / scale
		local deltaY = f.DragStartY - cursorY
		local trackH = bar:GetHeight()
		local thumbH = self:GetHeight()
		local travel = trackH - thumbH
		local range = f:GetVerticalScrollRange()
		if travel <= 0 or range <= 0 then return end
		local scrollDelta = deltaY * (range / travel)
		local newScroll = math.max(0, math.min(range, (f.DragStartScroll or 0) + scrollDelta))
		f:SetVerticalScroll(newScroll)
		UpdateScrollBar()
	end
	thumb:SetScript("OnMouseDown", function(self, button)
		if button ~= "LeftButton" then return end
		local _, cursorY = GetCursorPosition()
		local scale = self:GetEffectiveScale()
		f.DragStartY = cursorY / scale
		f.DragStartScroll = f:GetVerticalScroll()
		self:SetScript("OnUpdate", ThumbOnUpdate)
	end)
	thumb:SetScript("OnMouseUp", function(self, button)
		if button ~= "LeftButton" then return end
		self:SetScript("OnUpdate", nil)
	end)

	bar:EnableMouse(true)
	bar:SetScript("OnMouseDown", function(self, button)
		if button ~= "LeftButton" then return end
		local range = f:GetVerticalScrollRange()
		if range <= 0 then return end
		local _, cursorY = GetCursorPosition()
		local scale = self:GetEffectiveScale()
		cursorY = cursorY / scale
		local thumbCenter = select(2, thumb:GetCenter())
		local pageStep = 150
		if cursorY < thumbCenter then
			f:SetVerticalScroll(math.min(range, f:GetVerticalScroll() + pageStep))
		else
			f:SetVerticalScroll(math.max(0, f:GetVerticalScroll() - pageStep))
		end
		UpdateScrollBar()
	end)

	return f
end

--------------------------------------
--			Anchor Frame			--
--------------------------------------
function W.AnchorFrame(parent)
	local f = CreateFrame("Frame", nil, parent)
	f:SetSize(5, 5)
	f.SpecialType = "AnchorFrame"
	f:SetFrameStrata("HIGH")
	local borderOffset = Ns.maxOptionsWidth / 2.5
	AddLabel(f)
	f:AnchorLabel("TOP", 0, 0, true)
	f.Label:SetFont(Ns.ConfigDefaultFont, Ns.ConfigDefaultFontSize + anchorLableHeight)
	f.Label:SetJustifyH("CENTER")

	f.Description = f:CreateFontString()
	f.Description:SetFont(Ns.ConfigDefaultFont, Ns.ConfigDefaultFontSize -3)
	f.Description:SetPoint("TOP", f.Label, "BOTTOM", -25, -5)
	--f.Description:SetJustifyH('TOP')
	f.Description:SetJustifyH('LEFT')
	f.Description:SetWidth(Ns.maxOptionsWidth - (borderOffset / 2))

	f.CurrentWidgets = {}
	Mixin(f, Mixins.functions.AnchorFrames)
	hooksecurefunc(f, "SetLabel", SetAnchorText)

	return f
end

Mixins.functions.AnchorFrames = {}
function Mixins.functions.AnchorFrames.AddWidget(self, widget, parent)
	widget:SetParent(parent or self)
	tinsert(self.CurrentWidgets, widget)
end

function Mixins.functions.AnchorFrames.Reset(self)
	self.ActiveWidget = nil
	self.CenterAlign = nil
	self:SetLabel("")
	self.Description:SetFont(Ns.ConfigDefaultFont2, Ns.ConfigDefaultFontSize -3) -- STATS TEXT FIRST BREAKLINE
	for k = 1, #self.CurrentWidgets do
		Ns:KillWidget(self.CurrentWidgets[k])
	end
	wipe(self.CurrentWidgets)
	if self.TopSeparator then
		Ns:KillWidget(self.TopSeparator)
		self.TopSeparator = nil
	end
	if self.BottomSeparator then
		Ns:KillWidget(self.BottomSeparator)
		self.BottomSeparator = nil
	end
end

function Mixins.functions.AnchorFrames.SetActive(self, widget)
	local w = self.ActiveWidget
	self.ActiveWidget = widget
	if w and w ~= self.ActiveWidget then
		w:Click()
	end
end

function Mixins.functions.AnchorFrames.SetAnchor(self, anchor, width, height)
	if #self.CurrentWidgets == 0 or not anchor then
		return
	end
	self.PageAnchor = anchor
	self.BaseWidth = width and width or self.BaseWidth
	self.BaseHeight = height and height or self.BaseHeight
	self:ClearAllPoints()
	self:SetPoint("TOP", anchor, "BOTTOM", 0, settingsOffsetY)
	local reAnchor = self.CurrentWidgets[1]
	--local p, r, t, x, y = reAnchor:GetPoint(1)
 	local offsetY = 0
	self:SetSize(self.BaseWidth, self.BaseHeight + offsetY)
end

----------------------------------
--			Group Background	--
----------------------------------
function W.GroupBG(parent)
	local f = CreateFrame("Frame", nil, parent)
	f.SpecialType = "GroupBG"
	f.Texture = f:CreateTexture(nil, "BACKGROUND")
	f.Texture:SetAllPoints()
	f.Texture:SetColorTexture(0.08020, 0.08020, 0.08020, 0.8)
	f.Title = f:CreateFontString(nil, "OVERLAY")
	f.Title:SetFont(Ns.ConfigDefaultFont2, Ns.ConfigDefaultFontSize - 4) -- Compartment title font size (tweak the -5)
	f.Title:SetPoint("TOPLEFT", f, "TOPLEFT", 10, 15) -- Compartment title offset
	f.Title:SetTextColor(0.8, 0.8, 0.8, 1)
	f.Title:SetText("")
	Mixin(f, Mixins.functions.GroupBG)
	f:SetFrameLevel(1)
	f:Hide()
	return f
end

Mixins.functions.GroupBG = {}
function Mixins.functions.GroupBG.Reset(self)
	self.Title:SetText("")
end

----------------------------------
--			Separator			--
----------------------------------
function W.Separator(parent)
	local f = CreateFrame("Frame", nil, parent)
	f:SetSize(Ns.SeparatorWidth, 5)
	f.SpecialType = "Separator"
	f.Texture = f:CreateTexture()
	f.Texture:SetAllPoints()
	f.Texture:SetTexture(Ns.TexturePath.."Breakline")
	f.Texture:SetVertexColor(greenColor.r, greenColor.g, greenColor.b)
	--f.Texture:SetVertexColor(1, 1, 1, 0.7)
	AddLabel(f)
	f:AnchorLabel("TOPLEFT", 0, 20, true)
	f:SetFont(Ns.ConfigDefaultFont2, Ns.ConfigDefaultFontSize -3)
	f.Label:SetJustifyH("LEFT")
	--f.Label:SetJustifyV("BOTTOM")
	Mixin(f, Mixins.functions.Separator)
	for k, v in pairs(Mixins.scripts.Separator) do
		f:SetScript(k, v)
	end
	f.OrangeColor = orangeColor
	f:Hide()
	return f
end

Mixins.functions.Separator = {}
function Mixins.functions.Separator.Init(self, bottomlabel)
	if bottomlabel then
		self:AnchorLabel("TOPLEFT", 30, -20, true)
		self:SetFont(Ns.ConfigDefaultFont2, Ns.ConfigDefaultFontSize - 5)
		--self.Label:SetJustifyV("TOP")
	end
end

function Mixins.functions.Separator.EnableToolip(self, enable)
	self:SetHyperlinksEnabled(enable)
end

function Mixins.functions.Separator.Reset(self)
	self:SetLabel("")
	self:AnchorLabel("TOPLEFT", 0, 20, true)
	self:SetFont(Ns.ConfigDefaultFont2, Ns.ConfigDefaultFontSize -3)
	self:SetTextColor(1,1,1)
	self:EnableToolip(false)
	--self.Label:SetJustifyV("BOTTOM")
end

Mixins.scripts.Separator = {}
function Mixins.scripts.Separator.OnHyperlinkEnter(self, link, text, region, left, bottom, width, height)
	GameTooltip:SetOwner(self, "ANCHOR_CURSOR_RIGHT")
	GameTooltip:SetHyperlink(link)
	GameTooltip:Show()
end

function Mixins.scripts.Separator.OnHyperlinkLeave(self)
	GameTooltip:Hide()
end

----------------------------------
--			CheckButton			--
----------------------------------
function W.CheckButton(parent)
	local f = CreateFrame("CheckButton", nil, parent)
	f:SetSize(14, 14)
	f.SpecialType = "CheckButton"
	f:SetNormalTexture(Ns.TexturePath.."CheckOff")
	f:SetCheckedTexture(Ns.TexturePath.."CheckOn")
	f:GetCheckedTexture():SetVertexColor(greenColor.r, greenColor.g, greenColor.b)
	f:GetCheckedTexture():SetBlendMode("ALPHAKEY")
	Mixin(f, Mixins.functions.CheckButton)
	for k, v in pairs(Mixins.scripts.CheckButton) do
		f:SetScript(k, v)
	end
	return f
end

Mixins.functions.CheckButton = {}
function Mixins.functions.CheckButton.Reset(self)
	self.ParentLink = nil
	self.ChildLink = nil
	self.Settings = nil
	self.Key = nil
	self:SetChecked(false)
	self:SetEnabled(true)
	self:Show()
end

local function LinkEnable(parent, self, forceDisable)
	if parent.Linked and #parent.Linked > 0 then
		for k, v in ipairs(parent.Linked) do
			local enabled = not forceDisable and (self:GetChecked() ~= v[2])
			v[1]:SetEnabled(enabled)
			if v[1].Linked then
				LinkEnable(v[1], v[1].Check, not enabled)
			end
		end
	end
end

Mixins.scripts.CheckButton = {}
function Mixins.scripts.CheckButton.OnClick(self)
	PlayOnCheck()
	self.Settings[self.Key] = self:GetChecked() and true or false
	local parent = self:GetParent()
	if parent.SpecialType == "MinimapButton" then
		self.Settings.hide = not self.Settings[self.Key]
	end
	LinkEnable(parent, self)
	if not parent.MyInformation then
		parent = parent:GetParent()
	end
	if parent.MyInformation.onclick then
		Ns[parent.MyInformation.onclick](self, self.Key, self.Settings, parent.MyInformation)
	end
	if not self.DelayInit then
		Ns:QueueProfileRefresh()
	end
	if self.ParentLink then
		self.ParentLink:SetChecked(self:GetChecked())
	end
	if self.ChildLink then
		self.ChildLink:SetChecked(self:GetChecked())
	end
end

function Mixins.scripts.CheckButton.OnEnable(self)
	self:GetCheckedTexture():SetBlendMode("ALPHAKEY")
	local t = self:GetChecked() and self:GetNormalTexture() or self:GetCheckedTexture()
	t:Show()
end

function Mixins.scripts.CheckButton.OnDisable(self)
	self:GetCheckedTexture():SetBlendMode("ADD")
	local t = self:GetChecked() and self:GetNormalTexture() or self:GetCheckedTexture()
	t:Hide()

end
----------------------------------
--			ColorPicker			--
----------------------------------
function W.ColorPicker(parent)
	local f = CreateFrame("Button", nil, parent)
	f:SetSize(20, 20)
	f.SpecialType = "ColorPicker"
	f.ColorPad = f:CreateTexture(nil, "BACKGROUND", nil, 1)
	f.ColorPad:SetAllPoints()
	f.ColorPad:SetTexture(Ns.TexturePath.."ColorPad")
	f.ColorBorder = f:CreateTexture(nil, "BACKGROUND", nil, 4)
	f.ColorBorder:SetAllPoints()
	f.ColorBorder:SetTexture(Ns.TexturePath.."ColorBorder")
	f.ColorBorder:SetVertexColor(greenColor.r, greenColor.g, greenColor.b, 1)
	f:RegisterForClicks("LeftButtonUp", "RightButtonUp")
	AddLabel(f)
	f:AnchorLabel("RIGHT", 35, 0, true)
	AddTipText(f)
	Mixin(f, Mixins.functions.ColorPicker)
	for k, v in pairs(Mixins.scripts.ColorPicker) do
		f:SetScript(k, v)
	end
	return f
end

local function SetPickerPadColor(self, r, g, b)
	self.ColorPad:SetVertexColor(r, g, b)
end

local function ColourPickerCancel(self)
	local colour = ColorPickerFrame.previousValues
	ColorPickerFrame.colourBox.Settings[ColorPickerFrame.colourBox.Key] = colour
	SetPickerPadColor(ColorPickerFrame.colourBox, colour.r, colour.g, colour.b)
	Ns:QueueProfileRefresh()
end

local function ColourPickerChange(self)
	local r, g, b = ColorPickerFrame:GetColorRGB()
	local colour = { r=r, g=g, b=b }
	ColorPickerFrame.colourBox.Settings[ColorPickerFrame.colourBox.Key] = colour
	SetPickerPadColor(ColorPickerFrame.colourBox, colour.r, colour.g, colour.b)
	Ns:QueueProfileRefresh()
end

Mixins.functions.ColorPicker = {}
function Mixins.functions.ColorPicker.Init(self, anchorframe, parent)
	self.MyInformation = parent.DisplayOrder[self:GetID()]
	self.Settings = parent.MyInformation.svnotintable and parent.SvTable or parent.SvTable[parent.MyInformation.stat]
	self.Key = self.MyInformation.stat
	self:ColorInit()
end

function Mixins.functions.ColorPicker.Reset(self)
	self:SetEnabled(true)
end

function Mixins.functions.ColorPicker.ColorInit(self)
	local colour = self.Settings[self.Key]
	if not colour then
		colour = { r=1, g=1, b=1 }
	end
	self.ColorPad:SetVertexColor(colour.r, colour.g, colour.b)
	self:SetLabel(GetLabelText(self.MyInformation.stat, self.MyInformation, true))
	self.Tip:SetText(format(TipFormat, L[self.MyInformation.stat.."Tip"]))
	self.Tip:SetTextColor(orangeColor.r, orangeColor.g, orangeColor.b)
	self.Tip:Show()
end

Mixins.scripts.ColorPicker = {}
function Mixins.scripts.ColorPicker.OnClick(self, button, down)
	if button == "RightButton" then
		if self.DefaultColor then
			SetPickerPadColor(self, self.DefaultColor.r, self.DefaultColor.g, self.DefaultColor.b)
			self.Settings[self.Key].r = self.DefaultColor.r
			self.Settings[self.Key].g = self.DefaultColor.g
			self.Settings[self.Key].b = self.DefaultColor.b
			self:ColorInit()
		end
		return
	end

	local basecolour = self.Settings[self.Key]
	local colour = { r=basecolour.r, g=basecolour.g, b=basecolour.b, }
	ColorPickerFrame.previousValues = colour
	ColorPickerFrame.colourBox = self
    if ColorPickerFrame.SetupColorPickerAndShow then -- 10.2.5 suport for ColorPicker changes
        local info = UIDropDownMenu_CreateInfo()
        info.r, info.g, info.b = colour.r, colour.g, colour.b
        info.swatchFunc = ColourPickerChange
        info.cancelFunc = ColourPickerCancel
        ColorPickerFrame:SetupColorPickerAndShow(info)
        if ColorPickerFrame.Content then
			ColorPickerFrame.Content.ColorPicker:SetColorRGB(colour.r, colour.g, colour.b)
		else
			ColorPickerFrame:SetColorRGB(colour.r, colour.g, colour.b)
		end
    else
        ColorPickerFrame.func = ColourPickerChange
        ColorPickerFrame.cancelFunc = ColourPickerCancel
        ColorPickerFrame:SetColorRGB(colour.r, colour.g, colour.b)
    end
    ColorPickerFrame:ClearAllPoints()
	--ColorPickerFrame.cancelFunc = ColourPickerCancel
	--ColorPickerFrame.func = ColourPickerChange
	-- ColorPickerFrame:SetColorRGB(colour.r, colour.g, colour.b)

	if self:GetRight() < UIParent:GetWidth() / 2 then
		ColorPickerFrame:SetPoint("LEFT", self, "RIGHT", 10, 0)
	else
		ColorPickerFrame:SetPoint("RIGHT", self, "LEFT", -10, 0)
	end
	PlaySound(SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_ON)
	ColorPickerFrame:Show()
end

function Mixins.scripts.ColorPicker.OnDisable(self)
	self:SetAlpha(0.5)
end

function Mixins.scripts.ColorPicker.OnEnable(self)
	self:SetAlpha(1)
end

------------------------------
--			Radio			--
------------------------------
function W.Radio(parent)
	local f = CreateFrame("Frame", nil, UIParent, BackdropTemplateMixin and "BackdropTemplate")
	f:SetSize(checkBoxHeight, checkBoxHeight)
	f.SpecialType = "Radio"
	f:SetBackdrop(RadioBG)
	f:SetBackdropColor(0.184, 0.184, 0.184, 0.4)
	f:SetBackdropBorderColor(0.184, 0.184, 0.184, 0)
	Mixin(f, Mixins.functions.Radio)
	AddTipText(f)
	f.Tip:SetText(format(TipFormat, L["OptionsTip"]))
	f.Radios = {}
	for i=1, 3 do
		local r = CreateFrame("CheckButton", nil, f)
		tinsert(f.Radios, r)
		r:SetSize(radioSize, radioSize)
		r:SetNormalTexture(Ns.TexturePath.."RadioOff")
		r:SetCheckedTexture(Ns.TexturePath.."RadioOn")
		r:GetCheckedTexture():SetVertexColor(greenColor.r, greenColor.g, greenColor.b)--(0.784, 0.850, 0.941)--(1, 0.737, 0)
		r:SetHighlightTexture(Ns.TexturePath.."Highlight")
		r:GetCheckedTexture():SetBlendMode("ALPHAKEY")
		AddLabel(r)
		r:AnchorLabel("RIGHT", 4, 0)
		for k, v in pairs(Mixins.scripts.Radio) do
			r:SetScript(k, v)
		end
		if i == 1 then
			r:SetPoint("BOTTOMLEFT", 6, 12)
		else
			r:SetPoint("LEFT", f.Radios[i-1].Label, "RIGHT", 8, 0)
		end
	end
	return f
end

Mixins.functions.Radio = {}
function Mixins.functions.Radio.Init(self, anchorframe, parent)
	self.MyInformation = parent.DisplayOrder[self:GetID()]
	if parent.MyInformation.svnotintable then
		self.Settings = parent.SvTable
		self.Tip:SetText(format(TipFormat, L[self.MyInformation.stat]))
		self.UpdateFunc = self.MyInformation.updatefunc
		self.AnchorFrame = anchorframe
	else
		self.Settings = parent.SvTable[parent.MyInformation.stat]
	end
	self.Key = self.MyInformation.stat
	self.Option = Ns.Options[self.MyInformation.option]
	local width = 0
	for k, v in ipairs(self.Radios) do
		v.OptionVal = k
		v:SetLabel(L[self.MyInformation.labels[k]])
		v:SetChecked(false)
		width = width + v:GetWidth() + v.Label:GetWidth() + 4
	end
	self:SetWidth(width + 32) --(2*8) + 12)
	self.Radios[self.Settings[self.Key]]:SetChecked(true)
end

function Mixins.functions.Radio.Reset(self)
	self.MyInformation = nil
	self.Settings = nil
	self.Key = nil
	self.Option = nil
	self.OptionVal = nil
	self.AnchorFrame = nil
	self.UpdateFunc = nil
	for k, v in ipairs(self.Radios) do
		v:SetChecked(false)
	end
end

Mixins.scripts.Radio = {}
function Mixins.scripts.Radio.OnClick(self)
	PlayOnCheck()
	for k, v in ipairs(self:GetParent().Radios) do
		if v == self then
			v:SetChecked(true)
		else
			v:SetChecked(false)
		end
	end
	self:GetParent().Settings[self:GetParent().Key] = self.OptionVal
	if self:GetParent().UpdateFunc then
		Ns[self:GetParent().UpdateFunc](self, self.OptionVal)
	end
	Ns:QueueProfileRefresh()
end

----------------------------------
--			Checkbox			--
----------------------------------
function W.CheckBox(parent)
	local f = CreateFrame("Button", nil, parent)
	f:SetSize(10, checkBoxHeight)
	f.SpecialType = "CheckBox"
	Mixin(f, Mixins.functions.CheckBoxFrame)
	f:SetHighlightTexture(Ns.TexturePath.."Highlight")
	f:GetHighlightTexture():SetAlpha(0.2)
	AddLabel(f)
	f:AnchorLabel("LEFT",  30, 5, true)
	f:SetLabel("")
	AddTipText(f)

	for k, v in pairs(Mixins.scripts.CheckBoxFrame) do
		f:SetScript(k, v)
	end
	f.Check = Ns:GetWidget("CheckButton", f)
	f.Check:SetPoint("LEFT", 9, 5)
	f.Check:SetSize(14,14)
	f.Check:SetFrameLevel(parent:GetFrameLevel())
	f.Check:SetHighlightTexture(Ns.TexturePath.."Highlight")
	f.Check:GetHighlightTexture():ClearAllPoints()
	f.Check:GetHighlightTexture():SetAllPoints(f:GetHighlightTexture())
	f.Check:GetHighlightTexture():SetAlpha(0.2)
	f:SetPoint("CENTER")
	return f
end

Mixins.functions.CheckBoxFrame = {}
function Mixins.functions.CheckBoxFrame.Init(self, anchorframe, parent)
	self.MyInformation = parent.DisplayOrder[self:GetID()]
	self.Check.Settings = parent.MyInformation.svnotintable and parent.SvTable or parent.SvTable[parent.MyInformation.stat]
	self.Check.Key = self.MyInformation.stat
	if self.Linked then
		wipe(self.Linked)
	end
	self.DisableOn = nil
	local val = self.Check.Settings[self.MyInformation.stat]
	self.Check:SetChecked(val == true)
	self:SetLabel(GetLabelText(self.MyInformation.stat, self.MyInformation, true))
	self:SetWidth(self.Check:GetWidth() + self.Label:GetWidth() + 35)
	AdjustCheckTip(self, self.MyInformation.stat)
end

function Mixins.functions.CheckBoxFrame.Reset(self)
	self.Check:Reset()
	self:SetEnabled(true)
	self:SetLabel("")
	self:SetWidth(5)
end

function Mixins.functions.CheckBoxFrame.SetLink(self, anchorframe, parent)
	if not self.MyInformation.linked or #self.MyInformation.linked == 0 then
		return
	end
	self.Linked = self.Linked or {}
	wipe(self.Linked)
	local selfEnabled = self.Check:IsEnabled()
	local i, total = 1, #self.MyInformation.linked
	while i < total do-- #self.MyInformation.linked do 
		--local link = self.MyInformation.linked[i]
		for k, v in ipairs(anchorframe.CurrentWidgets) do
			if parent.DisplayOrder[v:GetID()].stat == self.MyInformation.linked[i] then
				tinsert(self.Linked, { v, self.MyInformation.linked[i+1] })
				v:SetEnabled(selfEnabled and (self.Check:GetChecked() ~= self.MyInformation.linked[i+1]))
				break
			end
		end
		i=i+2
	end

	for k, v in ipairs(self.Linked) do
		local widget = v[1]
		if widget.MyInformation and widget.MyInformation.onchange and widget.DropButton then
			Ns[widget.MyInformation.onchange](widget, widget.DropButton.Key, widget.DropButton.Settings, widget.MyInformation)
		end
	end
end

Mixins.scripts.CheckBoxFrame = {}
function Mixins.scripts.CheckBoxFrame.OnClick(self)
	PlayOnCheck()
	self.Check:Click()
end

function Mixins.scripts.CheckBoxFrame.OnEnable(self)
	self.Check:SetEnabled(true)
	self:SetAlpha(1)
end

function Mixins.scripts.CheckBoxFrame.OnDisable(self)
	self.Check:SetEnabled(false)
	self:SetAlpha(0.5)
end

----------------------------------
--			Droplists			--
----------------------------------

-- updates content position + scrollbar thumb from list.ScrollOffset
local function DropList_UpdateScroll(list)
	if list.Content then
		list.Content:ClearAllPoints()
		list.Content:SetPoint("TOPLEFT", 0, list.ScrollOffset or 0)
	end
	if list.ScrollBar and list.ScrollBar:IsShown() and (list.MaxScroll or 0) > 0 then
		local trackH = list:GetHeight() - 4
		local thumbH = list.ScrollBar.Thumb:GetHeight()
		local travel = trackH - thumbH
		local ratio = (list.ScrollOffset or 0) / list.MaxScroll
		list.ScrollBar.Thumb:ClearAllPoints()
		list.ScrollBar.Thumb:SetPoint("TOP", list.ScrollBar, "TOP", 0, -(ratio * travel))
	end
end

function W.DropList(parent)
	local f = CreateFrame("Frame", nil, parent, BackdropTemplateMixin and "BackdropTemplate")
	f:SetSize(140, 20)
	f.SpecialType = "DropList"
	AddTipText(f)
	Mixin(f, Mixins.functions.DropListFrame)
	for k, v in pairs(Mixins.scripts.DropListFrame) do
		f:SetScript(k, v)
	end
	f:SetBackdrop(WidgetBG)
	--f:SetBackdropColor(0.184, 0.184, 0.184, 0.3)
	f:SetBackdropColor(0.27451, 0.27451, 0.27451, 0.3)
	f:SetBackdropBorderColor(0, 0, 0, 1)
	f.DropButton = CreateFrame("Button", nil, f)
	f.DropButton:SetSize(13, 13)
	f.DropButton:SetPoint("TOPRIGHT", -4, -4)
	f.DropButton:SetNormalTexture(Ns.TexturePath.."DropNormal")
	f.DropButton:GetNormalTexture():SetVertexColor(greenColor.r, greenColor.g, greenColor.b, 1)--(0.784, 0.850, 0.941)--(1, 0.737, 0, 1)
	f.DropButton:SetPushedTexture(Ns.TexturePath.."DropPushed")
	f.DropButton:GetPushedTexture():SetVertexColor(greenColor.r, greenColor.g, greenColor.b, 1)--(0.784, 0.850, 0.941)--(1, 0.737, 0, 1)
	f.DropButton:SetHighlightTexture(Ns.TexturePath.."Highlight")
	for k, v in pairs(Mixins.scripts.DropListButton) do
		f.DropButton:SetScript(k, v)
	end
	f.Text = f:CreateFontString()
	f.Text:SetFont(Ns.ConfigDefaultFont, Ns.ConfigDefaultFontSize - 4)
	f.Text:SetJustifyH("LEFT")
	f.Text:SetPoint("TOPLEFT", 4, -4)
	f.Text:SetPoint("BOTTOMRIGHT", f.DropButton, "BOTTOMLEFT", -4, 0)

	f.List = CreateFrame("Frame", nil, f, BackdropTemplateMixin and "BackdropTemplate")
	f.List:Hide()
	f.List:SetSize(150, 70)
	f.List:SetPoint("TOPLEFT", f, "BOTTOMLEFT", 0, 0.6)
	f.List:SetFrameStrata("TOOLTIP")
	f.List:SetBackdrop(WidgetBG)
	f.List:SetBackdropColor(0.150, 0.150, 0.150)
	f.List:SetBackdropBorderColor(0, 0, 0, 0.3)
	f.List:SetClipsChildren(true)
	f.List.Buttons = {}

	f.List.Content = CreateFrame("Frame", nil, f.List)
	f.List.Content:SetPoint("TOPLEFT", 0, 0)
	f.List.Content:SetSize(150, 70)

	-- scrollbar track + thumb
	f.List.ScrollBar = CreateFrame("Frame", nil, f.List, BackdropTemplateMixin and "BackdropTemplate")
	f.List.ScrollBar:SetWidth(6)
	f.List.ScrollBar:SetPoint("TOPRIGHT", -1, -2)
	f.List.ScrollBar:SetPoint("BOTTOMRIGHT", -1, 2)
	f.List.ScrollBar:SetBackdrop({bgFile = "Interface/BUTTONS/WHITE8X8"})
	f.List.ScrollBar:SetBackdropColor(0.1, 0.1, 0.1, 0.4)
	f.List.ScrollBar:Hide()

	f.List.ScrollBar.Thumb = CreateFrame("Frame", nil, f.List.ScrollBar, BackdropTemplateMixin and "BackdropTemplate")
	f.List.ScrollBar.Thumb:SetWidth(6)
	f.List.ScrollBar.Thumb:SetBackdrop({bgFile = "Interface/BUTTONS/WHITE8X8"})
	f.List.ScrollBar.Thumb:SetBackdropColor(greenColor.r, greenColor.g, greenColor.b, 0.5)
	f.List.ScrollBar.Thumb:SetPoint("TOP", 0, 0)
	f.List.ScrollBar.Thumb:SetHeight(20)
	f.List.ScrollBar.Thumb:EnableMouse(true)
	local function DropThumbOnUpdate(thumb)
		local list = thumb:GetParent():GetParent()
		local _, cursorY = GetCursorPosition()
		local scale = thumb:GetEffectiveScale()
		cursorY = cursorY / scale
		local deltaY = list.DragStartY - cursorY -- positive = dragged down = scroll down
		local trackH = list:GetHeight() - 4
		local thumbH = thumb:GetHeight()
		local travel = trackH - thumbH
		if travel <= 0 or (list.MaxScroll or 0) <= 0 then return end
		local scrollDelta = deltaY * (list.MaxScroll / travel)
		local newOffset = math.max(0, math.min(list.MaxScroll, (list.DragStartOffset or 0) + scrollDelta))
		list.ScrollOffset = newOffset
		DropList_UpdateScroll(list)
	end
	f.List.ScrollBar.Thumb:SetScript("OnMouseDown", function(thumb, button)
		if button ~= "LeftButton" then return end
		local list = thumb:GetParent():GetParent()
		local _, cursorY = GetCursorPosition()
		local scale = thumb:GetEffectiveScale()
		list.DragStartY = cursorY / scale
		list.DragStartOffset = list.ScrollOffset or 0
		thumb:SetScript("OnUpdate", DropThumbOnUpdate)
	end)
	f.List.ScrollBar.Thumb:SetScript("OnMouseUp", function(thumb, button)
		if button ~= "LeftButton" then return end
		thumb:SetScript("OnUpdate", nil)
	end)
	--end)

	f.List.ScrollOffset = 0
	f.List.MaxScroll = 0

	Mixin(f.List, Mixins.functions.DropList)
	for k, v in pairs(Mixins.scripts.DropList) do
		f.List:SetScript(k, v)
	end
	-- scrollbar track
	f.List.ScrollBar:EnableMouse(true)
	f.List.ScrollBar:SetScript("OnMouseDown", function(track, button)
		if button ~= "LeftButton" then return end
		local list = track:GetParent()
		if (list.MaxScroll or 0) <= 0 then return end
		local _, cursorY = GetCursorPosition()
		local scale = track:GetEffectiveScale()
		cursorY = cursorY / scale
		local thumbCenter = select(2, track.Thumb:GetCenter())
		local pageStep = dropListRowHeight * DropListMaxVisibleRows * 0.75
		if cursorY < thumbCenter then
			list.ScrollOffset = math.min(list.MaxScroll, (list.ScrollOffset or 0) + pageStep)
		else
			list.ScrollOffset = math.max(0, (list.ScrollOffset or 0) - pageStep)
		end
		DropList_UpdateScroll(list)
	end)
	return f
end

function W.DropListRow(parent)
	local f = CreateFrame("CheckButton", nil, parent)
	f:SetSize(10, dropListRowHeight)
	f.SpecialType = "DropListRow"
	f:SetCheckedTexture(Ns.TexturePath.."HighlightMenu")
	f:GetCheckedTexture():SetAlpha(0.3)
	f:SetHighlightTexture(Ns.TexturePath.."HighlightMenu")
	f.Text = f:CreateFontString()
	f.Text:SetPoint("LEFT")
	f.Text:SetFont(Ns.ConfigDefaultFont2, Ns.ConfigDefaultFontSize - 4)
	f.Text:SetJustifyH("LEFT")
	Mixin(f, Mixins.functions.DropListRow)
	for k, v in pairs(Mixins.scripts.DropListRow) do
		f:SetScript(k, v)
	end
	f:EnableMouseWheel(true)
	return f

end

Mixins.functions.DropListFrame = {}
function Mixins.functions.DropListFrame.Init(self, anchorframe, parent, displaylist)
	self.MyInformation = parent.DisplayOrder[self:GetID()]
	self.AnchorFrame = anchorframe
	self:SetEnabled(true)
	self:SetWidth(self.MyInformation.widget.width)
	self:SetFrameStrata("DIALOG")
	self.DropButton.MyInformation = parent.DisplayOrder[self:GetID()]
	self.DropButton.Settings = parent.MyInformation.svnotintable and parent.SvTable or parent.SvTable[parent.MyInformation.stat]
	self.DropButton.Key = self.MyInformation.stat ~= "Profiles" and self.MyInformation.stat or parent.Key
	self.DropButton.Data = ListData[self.MyInformation.widget.data]
	for k, v in ipairs(self.DropButton.Data) do
		if v.value == self.DropButton.Settings[self.DropButton.Key] then
			self:SetText(v.text)
			break
		end
	end
	self.Tip:SetText(format(TipFormat, L[self.DropButton.Key.."Tip"]))
	self.Tip:SetTextColor(orangeColor.r, orangeColor.g, orangeColor.b)
	self.Tip:Show()
end

function Mixins.functions.DropListFrame.Reset(self)
	self.List:Hide()
	for k, v in pairs(Mixins.scripts.DropListButton) do
		self.DropButton:SetScript(k, v)
	end
end

function Mixins.functions.DropListFrame.SetEnabled(self, enabled)
	self:SetBackdropBorderColor(0.184, 0.184, 0.184, 0.8)
	self.Text:SetAlpha(enabled and 1 or 0.5)
	self.DropButton:SetEnabled(enabled)
	self.DropButton:SetAlpha(enabled and 1 or 0.5)
end

function Mixins.functions.DropListFrame.SetText(self, text)
	self.Text:SetText(text)
end

Mixins.scripts.DropListFrame = {}
function Mixins.scripts.DropListFrame.OnEnter(self)
	OnEnterFunc(self)
end

function Mixins.scripts.DropListFrame.OnLeave(self)
	OnLeaveFunc(self)
end

Mixins.scripts.DropListButton = {}
function Mixins.scripts.DropListButton.OnClick(self)
	PlayOnCheck()
	local parent = self:GetParent()
	if not self:GetParent().List:IsShown() then
		parent.List.MyInformation = self.MyInformation
		parent.List.Settings = self.Settings
		parent.List.Key = self.Key
		parent.List.Data = self.Data
	end
	self:GetParent().List:SetShown(not self:GetParent().List:IsShown())
end

function Mixins.scripts.DropListButton.OnEnter(self)
	OnEnterFunc(self:GetParent())
end

function Mixins.scripts.DropListButton.OnLeave(self)
	self:GetParent():GetScript("OnLeave")(self:GetParent())
end

Mixins.functions.DropList = {}
function Mixins.functions.DropList.Reset(self)
	-- Reparent back to the DropList widget
	local dropWidget = self.DropParent or self:GetParent()
	self.DropParent = nil
	self:SetParent(dropWidget)
	self:ClearAllPoints()
	self:SetPoint("TOPLEFT", dropWidget, "BOTTOMLEFT", 0, 0.6)
	self:SetSize(5, 5)
	self.MyInformation = nil
	self.Settings = nil
	self.Key = nil
	self.Data = nil
	self.IsCopyList = nil
	self.IsDeleteList = nil
	self.IsSpecList = nil
	self.SpecIndex = nil
	self.ScrollOffset = 0
	self.MaxScroll = 0
	self.ThumbDragging = false
	if self.Content then
		self.Content:SetPoint("TOPLEFT", 0, 0)
	end
	if self.ScrollBar then
		self.ScrollBar:Hide()
	end
	for k, v in ipairs(self.Buttons) do
		Ns:KillWidget(v)
	end
	wipe(self.Buttons)
end

Mixins.scripts.DropList = {}
function Mixins.scripts.DropList.OnHide(self)
	self:Reset()
end

function Mixins.scripts.DropList.OnEnter(self)
	OnEnterFunc(self.DropParent or self:GetParent())
end

function Mixins.scripts.DropList.OnLeave(self)
	local dp = self.DropParent or self:GetParent()
	dp:GetScript("OnLeave")(dp)
end

function Mixins.scripts.DropList.OnShow(self)
	self:SetFrameStrata("FULLSCREEN_DIALOG")
	self:SetSize(5, 5)
	self.ScrollOffset = 0
	self.MaxScroll = 0
	self.ThumbDragging = false
	if self.Content then
		self.Content:ClearAllPoints()
		self.Content:SetPoint("TOPLEFT", 0, 0)
	end

	local widest = 0
	local container = self.Content or self
	for k, v in ipairs(self.Data) do
		local row = Ns:GetWidget("DropListRow", container, k)
		row:SetChecked(false)
		tinsert(self.Buttons, row)
		local w = row:Init(self, v)
		if w > widest then
			widest = w
		end
		if v.value == self.Settings[self.Key] then
			self:GetParent():SetText(v.text)
			row:SetChecked(true)
		else
			row:SetChecked(false)
		end
	end

	local total = #self.Data
	widest = widest + 10

	-- layout rows in 2 columns
	local rowsPerCol = math.ceil(total / 2)
	local first, last
	first = self.Buttons[1]
	for i, v in ipairs(self.Buttons) do
		v:ClearAllPoints()
		v:SetWidth(widest)
		if i == 1 then
			v:SetPoint("TOPLEFT", container, "TOPLEFT", 2, -2)
		elseif i == rowsPerCol + 1 then
			v:SetPoint("TOPLEFT", first, "TOPRIGHT", 5, 0)
		else
			v:SetPoint("TOPLEFT", last, "BOTTOMLEFT", 0, 0)
		end
		last = v
	end

	local maxVisPerCol = math.ceil(DropListMaxVisibleRows / 2)
	local visibleRows = math.min(rowsPerCol, maxVisPerCol)
	local contentH = rowsPerCol * dropListRowHeight + 4
	local visibleH = visibleRows * dropListRowHeight + 4
	local listWidth = (2 * widest) + 9 -- 2 columns + gap + padding
	local scrollBarPad = 0

	if self.Content then
		self.Content:SetSize(listWidth, contentH)
	end

	if rowsPerCol > maxVisPerCol and self.ScrollBar then
		self.ScrollBar:Show()
		self.MaxScroll = contentH - visibleH
		scrollBarPad = 8
		local trackH = visibleH - 4
		local thumbH = math.max(16, trackH * (visibleRows / rowsPerCol))
		self.ScrollBar.Thumb:SetHeight(thumbH)
		self.ScrollBar.Thumb:ClearAllPoints()
		self.ScrollBar.Thumb:SetPoint("TOP", self.ScrollBar, "TOP", 0, 0)
	elseif self.ScrollBar then
		self.ScrollBar:Hide()
	end

	self:SetSize(listWidth + scrollBarPad, visibleH)

	local dropWidget = self:GetParent()
	self.DropParent = dropWidget
	self:SetParent(UIParent)
	self:SetFrameStrata("FULLSCREEN_DIALOG")
	self:ClearAllPoints()
	self:SetPoint("TOPLEFT", dropWidget, "BOTTOMLEFT", 0, 0.6)

	self:EnableMouseWheel(rowsPerCol > maxVisPerCol)
	self:SetScript("OnMouseWheel", function(sf, delta)
		if sf.MaxScroll <= 0 then return end
		sf.ScrollOffset = math.max(0, math.min(sf.MaxScroll, sf.ScrollOffset - delta * dropListRowHeight * 2))
		DropList_UpdateScroll(sf)
	end)
end

Mixins.functions.DropListRow = {}
function Mixins.functions.DropListRow.Init(self, parent, rowdata)--, id)
	local container = parent.Content or parent
	self:SetParent(container)
	self.Parent = parent
	self.RowData = rowdata
	local widthExtra = 0
	local t, v = parent.Data[self:GetID()].text, parent.Data[self:GetID()].value
	if parent.Data == ListData.Fonts then
		local f = Ns.LSM:Fetch("font", v)
		self.Text:SetFont(f, 15)
		if select(1, self.Text:GetFont()) ~= f then
			widthExtra = 60
		end
	else
		self.Text:SetFont(Ns.ConfigDefaultFont2, 12)
	end
	if parent.Data == ListData.BarTextures then
		if not self.BarSwatch then
			self.BarSwatch = self:CreateTexture(nil, "ARTWORK")
			self.BarSwatch:SetHeight(dropListRowHeight - 4)
		end
		self.BarSwatch:SetTexture(Ns.LSM:Fetch("statusbar", v))
		self.BarSwatch:SetVertexColor(0.65, 0.65, 0.65)
		self.BarSwatch:ClearAllPoints()
		self.BarSwatch:SetPoint("LEFT", self, "LEFT", 2, 0)
		self.BarSwatch:SetWidth(60)
		self.BarSwatch:Show()
		self.Text:SetPoint("LEFT", self.BarSwatch, "RIGHT", 4, 0)
		widthExtra = 66
	elseif self.BarSwatch then
		self.BarSwatch:Hide()
		self.Text:SetPoint("LEFT")
	end
	self.Text:SetText(t)
	self:Show()
	return self.Text:GetWidth() + widthExtra
end

Mixins.scripts.DropListRow = {}
function Mixins.scripts.DropListRow.OnClick(self)
	PlayOnCheck()
	local list = self.Parent
	local value = self.RowData.value
	local text = self.RowData.text
	local settings = list.Settings
	local key = list.Key
	local isCopyList = list.IsCopyList
	local isDeleteList = list.IsDeleteList
	local isSpecList = list.IsSpecList
	local specIndex = list.SpecIndex

	local dropWidget = list.DropParent or list:GetParent()
	dropWidget:SetText(text)
	list:Hide()

	local profileFrame = dropWidget:GetParent()
	if profileFrame and profileFrame.RunAction then
		if isCopyList then
			profileFrame.CopySourceProfile = value
			profileFrame:RunAction("copyselect")
		elseif isDeleteList then
			profileFrame.DeleteTargetProfile = value
			profileFrame:RunAction("deleteselect")
		elseif isSpecList then
			if not SinStatsDB.specProfiles then SinStatsDB.specProfiles = {} end
			if not SinStatsDB.specProfiles[Ns.ProfileKey] then SinStatsDB.specProfiles[Ns.ProfileKey] = {} end
			SinStatsDB.specProfiles[Ns.ProfileKey][specIndex] = value
			-- auto-switch active spec
			local currentSpec = GetSpecialization and GetSpecialization()
			if currentSpec and currentSpec == specIndex then
				Ns:OnSpecChanged()
				if profileFrame.CurrentProfileText then
					profileFrame.CurrentProfileText:SetText(format("%s: %s", L["CurrentProfile"], SinStatsDB.profileKeys[Ns.ProfileKey]))
				end
			end
		else
			settings[key] = value
			profileFrame:RunAction("select")
		end
		return
	end
	settings[key] = value
	if dropWidget.MyInformation and dropWidget.MyInformation.onchange then
		Ns[dropWidget.MyInformation.onchange](dropWidget, key, settings, dropWidget.MyInformation)
	end
	Ns:QueueProfileRefresh()
end

function Mixins.scripts.DropListRow.OnEnter(self)
	local dropListFrame = self.Parent.DropParent or self.Parent:GetParent()
	OnEnterFunc(dropListFrame)
end

function Mixins.scripts.DropListRow.OnLeave(self)
	local dropListFrame = self.Parent.DropParent or self.Parent:GetParent()
	dropListFrame:GetScript("OnLeave")(dropListFrame)
end

function Mixins.scripts.DropListRow.OnMouseWheel(self, delta)
	local list = self.Parent
	if list and list:GetScript("OnMouseWheel") then
		list:GetScript("OnMouseWheel")(list, delta)
	end
end

------------------------------------------
--			Main Menu Button			--
------------------------------------------
function W.MainMenuButton(parent, width)
	local f = CreateFrame("Button", nil, parent)
	f.SpecialType = "MainMenuButton"
	f.NoResize = true
	Mixin(f, Mixins.functions.MainMenuButton)

	-- Separate icon texture (independent size from text)
	f.Icon = f:CreateTexture(nil, "ARTWORK")
	f.Icon:SetSize(17, 17)
	f.Icon:SetPoint("LEFT", 5, 0)

	AddLabel(f)
	f.Label:SetPoint("LEFT", f.Icon, "RIGHT", 4, 0)
	f:SetJustifyH("LEFT")
	f:SetNormalTexture(Ns.TexturePath.."HighlightMenu")
	f:GetNormalTexture():SetAlpha(0)
	f:SetHighlightTexture(Ns.TexturePath.."HighlightMenu")
	f.Collapsed = true
	f:SetSize(width, MainMenuButtonHeight)
	for k, v in pairs(Mixins.scripts.MainMenuButton) do
		f:SetScript(k, v)
	end
	return f
end

Mixins.functions.MainMenuButton = {}
function Mixins.functions.MainMenuButton.Collapse(self)
	if self.Collapsed then
		return
	end
	self.Collapsed = true
	self:GetNormalTexture():SetAlpha(0)
	local configFrame = self:GetParent()
	if configFrame.MainMenuSelected.Anchor then
		Ns:KillWidget(configFrame.MainMenuSelected.Anchor)
		configFrame.MainMenuSelected.Anchor = nil
	end
	configFrame.MainMenuSelected = nil
end

function Mixins.functions.MainMenuButton.Expand(self)
	if not self.Collapsed then
		return
	end
	self.Collapsed = false
	self:GetNormalTexture():SetAlpha(0.5)
	local configFrame = self:GetParent()
	configFrame.MainMenuSelected = self
	if not self.DisplayFunc then
		print("|cffff0000SinStats: No display function supplied for Main Menu item:", self.MyInformation.stat.."!")
		return
	end
	Ns.ConfigFrame.MainAnchor = Ns:GetWidget("AnchorFrame", configFrame)
	Ns.ConfigFrame.MainAnchor:SetLabel(format(Ns.WidgetDisplayFormat, Ns:GetSpellIcon(self.MyInformation), (L[self.MyInformation.stat])))
	Ns.ConfigFrame.MainAnchor:SetFontSize(17)
	Ns.ConfigFrame.MainAnchor:SetTextColor(1,1,1)
	Ns.ConfigFrame.MainAnchor.Description:SetText(L[self.MyInformation.stat.."Description"])
	Ns.ConfigFrame.MainAnchor.CenterAlign = self.MyInformation.centeralign
	self.DisplayFunc(self, Ns.ConfigFrame.MainAnchor, Ns.TopAnchor, true, nil, self.NextDisplayOrder.ignoretoptabs)
	Ns.ConfigFrame.MainAnchor:Show()
end

Mixins.scripts.MainMenuButton = {}
function Mixins.scripts.MainMenuButton.OnClick(self)
	PlayOnTab()
	local configFrame = self:GetParent()
	if  configFrame.MainMenuSelected and configFrame.MainMenuSelected == self then
		return
	end
	if  configFrame.MainMenuSelected and configFrame.MainMenuSelected ~= self then
		KillPage("MainAnchor")
		KillPage("ChildAnchor")
		configFrame.MainMenuSelected:Collapse()
	end
	if self.Collapsed then
		self:Expand()
	else
		self:Collapse()
	end
end

--------------------------------------
--			Minimap Button			--
--------------------------------------
function W.MinimapButton(parent) -- All the funcions to do Profiles
	local f = Ns:GetWidget("CheckBox")
	f.SpecialType = "MinimapButton"
	Mixin(f, Mixins.functions.MinimapButton)
	return f
end

Mixins.functions.MinimapButton = {}
function Mixins.functions.MinimapButton.Init(self, anchorframe, parent)
	self.MyInformation = parent.DisplayOrder[self:GetID()]
	self.Check.Settings = parent.SvTable[self.MyInformation.stat]
	self.Check.Key = "Show"
	self.Check:SetChecked(self.Check.Settings[self.Check.Key])
	self:SetLabel(GetLabelText(self.MyInformation.stat, self.MyInformation, true))
	self:SetWidth(self.Check:GetWidth() + self.Label:GetWidth() + 35)
	AdjustCheckTip(self, self.MyInformation.stat)
end

----------------------------------
--			Plain Button		--
----------------------------------
function W.PlainButton(parent, width)
	local f = CreateFrame("Button", nil, parent)
	f:SetSize(50, 25)
	f.SpecialType = "PlainButton"
	Mixin(f, Mixins.functions.PlainButton)
	AddLabel(f)
	f:AnchorLabel("CENTER")
	f:SetNormalTexture(Ns.TexturePath.."ButtonNormal")
	f:SetPushedTexture(Ns.TexturePath.."ButtonPushed")
	f:SetHighlightTexture(Ns.TexturePath.."Highlight")
	f:GetNormalTexture():SetVertexColor(topTabColor.r, topTabColor.g, topTabColor.b, 0.8)
	f:GetPushedTexture():SetVertexColor(redColor.r, redColor.g, redColor.b, 0.8)
	f:GetHighlightTexture():SetVertexColor(redColor.r, redColor.g, redColor.b, 0.2)
	for k, v in pairs(Mixins.scripts.PlainButton) do
		f:SetScript(k, v)
	end
	return f
end

Mixins.functions.PlainButton = {}
function Mixins.functions.PlainButton.Init(self, anchorframe, parent)
	self.MyInformation = parent.DisplayOrder[self:GetID()]
	self.Settings = parent.MyInformation.svnotintable and parent.SvTable or parent.SvTable[parent.MyInformation.stat]
	self.Key = self.MyInformation.stat
	self:SetLabel(GetLabelText(self.MyInformation.stat, self.MyInformation, true))
	self:SetWidth(self.Label:GetWidth() + 30)
end

function Mixins.functions.PlainButton.Reset(self)
	self.MyInformation = nil
	self:SetLabel("")
	self:SetWidth(10)
end

Mixins.scripts.PlainButton = {}
function Mixins.scripts.PlainButton.OnClick(self)
	self.Settings[self.Key] = true
	Ns:QueueProfileRefresh()
	StaticPopup_Show("SINSTATS_RESET")
end

----------------------------------
--			Profiles			--
----------------------------------
local CurrentProfileFormat = "%s: %s"

local function FindInProfilekys(find)
	for k, v in pairs(SinStatsDB.profileKeys) do
		if v == find then
			return true
		end
	end
end

-- Return a list of "CharName-Realm" strings whose profileKey == profileName
local function GetCharactersUsingProfile(profileName)
	local chars = {}
	for charKey, pName in pairs(SinStatsDB.profileKeys) do
		if pName == profileName then
			tinsert(chars, charKey)
		end
	end
	sort(chars)
	return chars
end

local function ProfileDisableAll(self)
	-- select section
	self.SelectList:SetEnabled(true)
	self.LoadBtn:SetEnabled(false)
	-- create section
	self.CreateCopyBtn:SetEnabled(false)
	self.CreateDefaultBtn:SetEnabled(false)
	-- copy section
	self.CopyBtn:SetEnabled(false)
	self.CopyConfirmBtn:Hide()
	self.CopyCancelBtn:Hide()
	self.CopyBtn:Show()
	self.CopyWarning:SetText("")
	self.CopyWarning:Hide()
	-- delete section
	self.DeleteBtn:SetEnabled(false)
	self.DeleteConfirmBtn:Hide()
	self.DeleteCancelBtn:Hide()
	self.DeleteBtn:Show()
	self.DeleteWarning:SetText("")
	self.DeleteWarning:Hide()
	-- status message
	self.StatusMessage:SetText("")
	self.StatusMessage:Hide()
end

local function ResetProfileConfig(self)
	UpdateProfiles()
	wipe(self.CurrentProfile)
	self.CurrentProfile[Ns.ProfileKey] = SinStatsDB.profileKeys[Ns.ProfileKey]
	self.SvTable = self.CurrentProfile
	self.Key = Ns.ProfileKey
	-- Refresh select droplist
	self.SelectList:Init(self, self)
	self.SelectList.Tip:Hide()
	-- Refresh copy‑from droplist
	self.CopyList:Init(self, self)
	self.CopyList.Tip:Hide()
	-- Refresh delete droplist
	self.DeleteList:Init(self, self)
	self.DeleteList.Tip:Hide()
	-- Refresh spec droplists
	if self.HasSpecProfiles then
		self:UpdateSpecSection()
	end
	self:RunAction("select")
end

Mixins.scripts.ProfileConfigButton = {}
function Mixins.scripts.ProfileConfigButton.OnClick(self)
	PlayOnTab()
	self:GetParent():RunAction(self.Action)
end

function Mixins.scripts.ProfileConfigButton.OnEnable(self)
	self:SetAlpha(1)
	self:GetNormalTexture():SetVertexColor(greenColor.r, greenColor.g, greenColor.b, 0.8)
	self:GetPushedTexture():SetVertexColor(greenColor.r, greenColor.g, greenColor.b, 0.8)
end

function Mixins.scripts.ProfileConfigButton.OnDisable(self)
	self:SetAlpha(0.5)
	self:GetNormalTexture():SetVertexColor(topTabColor.r, topTabColor.g, topTabColor.b)
	self:GetPushedTexture():SetVertexColor(topTabColor.r, topTabColor.g, topTabColor.b)
end

local function CreateProfileButton(self, label, action)
	local f = CreateFrame("Button", nil, self, "TruncatedButtonTemplate")
	f:SetSize(80, 26)
	f.Text = f:CreateFontString()
	f.Text:SetPoint("CENTER")
	f.Text:SetFont(Ns.ConfigDefaultFont2, 11)
	f.Text:SetJustifyH("CENTER")
	f:SetNormalTexture(Ns.TexturePath.."ButtonNormal")
	f:SetPushedTexture(Ns.TexturePath.."ButtonPushed")
	f:GetNormalTexture():SetVertexColor(topTabColor.r, topTabColor.g, topTabColor.b, 0.8)
	f:GetPushedTexture():SetVertexColor(topTabColor.r, topTabColor.g, topTabColor.b, 0.8)
	f:SetHighlightTexture(Ns.TexturePath.."Highlight")
	local h = f:GetHighlightTexture()
	h:SetVertexColor(1, 1, 1, 1)
	f.Text:SetText(label)
	f.Action = action
	for k, v in pairs(Mixins.scripts.ProfileConfigButton) do
		f:SetScript(k, v)
	end
	return f
end

function W.ProfileConfig(parent)
	local f = CreateFrame("Frame", "$parentProfiles", parent)
	f.SpecialType = "ProfileConfig"
	f:SetSize(500, 100)
	Mixin(f, Mixins.functions.ProfileConfig)

	ListData.Deafults = {}
	for k, v in pairs(Ns:GetClassDefaults()) do
		tinsert(ListData.Deafults, { text=k, value=v })
	end
	sort(ListData.Deafults, function(a, b) return a.text < b.text end)

	-- column offsets
	local rightCol = 400

	f.CurrentProfileText = f:CreateFontString()
	f.CurrentProfileText:SetFont(Ns.ConfigDefaultFont2, Ns.ConfigDefaultFontSize - 2)
	f.CurrentProfileText:SetTextColor(greenColor.r, greenColor.g, greenColor.b)
	f.CurrentProfileText:SetPoint("TOP", 0, 0)
	f.CurrentProfileText:SetJustifyH("CENTER")

	-- vertical offset tracker
	local yOfs = -25

	f.SelectLabel = f:CreateFontString()
	f.SelectLabel:SetFont(Ns.ConfigDefaultFont2, Ns.ConfigDefaultFontSize - 4)
	f.SelectLabel:SetPoint("TOPLEFT", 0, yOfs - 7)
	f.SelectLabel:SetTextColor(orangeColor.r, orangeColor.g, orangeColor.b)
	f.SelectLabel:SetJustifyH("LEFT")
	f.SelectLabel:SetText(L["ProfileSelectLabel"])

	f.CreateLabel = f:CreateFontString()
	f.CreateLabel:SetFont(Ns.ConfigDefaultFont2, Ns.ConfigDefaultFontSize - 4)
	f.CreateLabel:SetPoint("TOPLEFT", rightCol, yOfs - 7)
	f.CreateLabel:SetTextColor(orangeColor.r, orangeColor.g, orangeColor.b)
	f.CreateLabel:SetJustifyH("LEFT")
	f.CreateLabel:SetText(L["NewProfile"])

	yOfs = yOfs - 22

	-- actual widget created in Init
	f.SelectListAnchorY = yOfs

	f.LoadBtn = CreateProfileButton(f, L["Selected"], "load")

	f.Edit = W.BaseEditBox(f, 200, 22)
	f.Edit:SetPoint("TOPLEFT", rightCol, yOfs)
	f.Edit.Action = "edit"
	f.Edit:SetScript("OnTextChanged", function(self)
		self:GetParent():RunAction(self.Action)
	end)

	yOfs = yOfs - 30

	f.CreateCopyBtn = CreateProfileButton(f, L["ProfileCreateCopy"], "createcopy")
	f.CreateCopyBtn:SetSize(120, 26)
	f.CreateCopyBtn:SetPoint("TOPLEFT", rightCol, yOfs)

	f.CreateDefaultBtn = CreateProfileButton(f, L["ProfileCreateDefault"], "createdefault")
	f.CreateDefaultBtn:SetSize(120, 26)
	f.CreateDefaultBtn:SetPoint("LEFT", f.CreateCopyBtn, "RIGHT", 10, 0)

	yOfs = yOfs - 45

	f.CopyLabel = f:CreateFontString()
	f.CopyLabel:SetFont(Ns.ConfigDefaultFont2, Ns.ConfigDefaultFontSize - 4)
	f.CopyLabel:SetPoint("TOPLEFT", 0, yOfs - 7)
	f.CopyLabel:SetTextColor(orangeColor.r, orangeColor.g, orangeColor.b)
	f.CopyLabel:SetJustifyH("LEFT")
	f.CopyLabel:SetText(L["ProfileCopyFrom"])

	f.DeleteLabel = f:CreateFontString()
	f.DeleteLabel:SetFont(Ns.ConfigDefaultFont2, Ns.ConfigDefaultFontSize - 4)
	f.DeleteLabel:SetPoint("TOPLEFT", rightCol, yOfs - 7)
	f.DeleteLabel:SetTextColor(orangeColor.r, orangeColor.g, orangeColor.b)
	f.DeleteLabel:SetJustifyH("LEFT")
	f.DeleteLabel:SetText(L["Delete"])

	yOfs = yOfs - 22
	f.CopyListAnchorY = yOfs
	f.DeleteListAnchorY = yOfs

	f.CopyBtn = CreateProfileButton(f, L["Copy"], "copy")
	-- anchored in Init relative to CopyList

	f.CopyConfirmBtn = CreateProfileButton(f, L["ConfirmDelete"], "copyapply")
	f.CopyConfirmBtn:GetNormalTexture():SetVertexColor(redColor.r, redColor.g, redColor.b, 0.9)
	f.CopyConfirmBtn:GetPushedTexture():SetVertexColor(redColor.r, redColor.g, redColor.b, 0.9)
	f.CopyConfirmBtn:Hide()

	f.CopyCancelBtn = CreateProfileButton(f, L["Cancel"], "copycancel")
	f.CopyCancelBtn:GetNormalTexture():SetVertexColor(greenColor.r, greenColor.g, greenColor.b, 0.8)
	f.CopyCancelBtn:GetPushedTexture():SetVertexColor(greenColor.r, greenColor.g, greenColor.b, 0.8)
	f.CopyCancelBtn:Hide()

	f.DeleteBtn = CreateProfileButton(f, L["Delete"], "delete")
	f.DeleteBtn:SetSize(120, 26)
	f.DeleteBtn:GetNormalTexture():SetVertexColor(redColor.r, redColor.g, redColor.b, 0.9)
	f.DeleteBtn:GetPushedTexture():SetVertexColor(redColor.r, redColor.g, redColor.b, 0.9)
	-- anchored in Init below DeleteList

	f.DeleteConfirmBtn = CreateProfileButton(f, L["ConfirmDelete"], "confirmdelete")
	f.DeleteConfirmBtn:SetSize(120, 26)
	f.DeleteConfirmBtn:GetNormalTexture():SetVertexColor(redColor.r, redColor.g, redColor.b, 0.9)
	f.DeleteConfirmBtn:GetPushedTexture():SetVertexColor(redColor.r, redColor.g, redColor.b, 0.9)
	f.DeleteConfirmBtn:Hide()

	f.DeleteCancelBtn = CreateProfileButton(f, L["Cancel"], "deletecancel")
	f.DeleteCancelBtn:SetSize(120, 26)
	f.DeleteCancelBtn:GetNormalTexture():SetVertexColor(greenColor.r, greenColor.g, greenColor.b, 0.8)
	f.DeleteCancelBtn:GetPushedTexture():SetVertexColor(greenColor.r, greenColor.g, greenColor.b, 0.8)
	f.DeleteCancelBtn:Hide()

	yOfs = yOfs - 30
	f.CopyWarning = f:CreateFontString()
	f.CopyWarning:SetFont(Ns.ConfigDefaultFont2, Ns.ConfigDefaultFontSize - 5)
	f.CopyWarning:SetPoint("TOPLEFT", 0, yOfs)
	f.CopyWarning:SetJustifyH("LEFT")
	f.CopyWarning:Hide()

	f.DeleteWarning = f:CreateFontString()
	f.DeleteWarning:SetFont(Ns.ConfigDefaultFont2, Ns.ConfigDefaultFontSize - 5)
	f.DeleteWarning:SetJustifyH("LEFT")
	f.DeleteWarning:Hide()

	-- spec profiles for retail and mists so far - rest will come
	f.HasSpecProfiles = (WOW_PROJECT_ID == WOW_PROJECT_MAINLINE) or (WOW_PROJECT_ID == 19)
	if f.HasSpecProfiles then
		yOfs = yOfs - 35

		f.SpecEnableCheck = CreateFrame("CheckButton", nil, f)
		f.SpecEnableCheck:SetSize(16, 16)
		f.SpecEnableCheck:SetPoint("TOPLEFT", 0, yOfs)
		f.SpecEnableCheck:SetNormalTexture(Ns.TexturePath.."CheckOff")
		f.SpecEnableCheck:SetCheckedTexture(Ns.TexturePath.."CheckOn")
		f.SpecEnableCheck:GetCheckedTexture():SetVertexColor(greenColor.r, greenColor.g, greenColor.b, 1)
		f.SpecEnableCheck:SetHighlightTexture(Ns.TexturePath.."Highlight")
		f.SpecEnableCheck:GetHighlightTexture():SetAlpha(0.2)

		f.SpecEnableLabel = f:CreateFontString()
		f.SpecEnableLabel:SetFont(Ns.ConfigDefaultFont2, Ns.ConfigDefaultFontSize - 4)
		f.SpecEnableLabel:SetPoint("LEFT", f.SpecEnableCheck, "RIGHT", 6, 0)
		f.SpecEnableLabel:SetTextColor(orangeColor.r, orangeColor.g, orangeColor.b)
		f.SpecEnableLabel:SetText(L["SpecProfilesEnable"])

		f.SpecEnableTip = CreateFrame("Frame", nil, f)
		f.SpecEnableTip:SetSize(12, 12)
		f.SpecEnableTip:SetPoint("LEFT", f.SpecEnableLabel, "RIGHT", 4, 0)
		f.SpecEnableTip.Texture = f.SpecEnableTip:CreateTexture()
		f.SpecEnableTip.Texture:SetAllPoints()
		f.SpecEnableTip.Texture:SetTexture(Ns.TexturePath.."Misc")
		f.SpecEnableTip:SetScript("OnEnter", function(self)
			if not gttFont then
				gttFont, gttFontSize, gttFontFlags = GameTooltipHeaderText:GetFont()
			end
			GameTooltip:SetOwner(self, "ANCHOR_TOPLEFT")
			GameTooltipHeaderText:SetFont(Ns.ConfigDefaultFont, 12, "")
			GameTooltip:SetText(L["SpecProfilesEnableTip"])
		end)
		f.SpecEnableTip:SetScript("OnLeave", function()
			GameTooltipHeaderText:SetFont(gttFont, gttFontSize, gttFontFlags)
			GameTooltip:Hide()
		end)

		yOfs = yOfs - 25
		f.SpecListAnchorY = yOfs
		f.SpecLists = {}
		f.SpecLabels = {}

		f.SpecEnableCheck:SetScript("OnClick", function(btn)
			PlayOnCheck()
			local checked = btn:GetChecked()
			if not SinStatsDB.specProfiles then SinStatsDB.specProfiles = {} end
			if not SinStatsDB.specProfiles[Ns.ProfileKey] then SinStatsDB.specProfiles[Ns.ProfileKey] = {} end
			SinStatsDB.specProfiles[Ns.ProfileKey].enabled = checked
			f:UpdateSpecSection()
			f:RunAction("select")
			if checked and GetSpecialization then
				Ns:OnSpecChanged()
				f.CurrentProfileText:SetText(format("%s: %s", L["CurrentProfile"], SinStatsDB.profileKeys[Ns.ProfileKey]))
			end
		end)
	end

	yOfs = yOfs + 60
	f.StatusMessageAnchorY = yOfs
	f.StatusMessage = f:CreateFontString()
	f.StatusMessage:SetFont(Ns.ConfigDefaultFont2, Ns.ConfigDefaultFontSize - 5)
	f.StatusMessage:SetPoint("TOPLEFT", 0, yOfs)
	f.StatusMessage:SetJustifyH("LEFT")
	f.StatusMessage:Hide()

	function f.UpdateSpecSection(self)
		for i, dl in pairs(self.SpecLists) do
			if dl then Ns:KillWidget(dl); self.SpecLists[i] = nil end
		end
		for i, lbl in pairs(self.SpecLabels) do
			if lbl then lbl:Hide() end
		end

		if not self.HasSpecProfiles then return end

		local specData = SinStatsDB.specProfiles and SinStatsDB.specProfiles[Ns.ProfileKey]
		local enabled = specData and specData.enabled
		self.SpecEnableCheck:SetChecked(enabled and true or false)

		local numSpecs = GetNumSpecializations()
		local specYOfs = self.SpecListAnchorY
		for i = 1, numSpecs do
			local specName, specIcon
			if GetSpecializationInfo then
				_, specName, _, specIcon = GetSpecializationInfo(i)
			elseif GetSpecializationInfoForClassID then
				local _, classID = UnitClassBase("player")
				_, specName, _, specIcon = GetSpecializationInfoForClassID(classID, i)
			end
			specName = specName or ("Spec " .. i)
			if not self.SpecLabels[i] then
				self.SpecLabels[i] = self:CreateFontString()
				self.SpecLabels[i]:SetFont(Ns.ConfigDefaultFont2, Ns.ConfigDefaultFontSize - 4)
				self.SpecLabels[i]:SetJustifyH("LEFT")
			end
			self.SpecLabels[i]:ClearAllPoints()
			self.SpecLabels[i]:SetPoint("TOPLEFT", 0, specYOfs)
			self.SpecLabels[i]:SetTextColor(1, 1, 1)
			local iconStr = specIcon and ("|T" .. specIcon .. ":0|t ") or ""
			self.SpecLabels[i]:SetText(iconStr .. format(L["SpecProfileLabel"], specName or ("Spec " .. i)))
			self.SpecLabels[i]:Show()

			self.SpecLists[i] = Ns:GetWidget("DropList", self, self:GetID())
			self.SpecLists[i]:SetParent(self)
			self.SpecLists[i]:ClearAllPoints()
			self.SpecLists[i]:SetPoint("TOPLEFT", 140, specYOfs)
			self.SpecLists[i]:Init(self, self)
			self.SpecLists[i].Tip:Hide()
			self.SpecLists[i]:Show()
			self.SpecLists[i]:SetEnabled(enabled and true or false)

			local specIndex = i
			if not SinStatsDB.specProfiles[Ns.ProfileKey] then SinStatsDB.specProfiles[Ns.ProfileKey] = {} end
			self.SpecLists[i].DropButton.Settings = SinStatsDB.specProfiles[Ns.ProfileKey]
			self.SpecLists[i].DropButton.Key = specIndex

			local savedProfile = SinStatsDB.specProfiles[Ns.ProfileKey][specIndex]
			if savedProfile and SinStatsDB.profiles[savedProfile] then
				self.SpecLists[i]:SetText(savedProfile)
			else
				local activeProfile = SinStatsDB.profileKeys[Ns.ProfileKey]
				SinStatsDB.specProfiles[Ns.ProfileKey][specIndex] = activeProfile
				self.SpecLists[i]:SetText(activeProfile)
			end

			self.SpecLists[i].DropButton:SetScript("OnClick", function(btn)
				PlayOnCheck()
				local listParent = btn:GetParent()
				if not listParent.List:IsShown() then
					listParent.List.MyInformation = btn.MyInformation
					listParent.List.Settings = btn.Settings
					listParent.List.Key = btn.Key
					listParent.List.Data = btn.Data
					listParent.List.IsSpecList = true
					listParent.List.SpecIndex = specIndex
				end
				listParent.List:SetShown(not listParent.List:IsShown())
			end)

			specYOfs = specYOfs - 28
		end
	end

	function f.RunAction(self, action)
		ProfileDisableAll(self)
		local newProfile = strtrim(self.Edit:GetText())

		if action == "load" then
			SinStatsDB.profileKeys[Ns.ProfileKey] = self.CurrentProfile[self.Key]
			Ns.Profile = SinStatsDB.profiles[SinStatsDB.profileKeys[Ns.ProfileKey]]
			Ns.NewSettings(Ns.Profile, Ns.Class)
			self.CurrentProfileText:SetText(format(CurrentProfileFormat, L["CurrentProfile"], SinStatsDB.profileKeys[Ns.ProfileKey]))
			ResetProfileConfig(self)
			Ns:UpdateProfile()
			local p = self:GetParent()
			if not p.PageAnchor then
				p = self:GetParent():GetParent():GetParent()
			end
			for k, v in pairs(p.PageAnchor.CurrentWidgets) do
				v.SvTable = v.Parent.SvTable
			end
			Ns:InitialiseProfile(Ns.Profile)
			return
		end

		if action == "select" then
			local specEnabled = self.HasSpecProfiles and SinStatsDB.specProfiles
				and SinStatsDB.specProfiles[Ns.ProfileKey]
				and SinStatsDB.specProfiles[Ns.ProfileKey].enabled
			if specEnabled then
				self.SelectList:SetEnabled(false)
				self.LoadBtn:SetEnabled(false)
			else
				if self.SvTable[self.Key] ~= SinStatsDB.profileKeys[Ns.ProfileKey] then
					self.LoadBtn:SetEnabled(true)
				end
			end
			return
		end

		if action == "edit" then
			if newProfile ~= "" and not SinStatsDB.profiles[newProfile] then
				self.CreateCopyBtn:SetEnabled(true)
				self.CreateDefaultBtn:SetEnabled(true)
			end
			return
		end

		if action == "createcopy" then
			if newProfile == "" or SinStatsDB.profiles[newProfile] then return end
			local sourceName = SinStatsDB.profileKeys[Ns.ProfileKey]
			SinStatsDB.profiles[newProfile] = {}
			Ns.CopyTable(SinStatsDB.profiles[sourceName], SinStatsDB.profiles[newProfile])
			UpdateProfiles()
			-- Switch to the new profile
			SinStatsDB.profileKeys[Ns.ProfileKey] = newProfile
			Ns.Profile = SinStatsDB.profiles[newProfile]
			Ns.NewSettings(Ns.Profile, Ns.Class)
			self.Edit:SetText("")
			self.CurrentProfileText:SetText(format(CurrentProfileFormat, L["CurrentProfile"], newProfile))
			ResetProfileConfig(self)
			self.StatusMessage:SetText(format(L["ProfileCreatedCopy"], Ns.Colors.Green .. newProfile .. "|r", Ns.Colors.Green .. sourceName .. "|r"))
			self.StatusMessage:Show()
			Ns:UpdateProfile()
			Ns:InitialiseProfile(Ns.Profile)
			return
		end

		if action == "createdefault" then
			if newProfile == "" or SinStatsDB.profiles[newProfile] then return end
			Ns:CreateProfile(newProfile, nil, Ns.Class)
			UpdateProfiles()
			-- Switch to the new profile
			SinStatsDB.profileKeys[Ns.ProfileKey] = newProfile
			Ns.Profile = SinStatsDB.profiles[newProfile]
			Ns.NewSettings(Ns.Profile, Ns.Class)
			self.Edit:SetText("")
			self.CurrentProfileText:SetText(format(CurrentProfileFormat, L["CurrentProfile"], newProfile))
			ResetProfileConfig(self)
			self.StatusMessage:SetText(format(L["ProfileCreatedDefault"], Ns.Colors.Green .. newProfile .. "|r"))
			self.StatusMessage:Show()
			Ns:UpdateProfile()
			Ns:InitialiseProfile(Ns.Profile)
			return
		end

		if action == "copyselect" then
			local sourceProfile = self.CopySourceProfile
			if sourceProfile and SinStatsDB.profiles[sourceProfile] then
				self.CopyBtn:SetEnabled(true)
			end
			return
		end

		if action == "copy" then
			local sourceProfile = self.CopySourceProfile
			if not sourceProfile or not SinStatsDB.profiles[sourceProfile] then return end
			local currentName = SinStatsDB.profileKeys[Ns.ProfileKey]
			self.CopyWarning:SetText(format(L["ProfileCopyWarning"], Ns.Colors.Green .. sourceProfile .. "|r", Ns.Colors.Green .. currentName .. "|r"))
			self.CopyWarning:Show()
			self.CopyBtn:Hide()
			self.CopyConfirmBtn:Show()
			self.CopyCancelBtn:Show()
			return
		end

		if action == "copyapply" then
			local sourceProfile = self.CopySourceProfile
			if not sourceProfile or not SinStatsDB.profiles[sourceProfile] then return end
			local currentName = SinStatsDB.profileKeys[Ns.ProfileKey]
			wipe(SinStatsDB.profiles[currentName])
			Ns.CopyTable(SinStatsDB.profiles[sourceProfile], SinStatsDB.profiles[currentName])
			Ns.Profile = SinStatsDB.profiles[currentName]
			Ns.NewSettings(Ns.Profile, Ns.Class)
			ResetProfileConfig(self)
			self.StatusMessage:SetText(format(L["ProfileCopiedMsg"], Ns.Colors.Green .. sourceProfile .. "|r", Ns.Colors.Green .. currentName .. "|r"))
			self.StatusMessage:Show()
			Ns:UpdateProfile()
			Ns:InitialiseProfile(Ns.Profile)
			return
		end

		if action == "copycancel" then
			ProfileDisableAll(self)
			ResetProfileConfig(self)
			return
		end

		if action == "deleteselect" then
			local target = self.DeleteTargetProfile
			if target and target ~= "Default" and SinStatsDB.profiles[target] then
				self.DeleteBtn:SetEnabled(true)
			end
			return
		end

		if action == "delete" then
			local target = self.DeleteTargetProfile
			if not target or target == "Default" then return end
			local chars = GetCharactersUsingProfile(target)
			if #chars > 0 then
				local coloredChars = {}
				for _, c in ipairs(chars) do
					tinsert(coloredChars, Ns.Colors.Green .. c .. "|r")
				end
				self.DeleteWarning:SetText(L["ProfileDeleteWarning"] .. "\n" .. table.concat(coloredChars, ", "))
			else
				self.DeleteWarning:SetText(L["ProfileDeleteNoChars"])
			end
			self.DeleteWarning:Show()
			self.DeleteBtn:Hide()
			self.DeleteConfirmBtn:Show()
			self.DeleteCancelBtn:Show()
			return
		end

		if action == "confirmdelete" then
			local target = self.DeleteTargetProfile
			if not target or target == "Default" then return end
			for charKey, pName in pairs(SinStatsDB.profileKeys) do
				if pName == target then
					SinStatsDB.profileKeys[charKey] = "Default"
				end
			end
			SinStatsDB.profiles[target] = nil
			UpdateProfiles()
			if SinStatsDB.profileKeys[Ns.ProfileKey] == "Default" then
				Ns.Profile = SinStatsDB.profiles["Default"]
				Ns.NewSettings(Ns.Profile, Ns.Class)
				Ns:UpdateProfile()
				Ns:InitialiseProfile(Ns.Profile)
			end
			self.CurrentProfileText:SetText(format(CurrentProfileFormat, L["CurrentProfile"], SinStatsDB.profileKeys[Ns.ProfileKey]))
			ResetProfileConfig(self)
			return
		end

		if action == "deletecancel" then
			ProfileDisableAll(self)
			ResetProfileConfig(self)
			return
		end
	end

	f.CurrentProfile = {}
	f.CopySourceProfile = nil
	f.DeleteTargetProfile = nil

	return f
end

Mixins.functions.ProfileConfig = {}
function Mixins.functions.ProfileConfig.Init(self, anchorframe, parent, displaylist)
	UpdateProfiles()
	self.Anchoreframe = anchorframe
	self.MyInformation = parent.DisplayOrder[self:GetID()]
	self.DisplayOrder = parent.DisplayOrder
	self.Edit:SetText("")
	self.CurrentProfile[Ns.ProfileKey] = SinStatsDB.profileKeys[Ns.ProfileKey]
	self.SvTable = self.CurrentProfile
	self.Key = Ns.ProfileKey
	self.CurrentProfileText:SetText(format(CurrentProfileFormat, L["CurrentProfile"], SinStatsDB.profileKeys[Ns.ProfileKey]))

	self.SelectList = Ns:GetWidget("DropList", self, self:GetID())
	self.SelectList:SetParent(self)
	self.SelectList:ClearAllPoints()
	self.SelectList:SetPoint("TOPLEFT", 0, self.SelectListAnchorY)
	self.SelectList:Init(self, self)
	self.SelectList.Tip:Hide()
	self.SelectList:Show()
	self.LoadBtn:ClearAllPoints()
	self.LoadBtn:SetPoint("LEFT", self.SelectList, "RIGHT", 10, 0)

	self.CopyList = Ns:GetWidget("DropList", self, self:GetID())
	self.CopyList:SetParent(self)
	self.CopyList:ClearAllPoints()
	self.CopyList:SetPoint("TOPLEFT", 0, self.CopyListAnchorY)
	self.CopyList:Init(self, self)
	self.CopyList.Tip:Hide()
	self.CopyList:Show()

	self.CopyList.DropButton:SetScript("OnClick", function(btn)
		PlayOnCheck()
		local listParent = btn:GetParent()
		if not listParent.List:IsShown() then
			listParent.List.MyInformation = btn.MyInformation
			listParent.List.Settings = btn.Settings
			listParent.List.Key = btn.Key
			listParent.List.Data = btn.Data
			listParent.List.IsCopyList = true
		end
		listParent.List:SetShown(not listParent.List:IsShown())
	end)
	self.CopyBtn:ClearAllPoints()
	self.CopyBtn:SetPoint("LEFT", self.CopyList, "RIGHT", 10, 0)
	self.CopyConfirmBtn:ClearAllPoints()
	self.CopyConfirmBtn:SetPoint("LEFT", self.CopyList, "RIGHT", 10, 0)
	self.CopyCancelBtn:ClearAllPoints()
	self.CopyCancelBtn:SetPoint("LEFT", self.CopyConfirmBtn, "RIGHT", 10, 0)

	self.DeleteList = Ns:GetWidget("DropList", self, self:GetID())
	self.DeleteList:SetParent(self)
	self.DeleteList:ClearAllPoints()
	self.DeleteList:SetPoint("TOPLEFT", 400, self.DeleteListAnchorY)
	self.DeleteList:Init(self, self)
	self.DeleteList.Tip:Hide()
	self.DeleteList:Show()
	self.DeleteList.DropButton:SetScript("OnClick", function(btn)
		PlayOnCheck()
		local listParent = btn:GetParent()
		if not listParent.List:IsShown() then
			listParent.List.MyInformation = btn.MyInformation
			listParent.List.Settings = btn.Settings
			listParent.List.Key = btn.Key
			listParent.List.Data = btn.Data
			listParent.List.IsDeleteList = true
		end
		listParent.List:SetShown(not listParent.List:IsShown())
	end)
	self.DeleteBtn:ClearAllPoints()
	self.DeleteBtn:SetPoint("TOPLEFT", self.DeleteList, "BOTTOMLEFT", 0, -8)
	self.DeleteConfirmBtn:ClearAllPoints()
	self.DeleteConfirmBtn:SetPoint("TOPLEFT", self.DeleteList, "BOTTOMLEFT", 0, -8)
	self.DeleteCancelBtn:ClearAllPoints()
	self.DeleteCancelBtn:SetPoint("LEFT", self.DeleteConfirmBtn, "RIGHT", 10, 0)
	self.DeleteWarning:ClearAllPoints()
	self.DeleteWarning:SetPoint("TOPLEFT", self.DeleteBtn, "BOTTOMLEFT", 0, -8)

	if self.HasSpecProfiles then
		-- ensure specProfiles table exists for this character
		if not SinStatsDB.specProfiles then SinStatsDB.specProfiles = {} end
		if not SinStatsDB.specProfiles[Ns.ProfileKey] then SinStatsDB.specProfiles[Ns.ProfileKey] = {} end
		self:UpdateSpecSection()
	end

	self:RunAction("select")
end

function Mixins.functions.ProfileConfig.Reset(self)
	if self.SelectList then Ns:KillWidget(self.SelectList); self.SelectList = nil end
	if self.CopyList then Ns:KillWidget(self.CopyList); self.CopyList = nil end
	if self.DeleteList then Ns:KillWidget(self.DeleteList); self.DeleteList = nil end
	if self.SpecLists then
		for i, dl in pairs(self.SpecLists) do
			if dl then Ns:KillWidget(dl); self.SpecLists[i] = nil end
		end
	end
end

------------------------------
--			Sliders			--
------------------------------
function W.Slider(parent)
	local f = CreateFrame("Slider", nil, parent, BackdropTemplateMixin and "BackdropTemplate")
	f.SpecialType = "Slider"
	f:SetSize(100, 12)
	f:SetBackdrop(SliderBG)
	f:SetBackdropColor(0.27451, 0.27451, 0.27451, 0.3)
	f:SetBackdropBorderColor(0.184, 0.184, 0.184, 0.3)
	AddTipText(f)
	f:SetValueStep(1)
	f:SetObeyStepOnDrag(true)
	f:SetOrientation("HORIZONTAL")
	f:SetFrameLevel(4)
	f:SetThumbTexture(Ns.TexturePath.."SliderThumb")
	f:GetThumbTexture():SetSize(22, 14)
	f:GetThumbTexture():SetVertexColor(greenColor.r, greenColor.g, greenColor.b, 0.9)--(0.784, 0.850, 0.941)--(1, 0.737, 0, 0.9)
	f:GetThumbTexture():SetBlendMode("ADD")
	f.Min = f:CreateFontString()
	f.Min:SetFont(Ns.ConfigDefaultFont, Ns.ConfigDefaultFontSize - 5)
	f.Min:SetPoint("LEFT", 3, 0)
	f.Min:SetJustifyH("LEFT")
	f.Min:SetJustifyV("BOTTOM")
	f.Max = f:CreateFontString()
	f.Max:SetFont(Ns.ConfigDefaultFont, Ns.ConfigDefaultFontSize - 5)
	f.Max:SetPoint("RIGHT", -3, 0)
	f.Max:SetJustifyH("RIGHT")
	f.Max:SetJustifyV("BOTTOM")
	f.Edit = W.BaseEditBox(f, 50, 12)
	f.Edit:SetPoint("TOP", f, "BOTTOM", 0, 1)
	CreateBorder(f.Edit, 0)
	f.Edit:SetFont(Ns.ConfigDefaultFont2, 12, "")
	f.Edit:SetTextColor(greenColor.r, greenColor.g, greenColor.b)
	f.Edit:SetJustifyH("CENTER")
	Mixin(f.Edit, Mixins.functions.SliderEdit)
	for k, v in pairs(Mixins.scripts.SliderEdit) do
		f.Edit:SetScript(k, v)
	end

	local arrowSize = 10
	local leftArrow = CreateFrame("Button", nil, f)
	leftArrow:SetSize(arrowSize, arrowSize)
	leftArrow:SetPoint("RIGHT", f.Edit, "LEFT", 0, 0)
	leftArrow.Icon = leftArrow:CreateTexture(nil, "ARTWORK")
	leftArrow.Icon:SetAllPoints()
	leftArrow.Icon:SetTexture(Ns.TexturePath .. "ArrowUp")
	leftArrow.Icon:SetTexCoord(1, 0, 0, 0, 1, 1, 0, 1)
	leftArrow.Icon:SetVertexColor(greenColor.r, greenColor.g, greenColor.b, 0.8)
	leftArrow:SetScript("OnEnter", function(self) self.Icon:SetVertexColor(greenColor.r, greenColor.g, greenColor.b, 1) end)
	leftArrow:SetScript("OnLeave", function(self) self.Icon:SetVertexColor(greenColor.r, greenColor.g, greenColor.b, 0.8) end)
	leftArrow:SetScript("OnClick", function()
		local step = f:GetValueStep()
		f:SetValue(f:GetValue() - step)
	end)
	f.LeftArrow = leftArrow

	local rightArrow = CreateFrame("Button", nil, f)
	rightArrow:SetSize(arrowSize, arrowSize)
	rightArrow:SetPoint("LEFT", f.Edit, "RIGHT", 0, 0)
	rightArrow.Icon = rightArrow:CreateTexture(nil, "ARTWORK")
	rightArrow.Icon:SetAllPoints()
	rightArrow.Icon:SetTexture(Ns.TexturePath .. "ArrowUp")
	rightArrow.Icon:SetTexCoord(0, 1, 1, 1, 0, 0, 1, 0)
	rightArrow.Icon:SetVertexColor(greenColor.r, greenColor.g, greenColor.b, 0.8)
	rightArrow:SetScript("OnEnter", function(self) self.Icon:SetVertexColor(greenColor.r, greenColor.g, greenColor.b, 1) end)
	rightArrow:SetScript("OnLeave", function(self) self.Icon:SetVertexColor(greenColor.r, greenColor.g, greenColor.b, 0.8) end)
	rightArrow:SetScript("OnClick", function()
		local step = f:GetValueStep()
		f:SetValue(f:GetValue() + step)
	end)
	f.RightArrow = rightArrow

	Mixin(f, Mixins.functions.Slider)
	for k, v in pairs(Mixins.scripts.Slider) do
		f:SetScript(k, v)
	end
	return f
end

Mixins.functions.SliderEdit = {}
function Mixins.functions.SliderEdit.GetValue(self)
	return self:GetNumber()
end

function Mixins.functions.SliderEdit.SetValue(self, value)
	local n = format("%.1f", value)
	self:SetText(n)
end


Mixins.scripts.SliderEdit = {}
function Mixins.scripts.SliderEdit.OnEnterPressed(self)
	local value = self:GetValue()
	local min, max = self:GetParent():GetMinMaxValues()
	if value < min then
		value = min
	end
	if value > max then
		value = max
	end
	self:SetValue(value)
	self:GetParent():SetValue(value)
	self:ClearFocus()
end

Mixins.functions.Slider = {}
--local minMaxFormat = "- %s -"
function Mixins.functions.Slider.Init(self, anchorframe, parent, displaylist, orderlist)
	self:SetEnabled(true)
	self.MyInformation = parent.DisplayOrder[self:GetID()]
	self.Settings = parent.MyInformation.svnotintable and parent.SvTable or parent.SvTable[parent.MyInformation.stat]
	self.Key = self.MyInformation.stat
	self.Key = self.Key
	self:SetWidth(self.MyInformation.widget.width)
	local min, max, value = self.MyInformation.widget.min, self.MyInformation.widget.max, self.Settings[self.Key]
	if value == nil then value = self.MyInformation.default or min end
	self.Min:SetText(type(min) == "string" and L[min] or min)
	self.Max:SetText(type(max) == "string" and L[max] or max)
	self:SetMinMaxValues(min, max)
	local step = self.MyInformation.widget.valuestep
	if step then self:SetValueStep(step) end
	self:SetValue(value)
	self.Tip:SetText(format(TipFormat, L[self.Key.."Tip"]))
	self.Tip:SetTextColor(orangeColor.r, orangeColor.g, orangeColor.b)
	self.Edit:SetValue(value)
end

Mixins.scripts.Slider = {}
function Mixins.scripts.Slider.OnValueChanged(self, value)
    if self.Settings[self.Key] == value then return end
    	self.Edit:SetValue(value)
	self.Settings[self.Key] = value
	Ns:QueueProfileRefresh()
end
function Mixins.scripts.Slider.OnDisable(self)
	self:SetAlpha(0.5)
end

function Mixins.scripts.Slider.OnEnable(self)
	self:SetAlpha(1)
end

------------------------------
--			ToolTip			--
------------------------------
function W.Tooltip(parent)
	if not gttFont then
		gttFont, gttFontSize, gttFontFlags = GameTooltipHeaderText:GetFont()
	end
	local f = CreateFrame("Frame")
	f.SpecialType = "Tooltip"
	f:SetSize(12, 12)
	f.Texture = f:CreateTexture()
	f.Texture:SetAllPoints()
	f.Texture:SetTexture(Ns.TexturePath.."Misc")
	Mixin(f, Mixins.functions.Tooltip)
	for k, v in pairs(Mixins.scripts.Tooltip) do
		f:SetScript(k, v)
	end
	return f
end
Mixins.functions.Tooltip = {}
function Mixins.functions.Tooltip.Init(self, parent, settings)
	self:SetParent(parent)
	self:Show()
	self:SetFrameStrata(parent:GetFrameStrata())
	self:SetFrameLevel(parent:GetFrameLevel() + 1)
	self:ClearAllPoints()
	local points = TooltipOffsets[parent.SpecialType] or TooltipOffsets.Default
	self:SetPoint(points.point, parent, points.topoint, points.x, points.y)
	self.Tip = settings.stat.."Tooltip"
end

function Mixins.functions.Tooltip.Reset(self)
	self.Tip = nil
	self:Hide()
end

Mixins.scripts.Tooltip = {}
function Mixins.scripts.Tooltip.OnEnter(self)
	GameTooltip:SetOwner(self, "ANCHOR_TOPLEFT")
	GameTooltipHeaderText:SetFont(Ns.ConfigDefaultFont, 12, "")
	GameTooltip:SetText(L[self.Tip])
end
function Mixins.scripts.Tooltip.OnLeave(self)
	GameTooltipHeaderText:SetFont(gttFont, gttFontSize, gttFontFlags)
	GameTooltip:Hide()
end

----------------------------------
--			Top Tabs			--
----------------------------------
function W.TopTab(parent, width)
	local f = CreateFrame("Button", nil, parent)
	f:SetSize(50, Ns.TopTabHeight)
	f.SpecialType = "TopTab"
	Mixin(f, Mixins.functions.TopTab)
	AddLabel(f)
	f:AnchorLabel("CENTER")
	hooksecurefunc(f, "SetLabel", AdjustTopTabText)
	f.Properties = {}
	f:SetNormalTexture(Ns.TexturePath.."LabelBG")
	f:GetNormalTexture():SetVertexColor(topTabColor.r, topTabColor.g, topTabColor.b, 0.2)
	f:SetHighlightTexture(Ns.TexturePath.."LabelBGLine")
	f:GetHighlightTexture():SetVertexColor(greenColor.r, greenColor.g, greenColor.b, 1)
	for k, v in pairs(Mixins.scripts.TopTab) do
		f:SetScript(k, v)
	end

	f.Status = Ns:GetWidget("CheckButton", f) -- TOP TAB CHECKBOX
	f.Status:SetSize(11, 11)
	f.Status:SetPoint("RIGHT", -3, 1)
	f.Status:SetFrameLevel(f:GetFrameLevel() + 4)
	f.Status:SetHighlightTexture(Ns.TexturePath.."LabelBGLine")
	f.Status:GetHighlightTexture():ClearAllPoints()
	f.Status:GetHighlightTexture():SetAllPoints(f:GetHighlightTexture())
	f.Status:GetHighlightTexture():SetVertexColor(greenColor.r, greenColor.g, greenColor.b, 1)
	return f
end

Mixins.functions.TopTab = {}
function Mixins.functions.TopTab.Init(self, anchorframe, parent, noicons)
	self.AnchorFrame = anchorframe
	self.Parent = parent
	self.MyInformation = parent.DisplayOrder[self:GetID()]
--	self.GroupOrder = nil
	self.DisplayOrder = parent.NextDisplayOrder
	self.SvTable = parent.SvTable
	self.Key = self.MyInformation.stat.."Menu"
	if self.ActiveOnInit then
		self:Click()
		self.ActiveOnInit = nil
	else
		--self:GetNormalTexture():SetAlpha(0.3)
		self:SetNormalTexture(Ns.TexturePath.."LabelBG")
		self:GetNormalTexture():SetVertexColor(topTabColor.r, topTabColor.g, topTabColor.b, 0.2)
		self.Properties.Checked = false
		--self.Label:SetTextColor(0.7, 0.7, 0.7)
	end
	self:SetLabel(GetLabelText(self.Key, self.MyInformation, noicons))
	if type(self.SvTable[self.MyInformation.stat]) == "table" then --[self.MyInformation.stat].Show then
		self.Status.MyInformation = parent.DisplayOrder[self:GetID()]
		self.Status.Settings = self.SvTable[self.MyInformation.stat]
		self.Status.Key = "Show"
		self.Status:SetChecked(self.Status.Settings[self.Status.Key])
		self.Status:Show()
		self:AnchorLabel("LEFT", 6, 0, true)
	else
		if self.MyInformation.enabled and self.MyInformation.enabledval then
			if Ns.Profile[self.MyInformation.enabled] ~= self.MyInformation.enabledval then
				self:SetEnabled(false)
			end
		end
		self:AnchorLabel("CENTER")
		self.Status:Hide()
	end
end

function Mixins.functions.TopTab.Reset(self)
	wipe(self.Properties)
	self.Status:Reset()
end

Mixins.scripts.TopTab = {}
function Mixins.scripts.TopTab.OnClick(self)
	--PlayOnTab()
	local parent = self.AnchorFrame or self:GetParent()
	if parent.ActiveWidget and parent.ActiveWidget == self then
		return
	end
	self.Properties.Checked = not self.Properties.Checked
	if self.Properties.Checked then
		--self:GetNormalTexture():SetVertexColor(greenColor.r, greenColor.g, greenColor.b, 0.3)
		self:SetNormalTexture(Ns.TexturePath.."LabelBGLine")
		self:GetNormalTexture():SetVertexColor(greenColor.r, greenColor.g, greenColor.b, 1)
		--self:GetNormalTexture():SetAlpha(1)
		--self.Label:SetTextColor(1, 1, 1)
	else
		--self:GetNormalTexture():SetVertexColor(topTabColor.r, topTabColor.g, topTabColor.b, 0.3)
		self:SetNormalTexture(Ns.TexturePath.."LabelBG")
		self:GetNormalTexture():SetVertexColor(topTabColor.r, topTabColor.g, topTabColor.b, 0.2)
		--self.Label:SetTextColor(1, 1,m)
	end
	KillPage("ChildAnchor")
	if self.Properties.Checked then
		self.AnchorFrame:SetActive(self)
		Ns.ConfigFrame.ChildAnchor = Ns:GetWidget("AnchorFrame", Ns.ConfigFrame)
		Ns.ConfigFrame.ChildAnchor:SetLabel("")
		Ns.ConfigFrame.ChildAnchor.Description:SetText(format(Ns.WidgetDisplayFormat, Ns:GetSpellIcon(self.MyInformation), L[self.MyInformation.stat.."Menu"])) -- .."Description" STATS TOP LINE TEXT
		Ns.ConfigFrame.ChildAnchor.Description:SetTextColor(1,1,1)
		Ns.ConfigFrame.ChildAnchor.CenterAlign = self.MyInformation.centeralign
		self.Parent.DisplayFunc(self, Ns.ConfigFrame.ChildAnchor, self.AnchorFrame, false, true, self.MyInformation.taboffset)
		for k, v in pairs(Ns.ConfigFrame.ChildAnchor.CurrentWidgets) do
			if v.Check and v.Check.Key == "Show" then
				v.Check.ParentLink = self.Status
				self.Status.ChildLink = v.Check
			end
		end
		Ns.ConfigFrame.ChildAnchor:Show()
	end
end

function Mixins.scripts.TopTab.OnDisable(self)
	self:SetAlpha(0.5)
end

function Mixins.scripts.TopTab.OnEnable(self)
	self:SetAlpha(1)
end

------------------------------------------
--			Get/Kill Widgets			--
------------------------------------------
function Ns:GetWidget(widgettype, parent, id)
	local widget
	if WidgetPool[widgettype] and #WidgetPool[widgettype] > 0 then
		local pool = WidgetPool[widgettype]
		widget = pool[#pool]
		pool[#pool] = nil
	end
	if not widget then
		widget = W[widgettype](parent, id)
	elseif parent then
		widget:SetParent(parent)
	end
	if type(id) == "number" then
		widget:SetID(id)
	end
	return widget
end

function Ns:KillWidget(widget)
	widget:Hide()
	local widgettype = widget.SpecialType or widget:GetObjectType()
	if not WidgetPool[widgettype] then
		WidgetPool[widgettype] = {}
	end
	tinsert(WidgetPool[widgettype], widget)
	widget:ClearAllPoints()
	widget:SetID(-1)
	if widget.Reset then
		widget:Reset()
	end
	if widget.Tooltip then
		Ns:KillWidget(widget.Tooltip)
		widget.Tooltip = nil
	end
end

----------------------------------
--			Edit Box			--
----------------------------------
--[[
local function EditBox_Init(self)--, data, index)
	local text = self.Data[self.Index]
	if not text then
		text = ""
	end
	self.prevvalue = text
	self:SetText(text)
end
]]--

function W.BaseEditBox(parent, width, height)
	local templates = "InputBoxTemplate"
	if BackdropTemplateMixin then
		templates = templates .. ", BackdropTemplate"
	end
	local f = CreateFrame("EditBox", nil, parent, templates)
	f:SetBackdrop(EditBoxBG)
	f:SetBackdropColor(0.27451, 0.27451, 0.27451, 0.3)
	f:SetBackdropBorderColor(0, 0, 0, 1)
	f.Left:Hide()
	f.Right:Hide()
	f.Middle:Hide()
	f:SetFont(Ns.ConfigDefaultFont, 12, "")
	f:SetJustifyH("CENTER")
	if width and height then
		f:SetSize(width, height)
	end
	f:SetMaxLetters(80)
	f:SetAutoFocus(false)
	f:SetScript("OnEscapePressed", function(self)
		if self.prevvalue then
			self:SetText(self.prevvalue)
		else
			self:SetText("")
		end
		self:ClearFocus()
	end)
	return f
end

function W.EditBox(parent)
	local f = W.BaseEditBox(parent)
	f.SpecialType = "EditBox"
	Mixin(f, Mixins.functions.EditBox)
	AddLabel(f)
	for k, v in pairs(Mixins.scripts.EditBox) do
		f:SetScript(k, v)
	end
	return f
end

Mixins.functions.EditBox = {}
function Mixins.functions.EditBox.Init(self, anchorframe, parent, displaylist, orderlist)
--	self:SetParent(parent)
	self.MyInformation = parent.DisplayOrder[self:GetID()]
	self.Settings = parent.MyInformation.svnotintable and parent.SvTable or parent.SvTable[parent.MyInformation.stat]
	self.Key = self.MyInformation.stat
	self:SetSize(self.MyInformation.widget.width, self.MyInformation.widget.height)
	self:AnchorLabel("TOPLEFT", 0, 14, true)
	self:SetFont(Ns.ConfigDefaultFont2, Ns.ConfigDefaultFontSize - 4, "")
	self:SetLabel(GetLabelText(self.MyInformation.stat, self.MyInformation, true))
	self.Label:SetTextColor(orangeColor.r, orangeColor.g, orangeColor.b)
	self:SetValue(self.Settings[self.Key] or "")
end

function Mixins.functions.EditBox.SetEnabled(self, enabled)
	self:EnableMouse(enabled)
	self:EnableKeyboard(enabled)
	if enabled then
		self:SetAlpha(1)
	else
		self:ClearFocus()
		self:SetAlpha(0.4)
	end
end

function Mixins.functions.EditBox.Reset(self)
	self:SetEnabled(true)
end

function Mixins.functions.EditBox.GetValue(self)
	if self.MyInformation.widget.numeric then
		return self:GetNumber()
	else
		return self:GetText()
	end
end

function Mixins.functions.EditBox.SetValue(self, value)
	if self.MyInformation.widget.numeric then
		value = tonumber(value)
		if self.MyInformation.widget.format then
			value = format(self.MyInformation.widget.format, value)
		end
	end
	self:SetText(value)
	self.prevvalue = value
end

Mixins.scripts.EditBox = {}
function Mixins.scripts.EditBox.OnEnterPressed(self)
	local value = self:GetValue()
	self:SetValue(value)
	self.Settings[self.Key] = value
	self:ClearFocus()
	Ns:QueueProfileRefresh()
end

----------------------------------
--			DragStat			--
----------------------------------
local DSWidth, DSHeight, Spacer = 120, 20, 12
function W.DragStat(parent)
	local f = CreateFrame("Frame", nil, parent, "BackdropTemplate")
	f.SpecialType = "DragStat"
	f:SetSize(DSWidth, DSHeight)
	f:EnableMouse(false)
	f.IdText = f:CreateFontString()
	f.IdText:SetFont(Ns.ConfigDefaultFont, Ns.ConfigDefaultFontSize - 6)
	f.IdText:SetTextColor(greenColor.r, greenColor.g, greenColor.b, 1)
	f.IdText:SetPoint("LEFT", 5, 0)
	f.Icon = f:CreateTexture(nil, "ARTWORK")
	f.Icon:SetSize(14, 14)
	f.Icon:SetPoint("LEFT", f.IdText, "RIGHT", 3, 0)
	f.Text = f:CreateFontString()
	f.Text:SetFont(Ns.ConfigDefaultFont, Ns.ConfigDefaultFontSize - 4)
	f.Text:SetJustifyH("LEFT")
	f.Text:SetPoint("LEFT", f.Icon, "RIGHT", 3, 0)
	f:SetBackdrop(DragBG)
	f:SetBackdropColor(topTabColor.r, topTabColor.g, topTabColor.b, 0.3)
	f:SetBackdropBorderColor(greenColor.r, greenColor.g, greenColor.b, 0)
	Mixin(f, Mixins.functions.DragStat)
	for k, v in pairs(Mixins.scripts.DragStat) do
		f:SetScript(k, v)
	end

	f.NewColumn = Ns:GetWidget("CheckButton", f)
	f.NewColumn:SetPoint("RIGHT", -3, 0)
	f.NewColumn:SetSize(12,12)
	f.NewColumn:SetCheckedTexture(Ns.TexturePath.."CheckColumn")
	f.NewColumn:GetCheckedTexture():SetVertexColor(1, 1, 1)

	return f
end
Mixins.functions.DragStat = {}

function Mixins.functions.DragStat.Init(self, parent, id, shufflefunc)
	self:SetParent(parent)
	self.ID = id
	if not self.LastOrder then
		if self.ID == 1 then
			if self.NewColumn:GetChecked() then
				self.NewColumn:Click()
			end
			self.NewColumn:Hide()
		elseif self.ID > 1 then
			self.NewColumn:Show()
		end
	end
	self.ShuffleFunc = shufflefunc
end

function Mixins.functions.DragStat.GetText(self)
	return self.Text:GetText()
end

function Mixins.functions.DragStat.Reset(self)
	self.ID = nil
	self.IdText:SetText("")
	self.ShuffleFunc = nil
end

function Mixins.functions.DragStat.SetIDText(self, text)
	self.IdText:SetText(text)
end

function Mixins.functions.DragStat.SetText(self, text)
	self.Text:SetText(text)
end

Mixins.scripts.DragStat = {}
function Mixins.scripts.DragStat.OnEnter(self)
	self:SetBackdropColor(greenColor.r, greenColor.g, greenColor.b, 0.6)
end

function Mixins.scripts.DragStat.OnHide(self)
end

function Mixins.scripts.DragStat.OnLeave(self)
	self:SetBackdropColor(topTabColor.r, topTabColor.g, topTabColor.b, 0.3)
end

function Mixins.scripts.DragStat.OnMouseDown(self)
end

function Mixins.scripts.DragStat.OnMouseUp(self)
end

--------------------------------------
--			Display Order			--
--------------------------------------

-- cell dimensions for layout
local CellW, CellH = 135, 18
local CellW_Compact = 68
local CellGapV, CellGapH = 2, 6
local PreviewPad = 6
local PreviewMaxW = 590 -- max visible width before horizontal scroll kicks in

local function UpdateHScrollUI(self)
	local maxH = self.MaxHScroll or 0
	local hOff = self.HScrollOffset or 0

	if self.HScrollLeft then
		if hOff > 0 then self.HScrollLeft:Show() else self.HScrollLeft:Hide() end
	end
	if self.HScrollRight then
		if hOff < maxH then self.HScrollRight:Show() else self.HScrollRight:Hide() end
	end

	if self.HScrollBar then
		if maxH > 0 then
			self.HScrollBar:Show()
			local trackW = self.HScrollBar:GetWidth()
			if trackW > 0 then
				local visW = self.Preview:GetWidth()
				local contentW = visW + maxH
				local thumbW = math.max(20, trackW * (visW / contentW))
				self.HScrollBar.Thumb:SetWidth(thumbW)
				local travel = trackW - thumbW
				local ratio = (maxH > 0) and (hOff / maxH) or 0
				self.HScrollBar.Thumb:ClearAllPoints()
				self.HScrollBar.Thumb:SetPoint("LEFT", self.HScrollBar, "LEFT", travel * ratio, 0)
			end
		else
			self.HScrollBar:Hide()
		end
	end
end

local function RefreshPreviewLayout(self)
	if not self.WidgetList or #self.WidgetList == 0 then return end

	local totalStats = #self.StatList
	local hOff = self.HScrollOffset or 0

	if self.RowLayoutRows and self.RowLayoutRows > 1 then
		self.CompactMode = false
		local rows = self.RowLayoutRows
		local cols = math.ceil(totalStats / rows)
		local maxRow = 0
		for k = 1, totalStats do
			local cell = self.WidgetList[k]
			if not cell then break end
			local r = math.floor((k - 1) / cols)
			local c = (k - 1) % cols
			cell.ColIndex = c + 1
			cell.RowIndex = r + 1
			cell:ClearAllPoints()
			cell:SetPoint("TOPLEFT", self.Preview, "TOPLEFT",
				PreviewPad + c * (CellW + CellGapH) - hOff,
				-(PreviewPad + r * (CellH + CellGapV)))
			cell:SetSize(CellW, CellH)
			cell:Show()
			cell.IdText:Show()
			cell.Icon:ClearAllPoints()
			cell.Icon:SetPoint("LEFT", cell.IdText, "RIGHT", 3, 0)
			if cell.Icon:IsShown() then cell.Icon:SetSize(14, 14) end
			cell.Text:ClearAllPoints()
			cell.Text:SetPoint("LEFT", cell.Icon, "RIGHT", 3, 0)
			cell.IdText:SetText(k .. ".")
			if r + 1 > maxRow then maxRow = r + 1 end
		end
		local contentW = cols * CellW + (cols - 1) * CellGapH + PreviewPad * 2
		local ph = maxRow * (CellH + CellGapV) - CellGapV + PreviewPad * 2
		local visW = math.min(math.max(contentW, 130), PreviewMaxW)
		local actualH = math.max(ph, 30)
		self.Preview:SetSize(visW, actualH)
		self.MaxHScroll = math.max(0, contentW - visW)
		if hOff > self.MaxHScroll then
			self.HScrollOffset = self.MaxHScroll
		end
		local hBarExtra = (self.MaxHScroll > 0) and 10 or 0
		local topBarH = self.Save:GetHeight() + 26
		local hintH = self.Hint:GetStringHeight() + 4
		self:SetHeight(topBarH + hintH + actualH + hBarExtra + 10)
		UpdateHScrollUI(self)
		return
	end

	local preComputedCols = 1
	for k = 2, totalStats do
		if self.ColumnBreaks[k] then preComputedCols = preComputedCols + 1 end
	end

	local isCompact = (preComputedCols == totalStats and totalStats > 1)
	local cellW = isCompact and CellW_Compact or CellW
	self.CompactMode = isCompact

	local preContentW = preComputedCols * cellW + (preComputedCols - 1) * CellGapH + PreviewPad * 2
	local preVisW = math.min(math.max(preContentW, 130), PreviewMaxW)
	local preMaxH = math.max(0, preContentW - preVisW)
	if hOff > preMaxH then
		self.HScrollOffset = preMaxH
		hOff = preMaxH
	end

	local col, row = 1, 1
	local colX = 0
	local maxRowPerCol = {}

	for k = 1, totalStats do
		local cell = self.WidgetList[k]
		if not cell then break end

		local isNewCol = (k > 1) and (self.ColumnBreaks[k] == true)
		if isNewCol then
			col = col + 1
			row = 1
			colX = colX + cellW + CellGapH
		end

		cell.ColIndex = col
		cell.RowIndex = row
		cell:ClearAllPoints()
		cell:SetPoint("TOPLEFT", self.Preview, "TOPLEFT",
			PreviewPad + colX - hOff,
			-(PreviewPad + (row - 1) * (CellH + CellGapV)))
		cell:SetSize(cellW, CellH)
		cell:Show()

		if isCompact then
			cell.IdText:Hide()
			cell.Icon:ClearAllPoints()
			cell.Icon:SetPoint("LEFT", 3, 0)
			if cell.Icon:IsShown() then cell.Icon:SetSize(10, 10) end
			cell.Text:ClearAllPoints()
			cell.Text:SetPoint("LEFT", cell.Icon, "RIGHT", 2, 0)
			cell.Text:SetPoint("RIGHT", cell, "RIGHT", -2, 0)
		else
			cell.IdText:Show()
			cell.Icon:ClearAllPoints()
			cell.Icon:SetPoint("LEFT", cell.IdText, "RIGHT", 3, 0)
			if cell.Icon:IsShown() then cell.Icon:SetSize(14, 14) end
			cell.Text:ClearAllPoints()
			cell.Text:SetPoint("LEFT", cell.Icon, "RIGHT", 3, 0)
		end

		maxRowPerCol[col] = row
		row = row + 1
	end

	local totalCols = col
	local maxRow = 0
	for _, r in pairs(maxRowPerCol) do
		if r > maxRow then maxRow = r end
	end
	local contentW = totalCols * cellW + (totalCols - 1) * CellGapH + PreviewPad * 2
	local ph = maxRow * (CellH + CellGapV) - CellGapV + PreviewPad * 2
	local visW = math.min(math.max(contentW, 130), PreviewMaxW)
	local actualH = math.max(ph, 30)
	self.Preview:SetSize(visW, actualH)

	self.MaxHScroll = math.max(0, contentW - visW)
	if hOff > self.MaxHScroll then
		self.HScrollOffset = self.MaxHScroll
	end


	local hBarExtra = (self.MaxHScroll > 0) and 10 or 0
	local topBarH = self.Save:GetHeight() + 26 -- gap between top and hint
	local hintH = self.Hint:GetStringHeight() + 4 -- hint text + gap
	self:SetHeight(topBarH + hintH + actualH + hBarExtra + 10)
	UpdateHScrollUI(self)

	local cellsByRow = {}
	for k = 1, totalStats do
		local cell = self.WidgetList[k]
		if cell then
			local r = cell.RowIndex or 1
			if not cellsByRow[r] then cellsByRow[r] = {} end
			tinsert(cellsByRow[r], cell)
		end
	end
	local visualNum = 1
	for r = 1, maxRow do
		if cellsByRow[r] then
			table.sort(cellsByRow[r], function(a, b) return a.ColIndex < b.ColIndex end)
			for _, cell in ipairs(cellsByRow[r]) do
				cell.IdText:SetText(visualNum .. ".")
				visualNum = visualNum + 1
			end
		end
	end
end

local function GridCell_OnEnter(self)
	local parent = self.ConfigParent
	if parent.IsDragging then
		self:SetBackdropColor(0, 0, 0, 0.6)
		self:SetBackdropBorderColor(greenColor.r, greenColor.g, greenColor.b, 0.5)
		parent.DropTarget = self
	else
		self:SetBackdropColor(0, 0, 0, 0.6)
		self:SetBackdropBorderColor(greenColor.r, greenColor.g, greenColor.b, 0.5)
	end
end

local function GridCell_OnLeave(self)
	local parent = self.ConfigParent
	self:SetBackdropBorderColor(0.25, 0.25, 0.25, 0.6)
	self:SetBackdropColor(topTabColor.r, topTabColor.g, topTabColor.b, 0.5)
	if parent.DropTarget == self then
		parent.DropTarget = nil
	end
end

local function GridCell_OnMouseDown(self, button)
	local parent = self.ConfigParent
	if button == "RightButton" then
		if self.ID > 1 then
			if parent.RowLayoutRows and parent.RowLayoutRows > 1 then
				local clickedStat = parent.StatList[self.ID]
				local total = #parent.StatList
				local rows = parent.RowLayoutRows
				local cols = math.ceil(total / rows)
				local colMajor = {}
				local newBreaks = {}
				for c = 0, cols - 1 do
					for r = 0, rows - 1 do
						local visualIdx = r * cols + c + 1
						if visualIdx <= total then
							tinsert(colMajor, parent.StatList[visualIdx])
							if c > 0 and r == 0 then
								newBreaks[#colMajor] = true
							end
						end
					end
				end

				local newClickID

				for k = 1, total do
					parent.StatList[k] = colMajor[k]
					parent.ColumnBreaks[k] = newBreaks[k] or false
					if colMajor[k] == clickedStat then newClickID = k end
				end

				for k, v in ipairs(parent.StatList) do
					parent.WidgetList[k].ID = k
					parent.WidgetList[k].Text:SetText(L[v.stat])
				end
				parent.RowLayoutRows = nil

				if newClickID and newClickID > 1 then
					parent.ColumnBreaks[newClickID] = not parent.ColumnBreaks[newClickID]
				end
			else
				parent.ColumnBreaks[self.ID] = not parent.ColumnBreaks[self.ID]
			end
			RefreshPreviewLayout(parent)
			parent.Save:SetEnabled(true)
		end
		return
	end

	parent.IsDragging = true
	parent.DragOrigin = self
	parent.DropTarget = nil
	parent.DropMode = nil   -- "swap" or "insert"
	parent.InsertAt = nil

	parent.Ghost:SetText(self:GetText())
	if self.Icon:IsShown() then
		parent.Ghost.Icon:SetTexture(self.Icon:GetTexture())
		parent.Ghost.Icon:SetSize(14, 14)
		parent.Ghost.Icon:Show()
	else
		parent.Ghost.Icon:SetTexture(nil)
		parent.Ghost.Icon:SetSize(0.1, 0.1)
		parent.Ghost.Icon:Hide()
	end
	local ghostW = parent.CompactMode and CellW_Compact or CellW
	parent.Ghost:SetSize(ghostW, CellH)
	if parent.CompactMode then
		parent.Ghost.Icon:SetSize(10, 10)
	else
		parent.Ghost.Icon:SetSize(14, 14)
	end
	parent.Ghost:Show()

	self:SetAlpha(0.35)
	parent.DragNextCheck = 0
	parent:SetScript("OnUpdate", function(p, elapsed)
		if not IsMouseButtonDown("LeftButton") then
			p.IsDragging = false
			p:SetScript("OnUpdate", nil)
			p.Ghost:Hide()
			p.InsertBar:Hide()
			if p.DragOrigin then p.DragOrigin:SetAlpha(1) end
			if p.DropTarget then
				p.DropTarget:SetBackdropBorderColor(0.25, 0.25, 0.25, 0.6)
				p.DropTarget:SetBackdropColor(topTabColor.r, topTabColor.g, topTabColor.b, 0.5)
			end
			p.DragOrigin = nil
			p.DropTarget = nil
			p.DropMode = nil
			p.InsertAt = nil
			return
		end

		local scale = p:GetEffectiveScale()
		local cx, cy = GetCursorPosition()
		cx, cy = cx / scale, cy / scale
		p.Ghost:ClearAllPoints()
		p.Ghost:SetPoint("CENTER", UIParent, "BOTTOMLEFT", cx, cy)

		p.DragNextCheck = (p.DragNextCheck or 0) - elapsed -- throttling hit-detection a bit
		if p.DragNextCheck > 0 then return end
		p.DragNextCheck = 0.033

		local best, bestMode, bestInsert
		for k = 1, #p.StatList do
			local cell = p.WidgetList[k]
			if cell and cell:IsMouseOver() and cell ~= p.DragOrigin then
				local cellL = cell:GetLeft()
				local cellR = cell:GetRight()
				local cellT = cell:GetTop()
				local cellB = cell:GetBottom()
				if cellL and cellR and cellT and cellB then
					local cellW = cellR - cellL
					local cellH = cellT - cellB
					local edgeH = cellW * 0.37  -- 35% edge zone on left/right
					local edgeV = cellH * 0.42   -- 40% edge zone on top/bottom
					local relX = cx - cellL
					local relY = cellT - cy

					local isRow = (p.RowLayoutRows and p.RowLayoutRows > 1) or p.CompactMode
					if isRow then
						if relX < edgeH then
							bestMode = "insert"
							bestInsert = cell.ID
						elseif relX > cellW - edgeH then
							bestMode = "insert"
							bestInsert = cell.ID + 1
						else
							bestMode = "swap"
						end
					else

						if relY < edgeV then
							bestMode = "insert"
							bestInsert = cell.ID
						elseif relY > cellH - edgeV then
							bestMode = "insert"
							bestInsert = cell.ID + 1
						else
							bestMode = "swap"
						end
					end
				end
				best = cell
				break
			end
		end

		if p.DropTarget and (p.DropTarget ~= best or p.DropMode ~= bestMode) then
			p.DropTarget:SetBackdropBorderColor(0.25, 0.25, 0.25, 0.6)
			p.DropTarget:SetBackdropColor(topTabColor.r, topTabColor.g, topTabColor.b, 0.5)
		end
		p.InsertBar:Hide()

		if best then
			p.DropTarget = best
			p.DropMode = bestMode
			p.InsertAt = bestInsert
				if bestMode == "swap" then
				best:SetBackdropColor(0, 0, 0, 0.6)
				best:SetBackdropBorderColor(greenColor.r, greenColor.g, greenColor.b, 0.5)
			else
				local isRow = (p.RowLayoutRows and p.RowLayoutRows > 1) or p.CompactMode
				if isRow then
					p.InsertBar:SetSize(2, CellH)
					p.InsertBar:ClearAllPoints()
					if bestInsert <= best.ID then
						p.InsertBar:SetPoint("RIGHT", best, "LEFT", -CellGapH / 2, 0)
					else
						p.InsertBar:SetPoint("LEFT", best, "RIGHT", CellGapH / 2, 0)
					end
				else
					p.InsertBar:SetSize(CellW, 2)
					p.InsertBar:ClearAllPoints()
					if bestInsert <= best.ID then
						p.InsertBar:SetPoint("BOTTOM", best, "TOP", 0, CellGapV / 2)
					else
						p.InsertBar:SetPoint("TOP", best, "BOTTOM", 0, -CellGapV / 2)
					end
				end
				p.InsertBar:Show()
			end
		else
			p.DropTarget = nil
			p.DropMode = nil
			p.InsertAt = nil
		end
	end)
end

local function GridCell_OnMouseUp(self, button)
	if button ~= "LeftButton" then return end
	local parent = self.ConfigParent
	if not parent.IsDragging then return end

	parent.IsDragging = false
	parent:SetScript("OnUpdate", nil)
	parent.Ghost:Hide()
	parent.InsertBar:Hide()

	if parent.DragOrigin then
		parent.DragOrigin:SetAlpha(1)
	end

	if parent.DropTarget then
		parent.DropTarget:SetBackdropBorderColor(0.25, 0.25, 0.25, 0.6)
		parent.DropTarget:SetBackdropColor(topTabColor.r, topTabColor.g, topTabColor.b, 0.5)
	end

	local target = parent.DropTarget
	local mode = parent.DropMode or "swap"
	local insertAt = parent.InsertAt
	if not target and self ~= parent.DragOrigin then
		target = self
		mode = "swap"
	end

	local origin = parent.DragOrigin
	if origin and target and origin ~= target then
		local fromID = origin.ID
		local toID = target.ID
		local changed = false

		if mode == "swap" then
			parent.StatList[fromID], parent.StatList[toID] = parent.StatList[toID], parent.StatList[fromID]
			changed = true
		else
			local insertingAbove = (insertAt == toID) -- true = above target cell
			if insertAt and (insertAt == fromID or insertAt == fromID + 1) then
				if insertAt == fromID and parent.ColumnBreaks[fromID] then
					parent.ColumnBreaks[fromID] = false
					if fromID + 1 <= #parent.ColumnBreaks then
						parent.ColumnBreaks[fromID + 1] = true
					end
					changed = true
				elseif insertAt == fromID + 1 and fromID + 1 <= #parent.ColumnBreaks and parent.ColumnBreaks[fromID + 1] then
					parent.ColumnBreaks[fromID + 1] = false
					if fromID > 1 and not parent.ColumnBreaks[fromID] then
						parent.ColumnBreaks[fromID] = true
					end
					changed = true
				end
				parent.ColumnBreaks[1] = false
			elseif insertAt then
				local targetIsColStart = insertingAbove
					and insertAt <= #parent.ColumnBreaks
					and parent.ColumnBreaks[insertAt]
				local movedStat = tremove(parent.StatList, fromID)
				local hadBreak = parent.ColumnBreaks[fromID]
				tremove(parent.ColumnBreaks, fromID)

				if hadBreak and fromID <= #parent.ColumnBreaks then
					parent.ColumnBreaks[fromID] = true
				end

				if fromID < insertAt then insertAt = insertAt - 1 end
				tinsert(parent.StatList, insertAt, movedStat)
				tinsert(parent.ColumnBreaks, insertAt, false)

				if targetIsColStart and insertAt + 1 <= #parent.ColumnBreaks then
					parent.ColumnBreaks[insertAt] = true
					parent.ColumnBreaks[insertAt + 1] = false
				end
				parent.ColumnBreaks[1] = false
				changed = true
			end
		end

		if changed then
			for k, v in ipairs(parent.StatList) do
				parent.WidgetList[k].ID = k
				parent.WidgetList[k].Text:SetText(L[v.stat])
				local defIdx = Ns.StatToDefaultOrder[v.stat]
				if defIdx then
					local iconTexture = Ns:GetSpellIcon(Ns.DefaultOrder[defIdx], Ns.IgnoreFormat)
					if iconTexture and iconTexture ~= "" then
						parent.WidgetList[k].Icon:SetTexture(iconTexture)
						parent.WidgetList[k].Icon:SetSize(14, 14)
						parent.WidgetList[k].Icon:Show()
					else
						parent.WidgetList[k].Icon:SetTexture(nil)
						parent.WidgetList[k].Icon:SetSize(0.1, 0.1)
						parent.WidgetList[k].Icon:Hide()
					end
				else
					parent.WidgetList[k].Icon:SetTexture(nil)
					parent.WidgetList[k].Icon:SetSize(0.1, 0.1)
					parent.WidgetList[k].Icon:Hide()
				end
			end
			RefreshPreviewLayout(parent)
			parent.Save:SetEnabled(true)
		end
	end

	parent.DragOrigin = nil
	parent.DropTarget = nil
	parent.DropMode = nil
	parent.InsertAt = nil
end

local CellBG = {
	bgFile = "Interface/BUTTONS/WHITE8X8",
	edgeFile = "Interface/Tooltips/UI-Tooltip-Border",
	tile = false,
	edgeSize = 6,
	tileSize = 0,
	insets = { left = 0, right = 0, top = 0, bottom = 0 },
}

---------------------------------------------------------------------------
-- DisplayOrderConfig constructor
---------------------------------------------------------------------------
function W.DisplayOrderConfig(parent)
	local f = CreateFrame("Frame", "$parentOrder", parent)
	f.SpecialType = "DisplayOrderConfig"
	f:SetSize(600, 400)
	f.WidgetList = {}
	f.ColumnBreaks = {} -- [index] = true/false
	Mixin(f, Mixins.functions.DisplayOrderConfig)

	f.Save = CreateProfileButton(f, L["Selected"])
	f.Save:SetSize(65, 20)
	f.Save:SetPoint("TOPLEFT", 0, 0)
	f.Save:SetEnabled(false)
	f.Save.Action = "Save"

	f.ClearAllColumns = CreateProfileButton(f, L["ClearAllColumns"])
	f.ClearAllColumns:SetPoint("LEFT", f.Save, "RIGHT", 10, 0)
	f.ClearAllColumns:SetSize(65, 20)
	f.ClearAllColumns.Action = "ClearAllColumns"
	f.ClearAllColumns.Tooltip = Ns:GetWidget("Tooltip", f.ClearAllColumns)

	local function PresetBtn(label, prev, gap, action)
		local btn = CreateProfileButton(f, label)
		btn:SetSize(17, 22)
		btn:SetPoint("LEFT", prev, "RIGHT", gap, 0)
		btn.Action = action
		return btn
	end

	local function CreatePresetGroup(label, anchorTo, anchorGap, actionStart)
		local header = f:CreateFontString()
		header:SetFont(Ns.ConfigDefaultFont, Ns.ConfigDefaultFontSize - 4)
		header:SetText(label)
		header:SetPoint("LEFT", anchorTo, "RIGHT", anchorGap, 0)
		local b1 = PresetBtn(L["OneRow"],    header, 3, actionStart)
		local b2 = PresetBtn(L["TwoRows"],   b1,     2, actionStart + 1)
		local b3 = PresetBtn(L["ThreeRows"], b2,     2, actionStart + 2)
		local b4 = PresetBtn(L["FourRows"],  b3,     2, actionStart + 3)
		return header, b1, b2, b3, b4
	end

	f.Rows, f.OneRow, f.TwoRows, f.ThreeRows, f.FourRows = CreatePresetGroup(L["Rows"], f.ClearAllColumns, 20, 1)

	f.Columns, f.OneCol, f.TwoCol, f.ThreeCol, f.FourCol = CreatePresetGroup(L["Columns"], f.FourRows, 20, 6)

	-- hint label
	f.Hint = f:CreateFontString()
	f.Hint:SetFont(Ns.ConfigDefaultFont, Ns.ConfigDefaultFontSize - 5)
	f.Hint:SetTextColor(greenColor.r, greenColor.g, greenColor.b)
	f.Hint:SetPoint("TOPLEFT", 0, -(f.Save:GetHeight() + 26))
	f.Hint:SetText(L["LayoutHint"])

	f.Preview = CreateFrame("Frame", nil, f, "BackdropTemplate")
	f.Preview:SetPoint("TOPLEFT", f.Hint, "BOTTOMLEFT", 0, -4)
	f.Preview:SetBackdrop({
		bgFile = "Interface/BUTTONS/WHITE8X8",
		edgeFile = "Interface/Tooltips/UI-Tooltip-Border",
		tile = false,
		edgeSize = 8,
		tileSize = 0,
		insets = { left = 1, right = 1, top = 1, bottom = 1 },
	})
	f.Preview:SetBackdropColor(0.08, 0.08, 0.08, 0.85)
	f.Preview:SetBackdropBorderColor(0.2, 0.2, 0.2, 0.5)
	f.Preview:SetClipsChildren(true)
	f.Preview:EnableMouse(true) -- absorb clicks so the config frame doesn't drag
	f.Preview:SetSize(PreviewMaxW, 80) -- width capped; height sized dynamically
	f.HScrollOffset = 0
	f.MaxHScroll = 0

	local hArrowSize = 14
	local hScrollStep = 60

	local leftArrow = CreateFrame("Button", nil, f)
	leftArrow:SetSize(hArrowSize, hArrowSize)
	leftArrow:SetPoint("RIGHT", f.Preview, "LEFT", -2, 0)
	leftArrow.Icon = leftArrow:CreateTexture(nil, "ARTWORK")
	leftArrow.Icon:SetAllPoints()
	leftArrow.Icon:SetTexture(Ns.TexturePath .. "ArrowUp")
	leftArrow.Icon:SetTexCoord(1, 0, 0, 0, 1, 1, 0, 1) -- rotate 90° CCW → points left
	leftArrow.Icon:SetVertexColor(greenColor.r, greenColor.g, greenColor.b, 1)
	leftArrow:SetScript("OnEnter", function(self) self.Icon:SetVertexColor(greenColor.r, greenColor.g, greenColor.b, 1) end)
	leftArrow:SetScript("OnLeave", function(self) self.Icon:SetVertexColor(greenColor.r, greenColor.g, greenColor.b, 0.7) end)
	leftArrow:SetScript("OnClick", function()
		f.HScrollOffset = math.max(0, (f.HScrollOffset or 0) - hScrollStep)
		RefreshPreviewLayout(f)
	end)
	leftArrow:Hide()
	f.HScrollLeft = leftArrow

	local rightArrow = CreateFrame("Button", nil, f)
	rightArrow:SetSize(hArrowSize, hArrowSize)
	rightArrow:SetPoint("LEFT", f.Preview, "RIGHT", 2, 0)
	rightArrow.Icon = rightArrow:CreateTexture(nil, "ARTWORK")
	rightArrow.Icon:SetAllPoints()
	rightArrow.Icon:SetTexture(Ns.TexturePath .. "ArrowUp")
	rightArrow.Icon:SetTexCoord(0, 1, 1, 1, 0, 0, 1, 0) -- rotate 90° CW → points right
	rightArrow.Icon:SetVertexColor(greenColor.r, greenColor.g, greenColor.b, 1)
	rightArrow:SetScript("OnEnter", function(self) self.Icon:SetVertexColor(greenColor.r, greenColor.g, greenColor.b, 1) end)
	rightArrow:SetScript("OnLeave", function(self) self.Icon:SetVertexColor(greenColor.r, greenColor.g, greenColor.b, 0.7) end)
	rightArrow:SetScript("OnClick", function()
		f.HScrollOffset = math.min(f.MaxHScroll or 0, (f.HScrollOffset or 0) + hScrollStep)
		RefreshPreviewLayout(f)
	end)
	rightArrow:Hide()
	f.HScrollRight = rightArrow

	local hbar = CreateFrame("Frame", nil, f, BackdropTemplateMixin and "BackdropTemplate")
	hbar:SetHeight(6)
	hbar:SetPoint("TOPLEFT", f.Preview, "BOTTOMLEFT", 0, -2)
	hbar:SetPoint("TOPRIGHT", f.Preview, "BOTTOMRIGHT", 0, -2)
	hbar:SetBackdrop({bgFile = "Interface/BUTTONS/WHITE8X8"})
	hbar:SetBackdropColor(0.1, 0.1, 0.1, 0.4)
	hbar:EnableMouse(true)
	hbar:Hide()
	f.HScrollBar = hbar

	local hthumb = CreateFrame("Frame", nil, hbar, BackdropTemplateMixin and "BackdropTemplate")
	hthumb:SetHeight(6)
	hthumb:SetBackdrop({bgFile = "Interface/BUTTONS/WHITE8X8"})
	hthumb:SetBackdropColor(1, 1, 1, 0.5)
	hthumb:SetPoint("LEFT", 0, 0)
	hthumb:SetWidth(40)
	hthumb:EnableMouse(true)
	hbar.Thumb = hthumb

	local function HThumbOnUpdate(self)
		local cx = GetCursorPosition()
		local scale = self:GetEffectiveScale()
		cx = cx / scale
		local deltaX = cx - f.HDragStartX
		local trackW = hbar:GetWidth()
		local thumbW = self:GetWidth()
		local travel = trackW - thumbW
		local maxH = f.MaxHScroll or 0
		if travel <= 0 or maxH <= 0 then return end
		local scrollDelta = deltaX * (maxH / travel)
		f.HScrollOffset = math.max(0, math.min(maxH, (f.HDragStartOffset or 0) + scrollDelta))
		RefreshPreviewLayout(f)
	end
	hthumb:SetScript("OnMouseDown", function(self, button)
		if button ~= "LeftButton" then return end
		local cx = GetCursorPosition()
		local scale = self:GetEffectiveScale()
		f.HDragStartX = cx / scale
		f.HDragStartOffset = f.HScrollOffset or 0
		self:SetScript("OnUpdate", HThumbOnUpdate)
	end)
	hthumb:SetScript("OnMouseUp", function(self, button)
		if button ~= "LeftButton" then return end
		self:SetScript("OnUpdate", nil)
	end)

	hbar:SetScript("OnMouseDown", function(self, button)
		if button ~= "LeftButton" then return end
		local maxH = f.MaxHScroll or 0
		if maxH <= 0 then return end
		local cx = GetCursorPosition()
		local scale = self:GetEffectiveScale()
		cx = cx / scale
		local thumbCenterX = select(1, hthumb:GetCenter())
		if cx > thumbCenterX then
			f.HScrollOffset = math.min(maxH, (f.HScrollOffset or 0) + hScrollStep * 2)
		else
			f.HScrollOffset = math.max(0, (f.HScrollOffset or 0) - hScrollStep * 2)
		end
		RefreshPreviewLayout(f)
	end)

	f.Ghost = CreateFrame("Frame", nil, UIParent, "BackdropTemplate")
	f.Ghost:SetBackdrop(CellBG)
	f.Ghost:SetBackdropColor(greenColor.r, greenColor.g, greenColor.b, 0.45)
	f.Ghost:SetBackdropBorderColor(greenColor.r, greenColor.g, greenColor.b, 0.8)
	f.Ghost:SetFrameStrata("TOOLTIP")
	f.Ghost.Icon = f.Ghost:CreateTexture(nil, "ARTWORK")
	f.Ghost.Icon:SetSize(14, 14)
	f.Ghost.Icon:SetPoint("LEFT", 5, 0)
	f.Ghost.Text = f.Ghost:CreateFontString()
	f.Ghost.Text:SetFont(Ns.ConfigDefaultFont, Ns.ConfigDefaultFontSize - 4)
	f.Ghost.Text:SetJustifyH("LEFT")
	f.Ghost.Text:SetPoint("LEFT", f.Ghost.Icon, "RIGHT", 3, 0)
	f.Ghost.Text:SetTextColor(1, 1, 1)
	f.Ghost:Hide()
	function f.Ghost:SetText(text) self.Text:SetText(text) end
	function f.Ghost:GetText() return self.Text:GetText() end

	f.InsertBar = f.Preview:CreateTexture(nil, "OVERLAY")
	f.InsertBar:SetColorTexture(greenColor.r, greenColor.g, greenColor.b, 1)
	f.InsertBar:SetSize(2, CellH)
	f.InsertBar:Hide()

	return f
end

-------------------------------
-- DisplayOrderConfig mixins --
-------------------------------
Mixins.functions.DisplayOrderConfig = {}

function Mixins.functions.DisplayOrderConfig.Init(self, anchorframe, parent)
	self.Anchoreframe = anchorframe
	self.MyInformation = parent.DisplayOrder[self:GetID()]
	self.DisplayOrder = parent.DisplayOrder
	self.SvTable = Ns.Profile

	if not self.StatList then
		self.StatList = {}
	end
	self.ClearAllColumns.Tooltip:Init(self.ClearAllColumns, self.MyInformation)
	self.ClearAllColumns.Tooltip:ClearAllPoints()
	self.ClearAllColumns.Tooltip:SetPoint("LEFT", self.ClearAllColumns, "RIGHT", -197, 0)
	wipe(self.StatList)
	wipe(self.ColumnBreaks)
	self.HScrollOffset = 0

	local added, removed = Ns:FillDisplayOrder(self.StatList, self.SvTable, true)
	if added or removed then
		self.Save:SetEnabled(true)
	end

	for k, v in ipairs(self.StatList) do
		self.ColumnBreaks[k] = (k > 1) and (Ns.Profile.NewColumns and Ns.Profile.NewColumns[v.stat] == true) or false
	end

	if added and not Ns.Profile.RowLayoutRows then
		local savedCount = Ns.Profile.CustomOrderList and #Ns.Profile.CustomOrderList or 0
		if savedCount > 1 then
			local allBreaks = true
			for k = 2, savedCount do
				if not self.ColumnBreaks[k] then allBreaks = false; break end
			end
			if allBreaks then
				for k = savedCount + 1, #self.StatList do
					self.ColumnBreaks[k] = true
				end
			end
		end
	end

	local savedRows = Ns.Profile.RowLayoutRows
	if not savedRows and Ns.Profile.LayoutType == "rows" then
		local firstColLen = 0
		for k = 1, #self.StatList do
			if k > 1 and self.ColumnBreaks[k] then break end
			firstColLen = firstColLen + 1
		end
		if firstColLen > 1 then
			savedRows = firstColLen
		end
	end
	self.RowLayoutRows = savedRows
	if self.RowLayoutRows and self.RowLayoutRows > 1 then
		local total = #self.StatList
		local rows = self.RowLayoutRows
		local cols = math.ceil(total / rows)
		local columns = {{}}
		for k = 1, total do
			if k > 1 and self.ColumnBreaks[k] then tinsert(columns, {}) end
			tinsert(columns[#columns], self.StatList[k])
		end

		local maxColLen = 0
		for _, col in ipairs(columns) do if #col > maxColLen then maxColLen = #col end end
		local visual = {}
		for r = 1, maxColLen do
			for _, col in ipairs(columns) do
				if col[r] then tinsert(visual, col[r]) end
			end
		end
		for k = 1, total do
			self.StatList[k] = visual[k]
			self.ColumnBreaks[k] = false
		end
	else
		self.RowLayoutRows = nil
	end

	for k, v in ipairs(self.StatList) do
		local cell = Ns:GetWidget("DragStat", self.Preview, k)
		cell:SetParent(self.Preview)
		cell.ID = k
		cell.NewColumn:Hide()
		cell:SetBackdrop(CellBG)
		cell:SetBackdropColor(topTabColor.r, topTabColor.g, topTabColor.b, 0.5)
		cell:SetBackdropBorderColor(0.25, 0.25, 0.25, 0.6)
		cell:EnableMouse(true)
		cell.IdText:SetText(k .. ".")
		local defIdx = Ns.StatToDefaultOrder[v.stat]
		if defIdx then
			local iconTexture = Ns:GetSpellIcon(Ns.DefaultOrder[defIdx], Ns.IgnoreFormat)
			if iconTexture and iconTexture ~= "" then
				cell.Icon:SetTexture(iconTexture)
				cell.Icon:SetSize(14, 14)
				cell.Icon:Show()
			else
				cell.Icon:SetTexture(nil)
				cell.Icon:SetSize(0.1, 0.1)
				cell.Icon:Hide()
			end
		else
			cell.Icon:SetTexture(nil)
			cell.Icon:SetSize(0.1, 0.1)
			cell.Icon:Hide()
		end
		cell.Text:SetText(L[v.stat])
		cell.ConfigParent = self
		cell:SetScript("OnEnter", GridCell_OnEnter)
		cell:SetScript("OnLeave", GridCell_OnLeave)
		cell:SetScript("OnMouseDown", GridCell_OnMouseDown)
		cell:SetScript("OnMouseUp", GridCell_OnMouseUp)
		cell:EnableMouseWheel(true)
		cell:SetScript("OnMouseWheel", function(c, delta)
			local p = c.ConfigParent
			if (p.MaxHScroll or 0) <= 0 then return end
			p.HScrollOffset = math.max(0, math.min(p.MaxHScroll, (p.HScrollOffset or 0) - delta * 60))
			RefreshPreviewLayout(p)
		end)
		tinsert(self.WidgetList, cell)
	end

	RefreshPreviewLayout(self)
end

function Mixins.functions.DisplayOrderConfig.Reset(self)
	for k, widget in ipairs(self.WidgetList) do
		Ns:KillWidget(widget)
	end
	wipe(self.WidgetList)
	self.IsDragging = false
	self.HScrollOffset = 0
	self:SetScript("OnUpdate", nil)
	if self.Ghost then self.Ghost:Hide() end
end

function Mixins.functions.DisplayOrderConfig.RunAction(self, action)
	if action == "Save" then
		if not Ns.Profile.CustomOrderList then
			Ns.Profile.CustomOrderList = {}
		end
		if not Ns.Profile.NewColumns then
			Ns.Profile.NewColumns = {}
		end
		wipe(Ns.Profile.CustomOrderList)
		wipe(Ns.Profile.NewColumns)
		if self.RowLayoutRows and self.RowLayoutRows > 1 then
			local total = #self.StatList
			local rows = self.RowLayoutRows
			local cols = math.ceil(total / rows)
			local idx = 0
			for c = 0, cols - 1 do
				for r = 0, rows - 1 do
					local src = r * cols + c + 1
					if src <= total then
						idx = idx + 1
						Ns.Profile.CustomOrderList[idx] = self.StatList[src].stat
						if c > 0 and r == 0 then
							Ns.Profile.NewColumns[self.StatList[src].stat] = true
						end
					end
				end
			end
			Ns.Profile.RowLayoutRows = self.RowLayoutRows
		else
			for k, v in ipairs(self.StatList) do
				Ns.Profile.CustomOrderList[k] = v.stat
				if self.ColumnBreaks[k] then
					Ns.Profile.NewColumns[v.stat] = true
				end
			end
			Ns.Profile.RowLayoutRows = nil
		end
		Ns.Profile.LayoutType = nil
		Ns.Profile.CanonicalOrder = nil
		self.Save:SetEnabled(false)
		Ns:QueueProfileRefresh()

	elseif action == "ClearAllColumns" then
		self.RowLayoutRows = nil
		for k = 2, #self.StatList do
			self.ColumnBreaks[k] = false
		end
		for k, v in ipairs(self.StatList) do
			self.WidgetList[k].ID = k
			self.WidgetList[k].Text:SetText(L[v.stat])
		end
		RefreshPreviewLayout(self)
		self.Save:SetEnabled(true)

	elseif type(action) == "number" then
		local total = #self.StatList
		if total == 0 then return end

		if action >= 1 and action <= 4 then
			local rows = action
			if rows == 1 then
				self.RowLayoutRows = nil
				for k = 1, total do
					self.ColumnBreaks[k] = (k > 1)
				end
			else
				self.RowLayoutRows = rows
				for k = 1, total do
					self.ColumnBreaks[k] = false
				end
			end
			for k, v in ipairs(self.StatList) do
				self.WidgetList[k].ID = k
				self.WidgetList[k].Text:SetText(L[v.stat])
			end
		elseif action >= 6 and action <= 9 then
			self.RowLayoutRows = nil
			local cols = action - 5
			if cols == 1 then
				for k = 1, total do
					self.ColumnBreaks[k] = false
				end
			else
				local perCol = math.ceil(total / cols)
				for k = 1, total do
					if k == 1 then
						self.ColumnBreaks[k] = false
					else
						self.ColumnBreaks[k] = ((k - 1) % perCol == 0)
					end
				end
			end
			for k, v in ipairs(self.StatList) do
				self.WidgetList[k].ID = k
				self.WidgetList[k].Text:SetText(L[v.stat])
			end
		end

		RefreshPreviewLayout(self)
		self.Save:SetEnabled(true)
	end
end

-----------------------------
--		Search Config	   --
-----------------------------
function W.SearchConfig(parent)
	local f = CreateFrame("Frame", "$parentSearch", parent)
	f.SpecialType = "SearchConfig"
	f:SetSize(600, 400)
	Mixin(f, Mixins.functions.SearchConfig)

	-- Search EditBox
	f.SearchBox = W.BaseEditBox(f, 250, 22)
	f.SearchBox:SetPoint("TOPLEFT", 0, 0)
	f.SearchBox:SetJustifyH("LEFT")
	f.SearchBox:SetMaxLetters(40)
	f.SearchBox:SetAutoFocus(false)
	f.SearchBox:SetFont(Ns.ConfigDefaultFont2, Ns.ConfigDefaultFontSize - 2, "")
	f.SearchBox.Instructions = f.SearchBox:CreateFontString(nil, "ARTWORK")
	f.SearchBox.Instructions:SetFont(Ns.ConfigDefaultFont2, Ns.ConfigDefaultFontSize - 3, "")
	f.SearchBox.Instructions:SetPoint("LEFT", 5, 0)
	f.SearchBox.Instructions:SetTextColor(0.5, 0.5, 0.5)
	f.SearchBox.Instructions:SetText(Ns.L["Search"] .. "...")

	f.SearchBox:SetScript("OnEditFocusGained", function(self) self.Instructions:Hide() end)
	f.SearchBox:SetScript("OnEditFocusLost", function(self)
		if self:GetText() == "" then self.Instructions:Show() end
	end)
	f.SearchBox:SetScript("OnEscapePressed", function(self)
		self:SetText("")
		self:ClearFocus()
		self.Instructions:Show()
		f:UpdateResults("")
	end)
	f.SearchBox:SetScript("OnTextChanged", function(self)
		local text = self:GetText()
		if text == "" then
			self.Instructions:Show()
		else
			self.Instructions:Hide()
		end
		f:UpdateResults(text)
	end)

	f.Results = {}

	f.RecentTitle = f:CreateFontString(nil, "OVERLAY")
	f.RecentTitle:SetFont(Ns.ConfigDefaultFont2, Ns.ConfigDefaultFontSize - 4, "")
	f.RecentTitle:SetPoint("TOPLEFT", f.SearchBox, "TOPRIGHT", 100, 0)
	f.RecentTitle:SetTextColor(greenColor.r, greenColor.g, greenColor.b)
	f.RecentTitle:SetText(Ns.L["RecentSearches"] or "Recent Search")
	f.RecentItems = {}

	return f
end

Mixins.functions.SearchConfig = {}

function Mixins.functions.SearchConfig.Init(self, anchorframe, parent)
	self.Anchoreframe = anchorframe
	self.MyInformation = parent.DisplayOrder[self:GetID()]
	self.DisplayOrder = parent.DisplayOrder
	self:UpdateRecentSearches()
end

function Mixins.functions.SearchConfig.AddRecentSearch(self, stat, spellclass)
	if not SinStatsDB then return end
	if not SinStatsDB.recentSearches then
		SinStatsDB.recentSearches = {}
	end
	-- remove duplicate if exists
	for i = #SinStatsDB.recentSearches, 1, -1 do
		if SinStatsDB.recentSearches[i].stat == stat then
			table.remove(SinStatsDB.recentSearches, i)
		end
	end
	-- insert at front
	table.insert(SinStatsDB.recentSearches, 1, { stat = stat, spellclass = spellclass })
	-- cap at 10
	while #SinStatsDB.recentSearches > 10 do
		table.remove(SinStatsDB.recentSearches)
	end
	self:UpdateRecentSearches()
end

function Mixins.functions.SearchConfig.RemoveRecentSearch(self, index)
	if not SinStatsDB or not SinStatsDB.recentSearches then return end
	table.remove(SinStatsDB.recentSearches, index)
	self:UpdateRecentSearches()
end

function Mixins.functions.SearchConfig.UpdateRecentSearches(self)

	for _, item in ipairs(self.RecentItems) do
		item:Hide()
	end
	if not SinStatsDB or not SinStatsDB.recentSearches or #SinStatsDB.recentSearches == 0 then
		self.RecentTitle:Hide()
		return
	end
	self.RecentTitle:Show()

	local L = Ns.L
	local btnHeight = 22
	local btnSpacing = 3
	local panelWidth = 250

	for i, entry in ipairs(SinStatsDB.recentSearches) do
		local btn = self.RecentItems[i]
		if not btn then
			btn = CreateFrame("Button", nil, self, "BackdropTemplate")
			btn:SetSize(panelWidth, btnHeight)
			btn:SetBackdrop(EditBoxBG)
			btn:SetBackdropColor(topTabColor.r, topTabColor.g, topTabColor.b, 0.3)
			btn:SetBackdropBorderColor(0, 0, 0, 0.5)
			btn.Icon = btn:CreateTexture(nil, "ARTWORK")
			btn.Icon:SetSize(16, 16)
			btn.Icon:SetPoint("LEFT", 4, 0)
			btn.Text = btn:CreateFontString(nil, "OVERLAY")
			btn.Text:SetFont(Ns.ConfigDefaultFont2, Ns.ConfigDefaultFontSize - 4, "")
			btn.Text:SetPoint("LEFT", btn.Icon, "RIGHT", 4, 0)
			btn.Text:SetPoint("RIGHT", btn, "RIGHT", -22, 0)
			btn.Text:SetJustifyH("LEFT")
			btn.Category = btn:CreateFontString(nil, "OVERLAY")
			btn.Category:SetFont(Ns.ConfigDefaultFont2, Ns.ConfigDefaultFontSize - 5, "")
			btn.Category:SetPoint("RIGHT", -24, 0)
			btn.Category:SetJustifyH("RIGHT")
			btn.Category:SetTextColor(greenColor.r, greenColor.g, greenColor.b, 1)

			btn.Remove = CreateFrame("Button", nil, btn)
			btn.Remove:SetSize(14, 14)
			btn.Remove:SetPoint("RIGHT", -4, 0)
			btn.Remove:SetNormalTexture(Ns.TexturePath .. "Close")
			btn.Remove:GetNormalTexture():SetVertexColor(0.6, 0.6, 0.6, 0.6)
			btn.Remove:SetHighlightTexture(Ns.TexturePath .. "Close")
			btn.Remove:GetHighlightTexture():SetVertexColor(1, 0.3, 0.3, 1)
			btn:SetScript("OnEnter", function(b) b:SetBackdropColor(greenColor.r, greenColor.g, greenColor.b, 0.3) end)
			btn:SetScript("OnLeave", function(b) b:SetBackdropColor(topTabColor.r, topTabColor.g, topTabColor.b, 0.2) end)
			self.RecentItems[i] = btn
		end

		local statName = L[entry.stat.."Menu"] or L[entry.stat] or entry.stat
		btn.Text:SetText(statName)
		btn.Category:SetText(entry.spellclass or "")

		local iconTexture
		for _, defEntry in ipairs(Ns.DefaultOrder) do
			if defEntry.stat == entry.stat then
				iconTexture = Ns:GetSpellIcon(defEntry, Ns.IgnoreFormat)
				break
			end
		end
		if iconTexture and iconTexture ~= "" then
			btn.Icon:SetTexture(iconTexture)
			btn.Icon:SetSize(16, 16)
		else
			btn.Icon:SetSize(0.1, 0.1)
		end

		btn:ClearAllPoints()
		btn:SetPoint("TOPLEFT", self.RecentTitle, "BOTTOMLEFT", 0, -(5 + (i - 1) * (btnHeight + btnSpacing)))

		local spellclass = entry.spellclass
		local targetStat = entry.stat
		btn:SetScript("OnClick", function()
			if not Ns.ConfigFrame or not Ns.ConfigFrame.MainMenuItems then return end
			for mi = 0, #Ns.ConfigFrame.MainMenuItems do
				local menuItem = Ns.ConfigFrame.MainMenuItems[mi]
				if menuItem and menuItem.SpellClass == spellclass and menuItem:IsEnabled() then
					menuItem:Click()
					if Ns.ConfigFrame.MainAnchor and Ns.ConfigFrame.MainAnchor.CurrentWidgets then
						for _, widget in ipairs(Ns.ConfigFrame.MainAnchor.CurrentWidgets) do
							if widget.MyInformation and widget.MyInformation.stat == targetStat then
								widget:Click()
								break
							end
						end
					end
					break
				end
			end
		end)
		
		-- my x button!
		local removeIndex = i
		btn.Remove:SetScript("OnClick", function()
			self:RemoveRecentSearch(removeIndex)
		end)

		btn:Show()
	end
end

function Mixins.functions.SearchConfig.UpdateResults(self, query)

	for _, btn in ipairs(self.Results) do
		btn:Hide()
	end

	if not query or strtrim(query) == "" then return end

	local L = Ns.L
	local lowerQuery = strlower(query)
	local resultIndex = 0
	local maxResults = 30
	local btnHeight = 22
	local btnSpacing = 3
	local colWidth = 270
	local maxPerCol = 14
	local maxCols = 2
	local maxVisible = maxPerCol * maxCols

	for _, entry in ipairs(Ns.DefaultOrder) do
		local statName = L[entry.stat.."Menu"] or L[entry.stat] or entry.stat
		local shortName = L[entry.stat] or entry.stat
		if strlower(statName):find(lowerQuery, 1, true) or strlower(shortName):find(lowerQuery, 1, true) or strlower(entry.stat):find(lowerQuery, 1, true) then
			resultIndex = resultIndex + 1
			if resultIndex > maxVisible then break end

			local btn = self.Results[resultIndex]
			if not btn then
				btn = CreateFrame("Button", nil, self, "BackdropTemplate")
				btn:SetFrameLevel(self:GetFrameLevel() + 10)
				btn:SetSize(colWidth - 5, btnHeight)
				btn:SetBackdrop(EditBoxBG)
				btn:SetBackdropColor(0.06020, 0.06020, 0.06020, 1)
				btn:SetBackdropBorderColor(0, 0, 0, 0.5)
				btn.Icon = btn:CreateTexture(nil, "ARTWORK")
				btn.Icon:SetSize(16, 16)
				btn.Icon:SetPoint("LEFT", 4, 0)
				btn.Text = btn:CreateFontString(nil, "OVERLAY")
				btn.Text:SetFont(Ns.ConfigDefaultFont2, Ns.ConfigDefaultFontSize - 4, "")
				btn.Text:SetPoint("LEFT", btn.Icon, "RIGHT", 4, 0)
				btn.Text:SetJustifyH("LEFT")
				btn.Category = btn:CreateFontString(nil, "OVERLAY")
				btn.Category:SetFont(Ns.ConfigDefaultFont2, Ns.ConfigDefaultFontSize - 5, "")
				btn.Category:SetPoint("RIGHT", -6, 0)
				btn.Category:SetJustifyH("RIGHT")
				btn.Category:SetTextColor(greenColor.r, greenColor.g, greenColor.b, 0.8)
				btn:SetScript("OnEnter", function(b) b:SetBackdropColor(greenColor.r, greenColor.g, greenColor.b, 0.3) end)
				btn:SetScript("OnLeave", function(b) b:SetBackdropColor(0.06020, 0.06020, 0.06020, 1) end)
				self.Results[resultIndex] = btn
			end

			btn:SetParent(self)
			btn.Text:SetText(statName)
			btn.Category:SetText(entry.spellclass or "")
			local iconTexture = Ns:GetSpellIcon(entry, Ns.IgnoreFormat)
			if iconTexture and iconTexture ~= "" then
				btn.Icon:SetTexture(iconTexture)
				btn.Icon:SetSize(16, 16)
			else
				btn.Icon:SetSize(0.1, 0.1)
			end

			local col = math.floor((resultIndex - 1) / maxPerCol)
			local row = (resultIndex - 1) % maxPerCol
			btn:ClearAllPoints()
			btn:SetPoint("TOPLEFT", self.SearchBox, "BOTTOMLEFT", col * colWidth, -(10 + row * (btnHeight + btnSpacing)))

			local spellclass = entry.spellclass
			local targetStat = entry.stat
			btn:SetScript("OnClick", function()
				self:AddRecentSearch(targetStat, spellclass)
				if not Ns.ConfigFrame or not Ns.ConfigFrame.MainMenuItems then return end
				for i = 0, #Ns.ConfigFrame.MainMenuItems do
					local menuItem = Ns.ConfigFrame.MainMenuItems[i]
					if menuItem and menuItem.SpellClass == spellclass and menuItem:IsEnabled() then
						menuItem:Click()
						if Ns.ConfigFrame.MainAnchor and Ns.ConfigFrame.MainAnchor.CurrentWidgets then
							for _, widget in ipairs(Ns.ConfigFrame.MainAnchor.CurrentWidgets) do
								if widget.MyInformation and widget.MyInformation.stat == targetStat then
									widget:Click()
									break
								end
							end
						end
						break
					end
				end
			end)

			btn:Show()
		end
	end
end

--------------------------
--			FAQ			--
--------------------------
local FAQEntries = { "First", "Second", "Third", "Fourth", "Fifth", "Sixth", "Seventh", "Eighth" }

local function CreateFAQFontString(parent, key, anchor)
	local fs = parent:CreateFontString()
	fs:SetFont(Ns.ConfigDefaultFont, Ns.ConfigDefaultFontSize - 4)
	fs:SetJustifyH("LEFT")
	if anchor then
		fs:SetPoint("TOPLEFT", anchor, 0, -70)
	else
		fs:SetPoint("TOPLEFT", 0, 0)
	end
	fs:SetText(L["FAQ" .. key])
	return fs
end

function W.FAQConfig(parent)
	local f = CreateFrame("Frame", "$parentFAQ", parent )
	f.SpecialType = "FAQConfig"
	f:SetSize(400, 170)
	Mixin(f, Mixins.functions.FAQConfig)

	local prev
	for _, key in ipairs(FAQEntries) do
		f[key] = CreateFAQFontString(f, key, prev)
		prev = f[key]
	end

	return f
end

Mixins.functions.FAQConfig = {}
function Mixins.functions.FAQConfig.Init(self, anchorframe, parent)
	self.Anchoreframe = anchorframe
	self.MyInformation = parent.DisplayOrder[self:GetID()]
	self.DisplayOrder = parent.DisplayOrder
end
