if BigWigsLoader.isBeta then return end -- XXX needs updating for 12.0

--------------------------------------------------------------------------------
-- Module Declaration
--

if BigWigsLoader.isVanilla then return end

local plugin, L = BigWigs:NewPlugin("AltPower")
if not plugin then return end

--------------------------------------------------------------------------------
-- Locals
--

local LibSharedMedia = LibStub("LibSharedMedia-3.0")
local FONT = LibSharedMedia.MediaType and LibSharedMedia.MediaType.FONT or "font"
plugin.displayName = L.altPowerTitle

local powerList, powerMaxList, sortedUnitList, roleColoredList = nil, nil, nil, nil
local unitList = nil
local maxPlayers = -1
local display, updater = nil, nil
local opener = nil
local currentTitle = nil
local inTestMode = nil
local sortDir = nil
local repeatSync = nil
local syncPowerList = nil
local syncPowerMaxList = nil
local UpdateDisplay
local tsort, min = table.sort, math.min
local UnitPower, IsInGroup = UnitPower, IsInGroup
local db = nil
local roleIcons = {
	["TANK"] = "|TInterface\\AddOns\\BigWigs\\Media\\Icons\\Menus\\Role_Tank:0|t",
	["HEALER"] = "|TInterface\\AddOns\\BigWigs\\Media\\Icons\\Menus\\Role_Healer:0|t",
	["DAMAGER"] = "|TInterface\\AddOns\\BigWigs\\Media\\Icons\\Menus\\Role_Damage:0|t",
	["NONE"] = "",
}

local function colorize(power, powerMax)
	if power == -1 then return 0, 255 end
	if not powerMax or powerMax == 0 then
		powerMax = 100
		if power > powerMax then
			powerMax = power
		end
	end
	local ratio = power/powerMax*510
	local r, g = min(ratio, 255), min(510-ratio, 255)
	if sortDir == "AZ" then -- red to green
		return r, g
	else -- green to red
		return g, r
	end
end

function plugin:RestyleWindow()
	display:ClearAllPoints()
	local point, relPoint = db.position[1], db.position[2]
	local x, y = db.position[3], db.position[4]
	display:SetPoint(point, UIParent, relPoint, x, y)

	if db.lock then
		display:SetMovable(false)
	else
		display:SetMovable(true)
	end

	local font = LibSharedMedia:Fetch(FONT, db.fontName)
	local flags = nil
	if db.monochrome and db.outline ~= "NONE" then
		flags = "MONOCHROME," .. db.outline
	elseif db.monochrome then
		flags = "MONOCHROME"
	elseif db.outline ~= "NONE" then
		flags = db.outline
	end

	display.bar:SetColorTexture(db.barColor[1], db.barColor[2], db.barColor[3], db.barColor[4])
	display.bg:SetColorTexture(db.backgroundColor[1], db.backgroundColor[2], db.backgroundColor[3], db.backgroundColor[4])
	display.title:SetFont(font, db.fontSize, flags)
	display.title:SetTextColor(db.barTextColor[1], db.barTextColor[2], db.barTextColor[3], 1)
	display.title:SetHeight(16+db.additionalHeight)
	for i = 1, 26 do
		display.text[i]:SetFont(font, db.fontSize, flags)
		display.text[i]:SetSize(115+db.additionalWidth, 16+db.additionalHeight)
	end
	-- 240 = 115*2 + (5*2 padding - left and right)
	-- 210 = 16*13 + (1*2 padding - top and bottom)
	-- 82 = 16*5 + (1*2 padding - top and bottom)
	display:SetSize(240+(db.additionalWidth*2), db.expanded and 210+(db.additionalHeight*13) or 82+(db.additionalHeight*5))
end

--------------------------------------------------------------------------------
-- Profile
--

do
	local name = plugin:GetDefaultFont()
	local _, size = plugin:GetDefaultFont(12)
	plugin.defaultDB = {
		position = {"CENTER", "CENTER", 450, -160},
		fontName = name,
		fontSize = size,
		outline = "NONE",
		additionalWidth = 0,
		additionalHeight = 0,
		barTextColor = {1, 0.82, 0},
		barColor = {0.2, 0, 1, 0.5},
		backgroundColor = {0, 0, 0, 0.3},
		monochrome = false,
		expanded = false,
		disabled = false,
		lock = false,
	}
