local AS, L, S, R = unpack(AddOnSkins)

local _G, _ = _G
local tremove, unpack, next, ipairs, pairs = tremove, unpack, next, ipairs, pairs
local abs, min, max, floor = abs, min, max, floor
local strlower, strfind, type = strlower, strfind, type
local hooksecurefunc = hooksecurefunc

local GetPhysicalScreenSize = GetPhysicalScreenSize
local CopyTable = CopyTable
local CreateFrame = CreateFrame
local UIParent = UIParent
local ITEM_QUALITY_COLORS = ITEM_QUALITY_COLORS

local LibStub = _G.LibStub

S.Classic = WOW_PROJECT_ID == WOW_PROJECT_CLASSIC
S.Retail = WOW_PROJECT_ID == WOW_PROJECT_MAINLINE
S.TBC = WOW_PROJECT_ID == WOW_PROJECT_BURNING_CRUSADE_CLASSIC
S.Wrath = WOW_PROJECT_ID == WOW_PROJECT_WRATH_CLASSIC

S.Hider = CreateFrame('Frame')
S.Hider:Hide()

S.noop = function() end

local BlizzardRegions = { 'Left', 'Middle', 'Right', 'LeftDisabled', 'MiddleDisabled', 'RightDisabled', 'TopLeft', 'TopRight', 'BottomLeft', 'BottomRight', 'TopMiddle', 'MiddleLeft', 'MiddleRight', 'BottomMiddle', 'MiddleMiddle', 'Center', 'TopTex', 'TopLeftTex', 'TopRightTex', 'LeftTex', 'BottomTex', 'BottomLeftTex', 'BottomRightTex', 'RightTex', 'MiddleTex', 'TabSpacer', 'TabSpacer1', 'TabSpacer2', '_RightSeparator', '_LeftSeparator', 'Cover', 'Mid', 'Border', 'Background', 'Delimiter' }
local BlizzardFrames = { 'Inset', 'inset', 'InsetFrame', 'LeftInset', 'RightInset', 'NineSlice', 'BG', 'bgLeft', 'bgRight', 'border', 'Border', 'BorderFrame', 'bottomInset', 'BottomInset', 'FilligreeOverlay', 'PortraitOverlay', 'ArtOverlayFrame', 'Portrait', 'portrait', 'ScrollFrameBorder' }
local BlizzardTextureIDs = {
	[137056] = true, -- Interface\\Tooltips\\UI-Tooltip-Background
	[137057] = true, -- Interface\\Tooltips\\UI-Tooltip-Border
	[131080] = true, -- Interface\\DialogFrame\\UI-DialogBox-Header
	[130838] = true, -- Interface\\Buttons\\UI-PlusButton-UP
	[130836] = true, -- Interface\\Buttons\\UI-PlusButton-DOWN
	[130821] = true, -- Interface\\Buttons\\UI-MinusButton-UP
	[130820] = true, -- Interface\\Buttons\\UI-MinusButton-DOWN
	[251966] = true, -- Interface\\PaperDollInfoFrame\\UI-GearManager-Title-Background
	[251963] = true, -- Interface\\PaperDollInfoFrame\\UI-GearManager-Border
	[130843] = true, -- Interface\\Buttons\\UI-RadioButton
	[130755] = true, -- Interface\\Buttons\\UI-CheckBox-Up
	[130751] = true, -- Interface\\Buttons\\UI-CheckBox-Check
	[130753] = true, -- Interface\\Buttons\\UI-CheckBox-Highlight
}

-- Depends on the arrow texture to be up by default.
S.ArrowRotation = {
	up = 0,
	down = 3.14,
	left = 1.57,
	right = -1.57,
}

S.Media = {
	valueColor = { 1, 1, 1, 1 },
	borderColor = { 0, 0, 0, 1 },
	defaultBackdrop = { 0, 0, 0, 1 },
	transparentBackdrop = { 0, 0, 0, .8 },
	barTexture = 'Solid',
	TexCoords = { .075, .925, .075, .925 },
	ClearTexture = S.Retail and 0 or '',

	Blank = AS.Libs.LSM:Fetch('statusbar', 'Solid'),
	StatusBar = AS.Libs.LSM:Fetch('statusbar', 'Solid'),
	Close = [[Interface\AddOns\AddOnSkins\Media\Textures\Close]],
	Plus = [[Interface\AddOns\AddOnSkins\Media\Textures\Plus]],
	Minus = [[Interface\AddOns\AddOnSkins\Media\Textures\Minus]],
	QuestBang = [[Interface\AddOns\AddOnSkins\Media\Textures\UI-Icon-QuestBang]],
	Arrow = [[Interface\AddOns\AddOnSkins\Media\Textures\Arrow]],
}

S.Templates = {}
S.Shadows = {}
S.Backdrops = {}
S.StatusBars = {}
S.Texts = {}

function AS:UpdateSettings()
	for Frame in pairs(S.Templates) do
		S:SetTemplate(Frame)
	end
end

local shadowTemplate = { edgeFile = [[Interface\AddOns\AddOnSkins\Media\Textures\Shadows]], edgeSize = 3 }

local Media = S.Media

-- Toolkit API
S.mult = 1
S.border = 1
S.PixelMode = false
_, S.ScreenHeight = GetPhysicalScreenSize()
S.UIScale = 1

function S:Scale(number)
	return S.mult * floor(number/S.mult + .5)
end

function S:GetPixelScale()
	S.mult = max(0.4, min(1.15, 768 / S.ScreenHeight)) / UIParent:GetEffectiveScale()
	S.border = S.mult * (AS:CheckOption('Theme') == 'ThickBorder' and 3 or AS:CheckOption('Theme') == 'TwoPixel' and 2 or 1)
	S.PixelMode = AS:CheckOption('Theme') == 'PixelPerfect'
	S.UIScale = UIParent:GetEffectiveScale()
end

function S:ColorGradient(perc, ...)
	local value = select('#', ...)
	if perc >= 1 then
		return select(value - 2, ...)
	elseif perc <= 0 then
		return ...
	end

	local num = value / 3
	local segment, relperc = math.modf(perc*(num-1))
	local r1, g1, b1, r2, g2, b2 = select((segment*3)+1, ...)

	return r1+(r2-r1)*relperc, g1+(g2-g1)*relperc, b1+(b2-b1)*relperc
end

function S:Kill(Object)
	if Object.UnregisterAllEvents then
		Object:UnregisterAllEvents()
		Object:SetParent(S.Hider)
	else
		Object.Show = Object.Hide
	end

	Object:Hide()
end

function S:AdjustForTheme(number, offset)
	local Theme = AS:CheckOption('Theme')
	local isNegative = number < 0

	number = abs(number)

	if Theme == 'TwoPixel' then
		number = number + (offset or 1)
	elseif Theme == 'ThickBorder' then
		number = number + (offset or 2)
	end

	return isNegative and -number or number
end

-- 8.2 restricted frame check
function S:SetPointsRestricted(frame)
	if frame and not pcall(frame.GetPoint, frame) then
		return true
	end
end

function S:SafeGetPoint(frame)
	if frame and frame.GetPoint and not S:SetPointsRestricted(frame) then
		return frame:GetPoint()
	end
end

function S:WatchPixelSnap(frame, snap)
	if (frame and not frame:IsForbidden()) and frame.PixelSnapDisabled and snap then
		frame.PixelSnapDisabled = nil
	end
end

function S:DisablePixelSnap(frame)
	if (frame and not frame:IsForbidden()) and not frame.PixelSnapDisabled then
		if frame.SetSnapToPixelGrid then
			frame:SetSnapToPixelGrid(false)
			frame:SetTexelSnappingBias(0)
		elseif frame.GetStatusBarTexture then
			local texture = frame:GetStatusBarTexture()
			if type(texture) == 'table' and texture.SetSnapToPixelGrid then
				texture:SetSnapToPixelGrid(false)
				texture:SetTexelSnappingBias(0)
			end
		end

		frame.PixelSnapDisabled = true
	end
end

function S:BackdropFrameLevel(frame, level)
	frame:SetFrameLevel(level)

	if frame.oborder then frame.oborder:SetFrameLevel(level) end
	if frame.iborder then frame.iborder:SetFrameLevel(level) end
end

function S:BackdropFrameLower(backdrop, parent)
	local level = parent:GetFrameLevel()
	local minus = level and (level - 1)
	if minus and (minus >= 0) then
		S:BackdropFrameLevel(backdrop, minus)
	else
		S:BackdropFrameLevel(backdrop, 0)
	end
end

function S:Size(frame, width, height, ...)
	local w = S:Scale(width)
	frame:SetSize(w, (height and S:Scale(height)) or w, ...)
end

function S:Width(frame, width, ...)
	frame:SetWidth(S:Scale(width), ...)
end

function S:Height(frame, height, ...)
	frame:SetHeight(S:Scale(height), ...)
end

function S:Point(obj, arg1, arg2, arg3, arg4, arg5, ...)
	if not arg2 then arg2 = obj:GetParent() end

	if type(arg2) == 'number' then arg2 = S:Scale(arg2) end
	if type(arg3) == 'number' then arg3 = S:Scale(arg3) end
	if type(arg4) == 'number' then arg4 = S:Scale(arg4) end
	if type(arg5) == 'number' then arg5 = S:Scale(arg5) end

	obj:SetPoint(arg1, arg2, arg3, arg4, arg5, ...)
end

function S:GetAnchorOffsets(obj, xOffset, yOffset, noScale)
	if not xOffset then xOffset = S.border end
	if not yOffset then yOffset = S.border end
	local x = (noScale and xOffset) or S:Scale(xOffset)
	local y = (noScale and yOffset) or S:Scale(yOffset)

	if S:SetPointsRestricted(obj) or obj:GetPoint() then
		obj:ClearAllPoints()
	end

	S:DisablePixelSnap(obj)

	return x, y
end

function S:SetOutside(obj, anchor, xOffset, yOffset, anchor2, noScale)
	local x, y = S:GetAnchorOffsets(obj, xOffset, yOffset, noScale)
	anchor = anchor or obj:GetParent()

	obj:SetPoint('TOPLEFT', anchor, 'TOPLEFT', -x, y)
	obj:SetPoint('BOTTOMRIGHT', anchor2 or anchor, 'BOTTOMRIGHT', x, -y)
end

function S:SetInside(obj, anchor, xOffset, yOffset, anchor2, noScale)
	local x, y = S:GetAnchorOffsets(obj, xOffset, yOffset, noScale)
	anchor = anchor or obj:GetParent()

	obj:SetPoint('TOPLEFT', anchor, 'TOPLEFT', x, -y)
	obj:SetPoint('BOTTOMRIGHT', anchor2 or anchor, 'BOTTOMRIGHT', -x, y)
end

