
local detailsFramework = _G["DetailsFramework"]
if (not detailsFramework or not DetailsFrameworkCanLoad) then
	return
end

local _
local loadedAPILabelFunctions = false

do
	local metaPrototype = {
		WidgetType = "label",
		dversion = detailsFramework.dversion,
	}

	--check if there's a metaPrototype already existing
	if (_G[detailsFramework.GlobalWidgetControlNames["label"]]) then
		--get the already existing metaPrototype
		local oldMetaPrototype = _G[detailsFramework.GlobalWidgetControlNames ["label"]]
		--check if is older
		if ( (not oldMetaPrototype.dversion) or (oldMetaPrototype.dversion < detailsFramework.dversion) ) then
			--the version is older them the currently loading one
			--copy the new values into the old metatable
			for funcName, _ in pairs(metaPrototype) do
				oldMetaPrototype[funcName] = metaPrototype[funcName]
			end
		end
	else
		--first time loading the framework
		_G[detailsFramework.GlobalWidgetControlNames ["label"]] = metaPrototype
	end
end

local LabelMetaFunctions = _G[detailsFramework.GlobalWidgetControlNames ["label"]]

detailsFramework:Mixin(LabelMetaFunctions, detailsFramework.SetPointMixin)
detailsFramework:Mixin(LabelMetaFunctions, detailsFramework.ScriptHookMixin)

------------------------------------------------------------------------------------------------------------
--metatables

	LabelMetaFunctions.__call = function(object, value)
		return object.label:SetText(value)
	end

