-- Portal Pro :: UI/Theme.lua
-- Single source of truth for colour, type and spacing. No widget hardcodes a colour.
--
-- Everything is built from solid colour fills and 1px hairline borders, which means the
-- addon ships with zero art assets and scales to any resolution without blurring. Square
-- corners are a deliberate choice, not a limitation: rounded corners would need a 9-slice
-- texture, and crisp edges read as "modern flat UI" rather than "Blizzard panel".

local ADDON_NAME, PP = ...
local U = PP.U

local T = {}
PP.Theme = T

--------------------------------------------------------------------------------
-- Colour tokens
--------------------------------------------------------------------------------

local function rgb(hex, alpha)
	local r = tonumber(hex:sub(1, 2), 16) / 255
	local g = tonumber(hex:sub(3, 4), 16) / 255
	local b = tonumber(hex:sub(5, 6), 16) / 255
	return { r, g, b, alpha or 1 }
end

T.color = {
	base      = rgb("14161A", 0.96),
	raised    = rgb("1C1F26", 1),
	overlay   = rgb("23272F", 1),
	hover     = rgb("2A2F38", 1),

	borderSubtle = rgb("2E333D", 1),
	borderStrong = rgb("3D444F", 1),

	textPrimary   = rgb("E8EAED", 1),
	textSecondary = rgb("9BA1AC", 1),
	textMuted     = rgb("6B7280", 1),

	accent      = rgb("4C9AFF", 1),
	accentHover = rgb("6BAEFF", 1),
	accentMuted = rgb("4C9AFF", 0.15),

	positive = rgb("34D399", 1),
	negative = rgb("F87171", 1),
	warning  = rgb("FBBF24", 1),
	gold     = rgb("F0C674", 1),

	shadow = rgb("000000", 0.55),
	scrim  = rgb("000000", 0.35),
}

--- Hex string for use in chat/FontString colour escapes.
T.hex = {
	accent   = "4C9AFF",
	positive = "34D399",
	negative = "F87171",
	warning  = "FBBF24",
	gold     = "F0C674",
	muted    = "6B7280",
}

function T:C(name)
	local c = self.color[name]
	if not c then return 1, 0, 1, 1 end
	return c[1], c[2], c[3], c[4]
end

--------------------------------------------------------------------------------
-- Spacing & sizing (4px grid)
--------------------------------------------------------------------------------

T.space = { xs = 4, sm = 8, md = 12, lg = 16, xl = 24, xxl = 32 }
T.size = {
	rowHeight    = 28,
	cardHeight   = 74,
	headerHeight = 48,
	railWidth    = 56,
	inputHeight  = 26,
	buttonHeight = 26,
	hairline     = 1,
}

--------------------------------------------------------------------------------
-- Typography
--------------------------------------------------------------------------------

-- Font registry.
--
-- Inter and Noto Sans are both SIL OFL 1.1 and redistribute freely, so they're the two
-- bundled options. Portal Pro looks for FIXED filenames rather than whatever the upstream
-- archive happens to be called this month — Inter 4.x renamed its static builds, and
-- hardcoding those names would break on the next release. Drop the file in, rename it to
-- the name below, done. See Media/fonts/README.md.
--
-- WoW needs a static .ttf. Variable-axis TTFs and .otf files fail silently: SetFont
-- "succeeds" and then nothing renders. That's exactly why every path is probed rather
-- than trusted.
local MEDIA = "Interface\\AddOns\\PortalPro\\Media\\fonts\\"

T.fonts = {
	{ key = "inter",    label = "Inter",         path = MEDIA .. "Inter.ttf",    bundled = true },
	{ key = "notosans", label = "Noto Sans",     path = MEDIA .. "NotoSans.ttf", bundled = true },
	{ key = "arialn",   label = "Arial Narrow",  path = "Fonts\\ARIALN.TTF" },
	{ key = "friz",     label = "Friz Quadrata", path = "Fonts\\FRIZQT__.TTF" },
	{ key = "custom",   label = "Custom path...", path = nil },
}

