-- TalkingHeadFrame Revamp: Less intrudingIsWorldQuest

local _, addon = ...
local API = addon.API;
local L = addon.L;

local FADE_OUT_AFTER_FINISH = 0.5;
local QUEST_DESCRIPTION_GRADIENT_LENGTH = 30;
local TEXT_WIDTH = 512;
local LINE_SPACING = 2;
local DEFAULT_POSITION_Y = 336;

local SPEECH_FORMAT_GENRIC = "|cffffd100%s: |r%s";
local SPEECH_FORMAT_PVP = "|cffff2020%s: |r%s";

local FadeFrame = API.UIFrameFade;
local Lerp = API.Lerp;
local Round = API.Round;
local strlenutf8 = strlenutf8;
local ceil = math.ceil;
local max = math.max;
local format = string.format;
local PlaySound = PlaySound;
local StopSound = StopSound;
local GetCurrentLineInfo = C_TalkingHead.GetCurrentLineInfo;
local IsWorldQuest = C_QuestLog.IsWorldQuest;
local IsQuestTask = C_QuestLog.IsQuestTask;
local IsInInstance = IsInInstance;
--local SetPortraitTextureFromCreatureDisplayID = SetPortraitTextureFromCreatureDisplayID;

local DB;

local PVP_CREATURE_DISPLAYID = {
	[108418] = true,
	[118482] = true,    --Ruffious
};


local NewTalkingHead = CreateFrame("Frame", nil, UIParent);
NewTalkingHead:Hide();
NewTalkingHead:SetFrameStrata("FULLSCREEN");  --LOW
NewTalkingHead:SetFrameLevel(980);
NewTalkingHead:SetSize(TEXT_WIDTH, 32);

--[[cause taint "UIParentBottomManagedFrameTemplate"
NewTalkingHead.layoutIndex = 1208;
NewTalkingHead.hideWhenActionBarIsOverriden = false;
NewTalkingHead.align = "center";
--]]

addon.TalkingHead = NewTalkingHead;


local function SetFontShadow(fontString)
	--Game Bug: Shadow is removed upon SetAlphaGradient
	fontString:SetShadowColor(0, 0, 0, 1);
	fontString:SetShadowOffset(1, -1);
end

local function SetupFontString(fontString)
	fontString:SetJustifyH("CENTER");
	fontString:SetJustifyV("TOP");
	fontString:SetSpacing(LINE_SPACING);
	SetFontShadow(fontString);
end


local function FadeOutAfter_OnUpdate(self, elapsed)
	self.t = self.t + elapsed;
	if self.t >= self.fadeOutDelay then
		self:FadeOutText(true);
	end
end

local function FadeIn_TypeWriter_OnUpdate(self, elapsed)
	self.t = self.t + elapsed;
	self.fadingProgress = self.fadingProgress + (elapsed * self.gradientCPS);

	self.gradientLength = self.gradientLength + 10 * elapsed;
	if self.gradientLength > QUEST_DESCRIPTION_GRADIENT_LENGTH then
		self.gradientLength = QUEST_DESCRIPTION_GRADIENT_LENGTH;
	end

	if not self.LineText:SetAlphaGradient(self.fadingProgress, self.gradientLength) then
		self:SetScript("OnUpdate", FadeOutAfter_OnUpdate);
		return true
	end

	if self.t < self.backgroundFadeDuration then
		self.Background:SetAlpha(Lerp(0, 1, self.t / self.backgroundFadeDuration));
	end
end

local function FadeIn_InstantText_OnUpdate(self, elapsed)
	self.t = self.t + elapsed;
	self.fadingProgress = self.fadingProgress + 4*elapsed;

	if self.fadingProgress >= 1 then
		self:SetScript("OnUpdate", FadeOutAfter_OnUpdate);
		self.fadingProgress = 1;
		self.LineText:SetAlpha(1);
		self.Background:SetAlpha(1);
		return true
	end

	self.LineText:SetAlpha(self.fadingProgress);
	self.Background:SetAlpha(self.fadingProgress);
end

