-- Portal Pro :: UI/Tab_About.lua

local ADDON_NAME, PP = ...
local UI, T, U = PP.UI, PP.Theme, PP.U
local L = PP.L

local AVATAR     = "Interface\\AddOns\\PortalPro\\Media\\images\\0x29a.tga"
local MUSIC      = "Interface\\AddOns\\PortalPro\\Media\\music\\about.mp3"
local MUSIC_CREDIT = "Music: ECLiPSE - Battle Castles"
local DISCORD    = "https://discord.gg/QvmKz4y9XR"
local CURSEFORGE = "https://www.curseforge.com/members/0x29a/projects"

--------------------------------------------------------------------------------
-- Theme music
--------------------------------------------------------------------------------

-- Played on the "Music" channel rather than "Master": this is music, and someone who has
-- turned game music off has explicitly said they don't want any. Overriding that to force
-- audio on them is how an addon earns a one-star review.
-- Length of about.mp3 in seconds. Re-measure if the track is ever replaced: there is no
-- API to ask how long a sound file is, and no event when one finishes, so this number is
-- the only way the addon can know playback has ended.
local TRACK_LENGTH = 83.3

local musicHandle, musicTimer, aboutFrame
local updateMusicButton   -- forward declaration; startMusic's timer calls it

local function stopMusic()
	if musicTimer then
		musicTimer:Cancel()
		musicTimer = nil
	end
	if musicHandle and StopSound then
		pcall(StopSound, musicHandle)
	end
	musicHandle = nil
end

local function startMusic()
	stopMusic()
	if not PlaySoundFile then return false end

	local willPlay, handle = PlaySoundFile(MUSIC, "Music")
	if not willPlay then return false end

	musicHandle = handle
	-- Nothing tells us when a sound ends. Without this the handle would stay set after the
	-- track finished, the button would go on claiming "Stop music", and clicking it would
	-- silently disable auto-play for something that had already stopped on its own.
	musicTimer = C_Timer.NewTimer(TRACK_LENGTH, function()
		musicHandle = nil
		musicTimer = nil
		updateMusicButton(aboutFrame)
	end)
	return true
end

local function isPlaying()
	return musicHandle ~= nil
end

function updateMusicButton(frame)
	if not frame or not frame.musicBtn then return end
	frame.musicBtn:SetTextValue(isPlaying() and "Stop music" or "Play music")
end

