-- PlayerChoiceFrame Revamp
-- Smaller UI, show item count, long-click to auto-donate

local _, addon = ...

local MAPID_EMRALD_DREAM = 2200;
local OPTION_FRAME_WIDTH = 196;
local OPTION_FRAME_GAP = 48;
local UI_OFFSET_X = 0;      --Set by user via RepositionButton
local UI_OFFSET_Y = 0;      --Controlled by Camera Zoom
local PATTERN_RESOURCE_DISPLAY = "%s |T%s:16:16:0:0:64:64:4:60:4:60|t";

local API = addon.API;
local L = addon.L;
local GetCreatureIDFromGUID = API.GetCreatureIDFromGUID;
local DreamseedUtil = API.DreamseedUtil;
local GetWaypointFromText = API.GetWaypointFromText;
local AreWaypointsClose = API.AreWaypointsClose;
local easingFunc = addon.EasingFunctions.outQuart;
local C_PlayerChoice = C_PlayerChoice;
local C_UIWidgetManager = C_UIWidgetManager;
local C_Item = C_Item;
local GetItemCount = C_Item.GetItemCount;
local GetCurrencyInfo = C_CurrencyInfo.GetCurrencyInfo;
local match = string.match;
local find = string.find;
local lower = string.lower;
local format = string.format;
local tonumber = tonumber;
local floor = math.floor;

local UIParent = UIParent;

local VALID_CREATURES = {};

do
	local DreamseedBloom = {
		211142, 211143, 211120, 208463, 208633,
		208635, 211091, 211126, 211130, 211219,
		211091, 211221,
	};

	for _, creatureID in ipairs(DreamseedBloom) do
		VALID_CREATURES[creatureID] = 2650;   --Emerald Dewdrop
	end
end


local function HideBlizzardFrame_Default()
	local f = PlayerChoiceFrame;
	if f then
		f:ClearAllPoints();
		f:SetClampedToScreen(false);
		f:SetPoint("TOP", UIParent, "BOTTOM", 0, -64);
	end
end

-- luacheck: push ignore 122
local function HideBlizzardFrame_MoveAny()
	--Fixed an compatibility issue where after "MoveAny" users adjust the PlayerChoiceFrame
	--the frame will be clamped to screen and constantly restore its previous position
	--Mechanism: MoveAny hooks frame's SetPoint method
	--see MoveAny\moveframes.lua

	local f = PlayerChoiceFrame;
	if f then
		f:ClearAllPoints();
		f:SetClampedToScreen(false);
		f.maframesetpoint = true;
		f:SetPoint("TOP", UIParent, "BOTTOM", 0, -64);
		f.maframesetpoint = false;
	end
end
-- luacheck: pop

-- luacheck: push ignore 122
local function HideBlizzardFrame_Drift()
	--Similar to MoveAny, they hook the SetPoint method
	--see Drift\DriftHelpers.lua

	local f = PlayerChoiceFrame;
	if f then
		f:ClearAllPoints();
		f:SetClampedToScreen(false);
		f.DriftAboutToSetPoint = true;
		f:SetPoint("TOP", UIParent, "BOTTOM", 0, -64);
		f.DriftAboutToSetPoint = false;
	end
end
-- luacheck: pop

local HideBlizzardFrame = HideBlizzardFrame_Default;

local function OnUpdate_OnShot(self)
	self:SetScript("OnUpdate", nil);
	HideBlizzardFrame();
end

local BlizzardFrameMover = CreateFrame("Frame");    --Attempted Fix for Report #10: "both the addon's UI and the default one are there"
function BlizzardFrameMover:HidePlayerChoiceFrame()
	HideBlizzardFrame();
	self:SetScript("OnUpdate", OnUpdate_OnShot);
end


local PlayerChoiceUI = CreateFrame("Frame", nil, UIParent);
PlayerChoiceUI:SetSize(64, 64);
PlayerChoiceUI:SetPoint("CENTER", UIParent, "CENTER", UI_OFFSET_X, UI_OFFSET_Y);
PlayerChoiceUI:Hide();
PlayerChoiceUI:SetFrameStrata("DIALOG");
PlayerChoiceUI:SetFixedFrameStrata(true);
PlayerChoiceUI:SetFrameLevel(50);
PlayerChoiceUI:SetClampedToScreen(true);
PlayerChoiceUI.LongClickWatcher = CreateFrame("Frame", nil, PlayerChoiceUI);


local function CalculateOffsetYFromZoom(zoom)
	--Move the UI downwards when zooming out, so it doesn't block the timer (nameplate)
	zoom = zoom or 0;

	local y = 0.094*zoom*zoom - 8.495*zoom + 140.73;
	if y > 0 then
		y = 0;
	elseif y < -50 then
		y = -50;
	end
	return floor(y + 0.5);
end

local ZoomCalculator = CreateFrame("Frame", nil, PlayerChoiceUI);
local FrameMover = CreateFrame("Frame", nil, PlayerChoiceUI);


local OptionFrameMixin = {};