------------------------------------------------------------------------------------------------------------
--members

	--get text
	local gmember_text = function(object)
		return object.label:GetText()
	end

	--text width
	local gmember_width = function(object)
		return object.label:GetStringWidth()
	end

	--text height
	local gmember_height = function(object)
		return object.label:GetStringHeight()
	end

	--text color
	local gmember_textcolor = function(object)
		return object.label:GetTextColor()
	end

	--text font
	local gmember_textfont = function(object)
		local fontface = object.label:GetFont()
		return fontface
	end

	--text size
	local gmember_textsize = function(object)
		local _, fontsize = object.label:GetFont()
		return fontsize
	end

	LabelMetaFunctions.GetMembers = LabelMetaFunctions.GetMembers or {}
	detailsFramework:Mixin(LabelMetaFunctions.GetMembers, detailsFramework.LayeredRegionMetaFunctionsGet)
	detailsFramework:Mixin(LabelMetaFunctions.GetMembers, detailsFramework.DefaultMetaFunctionsGet)

	LabelMetaFunctions.GetMembers["width"] = gmember_width
	LabelMetaFunctions.GetMembers["height"] = gmember_height
	LabelMetaFunctions.GetMembers["text"] = gmember_text
	LabelMetaFunctions.GetMembers["fontcolor"] = gmember_textcolor
	LabelMetaFunctions.GetMembers["fontface"] = gmember_textfont
	LabelMetaFunctions.GetMembers["fontsize"] = gmember_textsize
	LabelMetaFunctions.GetMembers["textcolor"] = gmember_textcolor --alias
	LabelMetaFunctions.GetMembers["textfont"] = gmember_textfont --alias
	LabelMetaFunctions.GetMembers["textsize"] = gmember_textsize --alias

	LabelMetaFunctions.__index = function(object, key)
		local func = LabelMetaFunctions.GetMembers[key]
		if (func) then
			return func(object, key)
		end

		local alreadyHaveKey = rawget(object, key)
		if (alreadyHaveKey) then
			return alreadyHaveKey
		end

		return LabelMetaFunctions[key]
	end

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

	--text
	local smember_text = function(object, value)
		--check if this is a loc table
		if (detailsFramework.Language.IsLocTable(value)) then
			local locTable = value
			detailsFramework.Language.RegisterObjectWithLocTable(object.widget or object, locTable)
		else
			return object.label:SetText(value)
		end
	end

	--text color
	local smember_textcolor = function(object, value)
		local value1, value2, value3, value4 = detailsFramework:ParseColors(value)
		return object.label:SetTextColor(value1, value2, value3, value4)
	end

	--text font
	local smember_textfont = function(object, value)
		return detailsFramework:SetFontFace(object.label, value)
	end

	--text size
	local smember_textsize = function(object, value)
		return detailsFramework:SetFontSize(object.label, value)
	end

	--text align
	local smember_textalign = function(object, value)
		if (value == "<") then
			value = "left"
		elseif (value == ">") then
			value = "right"
		elseif (value == "|") then
			value = "center"
		end
		return object.label:SetJustifyH(value)
	end

	--text valign
	local smember_textvalign = function(object, value)
		if (value == "^") then
			value = "top"
		elseif (value == "_") then
			value = "bottom"
		elseif (value == "|") then
			value = "middle"
		end
		return object.label:SetJustifyV(value)
	end

	--field size width
	local smember_width = function(object, value)
		return object.label:SetWidth(value)
	end

	--field size height
	local smember_height = function(object, value)
		return object.label:SetHeight(value)
	end

	--outline (shadow)
	local smember_outline = function(object, value)
		detailsFramework:SetFontOutline(object.label, value)
	end

	--text rotation
	local smember_rotation = function(object, rotation)
		if (type(rotation) == "number") then
			if (not object.__rotationAnimation) then
				object.__rotationAnimation = detailsFramework:CreateAnimationHub(object.label)
				object.__rotationAnimation.rotator = detailsFramework:CreateAnimation(object.__rotationAnimation, "rotation", 1, 0, 0)
				object.__rotationAnimation.rotator:SetEndDelay(10^8)
				object.__rotationAnimation.rotator:SetSmoothProgress(1)
			end
			object.__rotationAnimation.rotator:SetDegrees(rotation)
			object.__rotationAnimation:Play()
			object.__rotationAnimation:Pause()
		end
	end

	LabelMetaFunctions.SetMembers = LabelMetaFunctions.SetMembers or {}
	detailsFramework:Mixin(LabelMetaFunctions.SetMembers, detailsFramework.LayeredRegionMetaFunctionsSet)
	detailsFramework:Mixin(LabelMetaFunctions.SetMembers, detailsFramework.DefaultMetaFunctionsSet)

	LabelMetaFunctions.SetMembers["align"] = smember_textalign
	LabelMetaFunctions.SetMembers["valign"] = smember_textvalign
	LabelMetaFunctions.SetMembers["text"] = smember_text
	LabelMetaFunctions.SetMembers["width"] = smember_width
	LabelMetaFunctions.SetMembers["height"] = smember_height
	LabelMetaFunctions.SetMembers["fontcolor"] = smember_textcolor
	LabelMetaFunctions.SetMembers["color"] = smember_textcolor--alias
	LabelMetaFunctions.SetMembers["fontface"] = smember_textfont
	LabelMetaFunctions.SetMembers["fontsize"] = smember_textsize
	LabelMetaFunctions.SetMembers["textcolor"] = smember_textcolor--alias
	LabelMetaFunctions.SetMembers["textfont"] = smember_textfont--alias
	LabelMetaFunctions.SetMembers["textsize"] = smember_textsize--alias
	LabelMetaFunctions.SetMembers["shadow"] = smember_outline
	LabelMetaFunctions.SetMembers["outline"] = smember_outline--alias
	LabelMetaFunctions.SetMembers["rotation"] = smember_rotation

	LabelMetaFunctions.__newindex = function(object, key, value)
		local func = LabelMetaFunctions.SetMembers[key]
		if (func) then
			return func(object, value)
		else
			return rawset(object, key, value)
		end
	end

------------------------------------------------------------------------------------------------------------
--methods

	---return the UIObject (the underlying FontString) that is behind the wrapper table.
	---@param self df_label
	---@return fontstring
	function LabelMetaFunctions:GetUIObject()
		return self.widget
	end

	---set the text of the label and truncate it is its width passes 'maxWidth' threshold
	---@param self df_label
	---@param text string
	---@param maxWidth width
	function LabelMetaFunctions:SetTextTruncated(text, maxWidth)
		self.widget:SetText(text)
		detailsFramework:TruncateText(self.widget, maxWidth)
	end

	---set the text color
	---@param self df_label
	---@param red any
	---@param green number|nil
	---@param blue number|nil
	---@param alpha number|nil
	function LabelMetaFunctions:SetTextColor(red, green, blue, alpha)
		red, green, blue, alpha = detailsFramework:ParseColors(red, green, blue, alpha)
		return self.label:SetTextColor(red, green, blue, alpha)
	end

	function LabelMetaFunctions:SetText(text)
		return smember_text(self, text)
	end