do
	local borders = { 'iborder', 'oborder' }
	local borderBackdrop = { edgeFile = Media.Blank }
	local backdrop = { edgeFile = Media.Blank }
	local blank = { 0, 0, 0, 0 }

	function S:GetTemplate(template)
		if template == 'Custom' then
			return AS:CheckOption('CustomBorderColor'), AS:CheckOption('CustomBackdropColor')
		end
		return Media.borderColor, template == 'Transparent' and Media.transparentBackdrop or Media.defaultBackdrop
	end

	function S:SetTemplate(frame, template, glossTex, ignoreUpdates, _, _, _, noScale, noHook)
		frame.template = template or AS:CheckOption('SkinTemplate')
		frame.glossTex = glossTex and (type(glossTex) == 'string' and glossTex or AS.Libs.LSM:Fetch('statusbar', AS:CheckOption('StatusBarTexture')))
		frame.forcedBorderColors = frame.template == 'NoBorder' and blank or frame.forcedBorderColors
		frame.ignoreUpdates = ignoreUpdates

		local borderColor, backdropColor = S:GetTemplate(frame.template)

		if not frame.SetBackdrop then
			_G.Mixin(frame, _G.BackdropTemplateMixin)
			if not noHook then
				frame:HookScript('OnSizeChanged', frame.OnBackdropSizeChanged)
			end
		end

		if frame.template == 'NoBackdrop' then
			frame:SetBackdrop()
			S.Templates[frame] = nil

			if frame.iborder then
				frame.iborder:SetShown(false)
				frame.oborder:SetShown(false)
			end
		else
			local level = frame:GetFrameLevel()
			local edgeSize = AS:CheckOption('Theme') == 'TwoPixel' and 2 or 1

			backdrop.bgFile = frame.glossTex or Media.Blank
			backdrop.edgeSize = noScale and edgeSize or S:Scale(edgeSize)

			frame:SetBackdrop(backdrop)

			if frame.callbackBackdropColor then
				frame:callbackBackdropColor()
			else
				local r, g, b, a = unpack(backdropColor)
				frame:SetBackdropColor(r, g, b, frame.customBackdropAlpha or a)
			end

			borderBackdrop.edgeSize = noScale and 1 or S:Scale(1)

			for _, which in pairs(borders) do
				local border = CreateFrame('Frame', nil, frame, 'BackdropTemplate')
				border:SetBackdrop(borderBackdrop)
				border:SetBackdropBorderColor(0, 0, 0, 1)
				border:SetFrameLevel(level)
				frame[which] = border
			end

			S:SetInside(frame.iborder, frame, 1, 1, nil, noScale)
			S:SetOutside(frame.oborder, frame, 1, 1, nil, noScale)

			frame.iborder:SetShown(frame.template == 'NoBorder' or AS:CheckOption('Theme') == 'TwoPixel')
			frame.oborder:SetShown(frame.template == 'NoBorder' or AS:CheckOption('Theme') == 'TwoPixel')

			if frame.forcedBorderColors then
				borderColor = frame.forcedBorderColors
			end

			if frame.template ~= 'NoBorder' then
				frame:SetBackdropBorderColor(unpack(borderColor))
			end

			S.Templates[frame] = not frame.ignoreUpdates and true
		end
	end
end

function S:CreateBackdrop(frame, template, glossTex, ignoreUpdates, _, _, _, noScale, allPoints, frameLevel)
	local parent = (frame.IsObjectType and frame:IsObjectType('Texture') and frame:GetParent()) or frame
	local backdrop = frame.backdrop or CreateFrame('Frame', nil, parent)
	if not frame.backdrop then frame.backdrop = backdrop end

	S:SetTemplate(backdrop, template, glossTex, ignoreUpdates, _, _, _, noScale)

	if allPoints then
		if allPoints == true then
			backdrop:SetAllPoints()
		else
			backdrop:SetAllPoints(allPoints)
		end
	else
		S:SetOutside(backdrop, frame, nil, nil, nil, noScale)
	end

	if frameLevel then
		if frameLevel == true then
			S:BackdropFrameLevel(backdrop, parent:GetFrameLevel())
		else
			S:BackdropFrameLevel(backdrop, frameLevel)
		end
	else
		S:BackdropFrameLower(backdrop, parent)
	end
end

function S:CreateShadow(frame, size, pass)
	if not pass and frame.shadow then return end
	if not size then size = 3 end

	local offset = (S.PixelMode and size) or (size + 1)
	local shadow = CreateFrame('Frame', nil, frame, 'BackdropTemplate')
	shadow:SetFrameLevel(1)
	shadow:SetFrameStrata(frame:GetFrameStrata())
	S:SetOutside(shadow, frame, offset, offset, nil, true)
	shadow:SetBackdrop(shadowTemplate)
	shadow:SetBackdropColor(0, 0, 0, 0)
	shadow:SetBackdropBorderColor(0, 0, 0, 0.9)

	S.Shadows[shadow] = true

	if pass then
		return shadow
	else
		frame.shadow = shadow
	end
end

local STRIP_TEX = 'Texture'
local STRIP_FONT = 'FontString'
local function StripRegion(which, object, kill, zero)
	if kill then
		object:Kill()
	elseif zero then
		object:SetAlpha(0)
	elseif which == STRIP_TEX then
		object:SetTexture('')
		object:SetAtlas('')
	elseif which == STRIP_FONT then
		object:SetText('')
	end
end

local function StripType(which, object, kill, zero)
	if object:IsObjectType(which) then
		StripRegion(which, object, kill, zero)
	else
		if which == STRIP_TEX then
			local FrameName = object.GetName and object:GetName()
			for _, Blizzard in pairs(BlizzardFrames) do
				local BlizzFrame = object[Blizzard] or (FrameName and _G[FrameName..Blizzard])
				if BlizzFrame and BlizzFrame.StripTextures then
					S:StripTextures(BlizzFrame, kill, zero)
				end
			end
		end

		if object.GetNumRegions then
			for _, region in next, { object:GetRegions() } do
				if region and region.IsObjectType and region:IsObjectType(which) then
					StripRegion(which, region, kill, zero)
				end
			end
		end
	end
end

function S:StripTextures(object, kill, zero)
	StripType(STRIP_TEX, object, kill, zero)
end

function S:StripTexts(object, kill, zero)
	StripType(STRIP_FONT, object, kill, zero)
end

function S:FontTemplate(fs, font, size, style, skip)
	if not skip then -- ignore updates from UpdateFontTemplates
		fs.font, fs.fontSize, fs.fontStyle = font, size, style
	end

	-- grab values from profile before conversion
	if not style then style = AS:CheckOption('FontStyle') end
	if not size then size = AS:CheckOption('FontSize') end

	-- shadow mode when using 'NONE'
	if style == 'NONE' then
		fs:SetShadowOffset(1, -0.5)
		fs:SetShadowColor(0, 0, 0, 1)
	else
		fs:SetShadowOffset(0, 0)
		fs:SetShadowColor(0, 0, 0, 0)
	end

	-- convert because of bad values between versions
	if style == 'NONE' and not S.Classic then
		style = ''
	elseif style == '' and S.Classic then
		style = 'NONE'
	end

	fs:SetFont(font or AS.Libs.LSM:Fetch('font', AS:CheckOption('Font')), size, style)

	S.Texts[fs] = true
end

function S:StyleButton(button, noHover, noPushed, noChecked)
	if button.SetHighlightTexture and button.CreateTexture and not button.hover and not noHover then
		local hover = button:CreateTexture()
		S:SetInside(hover)
		hover:SetBlendMode('ADD')
		hover:SetColorTexture(1, 1, 1, 0.3)
		button:SetHighlightTexture(hover)
		button.hover = hover
	end

	if button.SetPushedTexture and button.CreateTexture and not button.pushed and not noPushed then
		local pushed = button:CreateTexture()
		S:SetInside(pushed)
		pushed:SetBlendMode('ADD')
		pushed:SetColorTexture(0.9, 0.8, 0.1, 0.3)
		button:SetPushedTexture(pushed)
		button.pushed = pushed
	end

	if button.SetCheckedTexture and button.CreateTexture and not button.checked and not noChecked then
		local checked = button:CreateTexture()
		S:SetInside(checked)
		checked:SetBlendMode('ADD')
		checked:SetColorTexture(1, 1, 1, 0.3)
		button:SetCheckedTexture(checked)
		button.checked = checked
	end

	if button.cooldown then
		button.cooldown:SetDrawEdge(false)
		S:SetInside(button.cooldown, button, 0, 0)
	end
end

do
	local function HighlightOnEnter(button)
		local r, g, b = unpack(Media.valueColor)
		button.HighlightTexture:SetVertexColor(r, g, b, 0.50)
		button.HighlightTexture:Show()
	end

	local function HighlightOnLeave(button)
		button.HighlightTexture:SetVertexColor(0, 0, 0, 0)
		button.HighlightTexture:Hide()
	end

	function S:HandleCategoriesButtons(button, strip)
		if button.isSkinned then return end

		if button.SetNormalTexture then button:SetNormalTexture(Media.ClearTexture) end
		if button.SetHighlightTexture then button:SetHighlightTexture(Media.ClearTexture) end
		if button.SetPushedTexture then button:SetPushedTexture(Media.ClearTexture) end
		if button.SetDisabledTexture then button:SetDisabledTexture(Media.ClearTexture) end

		if strip then button:StripTextures() end
		S:HandleBlizzardRegions(button)

		button.HighlightTexture = button:CreateTexture(nil, "BACKGROUND") -- Try HIGHLIGHT
		button.HighlightTexture:SetBlendMode("BLEND")
		button.HighlightTexture:SetSize(button:GetSize())
		S:Point(button.HighlightTexture, 'CENTER', button, 0, 2)
		button.HighlightTexture:SetTexture(Media.Blank)
		button.HighlightTexture:Hide()

		button:HookScript('OnEnter', HighlightOnEnter)
		button:HookScript('OnLeave', HighlightOnLeave)

		button.isSkinned = true
	end
end

do
	local gradients = { 'leftGrad', 'rightGrad' }
	function S:HandleButtonHighlight(frame, layer, color)
		if frame.SetHighlightTexture then
			frame:SetHighlightTexture(S.Media.ClearTexture)
		end

		local r, g, b = 0.9, 0.9, 0.9
		if color then
			r, g, b = color.r or color[1], color.g or color[2], color.b or color[3]
		end

		if not frame.leftGrad then
			for _, which in pairs(gradients) do
				local grad = frame:CreateTexture(nil, layer or 'HIGHLIGHT')
				S:Height(grad, frame:GetHeight() * 0.95)
				grad:SetTexture(Media.barTexture)
				frame[which] = grad
			end
		end

		S:Point(frame.leftGrad, 'LEFT', frame, 'CENTER')
		S:Point(frame.rightGrad, 'RIGHT', frame, 'CENTER')

		if S.Retail or S.Wrath then
			frame.leftGrad:SetGradient("Horizontal", { r, g, b, 0.35 }, { r, g, b, 0 })
			frame.rightGrad:SetGradient("Horizontal", { r, g, b, 0 }, { r, g, b, 0.35 })
		else
			frame.leftGrad:SetGradientAlpha("Horizontal", r, g, b, 0.35, r, g, b, 0)
			frame.rightGrad:SetGradientAlpha("Horizontal", r, g, b, 0, r, g, b, 0.35)
		end
	end