local function FadeOut_OnUpdate(self, elapsed)
	self.t = self.t + elapsed;
	if self.t >= 0 then
		self.textAlpha = self.textAlpha - 2 * elapsed;
		if self.textAlpha <= 0 then
			self.textAlpha = 0;
			self:Hide();
			self.isFadingOut = false;
			self:SetScript("OnUpdate", nil);
		end
		self.LineText:SetAlpha(self.textAlpha);
		self.Background:SetAlpha(self.textAlpha);
	end
end

function NewTalkingHead:SetText(text)
	self.LineText:SetText(text);
	self.LineText:Show();

	local textWidth = self.LineText:GetWrappedWidth();
	local textHeight = self.LineText:GetHeight() - (self.fontHeight + LINE_SPACING);
	local numLines = self.LineText:GetNumLines() + 1;

	self.Background:ClearAllPoints();
	self.Background:SetPoint("CENTER", self, "TOP", 0, -0.5*( numLines*(self.fontHeight + LINE_SPACING) - LINE_SPACING));
	self.Background:SetSize(textWidth + 32, textHeight + 32);

	self:UpdateFrameSize();
end

function NewTalkingHead:FadeInText(speakerName, text, voiceoverDuration, hideName, isFinalLine, displayInfo)
	if not (text and speakerName and voiceoverDuration) then return end;

	if not hideName then
		local speechFormat;
		--print(displayInfo, speakerName);    --debug
		if displayInfo and PVP_CREATURE_DISPLAYID[displayInfo] then
			speechFormat = SPEECH_FORMAT_PVP;
		else
			speechFormat = SPEECH_FORMAT_GENRIC;
		end
		text = format(speechFormat, speakerName, text);
	end

	local numChars = strlenutf8(text);
	if numChars == 0 or voiceoverDuration == 0 then return end;

	numChars = numChars + 15;
	text = "               \n"..text;

	self:SetText(text);

	self.gradientCPS = ceil(numChars / voiceoverDuration) + 10;
	self.fadingProgress = 0;
	self.backgroundFadeDuration = max(voiceoverDuration - 1, 1);

	self.t = 0;
	self.gradientLength = QUEST_DESCRIPTION_GRADIENT_LENGTH;

	if isFinalLine then
		self.fadeOutDelay = voiceoverDuration + 1;
	else
		self.fadeOutDelay = voiceoverDuration;
	end

	self.isFadingOut = false;

	if self.instantText then
		self.LineText:SetAlpha(0);
		self.Background:SetAlpha(0);
		self:SetScript("OnUpdate", FadeIn_InstantText_OnUpdate);
	else
		self.LineText:SetAlpha(1);
		self.LineText:SetAlphaGradient(0, QUEST_DESCRIPTION_GRADIENT_LENGTH);
		self.Background:SetAlpha(0);
		self:SetScript("OnUpdate", FadeIn_TypeWriter_OnUpdate);
	end

	FadeFrame(self, 0, 1);
end

function NewTalkingHead:FadeOutText(noDelay)
	if self:IsShown() then
		if not self.isFadingOut then
			self.textAlpha = self.LineText:GetAlpha();
			self.t = (noDelay and 0) or -1;
			self:SetScript("OnUpdate", FadeOut_OnUpdate);
		end
	else
		self.isFadingOut = false;
		self.LineText:Hide();
		self.LineText:SetAlpha(0);
		self.textAlpha = 0;
		self:SetScript("OnUpdate", nil);
	end
end

function NewTalkingHead:SetFontHeightByPercentage(percentage)
	if not percentage then
		percentage = 100;
	end

	if percentage < 100 then
		percentage = 100;
	elseif percentage > 120 then
		percentage = 120;
	end

	local font, baseFontHeight = QuestFont:GetFont();
	local fontHeight = Round(percentage * 0.01 * baseFontHeight);

	self.fontHeight = fontHeight;

	local style, gray;
	if DB.TalkingHead_TextOutline then
		style = "OUTLINE";
		gray = 0.898;   --VERY_LIGHT_GRAY_COLOR
	else
		style = "";
		gray = 1;
	end

	self.LineText:SetFont(font, fontHeight, style);
	self.LineText:SetTextColor(gray, gray, gray);
	SetupFontString(self.LineText);

	DB.TalkingHead_FontSize = percentage;