function OptionFrameMixin:SetupItem(itemID, showAsEarned)
	if itemID then
		self.RewardFrame:Show();

		if showAsEarned then
			self.RewardFrame.Checkmark:Show();
			if PlayerChoiceUI.requireRewardCheck then
				PlayerChoiceUI.requireRewardCheck = false;
				DreamseedUtil:MarkNearestPlantContributed();
			end
		else
			self.RewardFrame.Checkmark:Hide();
		end

		if itemID ~= self.itemID then
			self.itemID = itemID;
		else
			return
		end

		local icon = C_Item.GetItemIconByID(itemID);
		local name = C_Item.GetItemNameByID(itemID);
		local quality = C_Item.GetItemQualityByID(itemID);

		self.itemCached = quality and name and name ~= "";

		if not (self.itemCached and self:IsShown()) then
			C_Timer.After(0.1, function()
				if not self.itemCached then
					self.itemID = nil;
					self:SetupItem(itemID, showAsEarned);
				end
			end);
			return
		end

		local color = API.GetItemQualityColor(quality);
		local r, g, b = color:GetRGB();
		self.RewardFrame.Icon:SetTexture(icon);
		self.RewardFrame.Name:SetText(name);
		self.RewardFrame.Name:SetTextColor(r, g, b);
	else
		self.RewardFrame:Hide();
	end
end


--Display the number of required resources that you own below the button
local function ResourceDisplay_OnEnter(resourceFrame)
	GameTooltip:Hide();
	if resourceFrame.itemID then
		GameTooltip:SetOwner(resourceFrame, "ANCHOR_BOTTOMRIGHT");
		GameTooltip:SetItemByID(resourceFrame.itemID);
		GameTooltip:Show();
	elseif resourceFrame.currencyID then
		GameTooltip:SetOwner(resourceFrame, "ANCHOR_BOTTOMRIGHT");
		GameTooltip:SetCurrencyByID(resourceFrame.currencyID);
		GameTooltip:Show();
	end
end

local function ResourceDisplay_OnLeave()
	GameTooltip:Hide();
end

local function ResourceDisplay_SetResource(optionFrame, numRequired, numOwned, icon, itemID, currencyID)
	local f = optionFrame.ResourceDisplay;
	if not f then
		f = addon.CreateThreeSliceFrame(optionFrame, "Phantom");
		--f = CreateFrame("Frame", nil, optionFrame);
		optionFrame.ResourceDisplay = f;
		--f:SetSize(60, 20);
		f:SetHitRectInsets(8, 8, 2, 2);
		--f:SetPoint("BOTTOM", optionFrame, "BOTTOM", 0, 0);
		f.Text = f:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall");
		f.Text:SetTextColor(1, 1, 1);
		f.Text:SetJustifyH("CENTER");
		--f.Text:SetPoint("CENTER", f, "CENTER", 0, 0);
		f.Text:SetPoint("BOTTOM", optionFrame, "BOTTOM", 0, 0);
		f:SetHeight(20);
		f:SetPoint("LEFT", f.Text, "LEFT", -4, 0);
		f:SetPoint("RIGHT", f.Text, "RIGHT", 4, 0);
		f:SetScript("OnEnter", ResourceDisplay_OnEnter);
		f:SetScript("OnLeave", ResourceDisplay_OnLeave);
		--addon.CreateTextDropShadow(f.Text, f);
	end

	if numOwned > 9999 then
		numOwned = "A Lot";
	else
		if numRequired > numOwned then
			numOwned = "|cffff4800"..numOwned.."|r";    --WARNING_FONT_COLOR
		end
	end

	local text = format(PATTERN_RESOURCE_DISPLAY, numOwned, icon);
	f:Show();
	f.Text:SetText(text);
	f.itemID = itemID;
	f.currencyID = currencyID;
end

local function ResourceDisplay_Hide(optionFrame)
	if optionFrame.ResourceFrame then
		optionFrame.ResourceFrame:Hide();
	end
end

local function ResourceDisplay_SetItem(optionFrame, itemID, numRequired)
	local quantity = GetItemCount(itemID) or 0;
	local icon = C_Item.GetItemIconByID(itemID);
	ResourceDisplay_SetResource(optionFrame, numRequired, quantity, icon, itemID);
end

local function ResourceDisplay_SetCurrency(optionFrame, currencyID, numRequired)
	--Blizzard's Dreamseed tutorial quest requirement is wrong, but it's trival
	local info = GetCurrencyInfo(currencyID);
	if info then
		local icon = info.iconFileID;
		local quantity = info.quantity;
		ResourceDisplay_SetResource(optionFrame, numRequired, quantity, icon, nil, currencyID);
	else
		ResourceDisplay_Hide(optionFrame);
	end
end

local function ProcessButtonText(text)
	--Get itemID from iconFile
	local numRequired, iconFile = match(text, "(%d+)%s*|T([^|]+)|t");
	local resourceType, resourceID;
	--print(text);
	--print(iconFile)
	if numRequired and iconFile then
		numRequired = tonumber(numRequired);
		iconFile = lower(iconFile);
		if find(iconFile, "shadowdew") then
			resourceType = 2;
			resourceID = 2650;
		elseif find(iconFile, "green") then
			resourceType = 1;
			resourceID = 208066;
		elseif find(iconFile, "blue") then
			resourceType = 1;
			resourceID = 208067;
		elseif find(iconFile, "purple") then
			resourceType = 1;
			resourceID = 208047;
		end
	end
	--print(resourceType, resourceID);
	return text, numRequired, resourceType, resourceID
end