------------------------------------------------------------------------------------------------------------
--template

	function LabelMetaFunctions:SetTemplate(template)
		template = detailsFramework:ParseTemplate(self.type, template)

		if (template.size) then
			detailsFramework:SetFontSize(self.label, template.size)
		end
		if (template.color) then
			local r, g, b, a = detailsFramework:ParseColors(template.color)
			self:SetTextColor(r, g, b, a)
		end
		if (template.font) then
			local SharedMedia = LibStub:GetLibrary("LibSharedMedia-3.0")
			local font = SharedMedia:Fetch("font", template.font)
			detailsFramework:SetFontFace(self.label, font)
		end
	end

------------------------------------------------------------------------------------------------------------
--object constructor

---@class df_label: fontstring, df_widgets
---@field widget fontstring widget and label points to the same fontstring
---@field label fontstring widget and label points to the same fontstring
---@field GetUIObject fun(self: df_label): fontstring returns the UIObject (FontString) that is behind the wrapper table
---@field align justifyh
---@field valign justifyv
---@field text string
---@field width width
---@field height height
---@field fontcolor any
---@field color any
---@field fontface string
---@field fontsize number
---@field textcolor any
---@field textfont string
---@field textsize number
---@field shadow fontflags
---@field outline fontflags
---@field rotation number
---@field SetPoint fun(self: df_label, point: any, relativeTo: any, relativePoint: any, x: any, y: any) set the label position
---@field SetTemplate fun(self: df_label, template: table) set the fontstring visual by a template
---@field SetTextColor fun(self: df_label, red: any, green: number|nil, blue: number|nil, alpha: number|nil) set the button text color
---@field SetTextTruncated fun(self: df_label, text: string, maxWidth: width)

--there are two calls to create a label: detailsFramework:CreateLabel and detailsFramework:NewLabel
--NewLabel is the original function, CreateLabel is an alias with different parameters order to make it easier to use
--When converting the NewLabel() call with CreateLabel() do this:
--rename NewLabel to CreateLabel;
--change the order of the parameters: parent, container, name, member, text, font, size, color, layer => parent, text, size, color, font, member, name, layer
--container is gone from the parameters pm CreateLabel
--detailsFramework:CreateLabel(parent, text, size, color, font, member, name, layer)
--detailsFramework:NewLabel(parent, container, name, member, text, font, size, color, layer)

---create a new label object.
---This function returns a wrapper Lua table, NOT a Blizzard FontString. The underlying UIObject (the
---Blizzard FontString backing the label) is accessible at `wrapper.widget` (or equivalently
---`wrapper.label`) and via `wrapper:GetUIObject()`. Method calls on the wrapper itself are fine (the
---metatable forwards them), but when the wrapper is passed AS AN ARGUMENT to a Blizzard API that
---expects a real FontString or frame — e.g. as a `SetPoint` relative anchor for another widget, as
---the argument to `frame:SetFontString(...)`, or as a region passed into animation/layout APIs — it
---MUST be unwrapped via `wrapper:GetUIObject()` first, otherwise the C side will error or misbehave
---because the wrapper has no fontstring userdata.
---@param parent frame
---@param text string|table for used for localization, expects a locTable from the language system
---@param size any?
---@param color any|nil
---@param font string|nil
---@param member string|nil
---@param name string|nil
---@param layer drawlayer|nil
---@return df_label|nil
function detailsFramework:CreateLabel(parent, text, size, color, font, member, name, layer)
	--returns a wrapper table (not a FontString); unwrap via wrapper:GetUIObject() / wrapper.widget when handing to Blizzard APIs
	return detailsFramework:NewLabel(parent, parent, name, member, text, font, size, color, layer)
end