local function build(parent)
	local frame = CreateFrame("Frame", nil, parent)
	aboutFrame = frame   -- so the end-of-track timer can refresh the button

	local pad = T.space.xl

	--------------------------------------------------------------------------
	-- Header
	--------------------------------------------------------------------------
	local heading = UI:Text(frame, "", T.fontSize.xxl, "", "textPrimary")
	heading:SetPoint("TOPLEFT", pad, -pad)
	frame.heading = heading

	local status = UI:Text(frame, "", T.fontSize.base, "", "textMuted")
	status:SetPoint("TOPLEFT", heading, "BOTTOMLEFT", 0, -T.space.xs)
	frame.status = status

	-- Stopping doesn't just silence the current playback: it remembers, so the track
	-- doesn't ambush you again every time you open this tab.
	local musicBtn = UI:Button(frame, "Play music", function()
		if isPlaying() then
			stopMusic()
			PP.profile.ui.aboutMusic = false
		else
			PP.profile.ui.aboutMusic = true
			if not startMusic() then
				PP:Warn("Couldn't start the music - check that game music is enabled in Sound options.")
			end
		end
		updateMusicButton(frame)
	end, "secondary", 110, 26)
	musicBtn:SetPoint("TOPRIGHT", -pad, -pad)
	frame.musicBtn = musicBtn

	UI:Tooltip(musicBtn, "About theme", {
		MUSIC_CREDIT,
		" ",
		"Plays on the Music channel, so it follows your in-game music volume",
		"and stays silent if you've turned game music off.",
		" ",
		"Stopping it here also stops it auto-playing next time.",
	})

	local credit = UI:Text(frame, MUSIC_CREDIT, T.fontSize.sm, "", "textMuted", "RIGHT")
	credit:SetPoint("TOPRIGHT", musicBtn, "BOTTOMRIGHT", 0, -T.space.xs)

	local rule1 = UI:Divider(frame)
	rule1:SetPoint("TOPLEFT", status, "BOTTOMLEFT", 0, -T.space.lg)
	rule1:SetPoint("RIGHT", frame, "RIGHT", -pad, 0)

	--------------------------------------------------------------------------
	-- Author
	--------------------------------------------------------------------------
	local avatarFrame = UI:Panel(frame, "raised")
	avatarFrame:SetSize(80, 80)
	avatarFrame:SetPoint("TOPLEFT", rule1, "BOTTOMLEFT", 0, -T.space.lg)
	UI:Border(avatarFrame, "borderStrong")

	local avatar = avatarFrame:CreateTexture(nil, "ARTWORK")
	avatar:SetPoint("TOPLEFT", 2, -2)
	avatar:SetPoint("BOTTOMRIGHT", -2, 2)
	avatar:SetTexture(AVATAR)

	local name = UI:Text(frame, "0x29a", T.fontSize.lg, "", "accent")
	name:SetPoint("TOPLEFT", avatarFrame, "TOPRIGHT", T.space.lg, -2)

	local role = UI:Text(frame, "San Diego  ·  career software engineer and architect",
		T.fontSize.base, "", "textSecondary")
	role:SetPoint("TOPLEFT", name, "BOTTOMLEFT", 0, -T.space.xs)

	local playing = UI:Text(frame,
		"Playing WoW Hardcore on Doomhowl, mostly - until Classic+ lands.",
		T.fontSize.base, "", "textMuted")
	playing:SetPoint("TOPLEFT", role, "BOTTOMLEFT", 0, -T.space.xs)

	local rule2 = UI:Divider(frame)
	rule2:SetPoint("TOPLEFT", avatarFrame, "BOTTOMLEFT", 0, -T.space.lg)
	rule2:SetPoint("RIGHT", frame, "RIGHT", -pad, 0)

	--------------------------------------------------------------------------
	-- Hire me
	--------------------------------------------------------------------------
	local pitchLabel = UI:Label(frame, "Need a custom addon?")
	pitchLabel:SetPoint("TOPLEFT", rule2, "BOTTOMLEFT", 0, -T.space.lg)

	local pitch = UI:Text(frame,
		"Are you or your guild in need of a custom or one-off addon? I'll build it at a " ..
		"fair price, and quickly. I can repair old addons that have stopped working, write " ..
		"new ones from scratch, and handle just about anything else you need.\n\n" ..
		"Come and say hello on Discord.",
		T.fontSize.base, "", "textSecondary")
	pitch:SetPoint("TOPLEFT", pitchLabel, "BOTTOMLEFT", 0, -T.space.sm)
	pitch:SetPoint("RIGHT", frame, "RIGHT", -pad, 0)
	pitch:SetWordWrap(true)
	pitch:SetJustifyH("LEFT")
	pitch:SetSpacing(2)

	local discordBtn = UI:Button(frame, "Join the Discord", function()
		UI:ShowLink("Portal Pro - Discord", DISCORD)
	end, "primary", 150, 28)
	discordBtn:SetPoint("TOPLEFT", pitch, "BOTTOMLEFT", 0, -T.space.lg)

	local curseBtn = UI:Button(frame, "CurseForge projects", function()
		UI:ShowLink("0x29a on CurseForge", CURSEFORGE)
	end, "secondary", 160, 28)
	curseBtn:SetPoint("LEFT", discordBtn, "RIGHT", T.space.sm, 0)

	-- WoW has no API to open a browser, so both buttons present the URL for copying
	-- rather than pretending they'll launch anything.
	local linkNote = UI:Text(frame,
		"Addons can't open a browser, so these show the address for you to copy.",
		T.fontSize.sm, "", "textMuted")
	linkNote:SetPoint("TOPLEFT", discordBtn, "BOTTOMLEFT", 0, -T.space.sm)

	--------------------------------------------------------------------------
	-- Update mechanism footnote
	--------------------------------------------------------------------------
	local footer = UI:Text(frame,
		"Version checks work by listening for other players running Portal Pro - in your " ..
		"guild, your group, or standing near you. Addons have no internet access, so " ..
		"nothing is sent anywhere and the messages are invisible to everyone.",
		T.fontSize.sm, "", "textMuted")
	footer:SetPoint("BOTTOMLEFT", pad, pad)
	footer:SetPoint("RIGHT", frame, "RIGHT", -pad, 0)
	footer:SetWordWrap(true)
	footer:SetJustifyH("LEFT")

	--------------------------------------------------------------------------
	-- Playback lifecycle
	--------------------------------------------------------------------------
	-- OnHide covers every way of leaving: switching tabs, closing the window, and ESC.
	-- SelectTab hides the other tab frames and shows this one, so both fire naturally.
	frame:SetScript("OnShow", function(self)
		if PP.profile.ui.aboutMusic and not isPlaying() then
			startMusic()
		end
		updateMusicButton(self)
	end)

	frame:SetScript("OnHide", function(self)
		stopMusic()
		updateMusicButton(self)
	end)

	-- Built frames are visible by default, and SelectTab's Show() on an already-shown frame
	-- won't fire OnShow. Hiding it here guarantees the first open triggers playback.
	frame:Hide()

	return frame
end

local function refresh(frame)
	frame.heading:SetText(("Portal Pro  -  %s"):format(PP.version))

	local statusText, statusToken = PP.Comms:StatusText()
	frame.status:SetText(statusText)
	frame.status:SetTextColor(T:C(statusToken))

	-- refresh() also runs on unrelated events while this tab is open, so it must only
	-- reflect playback state, never start or restart it.
	updateMusicButton(frame)
end

UI:RegisterTab({
	key = "about",
	label = L["About"],
	icon = "Interface\\ICONS\\INV_Misc_Note_02",
	order = 7,
	build = build,
	refresh = refresh,
})