local fontsByKey = {}
for _, entry in ipairs(T.fonts) do fontsByKey[entry.key] = entry end

local probe

--- A font path is only usable if the file actually loads. A missing or unsupported file
--- renders nothing at all rather than erroring, so measure a probe string to confirm.
local function fontWorks(path)
	if not path or path == "" then return false end
	if not probe then
		probe = UIParent:CreateFontString(nil, "ARTWORK")
		probe:Hide()
	end
	local ok = pcall(probe.SetFont, probe, path, 12, "")
	if not ok then return false end
	probe:SetText("Portal Pro")
	return (probe:GetStringWidth() or 0) > 0
end

T.FontWorks = function(_, path) return fontWorks(path) end

--- Resolve to a usable font path: the user's pick first, then the registry order,
--- then whatever the client guarantees.
function T:ResolveFont()
	if self.fontPath then return self.fontPath end

	local ui = PP.profile and PP.profile.ui
	local choice = (ui and ui.font) or "auto"
	local ordered = {}

	if choice == "custom" then
		if ui and ui.fontPath and ui.fontPath ~= "" then
			ordered[#ordered + 1] = ui.fontPath
		end
	elseif choice ~= "auto" then
		local entry = fontsByKey[choice]
		if entry and entry.path then ordered[#ordered + 1] = entry.path end
	end

	-- Fallback chain, always appended so a missing pick degrades instead of breaking.
	for _, entry in ipairs(T.fonts) do
		if entry.path then ordered[#ordered + 1] = entry.path end
	end

	for _, path in ipairs(ordered) do
		if fontWorks(path) then
			self.fontPath = path
			return path
		end
	end

	self.fontPath = STANDARD_TEXT_FONT or "Fonts\\FRIZQT__.TTF"
	return self.fontPath
end

--- Registry entries annotated with whether the file is actually present. Used by the
--- Settings dropdown so a font you haven't installed is visibly unavailable rather than
--- silently doing nothing when picked.
function T:AvailableFonts()
	local out = {}
	for _, entry in ipairs(T.fonts) do
		out[#out + 1] = {
			key = entry.key,
			label = entry.label,
			path = entry.path,
			bundled = entry.bundled,
			available = (entry.key == "custom") or fontWorks(entry.path),
		}
	end
	return out
end

--- Clear the cached font so a settings change takes effect on the next :Font() call.
function T:InvalidateFont()
	self.fontPath = nil
end

T.fontSize = { xs = 10, sm = 11, base = 12, md = 14, lg = 18, xl = 24, xxl = 30 }

--- @param weight string|nil "" | "OUTLINE" | "THICKOUTLINE"
function T:Font(fontString, size, weight, colorName)
	fontString:SetFont(self:ResolveFont(), size or self.fontSize.base, weight or "")
	if colorName then
		fontString:SetTextColor(self:C(colorName))
	else
		fontString:SetTextColor(self:C("textPrimary"))
	end
	return fontString
end

--------------------------------------------------------------------------------
-- Texture helpers
--------------------------------------------------------------------------------

T.WHITE = "Interface\\Buttons\\WHITE8X8"

--- SetGradient changed signature in 10.0. Support both so this works across builds.
function T:Gradient(texture, orientation, r1, g1, b1, a1, r2, g2, b2, a2)
	if texture.SetGradient and CreateColor then
		local ok = pcall(texture.SetGradient, texture, orientation,
			CreateColor(r1, g1, b1, a1), CreateColor(r2, g2, b2, a2))
		if ok then return end
	end
	if texture.SetGradientAlpha then
		pcall(texture.SetGradientAlpha, texture, orientation, r1, g1, b1, a1, r2, g2, b2, a2)
	end
end

--- Raid target icon texture path (1-8).
function T:RaidIcon(index)
	return ("Interface\\TargetingFrame\\UI-RaidTargetingIcon_%d"):format(index)
end

--- Inline texture escape for the same icon, for use inside FontStrings.
function T:RaidIconInline(index, size)
	return ("|T%s:%d:%d|t"):format(self:RaidIcon(index), size or 12, size or 12)
end