function OptionFrameMixin:SetupButton(buttonInfo)
	if buttonInfo then
		local button = self.Button;
		if buttonInfo.disabled then
			button:SetButtonState(3);
		else
			if button.leftButtonDown then
				if button.stateIndex == 4 then
					button:SetButtonState(4);
				else
					button:SetButtonState(2);
				end
			else
				button:SetButtonState(1);
			end
		end
		button.canLongClick = false;
		button.id = buttonInfo.id;
		local text, numRequired, resourceType, resourceID = ProcessButtonText(buttonInfo.text);
		button:SetButtonText(text);
		if resourceType then
			if resourceType == 1 then
				ResourceDisplay_SetItem(self, resourceID, numRequired);
			elseif resourceType == 2 then
				ResourceDisplay_SetCurrency(self, resourceID, numRequired);
				if resourceID == 2650 then
					button.canLongClick = true;
					PlayerChoiceUI.repeatButtonID = buttonInfo.id;
				end
			end
		else
			ResourceDisplay_Hide(self);
		end
	else
		self.Button:Hide();
		ResourceDisplay_Hide(self);
	end
end

local function LongClick_OnUpdate(self, elapsed)
	self.t = self.t + elapsed;
	if self.t > 0.5 then
		--Now start auto-donating item
		--We apply additional animations to indicate this Long-click state
		self:SetScript("OnUpdate", nil);
		PlayerChoiceUI.autoClick = true;
		if PlayerChoiceUI.repeatButtonID then
			C_PlayerChoice.SendPlayerChoiceResponse(PlayerChoiceUI.repeatButtonID);
			PlayerChoiceUI.consumeNextClick = true;
			PlayerChoiceUI.ProgressBar.playShake = true;
		end
	end
end

function PlayerChoiceUI:StartHoldCountdown()
	self.autoClick = false;
	self.LongClickWatcher.t = 0;
	self.LongClickWatcher:SetScript("OnUpdate", LongClick_OnUpdate);
end

function PlayerChoiceUI:StopAutoClick()
	self.autoClick = false;
	self.LongClickWatcher.t = 0;
	self.LongClickWatcher:SetScript("OnUpdate", nil);
	self.ProgressBar.playShake = false;
end

local function Debug_ShowFrameSize(frame)
	local bg = frame:CreateTexture(nil, "BACKGROUND", nil, -1);
	bg:SetAllPoints(true);
	bg:SetColorTexture(1, 1, 1, 0.5);
	bg:SetIgnoreParentAlpha(true);
end

local function RewardItem_OnEnter(self)
	GameTooltip:Hide();
	if self.OptionFrame.itemID then
		GameTooltip:SetOwner(self, "ANCHOR_RIGHT");
		GameTooltip:SetItemByID(self.OptionFrame.itemID);
		GameTooltip:Show();
	end
end

local function RewardItem_OnLeave(self)
	GameTooltip:Hide();
end

local function OptionButton_OnClick(self)
	--We need to consume this click if long-click just ended
	--Sometimes the intial second click doesn't work due to Event not firing that fast?

	if self.id and not PlayerChoiceUI.consumeNextClick then
		C_PlayerChoice.SendPlayerChoiceResponse(self.id);
	end
	PlayerChoiceUI.consumeNextClick = false;
end

local function OptionButton_OnMouseDown(self)
	if self.canLongClick then
		PlayerChoiceUI:StartHoldCountdown();
	end
end

local function OptionButton_OnMouseUp(self)
	PlayerChoiceUI:StopAutoClick();
end

local function CreateOptionFrame()
	local f = CreateFrame("Frame", nil, PlayerChoiceUI);
	API.Mixin(f, OptionFrameMixin);
	f:SetSize(OPTION_FRAME_WIDTH, 102);

	--Item Reward
	local RewardFrame = CreateFrame("Frame", nil, f);
	f.RewardFrame = RewardFrame;
	RewardFrame:SetSize(OPTION_FRAME_WIDTH, 64);
	RewardFrame:SetPoint("TOP", f, "TOP", 0, 0);

	local RewardBorder = RewardFrame:CreateTexture(nil, "OVERLAY");
	RewardFrame.Border = RewardBorder;
	RewardBorder:SetSize(48, 48);
	RewardBorder:SetTexCoord(0, 0.1875, 0, 0.1875);
	RewardBorder:SetPoint("TOP", RewardFrame, "TOP", 0, -5);
	RewardBorder:SetTexture("Interface/AddOns/Plumber/Art/PlayerChoiceUI/UI");
	API.DisableSharpening(RewardBorder);

	local Hitbox = CreateFrame("Frame", nil, RewardFrame);
	Hitbox:SetSize(38, 38);
	Hitbox:SetPoint("CENTER", RewardBorder, "CENTER", 0, 0);
	Hitbox:SetScript("OnEnter", RewardItem_OnEnter);
	Hitbox:SetScript("OnLeave", RewardItem_OnLeave);
	Hitbox.OptionFrame = f;

	local RewardIcon = RewardFrame:CreateTexture(nil, "ARTWORK");
	RewardFrame.Icon = RewardIcon;
	RewardIcon:SetSize(34, 34);
	RewardIcon:SetTexCoord(0.0625, 0.9375, 0.0625, 0.9375);
	RewardIcon:SetPoint("CENTER", RewardBorder, "CENTER", 0, 0);

	local IconMask = RewardFrame:CreateMaskTexture(nil, "ARTWORK");
	IconMask:SetPoint("TOPLEFT", RewardIcon, "TOPLEFT", 0, 0);
	IconMask:SetPoint("BOTTOMRIGHT", RewardIcon, "BOTTOMRIGHT", 0, 0);
	IconMask:SetTexture("Interface/AddOns/Plumber/Art/BasicShape/Mask-Circle", "CLAMPTOBLACKADDITIVE", "CLAMPTOBLACKADDITIVE");
	RewardIcon:AddMaskTexture(IconMask);

	local RewardTick = RewardFrame:CreateTexture(nil, "OVERLAY", nil, 4);
	RewardFrame.Checkmark = RewardTick;
	RewardTick:SetSize(24, 24);
	RewardTick:SetTexture("Interface/AddOns/Plumber/Art/Button/Checkmark-Green");
	RewardTick:SetPoint("CENTER", RewardBorder, "CENTER", 16, -9);
	RewardTick:Show();

	local RewardName = RewardFrame:CreateFontString(nil, "OVERLAY", "GameTooltipText");
	RewardFrame.Name = RewardName;
	RewardName:SetJustifyH("CENTER");
	RewardName:SetShadowOffset(1, -1);
	RewardName:SetPoint("TOP", RewardBorder, "BOTTOM", 0, 0);
	RewardName:SetTextColor(1, 1, 1);
	RewardName:SetText("Item Name");

	local TextShadow = addon.CreateTextDropShadow(RewardName, RewardFrame);

	--Debug_ShowFrameSize(f);

	--Donate Button
	local Button = addon.CreateRedButton(f, "large");
	f.Button = Button;

	local resourceHeight = 22;

	Button:SetPoint("BOTTOM", f, "BOTTOM", 0, resourceHeight);
	Button:SetWidth(OPTION_FRAME_WIDTH);
	Button:SetScript("OnClick", OptionButton_OnClick);
	Button.onMouseDownFunc = OptionButton_OnMouseDown;
	Button.onMouseUpFunc = OptionButton_OnMouseUp;

	f:SetHeight(102 + resourceHeight);

	return f