---create a new label object
---@param parent frame
---@param container frame
---@param name string?
---@param member string?
---@param text string|table regular text or a locTable from the language system
---@param font string?
---@param size any?
---@param color any?
---@param layer drawlayer?
function detailsFramework:NewLabel(parent, container, name, member, text, font, size, color, layer)
	if (not parent) then
		return error("Details! Framework: parent not found.", 2)
	end
	if (not container) then
		container = parent
	end

	if (not name) then
		name = "DetailsFrameworkLabelNumber" .. detailsFramework.LabelNameCounter
		detailsFramework.LabelNameCounter = detailsFramework.LabelNameCounter + 1
	end

	if (name:find("$parent")) then
		local parentName = detailsFramework:GetParentName(parent)
		name = name:gsub("$parent", parentName)
	end

	local labelObject = {type = "label", dframework = true}

	if (member) then
		parent[member] = labelObject
	end

	if (parent.dframework) then
		parent = parent.widget
	end

	if (container.dframework) then
		container = container.widget
	end

	if (not font or font == "") then
		font = "GameFontNormal"
	end

	labelObject.container = container
	labelObject.label = parent:CreateFontString(name, layer or "overlay", font)
	labelObject.widget = labelObject.label
	labelObject.label.MyObject = labelObject

	if (not loadedAPILabelFunctions) then
		loadedAPILabelFunctions = true
		local idx = getmetatable(labelObject.label).__index
		for funcName, funcAddress in pairs(idx) do
			if (not LabelMetaFunctions[funcName]) then
				LabelMetaFunctions[funcName] = function(object, ...)
					local x = loadstring( "return _G['"..object.label:GetName().."']:"..funcName.."(...)")
					return x(...)
				end
			end
		end
	end

	--if the text is a table, it means a language table has been passed
	if (type(text) == "table") then
		local locTable = text
		if (detailsFramework.Language.IsLocTable(locTable)) then
			detailsFramework.Language.SetTextWithLocTable(labelObject.widget, locTable)
		else
			labelObject.label:SetText("type(text) is a table, but locTable isn't registered.")
		end
	else
		labelObject.label:SetText(text)
	end

	labelObject.label:SetJustifyH("left")

	if (color) then
		local r, g, b, a = detailsFramework:ParseColors(color)
		labelObject.label:SetTextColor(r, g, b, a)
	end

	if (size and type(size) == "number") then
		detailsFramework:SetFontSize(labelObject.label, size)
	end

	labelObject.HookList = {}

	setmetatable(labelObject, LabelMetaFunctions)

	--if template has been passed as the third parameter
	if (size and type(size) == "table") then
		labelObject:SetTemplate(size)

	--check if a template was passed as the 3rd argument
	elseif (size and type(size) == "string") then
		local template = detailsFramework:ParseTemplate(labelObject.type, size)
		labelObject:SetTemplate(template)
	end

	return labelObject
end

---@class df_errorlabel : df_label
---@field ShowErrorMsg fun(self: df_errorlabel, msg: string) show an error message to the user
---@field fadeInAnimationHub animationgroup
---@field fadeOutAnimationHub animationgroup
---@field shake df_frameshake

local showErrorMsg = function(self, text)
	if (self.HideTimer) then
		return
	end

	self.fadeInAnimationHub:Play()
	if (text) then
		self:SetText(text)
	end
	self:PlayFrameShake(self.shake)

	self.HideTimer = C_Timer.NewTimer(4, function()
		self.fadeOutAnimationHub:Play()
		self.HideTimer = nil
	end)
end

---error msg fontstring, this text is used to show errors to the user, its color is red, size 13 and it is placed centered and below the buttons above.
---It also has an animation to fade out after 5 seconds, and a shake animation when it's shown.
---This function returns a wrapper Lua table (df_errorlabel, extends df_label), NOT a Blizzard FontString.
---The underlying UIObject is at `wrapper.widget` (or `wrapper.label`) and via `wrapper:GetUIObject()`.
---Pass the unwrapped FontString to any Blizzard API that expects one (SetPoint relative anchor,
---frame:SetFontString, layout/animation APIs, etc.); method calls on the wrapper itself are fine
---because the metatable forwards them.
function detailsFramework:CreateErrorLabel(parent, text, size, color, layer, name)
	---@type df_errorlabel
	local errorMsg = detailsFramework:CreateLabel(parent, text or "", size or 13, color or "orangered", nil, nil, name, layer or "overlay")
	if (errorMsg) then
		errorMsg:SetJustifyH("center")
		errorMsg:SetAlpha(0)

		--fade out animation
		local fadeOutAnimationHub = detailsFramework:CreateAnimationHub(errorMsg, function()end, function() errorMsg:SetAlpha(0) end)
		detailsFramework:CreateAnimation(fadeOutAnimationHub, "Alpha", 1, 2, 1, 0)
		errorMsg.fadeOutAnimationHub = fadeOutAnimationHub

		--fade in animation
		local fadeInAnimationHub = detailsFramework:CreateAnimationHub(errorMsg, function() errorMsg:SetAlpha(0) end, function() errorMsg:SetAlpha(1) end)
		detailsFramework:CreateAnimation(fadeInAnimationHub, "Alpha", 1, 0.1, 0, 1)
		errorMsg.fadeInAnimationHub = fadeInAnimationHub

		--shake animation
		local shake = detailsFramework:CreateFrameShake(errorMsg, 0.4, 6, 20, false, true, 0, 1, 0, 0.3)
		errorMsg.shake = shake

		errorMsg.ShowErrorMsg = showErrorMsg

		return errorMsg
	end
end