end

function S:HandlePointXY(frame, x, y)
	local a, b, c, d, e = frame:GetPoint()
	frame:SetPoint(a, b, c, x or d, y or e)
end

function S:HandleFrame(frame, setBackdrop, template, x1, y1, x2, y2)
	local name = frame and frame.GetName and frame:GetName()
	local insetFrame = frame.Inset or name and _G[name..'Inset']
	local portraitFrame = frame.Portrait or frame.portrait or name and _G[name..'Portrait']
	local portraitFrameOverlay = frame.PortraitOverlay or name and _G[name..'PortraitOverlay']
	local artFrameOverlay = frame.ArtOverlayFrame or name and _G[name..'ArtOverlayFrame']
	local closeButton = frame.CloseButton or name and _G[name..'CloseButton']

	S:StripTextures(frame)

	if portraitFrame then portraitFrame:SetAlpha(0) end
	if portraitFrameOverlay then portraitFrameOverlay:SetAlpha(0) end
	if artFrameOverlay then artFrameOverlay:SetAlpha(0) end

	if insetFrame then
		S:HandleInsetFrame(insetFrame)
	end

	if closeButton then
		S:HandleCloseButton(closeButton)
	end

	if setBackdrop then
		S:CreateBackdrop(frame, template or 'Transparent')
	else
		S:SetTemplate(frame, template or 'Transparent')
	end

	if frame.backdrop then
		S:Point(frame.backdrop, 'TOPLEFT', x1 or 0, y1 or 0)
		S:Point(frame.backdrop, 'BOTTOMRIGHT', x2 or 0, y2 or 0)
	end
end

function S:HandleInsetFrame(frame)
	if frame.InsetBorderTop then frame.InsetBorderTop:Hide() end
	if frame.InsetBorderTopLeft then frame.InsetBorderTopLeft:Hide() end
	if frame.InsetBorderTopRight then frame.InsetBorderTopRight:Hide() end
	if frame.InsetBorderBottom then frame.InsetBorderBottom:Hide() end
	if frame.InsetBorderBottomLeft then frame.InsetBorderBottomLeft:Hide() end
	if frame.InsetBorderBottomRight then frame.InsetBorderBottomRight:Hide() end
	if frame.InsetBorderLeft then frame.InsetBorderLeft:Hide() end
	if frame.InsetBorderRight then frame.InsetBorderRight:Hide() end
	if frame.Bg then frame.Bg:Hide() end
end

-- All frames that have a Portrait
function S:HandlePortraitFrame(frame, createBackdrop, noStrip)
	local name = frame and frame.GetName and frame:GetName()
	local insetFrame = frame.Inset or name and _G[name..'Inset']
	local portraitFrame = frame.Portrait or frame.portrait or name and _G[name..'Portrait']
	local portraitFrameOverlay = frame.PortraitOverlay or name and _G[name..'PortraitOverlay']
	local artFrameOverlay = frame.ArtOverlayFrame or name and _G[name..'ArtOverlayFrame']

	if not noStrip then
		S:StripTextures(frame)

		if portraitFrame then portraitFrame:SetAlpha(0) end
		if portraitFrameOverlay then portraitFrameOverlay:SetAlpha(0) end
		if artFrameOverlay then artFrameOverlay:SetAlpha(0) end

		if insetFrame then
			S:HandleInsetFrame(insetFrame)
		end
	end

	if frame.CloseButton then
		S:HandleCloseButton(frame.CloseButton)
	end

	if createBackdrop then
		S:CreateBackdrop(frame, 'Transparent', nil, nil, nil, nil, nil, nil, true)
	else
		S:SetTemplate(frame, 'Transparent')
	end
end

function S:SetBackdropBorderColor(frame, script)
	if frame.backdrop then frame = frame.backdrop end
	if frame.SetBackdropBorderColor then
		frame:SetBackdropBorderColor(unpack(script == 'OnEnter' and Media.valueColor or Media.borderColor))
	end
end

function S:SetModifiedBackdrop()
	if self:IsEnabled() then
		S:SetBackdropBorderColor(self, 'OnEnter')
	end
end

function S:SetOriginalBackdrop()
	if self:IsEnabled() then
		S:SetBackdropBorderColor(self, 'OnLeave')
	end
end

function S:SetDisabledBackdrop()
	if self.GetFontString and self:GetFontString() ~= nil then
		self:GetFontString():SetTextColor(.5, .5, .5)
	end
	if self:IsMouseOver() then
		S:SetBackdropBorderColor(self, 'OnDisable')
	end
end

function S:SetEnabledBackdrop()
	if self.GetFontString and self:GetFontString() ~= nil then
		self:GetFontString():SetTextColor(.5, .5, .5)
	end
end

-- function to handle the recap button script
function S:UpdateRecapButton()
	-- when UpdateRecapButton runs and enables the button, it unsets OnEnter
	-- we need to reset it with ours. blizzard will replace it when the button
	-- is disabled. so, we don't have to worry about anything else.
	if self and self.button4 and self.button4:IsEnabled() then
		self.button4:SetScript('OnEnter', S.SetModifiedBackdrop)
		self.button4:SetScript('OnLeave', S.SetOriginalBackdrop)
	end
end

do -- We need to test this for the BGScore frame
	S.PVPHonorXPBarFrames = {}
	S.PVPHonorXPBarSkinned = false

	local function SetNextAvailable(XPBar)
		if not S.PVPHonorXPBarFrames[XPBar:GetParent():GetName()] then return end

		S:StripTextures(XPBar)

		if XPBar.Bar and not XPBar.Bar.backdrop then
			S:CreateBackdrop(XPBar.Bar)

			if XPBar.Bar.Background then
				S:SetInside(XPBar.Bar.Background, XPBar.Bar.backdrop)
			end
			if XPBar.Bar.Spark then
				XPBar.Bar.Spark:SetAlpha(0)
			end
			if XPBar.Bar.OverlayFrame and XPBar.Bar.OverlayFrame.Text then
				XPBar.Bar.OverlayFrame.Text:ClearAllPoints()
				S:Point(XPBar.Bar.OverlayFrame.Text, 'CENTER', XPBar.Bar)
			end
		end

		if XPBar.PrestigeReward and XPBar.PrestigeReward.Accept then
			XPBar.PrestigeReward.Accept:ClearAllPoints()
			S:Point(XPBar.PrestigeReward.Accept, 'TOP', XPBar.PrestigeReward, 'BOTTOM', 0, 0)
			if not XPBar.PrestigeReward.Accept.template then
				S:HandleButton(XPBar.PrestigeReward.Accept)
			end
		end

		if XPBar.NextAvailable then
			if XPBar.Bar then
				XPBar.NextAvailable:ClearAllPoints()
				S:Point(XPBar.NextAvailable, 'LEFT', XPBar.Bar, 'RIGHT', 0, -2)
			end

			if not XPBar.NextAvailable.backdrop then
				S:StripTextures(XPBar.NextAvailable)
				S:CreateBackdrop(XPBar.NextAvailable)

				if XPBar.NextAvailable.Icon then
					local x = S.PixelMode and 1 or 2
					S:Point(XPBar.NextAvailable.backdrop, 'TOPLEFT', XPBar.NextAvailable.Icon, -x, x)
					S:Point(XPBar.NextAvailable.backdrop, 'BOTTOMRIGHT', XPBar.NextAvailable.Icon, x, -x)
				end
			end

			if XPBar.NextAvailable.Icon then
				S:HandleIcon(XPBar.NextAvailable.Icon)
			end
		end
	end

	function S:SkinPVPHonorXPBar(frame)
		S.PVPHonorXPBarFrames[frame] = true

		if S.PVPHonorXPBarSkinned then return end
		S.PVPHonorXPBarSkinned = true

		hooksecurefunc('PVPHonorXPBar_SetNextAvailable', SetNextAvailable)
	end
end

function S:StatusBarColorGradient(bar, value, maxValue, backdrop)
	if not (bar and value) then return end

	local current = (not maxValue and value) or (value and maxValue and maxValue ~= 0 and value/maxValue)
	if not current then return end

	local r, g, b = S:ColorGradient(current, 0.8,0,0, 0.8,0.8,0, 0,0.8,0)
	bar:SetStatusBarColor(r, g, b)

	if not backdrop then
		backdrop = bar.backdrop
	end

	if backdrop then
		backdrop:SetBackdropColor(r * 0.25, g * 0.25, b * 0.25)
	end
end

-- DropDownMenu library support
function S:SkinLibDropDownMenu(prefix)
	if S[prefix..'_UIDropDownMenuSkinned'] then return end

	local key = (prefix == 'L4' or prefix == 'L3') and 'L' or prefix

	local bd = _G[key..'_DropDownList1Backdrop']
	local mbd = _G[key..'_DropDownList1MenuBackdrop']
	if bd and not bd.template then S:SetTemplate(bd, 'Transparent') end
	if mbd and not mbd.template then S:SetTemplate(mbd, 'Transparent') end

	S[prefix..'_UIDropDownMenuSkinned'] = true

	local lib = prefix == 'L4' and LibStub.libs['LibUIDropDownMenu-4.0']
	if (lib and lib.UIDropDownMenu_CreateFrames) or _G[key..'_UIDropDownMenu_CreateFrames'] then
		hooksecurefunc(lib or _G, (lib and '' or key..'_') .. 'UIDropDownMenu_CreateFrames', function()
			local lvls = _G[(key == 'Lib' and 'LIB' or key)..'_UIDROPDOWNMENU_MAXLEVELS']
			local ddbd = lvls and _G[key..'_DropDownList'..lvls..'Backdrop']
			local ddmbd = lvls and _G[key..'_DropDownList'..lvls..'MenuBackdrop']
			if ddbd and not ddbd.template then S:SetTemplate(ddbd, 'Transparent') end
			if ddmbd and not ddmbd.template then S:SetTemplate(ddmbd, 'Transparent') end
		end)
	end
end

function S:SkinTalentListButtons(frame)
	local name = frame and frame:GetName()
	if name then
		local bcl = _G[name..'BtnCornerLeft']
		local bcr = _G[name..'BtnCornerRight']
		local bbb = _G[name..'ButtonBottomBorder']
		if bcl then bcl:SetTexture() end
		if bcr then bcr:SetTexture() end
		if bbb then bbb:SetTexture() end
	end

	if frame.Inset then
		S:HandleInsetFrame(frame.Inset)

		S:Point(frame.Inset, 'TOPLEFT', 4, -60)
		S:Point(frame.Inset, 'BOTTOMRIGHT', -6, 26)
	end