end

function NewTalkingHead:LoadPosition()
	self:ClearAllPoints();
	if DB.TalkingHead_PositionX and DB.TalkingHead_PositionY then
		if DB.TalkingHead_PositionX > 0 then
			self:SetPoint("TOPLEFT", UIParent, "BOTTOMLEFT", DB.TalkingHead_PositionX, DB.TalkingHead_PositionY);
		else
			self:SetPoint("TOP", UIParent, "BOTTOM", 0, DB.TalkingHead_PositionY);
		end
	else
		self:SetPoint("TOP", UIParent, "BOTTOM", 0, DEFAULT_POSITION_Y);
	end
end

function NewTalkingHead:Init()
	DB = PlumberDB;

	self:LoadPosition();

	local _, fontHeight = QuestFont:GetFont();
	self.baseFontHeight = Round(fontHeight + 0.5);
	self.fontHeight = self.baseFontHeight;

	self.LineText = NewTalkingHead:CreateFontString(nil, "OVERLAY", "QuestFont");
	self.LineText:SetPoint("TOP", self, "TOP", 0, 0);
	self.LineText:SetWidth(TEXT_WIDTH);
	self.LineText:SetAlpha(0);
	self:SetFontHeightByPercentage(DB.TalkingHead_FontSize);

	self.Background = self:CreateTexture(nil, "BACKGROUND");
	self.Background:SetTexture("Interface/AddOns/Plumber/Art/Frame/SubtitleShadow_NineSlice");
	self.Background:SetTextureSliceMargins(30, 30, 30, 30);
	self.Background:SetTextureSliceMode(0);
	self.Background:SetSize(128, 32);
	self.Background:SetPoint("CENTER", self, "TOP", 0, 0);

	--[[
	self.Portrait = self:CreateTexture(nil, "BACKGROUND", nil, -1);
	self.Portrait:SetSize(115, 115);
	self.Portrait:SetPoint("CENTER", self, "CENTER", 0, -6);
	--]]

	self:OnSettingsChanged();

	self.Init = function() end;
end

function NewTalkingHead:WorldMapOnShow()
	if not self.enabled then return end;

	if self.belowWorldMap then
		if self:IsShown() then
			self:SetFrameStrata("MEDIUM");
		end
	end
end

function NewTalkingHead:WorldMapOnHide()
	if not self.enabled then return end;

	if self.belowWorldMap then
		if self:IsShown() then
			self:SetFrameStrata("FULLSCREEN");
		end
	end
end

function NewTalkingHead:EnableTalkingHead()
	if self.enabled then return end;

	if self.blizzardTalkingHeadEnabled == nil then
		self.blizzardTalkingHeadEnabled = TalkingHeadFrame:IsEventRegistered("TALKINGHEAD_REQUESTED");
	end
	TalkingHeadFrame:UnregisterEvent("TALKINGHEAD_REQUESTED");

	self.enabled = true;
	self:Init();

	self:RegisterEvent("TALKINGHEAD_REQUESTED");
	self:RegisterEvent("TALKINGHEAD_CLOSE");
	self:RegisterEvent("LOADING_SCREEN_ENABLED");
	self:RegisterEvent("PLAYER_ENTERING_WORLD");

	self:SetScript("OnEvent", self.OnEvent);
end

function NewTalkingHead:DisableTalkingHead()
	if self.enabled then
		if self.blizzardTalkingHeadEnabled then
			TalkingHeadFrame:RegisterEvent("TALKINGHEAD_REQUESTED");
		end
		self.blizzardTalkingHeadEnabled = nil;
		self.enabled = false;

		self:UnregisterEvent("TALKINGHEAD_REQUESTED");
		self:UnregisterEvent("TALKINGHEAD_CLOSE");
		self:UnregisterEvent("SOUNDKIT_FINISHED");
		self:UnregisterEvent("LOADING_SCREEN_ENABLED");
		self:UnregisterEvent("PLAYER_ENTERING_WORLD");
		self:UnregisterEvent("QUEST_ACCEPTED");
		self:UnregisterEvent("QUEST_TURNED_IN");

		self:OnTalkingHeadClose();
		self:SetScript("OnEvent", nil);
	end