end

local updateProfile
do
	local function ValidateColor(current, default, alphaLimit)
		for i = 1, 3 do
			local n = current[i]
			if type(n) ~= "number" or n < 0 or n > 1 then
				current[1] = default[1] -- If 1 entry is bad, reset the whole table
				current[2] = default[2]
				current[3] = default[3]
				current[4] = default[4]
				return
			end
		end
		if alphaLimit then
			if type(current[4]) ~= "number" or current[4] < alphaLimit or current[4] > 1 then
				current[4] = default[4]
			end
		elseif current[4] then
			current[4] = nil
		end
	end

	function updateProfile()
		db = plugin.db.profile

		for k, v in next, db do
			local defaultType = type(plugin.defaultDB[k])
			if defaultType == "nil" then
				db[k] = nil
			elseif type(v) ~= defaultType then
				db[k] = plugin.defaultDB[k]
			end
		end

		if type(plugin.db.profile.position[1]) ~= "string" or type(plugin.db.profile.position[2]) ~= "string"
		or type(plugin.db.profile.position[3]) ~= "number" or type(plugin.db.profile.position[4]) ~= "number"
		or not BigWigsAPI.IsValidFramePoint(plugin.db.profile.position[1]) or not BigWigsAPI.IsValidFramePoint(plugin.db.profile.position[2]) then
			plugin.db.profile.position[1] = plugin.defaultDB.position[1]
			plugin.db.profile.position[2] = plugin.defaultDB.position[2]
			plugin.db.profile.position[3] = plugin.defaultDB.position[3]
			plugin.db.profile.position[4] = plugin.defaultDB.position[4]
			plugin.db.profile.position[5] = plugin.defaultDB.position[5]
		else
			local x = math.floor(plugin.db.profile.position[3]+0.5)
			if x ~= plugin.db.profile.position[3] then
				plugin.db.profile.position[3] = x
			end
			local y = math.floor(plugin.db.profile.position[4]+0.5)
			if y ~= plugin.db.profile.position[4] then
				plugin.db.profile.position[4] = y
			end
		end
		if plugin.db.profile.position[5] ~= plugin.defaultDB.position[5] then
			local frame = _G[plugin.db.profile.position[5]]
			if type(frame) ~= "table" or type(frame.GetObjectType) ~= "function" or type(frame.IsForbidden) ~= "function" or frame:IsForbidden() then
				plugin.db.profile.position[1] = plugin.defaultDB.position[1]
				plugin.db.profile.position[2] = plugin.defaultDB.position[2]
				plugin.db.profile.position[3] = plugin.defaultDB.position[3]
				plugin.db.profile.position[4] = plugin.defaultDB.position[4]
				plugin.db.profile.position[5] = plugin.defaultDB.position[5]
			end
		end

		if not LibSharedMedia:IsValid("font", plugin.db.profile.fontName) then
			plugin.db.profile.fontName = plugin.defaultDB.fontName
		end
		if db.fontSize < 10 or db.fontSize > 200 then
			db.fontSize = plugin.defaultDB.fontSize
		end
		if db.outline ~= "NONE" and db.outline ~= "OUTLINE" and db.outline ~= "THICKOUTLINE" then
			db.outline = plugin.defaultDB.outline
		end
		if db.additionalWidth < 0 or db.additionalWidth > 100 then
			db.additionalWidth = plugin.defaultDB.additionalWidth
		end
		if db.additionalHeight < 0 or db.additionalHeight > 100 then
			db.additionalHeight = plugin.defaultDB.additionalHeight
		end
		ValidateColor(db.barTextColor, plugin.defaultDB.barTextColor)
		ValidateColor(db.barColor, plugin.defaultDB.barColor, 0)
		ValidateColor(db.backgroundColor, plugin.defaultDB.backgroundColor, 0)

		plugin:RestyleWindow()
	end
end

-------------------------------------------------------------------------------
-- Options
--