end


function PlayerChoiceUI:OnEvent(event, ...)
	if event == "PLAYER_CHOICE_UPDATE" then
		self:PLAYER_CHOICE_UPDATE();
	elseif event == "PLAYER_CHOICE_CLOSE" then
		self:CloseUI();
	elseif event == "UPDATE_UI_WIDGET" then
		self:UPDATE_UI_WIDGET(...);
	elseif event == "UPDATE_ALL_UI_WIDGETS" then
		self:ProcessAllWidgets();
	elseif event == "PLAYER_DEAD" or event == "PLAYER_ENTERING_WORLD" then
		self:CloseUI();
	end
end

function PlayerChoiceUI:OnShow()

end

function PlayerChoiceUI:OnHide()
	self:StopAutoClick();
	self:SetScript("OnUpdate", nil);
	self.consumeNextClick = false;
end

local function CloseButton_OnEnter(self)
	self.ButtonText:SetTextColor(1, 1, 1);
end

local function CloseButton_OnLeave(self)
	self.ButtonText:SetTextColor(1, 0.82, 0);
end

local function CloseButton_OnClick(self)
	PlayerChoiceUI:CloseUI();
	C_PlayerChoice.OnUIClosed();
end

function PlayerChoiceUI:Init()
	--Debug_ShowFrameSize(self);

	self.Init = nil;
	self.optionFrames = {};

	self:SetScript("OnHide", self.OnHide);

	local offsetY = 0;

	local Title = self:CreateFontString(nil, "OVERLAY", "GameTooltipText");
	self.Title = Title;
	Title:SetJustifyH("CENTER");
	Title:SetShadowOffset(1, -1);
	Title:SetPoint("TOP", self, "TOP", 0, -offsetY);
	Title:SetTextColor(1, 0.82, 0);

	offsetY = offsetY + 16;

	local BarName = self:CreateFontString(nil, "OVERLAY", "GameTooltipText");
	self.BarName = BarName;
	BarName:SetJustifyH("CENTER");
	BarName:SetShadowOffset(1, -1);
	BarName:SetPoint("TOP", self, "TOP", 0, -offsetY);
	BarName:SetTextColor(1, 1, 1);

	offsetY = offsetY + 16;

	local BarValue = self:CreateFontString(nil, "OVERLAY", nil, 6);
	self.BarValue = BarValue;
	local font, fontHeight, flag = GameFontNormal:GetFont();
	BarValue:SetFont(font, 16, "OUTLINE");
	BarValue:SetPoint("TOP", self, "TOP", 0, -offsetY);
	BarValue:SetJustifyH("CENTER");
	BarValue:SetJustifyV("MIDDLE");
	BarValue:SetShadowOffset(1, -1);
	BarValue:SetShadowColor(0, 0, 0);
	BarValue:SetTextColor(1, 1, 1);

	offsetY = offsetY + 20;

	local ProgressBar = addon.CreateMetalProgressBar(self, "large");
	self.ProgressBar = ProgressBar;
	ProgressBar:SetPoint("TOP", self, "TOP", 0, -offsetY);
	ProgressBar.BarValue = BarValue;
	ProgressBar:SetNumThreshold(1);
	ProgressBar:SetValue(0, 100);
	ProgressBar:SetSmoothFill(true);

	local BarHeight = ProgressBar:GetHeight();
	offsetY = offsetY + BarHeight;
	self:SetSize(174, offsetY); --Header Height 84

	local Shadow = self:CreateTexture(nil, "BACKGROUND", nil, -1);
	Shadow:SetTexture("Interface/AddOns/Plumber/Art/BasicShape/DispersiveShadow_Round");
	Shadow:SetPoint("CENTER", self, "CENTER", 0, 0);
	Shadow:SetVertexColor(0, 0, 0, 0.6);
	Shadow:SetSize(500, 500);
	self.CentralShadow = Shadow;


	local CloseButton = addon.CreateThreeSliceFrame(self, "Metal_Hexagon_Red", "Button");
	CloseButton:SetPoint("TOP", self, "BOTTOM", 0, -20);
	CloseButton:SetSize(60, 20);
	CloseButton.ButtonText = CloseButton:CreateFontString(nil, "OVERLAY", "GameFontNormal");
	CloseButton.ButtonText:SetJustifyH("CENTER");
	CloseButton.ButtonText:SetJustifyV("MIDDLE");
	CloseButton.ButtonText:SetTextColor(1, 0.82, 0);
	CloseButton.ButtonText:SetPoint("CENTER", CloseButton, "CENTER", 0, 0);
	CloseButton.ButtonText:SetText(CLOSE or "Close");

	local textWidth = CloseButton.ButtonText:GetWidth();
	if textWidth < 32 then textWidth = 32 end;
	textWidth = floor(textWidth + 0.5);
	CloseButton:SetWidth(textWidth + 24);

	CloseButton.Highlight = CloseButton:CreateTexture(nil, "HIGHLIGHT");
	CloseButton.Highlight:SetPoint("TOPLEFT", CloseButton, "TOPLEFT", 0, 0);
	CloseButton.Highlight:SetPoint("BOTTOMRIGHT", CloseButton, "BOTTOMRIGHT", 0, 0);
	CloseButton.Highlight:SetTexture("Interface/AddOns/Plumber/Art/Frame/RedButton-Highlight", nil, nil, "TRILINEAR");
	CloseButton.Highlight:SetVertexColor(0.4, 0.1, 0.1);
	CloseButton.Highlight:SetBlendMode("ADD");

	CloseButton:SetScript("OnEnter", CloseButton_OnEnter);
	CloseButton:SetScript("OnLeave", CloseButton_OnLeave);
	CloseButton:SetScript("OnClick", CloseButton_OnClick);

	local rb = addon.CreateRepositionButton(self);
	self.RepositionButton = rb;
	rb:SetOrientation("x");
	rb:SetPoint("TOP", CloseButton, "BOTTOM", 0, -40);