end

function NewTalkingHead:TryDisable()
	--Disable if not used in any other modules
	if not DB then DB = PlumberDB end;
	if DB.TalkingHead_MasterSwitch then return end;

	self:DisableTalkingHead();
end

function NewTalkingHead:OnTalkingHeadRequested()
	if self.voHandle then
		StopSound(self.voHandle);
		self.voHandle = nil;
	end

	local displayInfo, cameraID, vo, duration, lineNumber, numLines, name, text, isNewTalkingHead, textureKit = GetCurrentLineInfo();

	--print(name, displayInfo, vo, duration)  --Debug

	if displayInfo and displayInfo ~= 0 and duration then
		local success, voHandle = PlaySound(vo, "Talking Head", true, true);
		if success then
			self:RegisterEvent("SOUNDKIT_FINISHED");
			self.voHandle = voHandle;
		end

		--lineNumber start from 0
		local isFinalLine = lineNumber and numLines and (lineNumber + 1 == numLines);

		if self.shouldMuteLine then
			if isFinalLine then
				self.shouldMuteLine = nil;
			else
				self:MuteNextLine(duration);
			end
			return
		end

		if (self.hideEverything) or (self.hideInInstance and self.inInstance) then
			return
		end

		duration = duration - FADE_OUT_AFTER_FINISH;  --reserved for fade-out between line
		local hideName = (lineNumber ~= 0) and (name and name == self.lastName);
		self.lastName = name;

		self:FadeInText(name, text, duration, hideName, isFinalLine, displayInfo);
		--SetPortraitTextureFromCreatureDisplayID(self.Portrait, displayInfo);
	end
end


function NewTalkingHead:FadeOutFrame()
	FadeFrame(self, 0.5, 0);
end

function NewTalkingHead:OnTalkingHeadClose()
	self:FadeOutFrame();
	self.lastName = nil;
end

function NewTalkingHead:CloseImmediately()
	if self.voHandle then
		StopSound(self.voHandle);
	end
	FadeFrame(self, 0, 0);
	self.lastName = nil;
end

local function ShouldMuteText(questID)
	--Sometimes TALKINGHEAD_REQUESTED fires before QUEST_ACCEPTED, and we won't be able to mute it
	--Sira Moonwarden's WQ returns wrong type?
	return questID and (IsWorldQuest(questID) or IsQuestTask(questID))
end

function NewTalkingHead:OnEvent(event, ...)
	if self.isEditing then return end;

	if event == "TALKINGHEAD_REQUESTED" then
		self:OnTalkingHeadRequested();
	elseif event == "TALKINGHEAD_CLOSE" then
		--This event will triggered automatically, not closed by the user
		self:OnTalkingHeadClose();
	elseif event == "SOUNDKIT_FINISHED" then
		self:UnregisterEvent(event);
		local voHandle = ...
		if self.voHandle == voHandle then
			self.voHandle = nil;
		end
	elseif event == "LOADING_SCREEN_ENABLED" then
		self:CloseImmediately();
	elseif event == "PLAYER_ENTERING_WORLD" then
		self.inInstance = IsInInstance();
	elseif event == "QUEST_ACCEPTED" then
		local questID = ...
		if ShouldMuteText(questID) then
			self:MuteNextLine();
		end
	elseif event == "QUEST_TURNED_IN" then
		local questID = ...
		if ShouldMuteText(questID) then
			self:MuteNextLine();
		end
	end
end