do
	local disabled = function() return plugin.db.profile.disabled end
	plugin.pluginOptions = {
		name = "|TInterface\\AddOns\\BigWigs\\Media\\Icons\\Menus\\AltPower:20|t ".. L.altPowerTitle,
		type = "group",
		childGroups = "tab",
		order = 15,
		get = function(info)
			return db[info[#info]]
		end,
		set = function(info, value)
			local entry = info[#info]
			db[entry] = value
			plugin:RestyleWindow()
		end,
		args = {
			general = {
				type = "group",
				name = L.general,
				order = 1,
				args = {
					heading = {
						type = "description",
						name = L.altPowerDesc .."\n\n",
						order = 1,
						width = "full",
						fontSize = "medium",
					},
					test = {
						type = "execute",
						name = function()
							if inTestMode then
								return L.stopTest
							else
								return L.startTest
							end
						end,
						desc = L.altPowerTestDesc,
						func = function()
							if inTestMode then
								plugin:Close()
							else
								plugin:Test()
							end
						end,
						width = 1.5,
						order = 2,
						disabled = disabled,
					},
					lock = {
						type = "toggle",
						name = L.lock,
						desc = L.lockDesc,
						order = 3,
						disabled = disabled,
					},
					barHeader = {
						type = "header",
						name = L.yourPowerBar,
						order = 4,
					},
					barColor = {
						type = "color",
						name = L.barColor,
						get = function(info)
							return db.barColor[1], db.barColor[2], db.barColor[3], db.barColor[4]
						end,
						set = function(info, r, g, b, a)
							db.barColor = {r, g, b, a}
							plugin:RestyleWindow()
						end,
						hasAlpha = true,
						width = 1.5,
						order = 5,
						disabled = disabled,
					},
					barTextColor = {
						type = "color",
						name = L.barTextColor,
						get = function(info)
							return db.barTextColor[1], db.barTextColor[2], db.barTextColor[3]
						end,
						set = function(info, r, g, b)
							db.barTextColor = {r, g, b}
							plugin:RestyleWindow()
						end,
						width = 1.5,
						order = 6,
						disabled = disabled,
					},
					generalHeader = {
						type = "header",
						name = L.general,
						order = 7,
					},
					backgroundColor = {
						type = "color",
						name = L.background,
						get = function(info)
							return db.backgroundColor[1], db.backgroundColor[2], db.backgroundColor[3], db.backgroundColor[4]
						end,
						set = function(info, r, g, b, a)
							db.backgroundColor = {r, g, b, a}
							plugin:RestyleWindow()
						end,
						hasAlpha = true,
						order = 8,
						disabled = disabled,
					},
					fontName = {
						type = "select",
						name = L.font,
						order = 9,
						values = LibSharedMedia:List(FONT),
						itemControl = "DDI-Font",
						get = function()
							for i, v in next, LibSharedMedia:List(FONT) do
								if v == db.fontName then return i end
							end
						end,
						set = function(_, value)
							db.fontName = LibSharedMedia:List(FONT)[value]
							plugin:RestyleWindow()
						end,
						width = 2,
						disabled = disabled,
					},
					monochrome = {
						type = "toggle",
						name = L.monochrome,
						desc = L.monochromeDesc,
						order = 10,
						disabled = disabled,
					},
					fontSize = {
						type = "range",
						name = L.fontSize,
						desc = L.fontSizeDesc,
						order = 11,
						softMax = 25, max = 200, min = 10, step = 1,
						disabled = disabled,
					},
					outline = {
						type = "select",
						name = L.outline,
						order = 12,
						values = {
							NONE = L.none,
							OUTLINE = L.thin,
							THICKOUTLINE = L.thick,
						},
						disabled = disabled,
					},
					additionalWidth = {
						type = "range",
						name = L.additionalWidth,
						desc = L.additionalSizeDesc,
						order = 13,
						max = 100, softMax = 50,
						min = 0,
						step = 1,
						width = 1.5,
						disabled = disabled,
					},
					additionalHeight = {
						type = "range",
						name = L.additionalHeight,
						desc = L.additionalSizeDesc,
						order = 14,
						max = 100, softMax = 20,
						min = 0,
						step = 1,
						width = 1.5,
						disabled = disabled,
					},
					resetHeader = {
						type = "header",
						name = "",
						order = 15,
					},
					reset = {
						type = "execute",
						name = L.resetAll,
						desc = L.resetAltPowerDesc,
						func = function()
							plugin:Contract()
							plugin.db:ResetProfile()
							updateProfile()
						end,
						order = 16,
					},
					spacer = {
						type = "description",
						name = "\n\n",
						order = 17,
						width = "full",
						fontSize = "medium",
					},
					disabled = {
						type = "toggle",
						name = L.disabled,
						desc = L.disableAltPowerDesc,
						order = 18,
						set = function(_, value)
							db.disabled = value
							if value then
								plugin:Close()
							end
						end,
						confirm = function(_, value)
							if value then
								return L.disableDesc:format(L.altPowerTitle)
							end
						end,
					},
				},
			},
			exactPositioning = {
				type = "group",
				name = L.positionExact,
				order = 2,
				disabled = disabled,
				args = {
					posx = {
						type = "range",
						name = L.positionX,
						desc = L.positionDesc,
						min = -2048,
						max = 2048,
						step = 1,
						order = 1,
						width = "full",
						get = function()
							return db.position[3]
						end,
						set = function(_, value)
							db.position[3] = value
							plugin:RestyleWindow()
						end,
					},
					posy = {
						type = "range",
						name = L.positionY,
						desc = L.positionDesc,
						min = -2048,
						max = 2048,
						step = 1,
						order = 2,
						width = "full",
						get = function()
							return db.position[4]
						end,
						set = function(_, value)
							db.position[4] = value
							plugin:RestyleWindow()
						end,
					},
				},
			},
		},
	}
end

-------------------------------------------------------------------------------
-- Initialization
--

function plugin:OnPluginEnable()
	self:RegisterMessage("BigWigs_StartSyncingPower")
	self:RegisterMessage("BigWigs_ShowAltPower")
	self:RegisterMessage("BigWigs_HideAltPower", "Close")
	self:RegisterMessage("BigWigs_OnBossDisable")
	self:RegisterMessage("BigWigs_OnBossWipe", "BigWigs_OnBossDisable")
	self:RegisterMessage("BigWigs_ProfileUpdate", updateProfile)
	updateProfile()
end

function plugin:OnPluginDisable()
	self:Close()
end

-------------------------------------------------------------------------------
-- Event Handlers
--

do
	-- Realistically this should never fire during an encounter, we're just compensating for someone leaving the group
	-- whilst the display is shown (more likely to happen in LFR). The display should not be shown outside of an encounter
	-- where the event seems to fire frequently, which would make this very inefficient.
	local function GROUP_ROSTER_UPDATE()
		local players = GetNumGroupMembers()
		if players ~= maxPlayers then
			if updater then plugin:CancelTimer(updater) end

			if IsInGroup() then
				if repeatSync then
					syncPowerList = {}
					syncPowerMaxList = {}
				end
				maxPlayers = players
				unitList = IsInRaid() and plugin:GetRaidList() or plugin:GetPartyList()
				powerList, powerMaxList, sortedUnitList, roleColoredList = {}, {}, {}, {}

				local UnitClass, UnitGroupRolesAssigned = UnitClass, UnitGroupRolesAssigned
				local colorTbl = CUSTOM_CLASS_COLORS or RAID_CLASS_COLORS
				for i = 1, players do
					local unit = unitList[i]
					sortedUnitList[i] = unit

					local name = plugin:UnitName(unit, true) or "???"
					local _, class = UnitClass(unit)
					local tbl = class and colorTbl[class] or GRAY_FONT_COLOR
					roleColoredList[unit] = ("%s|cFF%02x%02x%02x%s|r"):format(roleIcons[UnitGroupRolesAssigned(unit)], tbl.r*255, tbl.g*255, tbl.b*255, name)
				end
			end
			updater = plugin:ScheduleRepeatingTimer(UpdateDisplay, 1)
		end

		if repeatSync then
			plugin:RosterUpdateForHiddenDisplay() -- Maybe a player logged back on after a DC, force sync refresh to send them our power.
		end
	end

	do
		-- USE THIS CALLBACK TO SKIN THIS WINDOW! NO NEED FOR UGLY HAX! E.g.
		-- local addonName, addonTable = ...
		-- if BigWigsLoader then
		-- 	BigWigsLoader.RegisterMessage(addonTable, "BigWigs_FrameCreated", function(event, frame, name) print(name.." frame created.") end)
		-- end
		display = CreateFrame("Frame", nil, UIParent)
		display:SetSize(230, 80)
		display:SetClampedToScreen(true)
		display:EnableMouse(true)
		display:SetFrameStrata("MEDIUM")
		display:SetFixedFrameStrata(true)
		display:SetFrameLevel(125)
		display:SetFixedFrameLevel(true)
		display:RegisterForDrag("LeftButton")
		display:SetScript("OnDragStart", function(self)
			if self:IsMovable() then
				self:StartMoving()
			end
		end)
		display:SetScript("OnDragStop", function(self)
			self:StopMovingOrSizing()
			local point, _, relPoint, x, y = self:GetPoint()
			db.position[1] = point
			db.position[2] = relPoint
			db.position[3] = x
			db.position[4] = y
			plugin:UpdateGUI() -- Update X/Y if GUI is open.
		end)
		display:Hide()

		local bg = display:CreateTexture()
		bg:SetPoint("BOTTOMLEFT", display, "BOTTOMLEFT")
		bg:SetPoint("BOTTOMRIGHT", display, "BOTTOMRIGHT")
		display.bg = bg

		local close = CreateFrame("Button", nil, display)
		close:SetPoint("BOTTOMRIGHT", display, "TOPRIGHT", -2, 2)
		close:SetSize(16, 16)
		close:SetNormalTexture("Interface\\AddOns\\BigWigs\\Media\\Icons\\close")
		close:SetScript("OnClick", function()
			if inTestMode then
				plugin:Close()
			else
				display:Hide() -- Don't call :Close as that would disable syncing (if enabled)
				BigWigs:Print(L.toggleDisplayPrint)
			end
		end)

		local expand = CreateFrame("Button", nil, display)
		expand:SetPoint("BOTTOMLEFT", display, "TOPLEFT", 2, 2)
		expand:SetSize(16, 16)
		expand:SetNormalTexture("Interface\\AddOns\\BigWigs\\Media\\Icons\\arrows_down")
		expand:SetScript("OnClick", function()
			if db.expanded then
				plugin:Contract()
			else
				plugin:Expand()
			end
		end)
		display.expand = expand

		local barDrag = CreateFrame("Frame", nil, display)
		barDrag:SetClampedToScreen(true)
		barDrag:EnableMouse(true)
		barDrag:SetFrameStrata("MEDIUM")
		barDrag:SetFixedFrameStrata(true)
		barDrag:SetFrameLevel(125)
		barDrag:SetFixedFrameLevel(true)
		barDrag:RegisterForDrag("LeftButton")
		barDrag:SetScript("OnDragStart", function()
			if display:IsMovable() then
				display:StartMoving()
			end
		end)
		barDrag:SetScript("OnDragStop", function()
			display:StopMovingOrSizing()
			local point, _, relPoint, x, y = display:GetPoint()
			db.position[1] = point
			db.position[2] = relPoint
			db.position[3] = x
			db.position[4] = y
			plugin:UpdateGUI() -- Update X/Y if GUI is open.
		end)
		barDrag:SetPoint("TOPLEFT", expand, "TOPRIGHT", 4, 0)
		barDrag:SetPoint("BOTTOMRIGHT", close, "BOTTOMLEFT", -4, 0)

		local header = barDrag:CreateFontString()
		header:SetShadowOffset(1, -1)
		header:SetTextColor(1,0.82,0,1)
		header:SetPoint("BOTTOM", display, "TOP", 0, 4)
		header:SetHeight(16)
		bg:SetPoint("TOP", header, "TOP", 0, 2)
		display.title = header

		local bar = barDrag:CreateTexture(nil, nil, nil, 1) -- above background
		bar:SetPoint("LEFT", expand, "RIGHT", 4, 0)
		bar:SetPoint("BOTTOM", header, "BOTTOM")
		bar:SetPoint("TOP", header, "TOP")
		display.bar = bar

		display.text = {}
		for i = 1, 26 do
			local text = display:CreateFontString()
			text:SetShadowOffset(1, -1)
			text:SetTextColor(1,0.82,0,1)
			text:SetSize(115, 16)
			text:SetJustifyH("LEFT")
			if i == 1 then
				text:SetPoint("TOPLEFT", display, "TOPLEFT", 5, -1)
			elseif i % 2 == 0 then
				text:SetPoint("LEFT", display.text[i-1], "RIGHT")
			else
				text:SetPoint("TOP", display.text[i-2], "BOTTOM")
			end
			display.text[i] = text
		end

		display:SetScript("OnEvent", GROUP_ROSTER_UPDATE)
	end

	-- This module is rarely used, and opened once during an encounter where it is.
	-- We will prefer on-demand variables over permanent ones.
	function plugin:BigWigs_ShowAltPower(event, module, title, sorting, sync)
		if db.disabled then return end

		self:RestyleWindow()
		self:Close()

		if sync then
			self:RegisterMessage("BigWigs_PluginComm")
		end

		display:RegisterEvent("GROUP_ROSTER_UPDATE")

		opener = module
		sortDir = sorting
		currentTitle = title
		maxPlayers = -1 -- Force an update via GROUP_ROSTER_UPDATE
		display:Show()
		GROUP_ROSTER_UPDATE()
		UpdateDisplay()
	end
end

do
	local classList = {"HUNTER","WARRIOR","ROGUE","MAGE","PRIEST","SHAMAN","WARLOCK","DEMONHUNTER","DEATHKNIGHT","DRUID","MONK","PALADIN"}
	local roleList = {"DAMAGER","TANK","DAMAGER","DAMAGER","HEALER","HEALER","DAMAGER","TANK","DAMAGER","HEALER","HEALER","DAMAGER"}
	local function testUpdate()
		if inTestMode then
			plugin:SimpleTimer(testUpdate, 3)
		else
			return
		end

		local amount = math.random(1,100)
		display.bar:SetWidth((amount/100) * (200+(db.additionalWidth*2)))
		display.title:SetFormattedText(L.yourPowerTest, amount)

		local colorTbl = CUSTOM_CLASS_COLORS or RAID_CLASS_COLORS
		local sortedUnitListTest, powerListTest = {}, {}
		local count = db.expanded and 26 or 10
		for i = 1, count do
			sortedUnitListTest[i] = i
			powerListTest[i] = math.random(1, 99)
		end
		tsort(sortedUnitListTest, function(x,y)
			local px, py = powerListTest[x], powerListTest[y]
			if px == py then
				return x > y
			else
				return px > py
			end
		end)
		for i = 1, count do
			local unitNumber = sortedUnitListTest[i]
			local tableSize = #classList
			local tableEntry = unitNumber % tableSize
			if tableEntry == 0 then tableEntry = tableSize end
			local class = classList[tableEntry]
			local role = roleList[tableEntry]
			local power = powerListTest[unitNumber]
			local r, g = colorize(power, 100)
			local name = (L.player):format(unitNumber)
			local classColorTbl = class and colorTbl[class] or GRAY_FONT_COLOR
			display.text[i]:SetFormattedText("|cFF%02x%02x00[%d]|r %s|cFF%02x%02x%02x%s|r", r, g, power, roleIcons[role], classColorTbl.r*255, classColorTbl.g*255, classColorTbl.b*255, name)
		end
	end
	function plugin:Test()
		if not inTestMode then
			self:Close()

			display:Show()
			inTestMode = true
			testUpdate()
		end
	end
end

do
	local function sortTbl(x,y)
		local px, py = powerList[x], powerList[y]
		if px == py then
			return x > y
		elseif sortDir == "AZ" then
			return px > py
		else
			return px < py
		end
	end

	function UpdateDisplay()
		local maxPower = UnitPowerMax("player", 10)
		if maxPower ~= 0 then
			local power = UnitPower("player", 10)
			local percent = power / UnitPowerMax("player", 10)
			display.bar:SetWidth(percent * (200+(db.additionalWidth*2)))
			display.title:SetFormattedText(L.yourAltPower, currentTitle, power)
		else
			display.bar:SetWidth(0)
			display.title:SetFormattedText(L.yourAltPower, currentTitle, 0)
		end

		if sortedUnitList then
			for i = 1, maxPlayers do
				local unit = unitList[i]
				-- If we don't have sync data (players not using BigWigs) use whatever (potentially incorrect) data Blizz gives us.
				powerList[unit] = syncPowerList and syncPowerList[unit] or UnitPower(unit, 10) -- Enum.PowerType.Alternate = 10
				powerMaxList[unit] = syncPowerMaxList and syncPowerMaxList[unit] or UnitPowerMax(unit, 10) -- Enum.PowerType.Alternate = 10
			end
			tsort(sortedUnitList, sortTbl)
			for i = 1, db.expanded and 26 or 10 do
				local unit = sortedUnitList[i]
				if unit then
					local power = powerList[unit]
					local powerMax = powerMaxList[unit]
					local r, g = colorize(power, powerMax)
					display.text[i]:SetFormattedText("|cFF%02x%02x00[%d]|r %s", r, g, power, roleColoredList[unit])
				else
					display.text[i]:SetText("")
				end
			end
		end
	end
end

function plugin:Expand()
	db.expanded = true
	display:SetHeight(210+(db.additionalHeight*13))
	display.expand:SetNormalTexture("Interface\\AddOns\\BigWigs\\Media\\Icons\\arrows_up")
	if not inTestMode then
		UpdateDisplay()
	end
end

function plugin:Contract()
	db.expanded = false
	display:SetHeight(82+(db.additionalHeight*5))
	display.expand:SetNormalTexture("Interface\\AddOns\\BigWigs\\Media\\Icons\\arrows_down")
	for i = 11, 26 do
		display.text[i]:SetText("")
	end
end

function plugin:Close()
	if repeatSync then
		self:UnregisterEvent("GROUP_ROSTER_UPDATE")
		self:CancelTimer(repeatSync)
		repeatSync = nil
	end

	if updater then self:CancelTimer(updater) end
	updater = nil
	display:UnregisterEvent("GROUP_ROSTER_UPDATE")
	display:Hide()
	self:UnregisterMessage("BigWigs_PluginComm")
	for i = 1, 26 do
		display.text[i]:SetText("")
	end

	powerList, powerMaxList, sortedUnitList, roleColoredList, syncPowerList, syncPowerMaxList = nil, nil, nil, nil, nil, nil
	unitList, opener, inTestMode, currentTitle = nil, nil, nil, nil
end

function plugin:BigWigs_OnBossDisable(_, module)
	if module == opener then
		self:Close()
	end
end

do
	local power = -1
	local powerMax = -1
	local function sendPower()
		local newPower = UnitPower("player", 10) -- Enum.PowerType.Alternate = 10
		local newPowerMax = UnitPowerMax("player", 10) -- Enum.PowerType.Alternate = 10
		if newPower ~= power then
			power = newPower
			plugin:Sync("AltPower", newPower, true)
		end
		if newPowerMax ~= powerMax then
			powerMax = newPowerMax
			plugin:Sync("AltPowerMax", newPowerMax)
		end
	end

	function plugin:RosterUpdateForHiddenDisplay()
		-- This is for people that don't show the AltPower display (event isn't registered to the display as it normally would be).
		-- It will force sending the current power for those that do have the display shown but just had their power list reset by a
		-- GROUP_ROSTER_UPDATE. Or someone DCd and is logging back on, so send an update.
		if not IsInGroup() then return end
		self:CancelTimer(repeatSync)
		power = -1
		repeatSync = self:ScheduleRepeatingTimer(sendPower, 1)
	end

	function plugin:BigWigs_StartSyncingPower(_, module)
		if not IsInGroup() then return end
		power = -1
		opener = module
		if not repeatSync then
			repeatSync = self:ScheduleRepeatingTimer(sendPower, 1)
			if display:IsShown() then
				syncPowerList = {}
				syncPowerMaxList = {}
			else
				self:RegisterEvent("GROUP_ROSTER_UPDATE", "RosterUpdateForHiddenDisplay")
			end
		end
	end

	function plugin:BigWigs_PluginComm(_, msg, amount, sender)
		if msg == "AltPower" then
			local curPower = tonumber(amount)
			if curPower then
				for i = 1, maxPlayers do
					local unit = unitList[i]
					if sender == self:UnitName(unit) then
						syncPowerList[unit] = curPower
						break
					end
				end
			end
		elseif msg == "AltPowerMax" then
			local curPowerMax = tonumber(amount)
			if curPowerMax then
				for i = 1, maxPlayers do
					local unit = unitList[i]
					if sender == self:UnitName(unit) then
						syncPowerMaxList[unit] = curPowerMax
						break
					end
				end
			end
		end
	end
end

-- We run this last to prevent the AltPower module breaking if some addon listening to this event causes an error
plugin:SendMessage("BigWigs_FrameCreated", display, "AltPower")