end


local AnnounceButtonData = {};

function AnnounceButtonData:SetTargetCoord(x, y)
	self.x, self.y = x, y;
end

function AnnounceButtonData.ValidityCheck(msg)
	local uiMapID, x, y = GetWaypointFromText(msg);
	if uiMapID and uiMapID == 2200 then
		x = x / 10000;
		y = y / 10000;

		local isClose = AreWaypointsClose(x, y, AnnounceButtonData.x or 0, AnnounceButtonData.y or 0);
		return isClose
	end
end

function AnnounceButtonData:GetDreamseedName()
	if not self.nameDeamseed then
		local spellID = 425856;
		local name = API.GetSpellInfo(spellID);
		if name then
			self.nameDeamseed = name;
		end
	end
	return self.nameDeamseed or "Dreamseed"
end

function AnnounceButtonData.UpdatePlayerLocation()
	local creatureID, coordX, coordY = DreamseedUtil:GetNearbyPlantInfo();
	if coordX and coordY then
		AnnounceButtonData:SetTargetCoord(coordX, coordY);
	end
end

function AnnounceButtonData.GenerateMessage()
	local creatureID, coordX, coordY = DreamseedUtil:GetNearbyPlantInfo();
	if not creatureID then
		return false, 0
	end

	AnnounceButtonData:SetTargetCoord(coordX, coordY);

	local DISABLE_ANNOUNCE_TIME = 20;

	--local name = DreamseedUtil:GetPlantNameByCreatureID(creatureID);
	local remainingTime = DreamseedUtil:GetGrowthTimesByCreatureID(creatureID);
	local tint = PlayerChoiceUI.ProgressBar and PlayerChoiceUI.ProgressBar:GetBarColorTint() or 0;
	local colorText;

	if tint == 6 then   --purple
		colorText = L["Seed Color Epic"];
	elseif tint == 7 then   --green
		colorText = L["Seed Color Uncommon"];
	elseif tint == 8 then   --blue
		colorText = L["Seed Color Rare"];
	end

	if colorText then
		colorText = string.gsub(colorText, "^%l",string.upper);
	end

	if not (remainingTime and colorText) then
		return false, 0
	end

	if remainingTime < DISABLE_ANNOUNCE_TIME then
		return false, 3
	end

	local waypointText = API.CreateWaypointHyperlink(MAPID_EMRALD_DREAM, coordX, coordY);
	local name = AnnounceButtonData:GetDreamseedName();
	local msg = string.format("%s %s %s", colorText, name, waypointText);

	if remainingTime < 90 then
		--Add time
		local abbreviated = true;
		local partialTime = false;
		local bakePluralEscapeSequence = true;
		local timeText = API.SecondsToTime(remainingTime, abbreviated, partialTime, bakePluralEscapeSequence);
		msg = msg.." "..timeText;
	end

	return true, msg
end