function NewTalkingHead:OnSettingsChanged()
	if not DB then DB = PlumberDB; end;
	if not DB then return end;


	if DB.TalkingHead_InstantText then
		self.instantText = true;
	else
		self.instantText = false;
	end

	if DB.TalkingHead_HideEverything then
		self.hideEverything = true;
	else
		self.hideEverything = false;
	end

	if DB.TalkingHead_HideInInstance then
		self.hideInInstance = true;
	else
		self.hideInInstance = false;
	end

	if DB.TalkingHead_HideWorldQuest then
		self:RegisterEvent("QUEST_ACCEPTED");
		self:RegisterEvent("QUEST_TURNED_IN");
	else
		self:UnregisterEvent("QUEST_ACCEPTED");
		self:UnregisterEvent("QUEST_TURNED_IN");
		self.shouldMuteLine = nil;
	end

	if DB.TalkingHead_BelowWorldMap then
		self.belowWorldMap = true;
		if not self.worldmapHooked then
			self.worldmapHooked = true;
			if WorldMapFrame and WorldMapFrame.RegisterCallback then
				EventRegistry:RegisterCallback("WorldMapOnShow", self.WorldMapOnShow, self);
				WorldMapFrame:RegisterCallback("WorldMapOnHide", self.WorldMapOnHide, self);
			end
		end
	else
		self.belowWorldMap = false;
	end
end

---- Edit Mode
function NewTalkingHead:EnterEditMode()
	if not self.enabled then return end;

	self:Init();

	if not self.Selection then
		local uiName = L["ModuleName TalkingHead"];
		local hideLabel = true;
		self.Selection = addon.CreateEditModeSelection(self, uiName, hideLabel);
	end

	self.isEditing = true;
	self:SetScript("OnUpdate", nil);
	FadeFrame(self, 0, 1);
	self.Selection:ShowHighlighted();

	self:ShowExampleText();
end

function NewTalkingHead:ExitEditMode()
	if self.Selection then
		self.Selection:Hide();
	end
	self:ShowOptions(false);
	self.isEditing = false;
	self:CloseImmediately();
end

local function FadeIn_TypeWriter_NoAutoHide_OnUpdate(self, elapsed)
	if FadeIn_TypeWriter_OnUpdate(self, elapsed) then
		self:SetScript("OnUpdate", nil);
	end
end

local function FadeIn_InstantText_NoAutoHide_OnUpdate(self, elapsed)
	if FadeIn_InstantText_OnUpdate(self, elapsed) then
		self:SetScript("OnUpdate", nil);
	end
end

function NewTalkingHead:ShowExampleText(animate)
	local exampleText = format(SPEECH_FORMAT_GENRIC, "The Speaker", "Time is an illusion that helps things make sense. So we are always living in the present tense.");
	self:SetText("               \n"..exampleText);
	self.LineText:SetAlpha(1);

	if animate then
		self.fadingProgress = 0;
		self.t = 0;
		if self.instantText then
			self:SetScript("OnUpdate", FadeIn_InstantText_NoAutoHide_OnUpdate);
		else
			local numChars = strlenutf8(exampleText);
			local voiceoverDuration = 3;
			self.gradientCPS = ceil(numChars / voiceoverDuration) + 10;
			self.fadingProgress = 0;
			self.backgroundFadeDuration = max(voiceoverDuration - 1, 1);
			self.gradientLength = QUEST_DESCRIPTION_GRADIENT_LENGTH;
			self:SetScript("OnUpdate", FadeIn_TypeWriter_NoAutoHide_OnUpdate);
		end
	end
end

function NewTalkingHead:IsFocused()
	return (self:IsShown() and self:IsMouseOver()) or (self.OptionFrame and self.OptionFrame:IsShown() and self.OptionFrame:IsMouseOver())
end


local function Options_FontSizeSlider_OnValueChanged(value)
	NewTalkingHead:SetFontHeightByPercentage(value);
	NewTalkingHead:UpdateFrameSize();
end

local function Options_FontSizeSlider_FormatValue(value)
	return format("%.0f%%", value);
end

local function Options_OnSettingsChanged()
	NewTalkingHead:OnSettingsChanged();
end

local function Options_InstantText_OnClick(self, state)
	Options_OnSettingsChanged();
	NewTalkingHead:ShowExampleText(true);
end

local function Options_TextOutline_OnClick(self, state)
	NewTalkingHead:SetFontHeightByPercentage(DB.TalkingHead_FontSize);