end

do
	local quality = Enum.ItemQuality
	local iconColors = {
		['auctionhouse-itemicon-border-gray']		= ITEM_QUALITY_COLORS[quality.Poor],
		['auctionhouse-itemicon-border-white']		= ITEM_QUALITY_COLORS[quality.Common or quality.Standard],
		['auctionhouse-itemicon-border-green']		= ITEM_QUALITY_COLORS[quality.Uncommon or quality.Good],
		['auctionhouse-itemicon-border-blue']		= ITEM_QUALITY_COLORS[quality.Rare],
		['auctionhouse-itemicon-border-purple']		= ITEM_QUALITY_COLORS[quality.Epic],
		['auctionhouse-itemicon-border-orange']		= ITEM_QUALITY_COLORS[quality.Legendary],
		['auctionhouse-itemicon-border-artifact']	= ITEM_QUALITY_COLORS[quality.Artifact],
		['auctionhouse-itemicon-border-account']	= ITEM_QUALITY_COLORS[quality.Heirloom]
	}

	local function hideBorder(border)
		border:SetAlpha(0)
		S:StripTextures(border)
	end

	local function colorAtlas(border, atlas)
		local color = iconColors[atlas]
		if not color then return end

		hideBorder(border)

		if border.customFunc then
			local br, bg, bb = unpack(Media.borderColor)
			border.customFunc(border, color.r, color.g, color.b, 1, br, bg, bb)
		elseif border.customBackdrop then
			border.customBackdrop:SetBackdropBorderColor(color.r, color.g, color.b)
		end
	end

	local function colorVertex(border, r, g, b, a)
		hideBorder(border)

		if border.customFunc then
			local br, bg, bb = unpack(Media.borderColor)
			border.customFunc(border, r, g, b, a, br, bg, bb)
		elseif border.customBackdrop then
			border.customBackdrop:SetBackdropBorderColor(r, g, b)
		end
	end

	local function borderHide(border)
		local br, bg, bb = unpack(Media.borderColor)
		if border.customFunc then
			local r, g, b, a = border:GetVertexColor()
			border.customFunc(border, r, g, b, a, br, bg, bb)
		elseif border.customBackdrop then
			border.customBackdrop:SetBackdropBorderColor(br, bg, bb)
		end
	end

	local function borderShown(border, show)
		if not show then
			borderHide(border)
		end
	end

	function S:HandleIconBorder(border, backdrop, customFunc)
		if not backdrop then
			local parent = border:GetParent()
			backdrop = parent.backdrop or parent
		end

		border.customBackdrop = backdrop

		if not border.IconBorderHooked then
			hideBorder(border)

			hooksecurefunc(border, 'SetAtlas', colorAtlas)
			hooksecurefunc(border, 'SetVertexColor', colorVertex)
			hooksecurefunc(border, 'SetShown', borderShown)
			hooksecurefunc(border, 'Hide', borderHide)

			border.IconBorderHooked = true
		end

		local r, g, b, a = border:GetVertexColor()
		local atlas = iconColors[border.GetAtlas and border:GetAtlas()]
		if customFunc then
			border.customFunc = customFunc
			local br, bg, bb = unpack(Media.borderColor)
			customFunc(border, r, g, b, a, br, bg, bb)
		elseif atlas then
			backdrop:SetBackdropBorderColor(atlas.r, atlas.g, atlas.b, 1)
		elseif r then
			backdrop:SetBackdropBorderColor(r, g, b, a)
		else
			local br, bg, bb = unpack(Media.borderColor)
			backdrop:SetBackdropBorderColor(br, bg, bb)
		end
	end
end

function S:HandleButton(button, strip, isDecline, noStyle, createBackdrop, template, noGlossTex, overrideTex, frameLevel, regionsKill, regionsZero)
	if button.isSkinned then return end

	if button.SetNormalTexture and not overrideTex then button:SetNormalTexture(S.Media.ClearTexture) end
	if button.SetHighlightTexture then button:SetHighlightTexture(S.Media.ClearTexture) end
	if button.SetPushedTexture then button:SetPushedTexture(S.Media.ClearTexture) end
	if button.SetDisabledTexture then button:SetDisabledTexture(S.Media.ClearTexture) end

	if strip then S:StripTextures(button) end

	S:HandleBlizzardRegions(button, nil, regionsKill, regionsZero)

	if button.Icon then
		local Texture = button.Icon:GetTexture()
		if Texture and (type(Texture) == 'string' and strfind(Texture, [[Interface\ChatFrame\ChatFrameExpandArrow]])) then
			button.Icon:SetTexture(Media.Arrow)
			button.Icon:SetRotation(S.ArrowRotation.right)
			button.Icon:SetVertexColor(1, 1, 1)
		end
	end

	if isDecline and button.Icon then
		button.Icon:SetTexture(Media.Close)
	end

	if not noStyle then
		if createBackdrop then
			S:CreateBackdrop(button, AS:CheckOption('ElvUIStyle', 'ElvUI') and 'Default' or template, not noGlossTex, nil, nil, nil, nil, nil, true, frameLevel)
		else
			S:SetTemplate(button, AS:CheckOption('ElvUIStyle', 'ElvUI') and 'Default' or template, not noGlossTex)
		end

		button:HookScript('OnEnter', S.SetModifiedBackdrop)
		button:HookScript('OnLeave', S.SetOriginalBackdrop)
		button:HookScript('OnEnable', S.SetEnabledBackdrop)
		button:HookScript('OnDisable', S.SetDisabledBackdrop)
	end

	button.isSkinned = true
end

do
	local function GetElement(frame, element, useParent)
		if useParent then frame = frame:GetParent() end

		local child = frame[element]
		if child then return child end

		local name = frame:GetName()
		if name then return _G[name..element] end
	end

	local function GetButton(frame, buttons)
		for _, data in ipairs(buttons) do
			if type(data) == 'string' then
				local found = GetElement(frame, data)
				if found then return found end
			else -- has useParent
				local found = GetElement(frame, data[1], data[2])
				if found then return found end
			end
		end
	end

	local function ThumbStatus(frame)
		if not frame.Thumb then
			return
		elseif not frame:IsEnabled() then
			frame.Thumb.backdrop:SetBackdropColor(0.3, 0.3, 0.3)
			return
		end

		local _, maxValue = frame:GetMinMaxValues()
		if maxValue == 0 then
			frame.Thumb.backdrop:SetBackdropColor(0.3, 0.3, 0.3)
		else
			frame.Thumb.backdrop:SetBackdropColor(unpack(Media.valueColor))
		end
	end

	local function ThumbWatcher(frame)
		hooksecurefunc(frame, 'Enable', ThumbStatus)
		hooksecurefunc(frame, 'Disable', ThumbStatus)
		hooksecurefunc(frame, 'SetEnabled', ThumbStatus)
		hooksecurefunc(frame, 'SetMinMaxValues', ThumbStatus)
		ThumbStatus(frame)
	end

	local upButtons = {'ScrollUpButton', 'UpButton', 'ScrollUp', {'scrollUp', true}, 'Back'}
	local downButtons = {'ScrollDownButton', 'DownButton', 'ScrollDown', {'scrollDown', true}, 'Forward'}
	local thumbButtons = {'ThumbTexture', 'thumbTexture', 'Thumb'}

	function S:HandleScrollBar(scrollBar, thumbY, thumbX, template)
		if scrollBar.backdrop then return end

		local upButton, downButton = GetButton(scrollBar, upButtons), GetButton(scrollBar, downButtons)
		local thumb = GetButton(scrollBar, thumbButtons) or (scrollBar.GetThumbTexture and scrollBar:GetThumbTexture())

		S:StripTextures(scrollBar)
		S:CreateBackdrop(scrollBar, template or 'Transparent', nil, nil, nil, nil, nil, nil, nil, true)
		S:Point(scrollBar.backdrop, 'TOPLEFT', upButton or scrollBar, upButton and 'BOTTOMLEFT' or 'TOPLEFT', 0, 1)
		S:Point(scrollBar.backdrop, 'BOTTOMRIGHT', downButton or scrollBar, upButton and 'TOPRIGHT' or 'BOTTOMRIGHT', 0, -1)

		if scrollBar.Background then scrollBar.Background:Hide() end
		if scrollBar.ScrollUpBorder then scrollBar.ScrollUpBorder:Hide() end
		if scrollBar.ScrollDownBorder then scrollBar.ScrollDownBorder:Hide() end

		local frameLevel = scrollBar:GetFrameLevel()
		if upButton then
			S:HandleNextPrevButton(upButton, 'up')
			upButton:SetFrameLevel(frameLevel + 2)
		end
		if downButton then
			S:HandleNextPrevButton(downButton, 'down')
			downButton:SetFrameLevel(frameLevel + 2)
		end
		if thumb and not thumb.backdrop then
			thumb:SetTexture()
			S:CreateBackdrop(thumb, nil, true, true, nil, nil, nil, nil, nil, frameLevel + 1)

			if not scrollBar.Thumb then
				scrollBar.Thumb = thumb
			end

			if thumb.backdrop then
				if not thumbX then thumbX = 0 end
				if not thumbY then thumbY = 0 end

				S:Point(thumb.backdrop, 'TOPLEFT', thumb, thumbX, -thumbY)
				S:Point(thumb.backdrop, 'BOTTOMRIGHT', thumb, -thumbX, thumbY)

				if scrollBar.SetEnabled then
					ThumbWatcher(scrollBar)
				else
					thumb.backdrop:SetBackdropColor(unpack(Media.valueColor))
				end
			end
		end
	end

	-- WoWTrimScrollBar
	local function ReskinScrollBarArrow(frame, direction)
		S:HandleNextPrevButton(frame, direction)

		if frame.Texture then
			frame.Texture:SetAlpha(0)

			if frame.Overlay then
				frame.Overlay:SetAlpha(0)
			end
		else
			S:StripTextures(frame)
		end
	end

	local function ThumbOnEnter(frame)
		local r, g, b = unpack(Media.valueColor)
		local thumb = frame.thumb or frame
		if thumb.backdrop then
			thumb.backdrop:SetBackdropColor(r, g, b, .75)
		end
	end

	local function ThumbOnLeave(frame)
		local r, g, b = unpack(Media.valueColor)
		local thumb = frame.thumb or frame

		if thumb.backdrop and not thumb.__isActive then
			thumb.backdrop:SetBackdropColor(r, g, b, .25)
		end
	end

	local function ThumbOnMouseDown(frame)
		local r, g, b = unpack(Media.valueColor)
		local thumb = frame.thumb or frame
		thumb.__isActive = true

		if thumb.backdrop then
			thumb.backdrop:SetBackdropColor(r, g, b, .75)
		end
	end

	local function ThumbOnMouseUp(frame)
		local r, g, b = unpack(Media.valueColor)
		local thumb = frame.thumb or frame
		thumb.__isActive = nil

		if thumb.backdrop then
			thumb.backdrop:SetBackdropColor(r, g, b, .25)
		end
	end

	function S:HandleTrimScrollBar(scrollBar, small)
		S:StripTextures(scrollBar)

		ReskinScrollBarArrow(scrollBar.Back, 'up')
		ReskinScrollBarArrow(scrollBar.Forward, 'down')

		if scrollBar.Background then
			scrollBar.Background:Hide()
		end

		local track = scrollBar.Track
		if track then
			track:DisableDrawLayer('ARTWORK')
		end

		local thumb = scrollBar:GetThumb()
		if thumb then
			thumb:DisableDrawLayer('BACKGROUND')
			S:CreateBackdrop(thumb, 'Transparent')
			thumb.backdrop:SetFrameLevel(thumb:GetFrameLevel()+1)

			local r, g, b = unpack(Media.valueColor)
			thumb.backdrop:SetBackdropColor(r, g, b, .25)

			if not small then
				S:Point(thumb.backdrop, 'TOPLEFT', 4, -1)
				S:Point(thumb.backdrop, 'BOTTOMRIGHT', -4, 1)
			end

			thumb:HookScript('OnEnter', ThumbOnEnter)
			thumb:HookScript('OnLeave', ThumbOnLeave)
			thumb:HookScript('OnMouseUp', ThumbOnMouseUp)
			thumb:HookScript('OnMouseDown', ThumbOnMouseDown)
		end
	end