function PlayerChoiceUI:SetupAnnounceButton()
	local Horn = addon.GetAnnounceButton();
	Horn:SetParent(self);
	Horn:ClearAllPoints();
	Horn:SetPoint("BOTTOM", self, "TOP", 0, 2);
	Horn:SetValidityCheck(AnnounceButtonData.ValidityCheck);
	Horn:SetAnnouceTextSource(AnnounceButtonData.GenerateMessage);
	Horn:EnableCursorBlocker(true);
	Horn:ShowAndRequestUpdate(AnnounceButtonData.UpdatePlayerLocation);
	AnnounceButtonData:GetDreamseedName();  --to cache item name
end

local INTRO_DURATION = 0.35;

local function Intro_OnUpdate(self, elapsed)
	self.t = self.t + elapsed;
	local alpha = self.t * 2.857;
	if alpha > 1 then
		alpha = 1;
	end

	local height = easingFunc(self.t, self.fullHeight - 48, self.fullHeight, INTRO_DURATION);

	if self.t > INTRO_DURATION then
		alpha = 1;
		height = self.fullHeight;
		self:SetScript("OnUpdate", nil);
	end

	PlayerChoiceUI:SetAlpha(alpha);
	PlayerChoiceUI:SetHeight(height);
end

function PlayerChoiceUI:PlayIntro()
	self.t = 0;
	Intro_OnUpdate(self, 0);
	self:SetScript("OnUpdate", Intro_OnUpdate);
end

function PlayerChoiceUI:ShowUI()
	if self.Init then
		self:Init();
	end

	local success = self:Setup(self.choiceInfo and self.choiceInfo.options[1]);

	if not success then
		self:CloseUI();
		return false
	end

	if not self:IsShown() then
		ZoomCalculator:Sync();
		self:PlayIntro();
		self:RegisterEvent("UPDATE_UI_WIDGET");
		self:RegisterEvent("UPDATE_ALL_UI_WIDGETS");
		self:RegisterEvent("PLAYER_DEAD");
		self:RegisterEvent("PLAYER_ENTERING_WORLD");
		self.consumeNextClick = false;
		self.requireRewardCheck = true;
		self:SetupAnnounceButton();
	end

	self:Show();
	return true
end

function PlayerChoiceUI:CloseUI()
	self:Hide();
	self.choiceInfo = nil;
	self.items = nil;
	self:UnregisterEvent("UPDATE_UI_WIDGET");
	self:UnregisterEvent("UPDATE_ALL_UI_WIDGETS");
	self:UnregisterEvent("PLAYER_DEAD");
	self:UnregisterEvent("PLAYER_ENTERING_WORLD");
end

local function ContinueAutoClick()
	if PlayerChoiceUI:IsShown() and PlayerChoiceUI.autoClick and PlayerChoiceUI.repeatButtonID then
		C_PlayerChoice.SendPlayerChoiceResponse(PlayerChoiceUI.repeatButtonID);
	end
end

function PlayerChoiceUI:PLAYER_CHOICE_UPDATE()
	local choiceInfo = C_PlayerChoice.GetCurrentPlayerChoiceInfo();

	self.choiceInfo = choiceInfo;
	self.repeatButtonID = nil;

	if not choiceInfo then
		self:CloseUI();
		return
	end

	local creatureID = GetCreatureIDFromGUID(choiceInfo.objectGUID);

	if VALID_CREATURES[creatureID] then
		local success = self:ShowUI();
		if success then
			BlizzardFrameMover:HidePlayerChoiceFrame();
		end

		if self.autoClick and self.repeatButtonID then
			local barValue = self.ProgressBar:GetValue();
			if barValue == 50 then
				C_Timer.After(0.75, ContinueAutoClick);
			else
				C_PlayerChoice.SendPlayerChoiceResponse(self.repeatButtonID);
			end
		end
	else
		self:CloseUI();
	end
end

function PlayerChoiceUI:UPDATE_UI_WIDGET(widgetInfo)
	if widgetInfo.widgetSetID and widgetInfo.widgetSetID == self.widgetSetID then
		self:ProcessWidget(widgetInfo.widgetID, widgetInfo.widgetType);
	end
end

function PlayerChoiceUI:AcquireOptionFrame(optionIndex)
	if not self.optionFrames[optionIndex] then
		self.optionFrames[optionIndex] = CreateOptionFrame();
	end
	self.optionFrames[optionIndex]:Show();
	return self.optionFrames[optionIndex];
end

function PlayerChoiceUI:Setup(option)
	if not (option and option.buttons) then
		return false
	end

	local buttons = option.buttons;

	for buttonIndex, buttonInfo in ipairs(buttons) do
		self:AcquireOptionFrame(buttonIndex);
		self.optionFrames[buttonIndex]:SetupButton(buttonInfo);
	end

	self.Title:SetText(option.header);

	local numOptions = #buttons;
	local fullHeight = 220;

	if numOptions ~= self.numOptions then
		self.numOptions = numOptions;
		local optionFrame;
		local optionHeight;

		for i = 1, numOptions do
			self.optionFrames[i]:ClearAllPoints();
			self.optionFrames[i]:SetPoint("BOTTOMLEFT", self, "BOTTOMLEFT", (i- 1)*(OPTION_FRAME_WIDTH + OPTION_FRAME_GAP), 0);
			if not optionHeight then
				optionHeight = self.optionFrames[i]:GetHeight();
			end
		end

		for i = numOptions + 1, #self.optionFrames do
			self.optionFrames[i]:Hide();
		end

		if numOptions > 0 then
			self:SetWidth(numOptions*(OPTION_FRAME_WIDTH + OPTION_FRAME_GAP) - OPTION_FRAME_GAP);
			local headerHeight = 84;
			local gap = 12;
			fullHeight = headerHeight + gap + optionHeight;
			self:SetHeight(fullHeight);
			self.fullHeight = fullHeight;
		else
			self:SetHeight(174);
			self.fullHeight = 174;
		end
	end

	self.fullHeight = fullHeight;
	self.widgetSetID = option.widgetSetID;

	self:ProcessAllWidgets();

	return true