end


local function Options_BelowWorldMap_OnClick(self, state)
	NewTalkingHead:OnSettingsChanged();
	if WorldMapFrame:IsVisible() then
		NewTalkingHead:WorldMapOnShow();
	else
		NewTalkingHead:SetFrameStrata("FULLSCREEN");
	end
end

local function Options_HideEverything_OnClick(self, state)
	Options_OnSettingsChanged();
	NewTalkingHead:ShowOptions(true, true);
end

local function Options_IsHideTextCheckboxEnabled()
	return not addon.GetDBBool("TalkingHead_HideEverything")
end

local function Options_ResetPosition_ShouldEnable(self)
	if DB.TalkingHead_PositionX and DB.TalkingHead_PositionY then
		return true
	else
		return false
	end
end

local function Options_ResetPosition_OnClick(self)
	self:Disable();
	DB.TalkingHead_PositionX = nil;
	DB.TalkingHead_PositionY = nil;
	NewTalkingHead:LoadPosition();
end

local OPTIONS_SCHEMATIC = {
	title = L["Addon Name Colon"]..L["ModuleName TalkingHead"],
	moduleDBKey = "TalkingHead_MasterSwitch",
	widgets = {
		{type = "Checkbox", label = L["TalkingHead Option InstantText"], onClickFunc = Options_InstantText_OnClick, dbKey = "TalkingHead_InstantText"},
		{type = "Checkbox", label = L["TalkingHead Option TextOutline"], onClickFunc = Options_TextOutline_OnClick, dbKey = "TalkingHead_TextOutline"},
		{type = "Slider", label = L["Font Size"], minValue = 100, maxValue = 120, valueStep = 10, onValueChangedFunc = Options_FontSizeSlider_OnValueChanged, formatValueFunc = Options_FontSizeSlider_FormatValue, dbKey = "TalkingHead_FontSize"},

		{type = "Divider"},
		{type = "Header", label = L["TalkingHead Option Condition Header"]};
		{type = "Checkbox", label = L["TalkingHead Option Hide Everything"] , onClickFunc = Options_HideEverything_OnClick, dbKey = "TalkingHead_HideEverything", tooltip = L["TalkingHead Option Hide Everything Tooltip"]},
		{type = "Checkbox", label = L["TalkingHead Option Condition Instance"] , onClickFunc = Options_OnSettingsChanged, dbKey = "TalkingHead_HideInInstance", tooltip = L["TalkingHead Option Condition Instance Tooltip"], shouldEnableOption = Options_IsHideTextCheckboxEnabled},
		{type = "Checkbox", label = L["TalkingHead Option Condition WorldQuest"], onClickFunc = Options_OnSettingsChanged, dbKey = "TalkingHead_HideWorldQuest", tooltip = L["TalkingHead Option Condition WorldQuest Tooltip"], shouldEnableOption = Options_IsHideTextCheckboxEnabled},

		{type = "Divider"},
		{type = "Checkbox", label = L["TalkingHead Option Below WorldMap"], onClickFunc = Options_BelowWorldMap_OnClick, dbKey = "TalkingHead_BelowWorldMap", tooltip = L["TalkingHead Option Below WorldMap Tooltip"]},

		{type = "Divider"},
		{type = "UIPanelButton", label = L["Reset To Default Position"], onClickFunc = Options_ResetPosition_OnClick, stateCheckFunc = Options_ResetPosition_ShouldEnable, widgetKey = "ResetButton"},
	}
};

function NewTalkingHead:CreateOptions(forceUpdate)
	self.OptionFrame = addon.SetupSettingsDialog(self, OPTIONS_SCHEMATIC, forceUpdate);
end

function NewTalkingHead:ShowOptions(state, forceUpdate)
	if state then
		self:CreateOptions(forceUpdate);
		self.OptionFrame:Show();
		if self.OptionFrame.requireResetPosition then
			self.OptionFrame.requireResetPosition = false;
			self.OptionFrame:ClearAllPoints();
			self.OptionFrame:SetPoint("LEFT", UIParent, "CENTER", TEXT_WIDTH * 0.5, 0);
		end
	else
		if self.OptionFrame then
			self.OptionFrame:HideOption(self);
		end
		if not API.IsInEditMode() then
			self:CloseImmediately();
		end
	end
