local ADDON, Private = ...
local Core = Wheel("LibModule"):GetModule(ADDON)
if (not Core) then
	return
end

local L = Wheel("LibLocale"):GetLocale(ADDON)
local Module = Core:NewModule("BlizzardGameMenu", "LibEvent", "LibDB", "LibTooltip", "LibFrame")

-- Lua API
local _G = _G
local ipairs = ipairs
local table_remove = table.remove
local type = type

-- WoW API
local InCombatLockdown = InCombatLockdown
local IsMacClient = IsMacClient

-- Private API
local GetLayout = Private.GetLayout
local IsDragonflight = Private.IsRetailDragonflight

local BLANK_TEXTURE = [[Interface\ChatFrame\ChatFrameBackground]]
local buttonWidth, buttonHeight, buttonSpacing, sizeMod = 300, 50, 10, 3/4

Module.OnEvent = function(self, event, ...)
	if (event == "PLAYER_REGEN_ENABLED") then
		self:UnregisterEvent("PLAYER_REGEN_ENABLED", "OnEvent")
		self:UpdateButtonLayout()
	end
end

Module.UpdateButtonLayout = function(self)
	if InCombatLockdown() then
		return self:RegisterEvent("PLAYER_REGEN_ENABLED", "OnEvent")
	end

	local UICenter = self:GetFrame("UICenter")

	local previous, bottom_previous
	local first, last
	for i,v in ipairs(self.buttons) do
		local button = v.button
		if button and button:IsShown() then
			button:ClearAllPoints()
			if previous then
				button:SetPoint("TOPLEFT", previous, "BOTTOMLEFT", 0, -buttonSpacing)
			else
				button:SetPoint("TOP", UICenter, "TOP", 0, -300) -- we'll change this later
				first = button
			end
			previous = button
			last = button
		end
	end

	-- re-align first button so that the menu will be vertically centered
	local top = first:GetTop()
	local bottom = last:GetBottom()
	local screen_height = UICenter:GetHeight()
	local height = top - bottom
	local y_position = (screen_height - height) *2/5

	first:ClearAllPoints()
	first:SetPoint("TOP", UICenter, "TOP", 0, -y_position)
end

Module.StyleButtons = function(self)
	local layout = self.layout
	local UICenter = self:GetFrame("UICenter")

	local need_addon_watch
	for i,v in ipairs(self.buttons) do

		-- figure out the real frame handle of the button
		local button
		if type(v.content) == "string" then
			button = _G[v.content]
		else
			button = v.content
		end

		-- style it unless we've already done it
		if (not v.styled) then

			if (button) then
				-- Ignore hidden buttons, because that means Blizzard aren't using them.
				-- An example of this is the mac options button which is hidden on windows/linux.

				local label
				if (type(v.label) == "function") then
					label = v.label()
				else
					label = v.label
				end
				local anchor = v.anchor

				-- run custom scripts on the button, if any
				if (v.run) then
					v.run(button)
				end

				-- Hide some textures added in Legion that cause flickering
				if button.Left then
					button.Left:SetAlpha(0)
				end
				if button.Right then
					button.Right:SetAlpha(0)
				end
				if button.Middle then
					button.Middle:SetAlpha(0)
				end

				-- Clear away blizzard artwork
				button:SetNormalTexture("")
				button:SetHighlightTexture("")
				button:SetPushedTexture("")

				--button:SetText(" ") -- this is not enough, blizzard adds it back in some cases

				local fontstring = button:GetFontString()
				if fontstring then
					fontstring:SetAlpha(0) -- this is compatible with the Shop button
				end

				button:SetSize(layout.MenuButtonSize[1]*layout.MenuButtonSizeMod, layout.MenuButtonSize[2]*layout.MenuButtonSizeMod)
				layout.MenuButton_PostCreate(button, label)

				local PostUpdate = layout.MenuButton_PostUpdate
				button:HookScript("OnEnter", PostUpdate)
				button:HookScript("OnLeave", PostUpdate)
				button:HookScript("OnMouseDown", function(self) self.isDown = true; return PostUpdate(self) end)
				button:HookScript("OnMouseUp", function(self) self.isDown = false; return PostUpdate(self) end)
				button:HookScript("OnShow", function(self) self.isDown = false; return PostUpdate(self) end)
				button:HookScript("OnHide", function(self) self.isDown = false; return PostUpdate(self) end)
				PostUpdate(button)

				v.button = button -- add a reference to the frame handle for the layout function
				v.styled = true -- avoid double styling

			else
				-- If the button doesn't exist, it could be something added by an addon later.
				if v.addon then
					need_addon_watch = true
				end
			end

		end
	end

	-- Add this as a callback if a button from an addon wasn't loaded.
	-- *Could add in specific addons to look for here, but I'm not going to bother with it.
	if need_addon_watch then
		if not self.looking_for_addons then
			self:RegisterEvent("ADDON_LOADED", "StyleButtons")
			self.looking_for_addons = true
		end
	else
		if self.looking_for_addons then
			self:UnregisterEvent("ADDON_LOADED", "StyleButtons")
			self.looking_for_addons = nil
		end
	end

	self:UpdateButtonLayout()
end