end

do --Tab Regions
	local tabs = { 'LeftDisabled', 'MiddleDisabled', 'RightDisabled', 'Left', 'Middle', 'Right' }

	function S:HandleTab(tab, noBackdrop, template)
		if not tab or (tab.backdrop and not noBackdrop) then return end

		for _, object in pairs(tabs) do
			if tab[object] then
				tab[object]:SetTexture()
			else
				local textureName = tab:GetName() and _G[tab:GetName()..object]
				if textureName then
					textureName:SetTexture()
				end
			end
		end

		local highlightTex = tab.GetHighlightTexture and tab:GetHighlightTexture()
		if highlightTex then
			highlightTex:SetTexture()
		else
			S:StripTextures(tab)
		end

		if not noBackdrop then
			S:CreateBackdrop(tab, AS:CheckOption('ElvUIStyle', 'ElvUI') and 'Default' or template)

			local spacing = S.Retail and 3 or 10
			S:Point(tab.backdrop, 'TOPLEFT', spacing, S.PixelMode and -1 or -3)
			S:Point(tab.backdrop, 'BOTTOMRIGHT', -spacing, 3)
		end
	end
end

function S:HandleRotateButton(button)
	if button.isSkinned then return end

	S:SetTemplate(button)
	S:Size(button, button:GetWidth() - 14, button:GetHeight() - 14)

	local normTex = button:GetNormalTexture()
	local pushTex = button:GetPushedTexture()
	local highlightTex = button:GetHighlightTexture()

	S:SetInside(normTex)
	normTex:SetTexCoord(0.3, 0.29, 0.3, 0.65, 0.69, 0.29, 0.69, 0.65)

	pushTex:SetAllPoints(normTex)
	pushTex:SetTexCoord(0.3, 0.29, 0.3, 0.65, 0.69, 0.29, 0.69, 0.65)

	highlightTex:SetAllPoints(normTex)
	highlightTex:SetColorTexture(1, 1, 1, 0.3)

	button.isSkinned = true
end

do
	local btns = { MaximizeButton = 'up', MinimizeButton = 'down' }

	local function buttonOnEnter(button)
		local r,g,b = unpack(Media.valueColor)
		button:GetNormalTexture():SetVertexColor(r,g,b)
		button:GetPushedTexture():SetVertexColor(r,g,b)
	end
	local function buttonOnLeave(button)
		button:GetNormalTexture():SetVertexColor(1, 1, 1)
		button:GetPushedTexture():SetVertexColor(1, 1, 1)
	end

	function S:HandleMaxMinFrame(frame)
		if frame.isSkinned then return end

		S:StripTextures(frame, true)

		for name, direction in pairs(btns) do
			local button = frame[name]
			if button then
				S:Size(button, 14, 14)
				button:ClearAllPoints()
				S:Point(button, 'CENTER')
				button:SetHitRectInsets(1, 1, 1, 1)
				S:Kill(button:GetHighlightTexture())

				button:SetScript('OnEnter', buttonOnEnter)
				button:SetScript('OnLeave', buttonOnLeave)

				button:SetNormalTexture(Media.Arrow)
				button:GetNormalTexture():SetRotation(S.ArrowRotation[direction])

				button:SetPushedTexture(Media.Arrow)
				button:GetPushedTexture():SetRotation(S.ArrowRotation[direction])
			end
		end

		frame.isSkinned = true
	end
end

function S:HandleBlizzardRegions(frame, name, kill, zero)
	if not name then name = frame.GetName and frame:GetName() end
	for _, area in pairs(BlizzardRegions) do
		local object = (name and _G[name..area]) or frame[area]
		if object then
			if kill then
				S:Kill(object)
			elseif zero then
				object:SetAlpha(0)
			else
				object:Hide()
			end
		end
	end
end

function S:HandleEditBox(editBox, template)
	if editBox.backdrop then return end

	S:CreateBackdrop(editBox, AS:CheckOption('ElvUIStyle', 'ElvUI') and 'Default' or template, nil, nil, nil, nil, nil, nil, nil, true)
	editBox.backdrop:SetPoint('TOPLEFT', -2, 0)
	editBox.backdrop:SetPoint('BOTTOMRIGHT')
	S:HandleBlizzardRegions(editBox)

	local EditBoxName = editBox:GetName()
	if EditBoxName and (strfind(EditBoxName, 'Silver') or strfind(EditBoxName, 'Copper')) then
		S:Point(editBox.backdrop, 'BOTTOMRIGHT', -12, -2)
	end
end

function S:HandleDropDownBox(dropDown, width, pos, template)
	local frameName = dropDown.GetName and dropDown:GetName()
	local button = dropDown.Button or frameName and (_G[frameName..'Button'] or _G[frameName..'_Button'])
	local text = frameName and _G[frameName..'Text'] or dropDown.Text
	local icon = dropDown.Icon

	if not width then
		width = 155
	end

	S:Width(dropDown, width)
	S:StripTextures(dropDown)
	S:CreateBackdrop(dropDown, template)
	dropDown:SetFrameLevel(dropDown:GetFrameLevel() + 2)
	S:Point(dropDown.backdrop, 'TOPLEFT', 20, -2)
	S:Point(dropDown.backdrop, 'BOTTOMRIGHT', button, 'BOTTOMRIGHT', 2, -2)

	button:ClearAllPoints()

	if pos then
		S:Point(button, 'TOPRIGHT', dropDown.Right, -20, -21)
	else
		S:Point(button, 'RIGHT', dropDown, 'RIGHT', -10, 3)
	end

	button.SetPoint = S.noop
	S:HandleNextPrevButton(button, 'down')

	if text then
		text:ClearAllPoints()
		S:Point(text, 'RIGHT', button, 'LEFT', -2, 0)
	end

	if icon then
		S:Point(icon, 'LEFT', 23, 0)
	end
end

function S:HandleStatusBar(statusBar, color, template)
	statusBar:SetFrameLevel(statusBar:GetFrameLevel() + 1)
	S:StripTextures(statusBar)
	S:CreateBackdrop(statusBar, template or 'Transparent')
	statusBar:SetStatusBarTexture(Media.barTexture)
	statusBar:SetStatusBarColor(unpack(color or AS:CheckOption('StatusBarColor')))

	S.StatusBar[statusBar] = true
end

do
	local function checkNormalTexture(checkBox, texture) if texture ~= S.Media.ClearTexture then checkBox:SetNormalTexture(S.Media.ClearTexture) end end
	local function checkPushedTexture(checkBox, texture) if texture ~= S.Media.ClearTexture then checkBox:SetPushedTexture(S.Media.ClearTexture) end end
	local function checkHighlightTexture(checkBox, texture) if texture ~= S.Media.ClearTexture then checkBox:SetHighlightTexture(S.Media.ClearTexture) end end
	local function checkCheckedTexture(checkBox, texture)
		if texture == Media.StatusBar then return end
		checkBox:SetCheckedTexture(Media.StatusBar)
	end
	local function checkOnDisable(checkBox)
		if not checkBox.SetDisabledTexture then return end
		checkBox:SetDisabledTexture(checkBox:GetChecked() and Media.StatusBar or '')
	end

	function S:HandleCheckBox(checkBox, noBackdrop, noReplaceTextures, frameLevel, template)
		if checkBox.isSkinned then return end

		S:StripTextures(checkBox)

		if noBackdrop then
			S:Size(checkBox, 16)
		else
			S:CreateBackdrop(checkBox, AS:CheckOption('ElvUIStyle', 'ElvUI') and 'Default' or template, nil, nil, nil, nil, nil, nil, nil, frameLevel)
			S:SetInside(checkBox.backdrop, nil, 4, 4)
		end

		if not noReplaceTextures then
			if checkBox.SetCheckedTexture then
				checkBox:SetCheckedTexture(Media.StatusBar)

				local checkedTexture = checkBox:GetCheckedTexture()
				checkedTexture:SetVertexColor(1, .82, 0, 0.8)
				S:SetInside(checkedTexture, checkBox.backdrop)
			end

			if checkBox.SetDisabledTexture then
				checkBox:SetDisabledTexture(Media.StatusBar)

				local disabledTexture = checkBox:GetDisabledTexture()
				disabledTexture:SetVertexColor(.6, .6, .6, .8)
				S:SetInside(disabledTexture, checkBox.backdrop)
			end

			checkBox:HookScript('OnDisable', checkOnDisable)

			hooksecurefunc(checkBox, 'SetNormalTexture', checkNormalTexture)
			hooksecurefunc(checkBox, 'SetPushedTexture', checkPushedTexture)
			hooksecurefunc(checkBox, 'SetCheckedTexture', checkCheckedTexture)
			hooksecurefunc(checkBox, 'SetHighlightTexture', checkHighlightTexture)
		end

		checkBox.isSkinned = true
	end
end