end

function PlayerChoiceUI:ProcessAllWidgets()
	self.itemOptionFrame = {};
	self.items = {};
	if self.widgetSetID then
		local setWidgets = C_UIWidgetManager.GetAllWidgetsBySetID(self.widgetSetID);
		for _, widgetInfo in ipairs(setWidgets) do
			self:ProcessWidget(widgetInfo.widgetID, widgetInfo.widgetType);
		end

		local numItems = 0;
		local optionFrame;
		for order = 1, 10 do
			if self.items[order] then
				numItems = numItems + 1;
				optionFrame = self:AcquireOptionFrame(numItems);
				self.itemOptionFrame[order] = optionFrame;
				optionFrame:SetupItem(self.items[order][1], self.items[order][2]);
				if numItems == 2 then
					break
				end
			end
		end
	end
end

function PlayerChoiceUI:ProcessWidget(widgetID, widgetType)
	local widgetInfo;

	if widgetType == 2 then
		widgetInfo = C_UIWidgetManager.GetStatusBarWidgetVisualizationInfo(widgetID);
		if widgetInfo then
			self.BarName:SetText(widgetInfo.text);
			self.ProgressBar:SetValue(widgetInfo.barValue, widgetInfo.barMax, true);
			self.ProgressBar:SetNumThreshold( widgetInfo.barMax > 10 and 1 or 0 );  --Tutorial Plant only requires 5
			self.ProgressBar:SetBarColorTint(widgetInfo.colorTint);
		end
	elseif widgetType == 27 then
		--all items fire, we only need shownState == 1
		widgetInfo = C_UIWidgetManager.GetItemDisplayVisualizationInfo(widgetID);
		if widgetInfo and widgetInfo.shownState == 1 then
			local order = widgetInfo.orderIndex;
			local itemID = widgetInfo.itemInfo.itemID;
			local isEarned = widgetInfo.itemInfo.showAsEarned;
			if not self.items[order] then
				self.items[order] = {itemID, isEarned};
			else
				self.items[order][2] = isEarned;
				if self.itemOptionFrame[order] then
					self.itemOptionFrame[order]:SetupItem(self.items[order][1], self.items[order][2]);
				end
			end
			--local layoutDirection = widgetInfo.layoutDirection;
			--local name = C_Item.GetItemNameByID(itemID);
			--print(itemID, name, order, layoutDirection, isEarned);
		end
	end
end

function PlayerChoiceUI:Enable()
	self:RegisterEvent("PLAYER_CHOICE_UPDATE");
	self:RegisterEvent("PLAYER_CHOICE_CLOSE");
	self:SetScript("OnEvent", self.OnEvent);
end

function PlayerChoiceUI:Disable()
	self:CloseUI();
	self:UnregisterEvent("PLAYER_CHOICE_UPDATE");
	self:UnregisterEvent("PLAYER_CHOICE_CLOSE");
	self:SetScript("OnEvent", nil);
end


local function SetAndSaveFramePosition(offsetX)
	UI_OFFSET_X = offsetX;
	PlumberDB.playerchoiceuiOffsetX = offsetX;
	local y = ZoomCalculator.fromY or 0;
	PlayerChoiceUI:SetPoint("CENTER", UI_OFFSET_X, y);
end


local function LoadFramePosition()
	local userPositionX = PlumberDB and PlumberDB.playerchoiceuiOffsetX;
	if userPositionX then
		if type(userPositionX) ~= "number" then
			userPositionX = 0;
			PlumberDB.playerchoiceuiOffsetX = userPositionX;
		end
		SetAndSaveFramePosition(userPositionX);
	end
end


local ZoneTriggerModule;

local function EnableModule(state)
	if state then
		if not ZoneTriggerModule then
			local module = API.CreateZoneTriggeredModule("nurture");
			ZoneTriggerModule = module;
			module:SetValidZones(MAPID_EMRALD_DREAM);

			local function OnEnterZoneCallback()
				PlayerChoiceUI:Enable();
			end

			local function OnLeaveZoneCallback()
				PlayerChoiceUI:Disable();
			end

			module:SetEnterZoneCallback(OnEnterZoneCallback);
			module:SetLeaveZoneCallback(OnLeaveZoneCallback);

			LoadFramePosition();
		end
		ZoneTriggerModule:SetEnabled(true);
		ZoneTriggerModule:Update();

		if C_AddOns then
			if C_AddOns.IsAddOnLoaded("MoveAny") then
				HideBlizzardFrame = HideBlizzardFrame_MoveAny;
			elseif C_AddOns.IsAddOnLoaded("Drift") then
				HideBlizzardFrame = HideBlizzardFrame_Drift;
			end
			--BlizzMove does not support PlayerChoiceFrame
			--MoveAnything cannot move anything after 10.0
		end
	else
		if ZoneTriggerModule then
			ZoneTriggerModule:SetEnabled(false);
		end
		PlayerChoiceUI:Disable();
	end
end