Module.StyleWindow = function(self, frame)

	self.frame:EnableMouse(false) -- only need the mouse on the actual buttons

	if (self.frame.SetBackdrop) then
		self.frame:SetBackdrop(nil)
	end

	self.frame:SetFrameStrata("DIALOG")
	self.frame:SetFrameLevel(120)

	-- registry of objects we won't strip (not actually used yet)
	if (not self.objects) then
		self.objects = {}
	end

	for i = 1, self.frame:GetNumRegions() do
		local region = select(i, self.frame:GetRegions())
		if region and not self.objects[region] then
			local object_type = region.GetObjectType and region:GetObjectType()
			local hide
			if object_type == "Texture" then
				region:SetTexture(nil)
				region:SetAlpha(0)
			elseif object_type == "FontString" then
				region:SetText("")
			end
		end
	end

	-- 8.2.0 weirdness
	if (self.frame.Border) then
		self.frame.Border:SetParent(self.UIHider)
	end

	-- 8.3.0 weirdness
	if (self.frame.Header) then
		self.frame.Header:SetParent(self.UIHider)
	end

end

Module.OnInit = function(self)
	self.layout = GetLayout(self:GetName())
	if (not self.layout) then
		return self:SetUserDisabled(true)
	end

	local UICenter = self:GetFrame("UICenter")

	-- does this taint? :/
	self.frame = GameMenuFrame
	self.frame:SetParent(UICenter)
	self.buttons = {
		{ content = GameMenuButtonHelp, label = IsDragonflight and GAMEMENU_SUPPORT or GAMEMENU_HELP },
		{ content = GameMenuButtonStore, label = BLIZZARD_STORE },
		{ content = GameMenuButtonWhatsNew, label = GAMEMENU_NEW_BUTTON },
		{ content = GameMenuButtonSettings, label = GAMEMENU_SETTINGS },
		{ content = GameMenuButtonEditMode, label = HUD_EDIT_MODE_MENU },
		{ content = GameMenuButtonOptions, label = SYSTEMOPTIONS_MENU },
		{ content = GameMenuButtonUIOptions, label = UIOPTIONS_MENU },
		{ content = GameMenuButtonKeybindings, label = KEY_BINDINGS },
		{ content = "GameMenuButtonMoveAnything", label = function() return GameMenuButtonMoveAnything:GetText() end, addon = true },
		{ content = GameMenuButtonMacros, label = MACROS },
		{ content = GameMenuButtonAddons, label = ADDONS },
		{ content = GameMenuButtonRatings, label = RATINGS_MENU },
		{ content = GameMenuButtonLogout, label = LOGOUT },
		{ content = GameMenuButtonQuit, label = EXIT_GAME },
		{ content = GameMenuButtonContinue, label = RETURN_TO_GAME, anchor = "BOTTOM" }
	}

	local UIHider = CreateFrame("Frame")
	UIHider:Hide()
	self.UIHider = UIHider

	-- kill mac options button if not a mac client
	if GameMenuButtonMacOptions and (not IsMacClient()) then
		for i,v in ipairs(self.buttons) do
			if v.content == GameMenuButtonMacOptions then
				GameMenuButtonMacOptions:UnregisterAllEvents()
				GameMenuButtonMacOptions:SetParent(UIHider)
				GameMenuButtonMacOptions.SetParent = function() end
				table_remove(self.buttons, i)
				break
			end
		end
	end

	-- Remove store button if there's no store available,
	-- if we're currently using a trial account,
	-- or if the account is in limited (no paid gametime) mode.
	-- TODO: Hook a callback post-styling and post-showing this
	-- when the store becomes available mid-session.
	if GameMenuButtonStore
	and ((C_StorePublic and not C_StorePublic.IsEnabled())
	or (IsTrialAccount and IsTrialAccount())
	or (GameLimitedMode_IsActive and GameLimitedMode_IsActive())) then
		for i,v in ipairs(self.buttons) do
			if v.content == GameMenuButtonStore then
				GameMenuButtonStore:UnregisterAllEvents()
				GameMenuButtonStore:SetParent(UIHider)
				GameMenuButtonStore.SetParent = function() end
				table_remove(self.buttons, i)
				break
			end
		end
	end

	-- add a hook to blizzard's button visibility function to properly re-align the buttons when needed
	if GameMenuFrame_UpdateVisibleButtons then
		hooksecurefunc("GameMenuFrame_UpdateVisibleButtons", function() self:UpdateButtonLayout() end)
	end

	self:RegisterEvent("UI_SCALE_CHANGED", "UpdateButtonLayout")
	self:RegisterEvent("DISPLAY_SIZE_CHANGED", "UpdateButtonLayout")

	if (not IsDragonflight) then
		if VideoOptionsFrameApply then
			VideoOptionsFrameApply:HookScript("OnClick", function() self:UpdateButtonLayout() end)
		end

		if VideoOptionsFrameOkay then
			VideoOptionsFrameOkay:HookScript("OnClick", function() self:UpdateButtonLayout() end)
		end
	end

end

Module.OnEnable = function(self)
	self:StyleWindow()
	self:StyleButtons()

	-- Hack to trigger everything else(?)
	GameMenuFrame_OnShow(GameMenuFrame)
end