do
	local background = [[Interface\Minimap\UI-Minimap-Background]]

	local function buttonNormalTexture(button, texture) if texture ~= Media.ClearTexture then button:SetNormalTexture(Media.ClearTexture) end end
	local function buttonPushedTexture(button, texture) if texture ~= Media.ClearTexture then button:SetPushedTexture(Media.ClearTexture) end end
	local function buttonDisabledTexture(button, texture) if texture ~= Media.ClearTexture then button:SetDisabledTexture(Media.ClearTexture) end end
	local function buttonHighlightTexture(button, texture) if texture ~= Media.ClearTexture then button:SetHighlightTexture(Media.ClearTexture) end end

	function S:HandleRadioButton(button)
		if button.isSkinned then return end

		local InsideMask = button:CreateMaskTexture()
		InsideMask:SetTexture(background, 'CLAMPTOBLACKADDITIVE', 'CLAMPTOBLACKADDITIVE')
		S:Size(InsideMask, 10, 10)
		S:Point(InsideMask, 'CENTER')
		button.InsideMask = InsideMask

		local OutsideMask = button:CreateMaskTexture()
		OutsideMask:SetTexture(background, 'CLAMPTOBLACKADDITIVE', 'CLAMPTOBLACKADDITIVE')
		S:Size(OutsideMask, 13, 13)
		S:Point(OutsideMask, 'CENTER')
		button.OutsideMask = OutsideMask

		button:SetCheckedTexture(Media.StatusBar)
		button:SetNormalTexture(Media.StatusBar)
		button:SetHighlightTexture(Media.StatusBar)
		button:SetDisabledTexture(Media.StatusBar)

		local Check = button:GetCheckedTexture()
		Check:SetVertexColor(unpack(Media.valueColor))
		Check:SetTexCoord(0, 1, 0, 1)
		S:SetInside(Check)
		Check:AddMaskTexture(InsideMask)

		local Highlight = button:GetHighlightTexture()
		Highlight:SetTexCoord(0, 1, 0, 1)
		Highlight:SetVertexColor(1, 1, 1)
		Highlight:AddMaskTexture(InsideMask)

		local Normal = button:GetNormalTexture()
		S:SetOutside(Normal)
		Normal:SetTexCoord(0, 1, 0, 1)
		Normal:SetVertexColor(unpack(Media.borderColor))
		Normal:AddMaskTexture(OutsideMask)

		local Disabled = button:GetDisabledTexture()
		Disabled:SetVertexColor(.3, .3, .3)
		Disabled:AddMaskTexture(OutsideMask)

		hooksecurefunc(button, 'SetNormalTexture', buttonNormalTexture)
		hooksecurefunc(button, 'SetPushedTexture', buttonPushedTexture)
		hooksecurefunc(button, 'SetDisabledTexture', buttonDisabledTexture)
		hooksecurefunc(button, 'SetHighlightTexture', buttonHighlightTexture)

		button.isSkinned = true
	end
end

function S:HandleIcon(icon, backdrop, frameLevel)
	icon:SetTexCoord(unpack(Media.TexCoords))

	if icon:GetDrawLayer() ~= 'ARTWORK' then
		icon:SetDrawLayer('ARTWORK')
	end

	S:DisablePixelSnap(icon)

	if backdrop and not icon.backdrop then
		S:CreateBackdrop(icon, nil, nil, nil, nil, nil, nil, nil, nil, frameLevel)
	end
end

function S:HandleItemButton(button, setInside)
	if button.isSkinned then return end

	local name = button:GetName()
	local icon = button.icon or button.Icon or button.IconTexture or button.iconTexture or (name and (_G[name..'IconTexture'] or _G[name..'Icon']))
	local texture = icon and icon.GetTexture and icon:GetTexture()

	S:StripTextures(button)
	S:CreateBackdrop(button, nil, true, nil, nil, nil, nil, nil, true)
	S:StyleButton(button)

	if icon then
		S:HandleIcon(icon)

		if setInside then
			S:SetInside(icon, button)
		else
			S:SetOutside(button.backdrop, icon, 1, 1)
		end

		icon:SetParent(button.backdrop)

		if texture then
			icon:SetTexture(texture)
		end
	end

	button.isSkinned = true
end

do
	local closeOnEnter = function(btn) if btn.Texture then btn.Texture:SetVertexColor(unpack(Media.valueColor)) end end
	local closeOnLeave = function(btn) if btn.Texture then btn.Texture:SetVertexColor(1, 1, 1) end end

	function S:HandleCloseButton(button, point, x, y)
		S:StripTextures(button)

		if not button.Texture then
			button.Texture = button:CreateTexture(nil, 'OVERLAY')
			S:Point(button.Texture, 'CENTER')
			button.Texture:SetTexture(Media.Close)
			S:Size(button.Texture, 12, 12)
			button:HookScript('OnEnter', closeOnEnter)
			button:HookScript('OnLeave', closeOnLeave)
			button:SetHitRectInsets(6, 6, 7, 7)
		end

		if point then
			S:Point(button, 'TOPRIGHT', point, 'TOPRIGHT', x or 2, y or 2)
		end
	end

	function S:HandleNextPrevButton(button, arrowDir, color, noBackdrop, stripTexts, frameLevel)
		if button.isSkinned then return end

		if not arrowDir then
			arrowDir = 'down'

			local name = button:GetDebugName()
			local ButtonName = name and name:lower()
			if ButtonName then
				if strfind(ButtonName, 'left') or strfind(ButtonName, 'prev') or strfind(ButtonName, 'decrement') or strfind(ButtonName, 'backward') or strfind(ButtonName, 'back') then
					arrowDir = 'left'
				elseif strfind(ButtonName, 'right') or strfind(ButtonName, 'next') or strfind(ButtonName, 'increment') or strfind(ButtonName, 'forward') then
					arrowDir = 'right'
				elseif strfind(ButtonName, 'scrollup') or strfind(ButtonName, 'upbutton') or strfind(ButtonName, 'top') or strfind(ButtonName, 'asc') or strfind(ButtonName, 'home') or strfind(ButtonName, 'maximize') then
					arrowDir = 'up'
				end
			end
		end

		S:StripTextures(button)

		if button.Texture then
			button.Texture:SetAlpha(0)
		end

		if not noBackdrop then
			S:HandleButton(button, nil, nil, true, nil, nil, nil, nil, frameLevel)
		end

		if stripTexts then
			S:StripTexts(button)
		end

		button:SetNormalTexture(Media.Arrow)
		button:SetPushedTexture(Media.Arrow)
		button:SetDisabledTexture(Media.Arrow)

		local Normal, Disabled, Pushed = button:GetNormalTexture(), button:GetDisabledTexture(), button:GetPushedTexture()

		if noBackdrop then
			S:Size(button, 20, 20)
			Disabled:SetVertexColor(.5, .5, .5)
			button.Texture = Normal

			if not color then
				button:HookScript('OnEnter', closeOnEnter)
				button:HookScript('OnLeave', closeOnLeave)
			end
		else
			S:Size(button, 18, 18)
			Disabled:SetVertexColor(.3, .3, .3)
		end

		S:SetInside(Normal)
		S:SetInside(Pushed)
		S:SetInside(Disabled)

		Normal:SetTexCoord(0, 1, 0, 1)
		Pushed:SetTexCoord(0, 1, 0, 1)
		Disabled:SetTexCoord(0, 1, 0, 1)

		local rotation = S.ArrowRotation[arrowDir]
		if rotation then
			Normal:SetRotation(rotation)
			Pushed:SetRotation(rotation)
			Disabled:SetRotation(rotation)
		end

		if color then
			Normal:SetVertexColor(color.r, color.g, color.b)
		else
			Normal:SetVertexColor(1, 1, 1)
		end

		button.isSkinned = true
	end
end

function S:HandleSliderFrame(frame, template, frameLevel)
	local orientation = frame:GetOrientation()
	local size = 12

	if frame.SetBackdrop then
		frame:SetBackdrop()
	end

	S:StripTextures(frame)
	frame:SetThumbTexture(Media.StatusBar)

	if not frame.backdrop then
		S:CreateBackdrop(frame, AS:CheckOption('ElvUIStyle', 'ElvUI') and 'Default' or template, nil, nil, nil, nil, nil, nil, true, frameLevel)
	end

	local thumb = frame:GetThumbTexture()
	thumb:SetVertexColor(1, .82, 0, 0.8)
	S:Size(thumb, size - 2)

	if orientation == 'VERTICAL' then
		S:Width(frame, size)
	else
		S:Height(frame, size)

		for _, region in next, { frame:GetRegions() } do
			if region:IsObjectType('FontString') then
				local point, anchor, anchorPoint, x, y = region:GetPoint()
				if strfind(anchorPoint, 'BOTTOM') then
					S:Point(region, point, anchor, anchorPoint, x, y - 4)
				end
			end
		end
	end
end

-- Needs Redone
function S:CleanTexture(Object, Kill, Alpha)
	if Kill then
		S:Kill(Object)
	elseif Alpha then
		Object:SetAlpha(0)
	else
		Object:SetTexture('')
	end
end

function S:StripTexture(Object, Texture, Kill, Alpha)
	if Object:IsObjectType('Texture') and type(Object:GetTexture()) == 'string' and strlower(Object:GetTexture()) == strlower(Texture) then
		S:CleanTexture(Object, Kill, Alpha)
	else
		if Object.GetNumRegions then
			for _, Region in next, { Object:GetRegions() } do
				if Region and Region:IsObjectType('Texture') and type(Region:GetTexture()) == 'string' and strlower(Region:GetTexture()) == strlower(Texture) then
					S:CleanTexture(Region, Kill, Alpha)
				end
			end
		end
	end
end

local function Desaturate(self, value)
	if value ~= true then self:SetDesaturated(true) end
end

function S:Desaturate(frame)
	if frame.IsObjectType and (frame:IsObjectType('Button') or frame:IsObjectType('CheckButton')) then
		local Normal, Pushed, Highlight = frame:GetNormalTexture(), frame:GetPushedTexture(), frame:GetHighlightTexture()

		if Normal then
			Normal:SetDesaturated(true)
			hooksecurefunc(Normal, 'SetDesaturated', Desaturate)
		end

		if Pushed then
			Pushed:SetDesaturated(true)
			hooksecurefunc(Pushed, 'SetDesaturated', Desaturate)
		end

		if Highlight then
			Highlight:SetDesaturated(true)
			hooksecurefunc(Highlight, 'SetDesaturated', Desaturate)
		end
	end
end

function S:HandleTooltip(tooltip, scale, showHook)
	S:HandleBlizzardRegions(tooltip)
	S:SetTemplate(tooltip, nil, nil, nil, nil, nil, nil, nil, true)

	if showHook then
		tooltip:HookScript('OnShow', function(tt) S:SetTemplate(tt) end)
	end

	if scale then
		tooltip:SetScale(S.UIScale)
	end
end