end

function NewTalkingHead:ToggleOptions()
	self:ShowOptions( self.OptionFrame and self.OptionFrame:IsShown() );
	NewTalkingHead:EnterEditMode()
end

function NewTalkingHead:OnDragStart()
	self:SetMovable(true);
	self:SetDontSavePosition(true);
	self:SetClampedToScreen(true);
	self:StartMoving();
end

function NewTalkingHead:OnDragStop()
	self:StopMovingOrSizing();

	local centerX = self:GetCenter();
	local uiCenter = UIParent:GetCenter();
	local left = self:GetLeft();
	local top = self:GetTop();

	left = Round(left);
	top = Round(top);

	self:ClearAllPoints();

	--Convert anchor and save position
	if math.abs(uiCenter - centerX) <= 48 then
		--Snap to centeral line
		self:SetPoint("TOP", UIParent, "BOTTOM", 0, top);
		DB.TalkingHead_PositionX = -1;

	else
		self:SetPoint("TOPLEFT", UIParent, "BOTTOMLEFT", left, top);
		DB.TalkingHead_PositionX = left;
	end
	DB.TalkingHead_PositionY = top;

	if self.OptionFrame and self.OptionFrame:IsOwner(self) then
		local button = self.OptionFrame:FindWidget("ResetButton");
		if button then
			button:Enable();
		end
	end
end

function NewTalkingHead:UpdateFrameSize()
	local height = (self.LineText:GetHeight() or 0) + self.fontHeight;
	self:SetHeight(height);
end

local MuteDelay;
local function MuteDelay_OnUpdate(self, elapsed)
	self.t = self.t + elapsed;
	if self.t > self.duration then
		self.t = nil;
		self:SetScript("OnUpdate", nil);
		NewTalkingHead.shouldMuteLine = nil;
	end
end

function NewTalkingHead:MuteNextLine(duration)
	--Sound is still played, just no text
	--Doesn't always work when you accept multiple WQ at the same location
	if not MuteDelay then
		MuteDelay = CreateFrame("Frame");
	end
	if (not duration) or duration <= 0.5 then
		duration = 0.5;
	end
	if MuteDelay.t then
		MuteDelay.duration = (MuteDelay.duration or 0) + duration + 0.5;
	else
		MuteDelay.t = 0;
		MuteDelay.duration = duration;
	end
	MuteDelay:SetScript("OnUpdate", MuteDelay_OnUpdate);
	self.shouldMuteLine = true;
end


do
	local function EnableModule(state)
		if state then
			NewTalkingHead:EnableTalkingHead();
		else
			NewTalkingHead:DisableTalkingHead();
		end
	end

	local function OptionToggle_OnClick(self, button)
		local OptionFrame = NewTalkingHead.OptionFrame;
		if OptionFrame and OptionFrame:IsShown() and OptionFrame:IsFromSchematic(OPTIONS_SCHEMATIC) then
			NewTalkingHead:ShowOptions(false);
			NewTalkingHead:ExitEditMode();
		else
			NewTalkingHead:EnterEditMode();
			NewTalkingHead:ShowOptions(true);
		end
	end

	local moduleData = {
		name = L["ModuleName TalkingHead"],
		dbKey = "TalkingHead_MasterSwitch",
		description = L["ModuleDescription TalkingHead"],
		toggleFunc = EnableModule,
		categoryID = 2,
		uiOrder = 3,
		moduleAddedTime = 1704423300,
		optionToggleFunc = OptionToggle_OnClick,
		hasMovableWidget = true,
		visibleInEditMode = true,
		enterEditMode = function()
			NewTalkingHead:EnterEditMode();
		end,
		exitEditMode = function()
			NewTalkingHead:ExitEditMode();
		end,

		categoryKeys = {
			"Signature",
		},
	};

	addon.ControlCenter:AddModule(moduleData);
end