do
	local moduleData = {
		name = L["ModuleName AlternativePlayerChoiceUI"],
		dbKey = "AlternativePlayerChoiceUI",
		description = L["ModuleDescription AlternativePlayerChoiceUI"],
		toggleFunc = EnableModule,
		categoryID = 1002,
		uiOrder = 3,
		categoryKeys = {
			"Old",
		},
	};

	addon.ControlCenter:AddModule(moduleData);
end



--[[
Rework PlayerChoiceFrame
	Title.Left/Middle/Right/Text
	NineSlice


	PlayerChoiceTimeRemaining
	PlayerChoiceNormalOptionTemplate
	PlayerChoiceFrame.optionPools.EnumerateActive

	function ModifyDefaultUI()
		local f = _G["PlayerChoiceFrame"];
		local pool = f and f.optionPools;
		if not pool then return end;

		f:EnableMouse(false);
		f.NineSlice:Hide();

		f.Background:Hide();
		f.Title.Left:SetTexture();
		f.Title.Middle:SetTexture();
		f.Title.Right:SetTexture();

		for optionFrame in pool:EnumerateActive() do
			--optionFrame.WidgetContainer:Hide(); --progress bar
			optionFrame:EnableMouse(false);
			optionFrame.Artwork:SetTexture();
			optionFrame.ArtworkBorder:SetTexture();
			optionFrame.Background:SetTexture();
		end
	end

--]]


ZoomCalculator.GetCameraZoom = GetCameraZoom;
ZoomCalculator.fromY = UI_OFFSET_Y;
ZoomCalculator.t = 0;

FrameMover:Hide();
FrameMover.t = 0;

function ZoomCalculator:Sync()
	local zoom = self.GetCameraZoom();
	if zoom ~= self.zoom then
		self.zoom = zoom;
		local toY = CalculateOffsetYFromZoom(zoom);
		self.fromY = toY;
		PlayerChoiceUI:SetPoint("CENTER", UI_OFFSET_X, toY);
	end
end

ZoomCalculator:SetScript("OnUpdate", function(self, elapsed)
	self.t = self.t + elapsed;
	if self.t > 0.1 then
		self.t = 0;
	else
		return
	end

	local zoom = self.GetCameraZoom();
	if zoom ~= self.zoom then
		self.zoom = zoom;
		local toY = CalculateOffsetYFromZoom(zoom)
		FrameMover.t = 0;
		FrameMover.fromY = self.fromY;
		FrameMover.toY = toY;
		FrameMover:Show();
	end
end);

local EASING_DURATION = 0.25;
local inOutSine = addon.EasingFunctions.outSine;

FrameMover:SetScript("OnUpdate", function(self, elapsed)
	self.t = self.t + elapsed;
	local y = inOutSine(self.t, self.fromY, self.toY, EASING_DURATION);
	if self.t >= EASING_DURATION then
		y = self.toY;
		self:Hide();
	end
	ZoomCalculator.fromY = y;
	PlayerChoiceUI:SetPoint("CENTER", UI_OFFSET_X, y);
end);




local function CalculateOffsetXToUIParentCenter()
	local uiCenterX, uiCenterY = UIParent:GetCenter();
	local x, y = PlayerChoiceUI:GetCenter();
	return x - uiCenterX, y - uiCenterY
end

function PlayerChoiceUI:ResetFramePosition()
	SetAndSaveFramePosition(0);
end

function PlayerChoiceUI:SnapShotFramePosition()
	self.repositionFromX, self.repositionFromY = CalculateOffsetXToUIParentCenter();
end

function PlayerChoiceUI:ConfirmNewPosition()
	local x = CalculateOffsetXToUIParentCenter();
	SetAndSaveFramePosition(x);
	self.repositionFromX, self.repositionFromY = nil, nil;
end

function PlayerChoiceUI:RepositionFrame(offsetX, offsetY)
	if not self.repositionFromX then
		self:SnapShotFramePosition();
	end
	self:SetPoint("CENTER", self.repositionFromX + (offsetX or 0), self.repositionFromY + (offsetY or 0));
end




do
	if GetLocale() == "deDE" then
		--German words are longer, so we need to adjust the button width
		OPTION_FRAME_WIDTH = OPTION_FRAME_WIDTH + 24;
	end
end


--[[
	--Dev Tool for ajust UI offset based on CameraZoom
local AjustButton = CreateFrame("Frame", nil, UIParent);
AjustButton.frameY = 0;
AjustButton.GetCursorPosition = GetCursorPosition;
Debug_ShowFrameSize(AjustButton);
AjustButton:SetPoint("CENTER", 400, 0);
AjustButton:SetSize(30, 30);
local function AjustButton_OnUpdate(self)
	local _, y = self.GetCursorPosition();
	if y ~= self.lastY then
		local d = y - self.lastY;
		self.lastY = y;
		self.frameY = self.frameY + d;
		print(floor(self.frameY*1000)/1000,"  ", floor(GetCameraZoom() * 1000)/1000);
		PlayerChoiceUI:SetPoint("CENTER", 0, self.frameY);
	end
end

AjustButton:SetScript("OnMouseDown", function(self)
	local _, y = self.GetCursorPosition();
	self.lastY = y;
	self:SetScript("OnUpdate", AjustButton_OnUpdate);
end);

AjustButton:SetScript("OnMouseUp", function(self)
	self:SetScript("OnUpdate", nil);
end);

function TestAnim()
	PlayerChoiceUI:ShowUI();
	PlayerChoiceUI:Show();
end
-]]