local function EnumObjectsHelper(enumFuncs, yieldFunc, iobj)
	local depth = #enumFuncs
	local i = 1
	local obj
	repeat
		if (iobj) then
			obj = enumFuncs[1](iobj, i)
		else
			obj = enumFuncs[1](i)
		end
		if (obj) then
			if (depth == 1) then
				yieldFunc(obj)
			else
				local innerEnumFuncs = CopyTable(enumFuncs)
				tremove(innerEnumFuncs, 1)
				EnumObjectsHelper(innerEnumFuncs, yieldFunc, obj)
			end
		end
		i = i + 1
	until not obj
end

function S:EnumObjects(enumFuncs, yieldFunc)
	if (type(enumFuncs) == 'function') then
		enumFuncs = {enumFuncs}
	end

	EnumObjectsHelper(enumFuncs, yieldFunc)
end

-- ToDO: DF => UpdateME => Credits: NDUI
local sparkTexture = [[Interface\CastingBar\UI-CastingBar-Spark]]
function S:HandleStepSlider(frame, minimal)
	local slider = frame.Slider
	if not slider then return end
	S:StripTextures(frame)

	slider:DisableDrawLayer('ARTWORK')

	local thumb = slider.Thumb
	if thumb then
		thumb:SetTexture(sparkTexture)
		thumb:SetBlendMode('ADD')
		thumb:SetSize(20, 30)
	end

	local offset = minimal and 10 or 13
	S:CreateBackdrop(slider)
	slider.backdrop:SetPoint('TOPLEFT', 10, -offset)
	slider.backdrop:SetPoint('BOTTOMRIGHT', -10, offset)

	if not slider.barStep then
		local step = CreateFrame('StatusBar', nil, slider.backdrop)
		S:HandleStatusBar(step, {1, .8, 0, .5})
		step:SetPoint('TOPLEFT', slider.backdrop, S.mult, -S.mult)
		step:SetPoint('BOTTOMLEFT', slider.backdrop, S.mult, S.mult)
		step:SetPoint('RIGHT', thumb, 'CENTER')

		slider.barStep = step
	end
end

-- TODO: Update the function for BFA/Shadowlands
function S:HandleFollowerAbilities(followerList)
	local followerTab = followerList and followerList.followerTab
	local abilityFrame = followerTab.AbilitiesFrame
	if not abilityFrame then return end

	local abilities = abilityFrame.Abilities
	if abilities then
		for i = 1, #abilities do
			local iconButton = abilities[i].IconButton
			local icon = iconButton and iconButton.Icon
			if icon then
				iconButton.Border:SetAlpha(0)
				S:HandleIcon(icon, true)
			end
		end
	end

	local equipment = abilityFrame.Equipment
	if equipment then
		for i = 1, #equipment do
			local equip = equipment[i]
			if equip then
				equip.Border:SetAlpha(0)
				equip.BG:SetAlpha(0)

				S:HandleIcon(equip.Icon, true)
				equip.Icon.backdrop:SetBackdropColor(1, 1, 1, .15)
			end
		end
	end

	local combatAllySpell = abilityFrame.CombatAllySpell
	if combatAllySpell then
		for i = 1, #combatAllySpell do
			local icon = combatAllySpell[i].iconTexture
			if icon then
				S:HandleIcon(icon, true)
			end
		end
	end

	local xpbar = followerTab.XPBar
	if xpbar and not xpbar.backdrop then
		S:StripTextures(xpbar)
		S:HandleStatusBar(xpbar)
	end
end

function S:HandleShipFollowerPage(followerTab)
	local traits = followerTab.Traits
	for i = 1, #traits do
		local icon = traits[i].Portrait
		local border = traits[i].Border
		border:SetTexture() -- I think the default border looks nice, not sure if we want to replace that
		-- The landing page icons display inner borders
		if followerTab.isLandingPage then
			S:HandleIcon(icon)
		end
	end

	local equipment = followerTab.EquipmentFrame.Equipment
	for i = 1, #equipment do
		local icon = equipment[i].Icon
		local border = equipment[i].Border
		border:SetAtlas('ShipMission_ShipFollower-TypeFrame') -- This border is ugly though, use the traits border instead
		-- The landing page icons display inner borders
		if followerTab.isLandingPage then
			S:HandleIcon(icon)
		end
	end
end

local function UpdateFollowerQuality(self, followerInfo)
	if followerInfo then
		local color = S.QualityColors[followerInfo.quality or 1]
		self.Portrait.backdrop:SetBackdropBorderColor(color.r, color.g, color.b)
	end
end

do
	S.FollowerListUpdateDataFrames = {}

	local function UpdateFollower(button)
		if not S.Retail then
			button:SetTemplate(button.mode == 'CATEGORY' and 'NoBackdrop' or 'Transparent')
		end

		local category = button.Category
		if category then
			category:ClearAllPoints()
			category:Point('TOP', button, 'TOP', 0, -4)
		end

		local follower = button.Follower
		if follower then
			if not follower.template then
				S:SetTemplate(follower, 'Transparent')
				follower.Name:SetWordWrap(false)
				follower.Selection:SetTexture()
				follower.AbilitiesBG:SetTexture()
				follower.BusyFrame:SetAllPoints()
				follower.BG:Hide()

				local hl = follower:GetHighlightTexture()
				hl:SetColorTexture(0.9, 0.9, 0.9, 0.25)
				S:SetInside(hl)
			end

			local counters = follower.Counters
			if counters then
				for _, counter in next, counters do
					if not counter.template then
						S:SetTemplate(counter)

						if counter.Border then
							counter.Border:SetTexture()
						end

						if counter.Icon then
							S:HandleIcon(counter.Icon)
							S:SetInside(counter.Icon)
						end
					end
				end
			end

			local portrait = follower.PortraitFrame
			if portrait then
				S:HandleGarrisonPortrait(portrait, true)

				portrait:ClearAllPoints()
				S:Point(portrait, 'TOPLEFT', 3, -3)

				if not follower.PortraitFrameStyled then
					hooksecurefunc(portrait, 'SetupPortrait', UpdateFollowerQuality)
					follower.PortraitFrameStyled = true
				end

				local quality = portrait.quality or (follower.info and follower.info.quality)
				local color = portrait.backdrop and ITEM_QUALITY_COLORS[quality]
				if color then -- sometimes it doesn't have this data since DF
					portrait.backdrop:SetBackdropBorderColor(color.r, color.g, color.b)
				end
			end

			if follower.Selection then
				if follower.Selection:IsShown() then
					follower:SetBackdropColor(0.9, 0.8, 0.1, 0.25)
				else
					follower:SetBackdropColor(0, 0, 0, 0.5)
				end
			end
		end
	end

	function S:HandleFollowerListOnUpdateDataFunc(buttons, numButtons, offset, numFollowers)
		if not buttons or (not numButtons or numButtons == 0) or not offset or not numFollowers then return end

		for i = 1, numButtons do
			local button = buttons[i]
			if button then
				local index = offset + i -- adjust index
				if index <= numFollowers then
					UpdateFollower(button)
				end
			end
		end
	end

	local function UpdateListScroll(dataFrame)
		if not (dataFrame and dataFrame.listScroll) or not S.FollowerListUpdateDataFrames[dataFrame:GetName()] then return end

		local buttons = dataFrame.listScroll.buttons
		local offset = _G.HybridScrollFrame_GetOffset(dataFrame.listScroll)
		S:HandleFollowerListOnUpdateDataFunc(buttons, buttons and #buttons, offset, dataFrame.listScroll and #dataFrame.listScroll)
	end

	function S:HandleFollowerListOnUpdateData(frame)
		if frame == 'GarrisonLandingPageFollowerList' and (not AS:IsSkinEnabled(nil, 'orderhall') or not AS:IsSkinEnabled(nil, 'garrison')) then
			return -- Only hook this frame if both Garrison and Orderhall skins are enabled because it's shared.
		end

		if S.FollowerListUpdateDataFrames[frame] then return end -- make sure we don't double hook `GarrisonLandingPageFollowerList`
		S.FollowerListUpdateDataFrames[frame] = true

		if _G.GarrisonFollowerList_InitButton then
			hooksecurefunc(_G, 'GarrisonFollowerList_InitButton', UpdateFollower)
		else
			hooksecurefunc(_G[frame], 'UpdateData', UpdateListScroll) -- pre DF
		end
	end
end

-- Shared Template on LandingPage/Orderhall-/Garrison-FollowerList
local ReplacedRoleTex = {
	['Adventures-Tank'] = 'Soulbinds_Tree_Conduit_Icon_Protect',
	['Adventures-Healer'] = 'ui_adv_health',
	['Adventures-DPS'] = 'ui_adv_atk',
	['Adventures-DPS-Ranged'] = 'Soulbinds_Tree_Conduit_Icon_Utility',
}

local function HandleFollowerRole(roleIcon, atlas)
	local newAtlas = ReplacedRoleTex[atlas]
	if newAtlas then
		roleIcon:SetAtlas(newAtlas)
	end
end

function S:HandleGarrisonPortrait(portrait, updateAtlas)
	local main = portrait.Portrait
	if not main then return end

	if not main.backdrop then
		S:CreateBackdrop(main, 'Transparent')
	end

	local level = portrait.Level or portrait.LevelText
	if level then
		level:ClearAllPoints()
		S:Point(level, 'BOTTOM', portrait, 0, 15)
		S:FontTemplate(level, nil, 14, 'OUTLINE')

		if portrait.LevelCircle then portrait.LevelCircle:Hide() end
		if portrait.LevelBorder then portrait.LevelBorder:SetScale(.0001) end
	end

	if portrait.PortraitRing then
		portrait.PortraitRing:Hide()
		portrait.PortraitRingQuality:SetTexture('')
		portrait.PortraitRingCover:SetColorTexture(0, 0, 0)
		portrait.PortraitRingCover:SetAllPoints(main.backdrop)
	end

	if portrait.Empty then
		portrait.Empty:SetColorTexture(0, 0, 0)
		portrait.Empty:SetAllPoints(main)
	end

	if portrait.Highlight then portrait.Highlight:Hide() end
	if portrait.PuckBorder then portrait.PuckBorder:SetAlpha(0) end
	if portrait.TroopStackBorder1 then portrait.TroopStackBorder1:SetAlpha(0) end
	if portrait.TroopStackBorder2 then portrait.TroopStackBorder2:SetAlpha(0) end

	if portrait.HealthBar then
		portrait.HealthBar.Border:Hide()

		local roleIcon = portrait.HealthBar.RoleIcon
		roleIcon:ClearAllPoints()
		S:Point(roleIcon, 'CENTER', main.backdrop, 'TOPRIGHT')

		if updateAtlas then
			HandleFollowerRole(roleIcon, roleIcon:GetAtlas())
		else
			hooksecurefunc(roleIcon, 'SetAtlas', HandleFollowerRole)
		end

		local background = portrait.HealthBar.Background
		background:SetAlpha(0)
		S:SetInside(background, main.backdrop, 2, 1) -- unsnap it
		S:Point(background, 'TOPLEFT', main.backdrop, 'BOTTOMLEFT', 2, 7)
		portrait.HealthBar.Health:SetTexture(S.Media.StatusBar)
	end
end

do
	local function selectionOffset(frame)
		local point, anchor, relativePoint, xOffset = frame:GetPoint()
		if xOffset <= 0 then
			local x = frame.BorderBox and 4 or 38 -- adjust values for wrath
			local y = frame.BorderBox and 0 or -10

			frame:ClearAllPoints()
			S:Point(frame, point, (frame == _G.MacroPopupFrame and _G.MacroFrame) or anchor, relativePoint, strfind(point, 'LEFT') and x or -x, y)
		end
	end

	local function handleButton(button, i, buttonNameTemplate)
		local icon, texture = button.Icon or _G[buttonNameTemplate..i..'Icon']
		if icon then
			S:HandleIcon(icon)
			S:SetInside(icon, button)
			texture = icon:GetTexture() -- keep this before strip textures
		end

		S:HandleFrame(button)
		S:StyleButton(button, nil, true)

		if texture then
			icon:SetTexture(texture)
		end
	end

	function S:HandleIconSelectionFrame(frame, numIcons, buttonNameTemplate, nameOverride, dontOffset)
		if frame.isSkinned then
			return
		elseif not S.Retail and (nameOverride and nameOverride ~= 'MacroPopup') then -- skip macros because it skins on show
			frame:Show() -- spawn the info so we can skin the buttons
			if frame.Update then frame:Update() end -- guild bank popup has update function
			frame:Hide() -- can hide it right away
		end

		if not dontOffset then -- place it off to the side of parent with correct offsets
			frame:HookScript('OnShow', selectionOffset)
			frame:Height(frame:GetHeight() + 10)
		end

		local borderBox = frame.BorderBox or _G.BorderBox -- it's a sub frame only on retail, on wrath it's a global?
		local frameName = nameOverride or frame:GetName() -- we need override in case Blizzard fucks up the naming (guild bank)
		local scrollFrame = frame.ScrollFrame or _G[frameName..'ScrollFrame']
		local editBox = (borderBox and borderBox.IconSelectorEditBox) or frame.EditBox or _G[frameName..'EditBox']
		local cancel = frame.CancelButton or (borderBox and borderBox.CancelButton) or _G[frameName..'Cancel']
		local okay = frame.OkayButton or (borderBox and borderBox.OkayButton) or _G[frameName..'Okay']

		S:HandleFrame(frame)

		if borderBox then
			S:StripTextures(borderBox)

			local button = borderBox.SelectedIconArea and borderBox.SelectedIconArea.SelectedIconButton
			if button then
				button:DisableDrawLayer('BACKGROUND')
				S:HandleItemButton(button, true)
			end
		end

		cancel:ClearAllPoints()
		cancel:SetPoint('BOTTOMRIGHT', frame, -4, 4)
		S:HandleButton(cancel)

		okay:ClearAllPoints()
		okay:SetPoint('RIGHT', cancel, 'LEFT', -10, 0)
		S:HandleButton(okay)

		if editBox then
			editBox:DisableDrawLayer('BACKGROUND')
			S:HandleEditBox(editBox)
		end

		if numIcons then
			S:StripTextures(scrollFrame)
			scrollFrame:Height(scrollFrame:GetHeight() + 10)
			S:HandleScrollBar(scrollFrame.ScrollBar)

			for i = 1, numIcons do
				local button = _G[buttonNameTemplate..i]
				if button then
					handleButton(button, i, buttonNameTemplate)
				end
			end
		else
			S:HandleTrimScrollBar(frame.IconSelector.ScrollBar)

			for _, button in next, { frame.IconSelector.ScrollBox.ScrollTarget:GetChildren() } do
				handleButton(button)
			end
		end

		frame.isSkinned = true
	end
end

do -- Handle collapse
	local function UpdateCollapseTexture(button, texture, skip)
		if skip then return end

		if type(texture) == 'number' then -- 130821 minus, 130838 plus
			button:SetNormalTexture(texture == 130838 and S.Media.Plus or S.Media.Minus, true)
		elseif strfind(texture, 'Plus') or strfind(texture, 'Closed') then
			button:SetNormalTexture(S.Media.Plus, true)
		elseif strfind(texture, 'Minus') or strfind(texture, 'Open') then
			button:SetNormalTexture(S.Media.Minus, true)
		end
	end

	local function syncPushTexture(button, _, skip)
		if skip then return end

		local normal = button:GetNormalTexture():GetTexture()
		button:SetPushedTexture(normal, true)
	end

	function S:HandleCollapseTexture(button, syncPushed)
		if syncPushed then -- not needed always
			hooksecurefunc(button, 'SetPushedTexture', syncPushTexture)
			syncPushTexture(button)
		else
			button:SetPushedTexture(S.Media.ClearTexture)
		end

		hooksecurefunc(button, 'SetNormalTexture', UpdateCollapseTexture)
		UpdateCollapseTexture(button, button:GetNormalTexture():GetTexture())
	end
end

-- World Map related Skinning functions used for WoW 8.0
function S:WorldMapMixin_AddOverlayFrame(frame, templateName)
	S[templateName](frame.overlayFrames[#frame.overlayFrames])
end

-- UIWidgets
function S:SkinIconAndTextWidget()
end

-- For now see the function below
function S:SkinCaptureBarWidget()
end

function S:SkinStatusBarWidget(widgetFrame)
	local bar = widgetFrame.Bar
	if not bar or bar.backdrop then return end

	S:CreateBackdrop(bar, 'Transparent')
	bar:SetScale(0.99) -- lol yes, this will keep it placed correctly for Simpy

	if bar.BGLeft then bar.BGLeft:SetAlpha(0) end
	if bar.BGRight then bar.BGRight:SetAlpha(0) end
	if bar.BGCenter then bar.BGCenter:SetAlpha(0) end
	if bar.BorderLeft then bar.BorderLeft:SetAlpha(0) end
	if bar.BorderRight then bar.BorderRight:SetAlpha(0) end
	if bar.BorderCenter then bar.BorderCenter:SetAlpha(0) end
end

do
	local function handleBar(bar)
		if not bar or bar.backdrop then return end

		S:CreateBackdrop(bar, 'Transparent')

		if bar.BG then bar.BG:SetAlpha(0) end
		if bar.Spark then bar.Spark:SetAlpha(0) end
		if bar.SparkGlow then bar.SparkGlow:SetAlpha(0) end
		if bar.BorderLeft then bar.BorderLeft:SetAlpha(0) end
		if bar.BorderRight then bar.BorderRight:SetAlpha(0) end
		if bar.BorderCenter then bar.BorderCenter:SetAlpha(0) end
		if bar.BorderGlow then bar.BorderGlow:SetAlpha(0) end
	end

	function S:SkinDoubleStatusBarWidget(widgetFrame)
		handleBar(widgetFrame.LeftBar)
		handleBar(widgetFrame.RightBar)
	end
end

function S:SkinIconTextAndBackgroundWidget()
end

function S:SkinDoubleIconAndTextWidget()
end

function S:SkinStackedResourceTrackerWidget()
end

function S:SkinIconTextAndCurrenciesWidget()
end

function S:SkinTextWithStateWidget(widgetFrame)
	local text = widgetFrame.Text
	if not text then return end

	text:SetTextColor(1, 1, 1)
end

function S:SkinHorizontalCurrenciesWidget()
end

function S:SkinBulletTextListWidget()
end

function S:SkinScenarioHeaderCurrenciesAndBackgroundWidget()
end

function S:SkinTextureAndTextWidget()
end

function S:SkinSpellDisplay(widgetFrame)
	local spell = widgetFrame.Spell
	if not spell then return end

	if spell.Border then
		spell.Border:Hide()
	end

	if spell.Text then
		spell.Text:SetTextColor(1, 1, 1)
	end

	if spell.Icon then
		S:HandleIcon(spell.Icon, true)
	end
end

function S:SkinDoubleStateIconRow()
end

function S:SkinTextureAndTextRowWidget()
end

function S:SkinZoneControl()
end

function S:SkinCaptureZone()
end

do
	local W = Enum.UIWidgetVisualizationType
	S.WidgetSkinningFuncs = {
		[W.IconAndText] = 'SkinIconAndTextWidget',
		[W.CaptureBar] = 'SkinCaptureBarWidget',
		[W.StatusBar] = 'SkinStatusBarWidget',
		[W.DoubleStatusBar] = 'SkinDoubleStatusBarWidget',
		[W.IconTextAndBackground] = 'SkinIconTextAndBackgroundWidget',
		[W.DoubleIconAndText] = 'SkinDoubleIconAndTextWidget',
		[W.StackedResourceTracker] = 'SkinStackedResourceTrackerWidget',
		[W.IconTextAndCurrencies] = 'SkinIconTextAndCurrenciesWidget',
		[W.TextWithState] = 'SkinTextWithStateWidget',
		[W.HorizontalCurrencies] = 'SkinHorizontalCurrenciesWidget',
		[W.BulletTextList] = 'SkinBulletTextListWidget',
		[W.ScenarioHeaderCurrenciesAndBackground] = 'SkinScenarioHeaderCurrenciesAndBackgroundWidget',
	}

	if S.Retail then
		S.WidgetSkinningFuncs[W.SpellDisplay] = 'SkinSpellDisplay'
		S.WidgetSkinningFuncs[W.TextureAndText] = 'SkinTextureAndTextWidget'
		S.WidgetSkinningFuncs[W.DoubleStateIconRow] = 'SkinDoubleStateIconRow'
		S.WidgetSkinningFuncs[W.TextureAndTextRow] = 'SkinTextureAndTextRowWidget'
		S.WidgetSkinningFuncs[W.ZoneControl] = 'SkinZoneControl'
		S.WidgetSkinningFuncs[W.CaptureZone] = 'SkinCaptureZone'
	end
end

function S:SkinWidgetContainer(widget)
	local typeFunc = S.WidgetSkinningFuncs[widget.widgetType]
	if typeFunc and S[typeFunc] then
		S[typeFunc](S, widget)
	end
end

function S:UpdateAllWidgets()
	for _, widget in pairs(_G.UIWidgetTopCenterContainerFrame.widgetFrames) do
		S:SkinWidgetContainer(widget)
	end
end

function S:PLAYER_LOGIN()
	S:GetPixelScale()

	if S.Retail then
		S:RegisterEvent('PLAYER_ENTERING_WORLD', 'UpdateAllWidgets')
		S:RegisterEvent('UPDATE_ALL_UI_WIDGETS', 'UpdateAllWidgets')
	end
end

S:RegisterEvent('PLAYER_LOGIN')
