if not CPAPI.IsRetailVersion then return end;
local _, db = ...;
local Selector, Fade, FlyoutButtonMixin = CPAPI.EventHandler(ConsolePortSpellFlyout, {'SPELL_FLYOUT_UPDATE'}), db('Alpha/Fader'), {}

Selector:SetFrameRef('flyout', SpellFlyout)
Selector:Execute('this = self; that = self:GetFrameRef("flyout"); BUTTONS = newtable()')
Selector:SetAttribute('pressAndHoldAction', true)
Selector:SetAttribute('ClearAndHide', [[
	self:CallMethod('ClearInstantly')
	self:SetAttribute('type', nil)
	local owner = that:GetParent()
	owner:Hide()
	owner:Show()
]])
Selector:WrapScript(SpellFlyout, 'OnShow', [[
	if this:RunAttribute('SetBindingsForTriggers') then
		this:Show()
		self:SetAlpha(0)
	else
		self:SetAlpha(1)
		this:Hide()
	end
]])

Selector:WrapScript(SpellFlyout, 'OnHide', [[
	wipe(BUTTONS)
	this:ClearBindings()
	this:Hide()
	this:CallMethod('ReleaseAll')
	self:SetAlpha(1)
]])

Selector:WrapScript(Selector, 'PreClick', ([[
	local index = self:RunAttribute('GetIndex')
	local button = index and BUTTONS[index]
	if button then
		self:SetAttribute('%s', 'macro')
		self:SetAttribute('macrotext', '/click '..button:GetName())
	else
		self:RunAttribute('ClearAndHide')
	end
]]):format(CPAPI.ActionTypeRelease))

function Selector:SPELL_FLYOUT_UPDATE(...)
--	print(GetTime(), ...)
end

function Selector:OnDataLoaded(...)
	self:CreateFramePool('CPButtonTemplate', FlyoutButtonMixin, function(pool, self)
		self:Hide()
		self:ClearAllPoints()
		if self.OnClear then
			self:OnClear()
		end
	end)
	local sticks = db.Radial:GetStickStruct(db('radialPrimaryStick'))
	db.Radial:Register(self, 'SpellFlyout', {
		sticks = sticks;
		target = {sticks[1]};
		sizer  = [[
			wipe(BUTTONS)
			for i, child in ipairs(newtable(that:GetChildren())) do
				if child:IsVisible() then
					BUTTONS[#BUTTONS+1] = child
				end
			end
			local size = #BUTTONS;
		]];
	});
	self:OnAxisInversionChanged()
end

function Selector:OnAxisInversionChanged()
	self.axisInversion = db('radialCosineDelta')
end

function Selector:OnPrimaryStickChanged()
	local sticks = db.Radial:GetStickStruct(db('radialPrimaryStick'))
	self:SetInterrupt(sticks)
	self:SetIntercept({sticks[1]})
end

db:RegisterSafeCallback('Settings/radialCosineDelta', Selector.OnAxisInversionChanged, Selector)
db:RegisterSafeCallback('Settings/radialPrimaryStick', Selector.OnPrimaryStickChanged, Selector)

function Selector:OnInput(x, y, len, stick)
	self:SetFocusByIndex(self:GetIndexForPos(x, y, len, self:GetNumActive()))
	self:ReflectStickPosition(self.axisInversion * x, self.axisInversion * y, len, len > self:GetValidThreshold())
end

function Selector:OnBindingSet(btn, mod)
	if ( not mod ) then
		self.buttonTrigger = btn;
	end
end

function Selector:AddButton(i, size)
	local button = self:Acquire(i)
	local p, x, y = self:GetPointForIndex(i, size)
	button:RegisterForDrag('LeftButton')
	button:SetScript('OnDragStart', FlyoutButtonMixin.OnDragStart)
	button:SetPoint(p, x, self.axisInversion * y)
	button:Show()
	return button
end

function FlyoutButtonMixin:OnFocus(newFocus)
	self:SetChecked(true)
	if newFocus then
		local button = self:GetParent().buttonTrigger;
		if button then
			local device = db('Gamepad/Active')
			button = device and device:GetTooltipButtonPrompt(button, USE, 64)
		end
		self:StartFlash()
		GameTooltip_SetDefaultAnchor(GameTooltip, self)
		GameTooltip:SetSpellByID(self.spellID)
		if button then
			GameTooltip:AddLine(button)
		end
		GameTooltip:Show()
	end
end

function FlyoutButtonMixin:OnClear()
	self:SetChecked(false)
	self:StopFlash()
	if GameTooltip:IsOwned(self) then
		GameTooltip:Hide()
	end
end

function FlyoutButtonMixin:UpdateState(i, name)
	local proxy = _G[name]
	if not proxy or not proxy:IsVisible() then return false end
	self.offSpec = proxy.offSpec;
	self.spellID = proxy.spellID;
	self.spellName = proxy.spellName;
	self:SetIcon(proxy.icon:GetTexture())
	return true
end

function FlyoutButtonMixin:Update(offSpec, spellID, spellName, overrideSpellID)
	self.offSpec = offSpec;
	self.spellID = spellID;
	self.spellName = spellName;
	self:SetIcon(GetSpellTexture(overrideSpellID))
end

function FlyoutButtonMixin:OnDragStart()
	if (self.spellID) and not InCombatLockdown() then
		PickupSpell(self.spellID);
	end
end

-- signature: (self, flyoutID, parent, direction, distance, isActionBar, specID, showFullTooltip, reason)
hooksecurefunc(SpellFlyout, 'Toggle', function(flyout, flyoutID, _, _, _, isActionBar, specID)
	local self = Selector; self:ReleaseAll();
	if not flyout:IsShown() then return end

	-- BUG: Flyout sometimes clicks the wrong button
	local active, offSpec, _, _, numSlots, isKnown = {}, specID and (specID ~= 0), GetFlyoutInfo(flyoutID)
	for i=1, numSlots do
		local spellID, overrideSpellID, isKnown, spellName, slotSpecID = GetFlyoutSlotInfo(flyoutID, i)
		local visible = true

		-- ignore Call Pet spells if there isn't a pet in that slot
		local petIndex, petName = GetCallPetSpellInfo(spellID)
		if (isActionBar and petIndex and (not petName or petName == '')) then
			visible = false
		end

		-- show buttons
		if ( ((not offSpec or slotSpecID == 0) and visible and isKnown) or (offSpec and slotSpecID == specID) ) then
			active[#active+1] = {
				overrideSpellID = overrideSpellID;
				desaturated = offSpec;
				offSpec = offSpec;
				spellID = spellID;
				spellName = spellName;
			}
			-- TODO: all this
			-- SpellFlyoutButton_UpdateCooldown(button);
			-- SpellFlyoutButton_UpdateState(button);
			-- SpellFlyoutButton_UpdateUsable(button);
			-- SpellFlyoutButton_UpdateCount(button);
			-- SpellFlyoutButton_UpdateGlyphState(button, reason);
		end
	end
	for i, data in ipairs(active) do
		local button = self:AddButton(i, #active)
		button:SetIcon(GetSpellTexture(data.overrideSpellID))
		button:GetNormalTexture():SetDesaturated(data.offSpec)
		button.offSpec = data.offSpec;
		button.spellID = data.spellID;
		button.spellName = data.spellName;
		button:SetEnabledState(not data.offSpec)
		button:SetDescription(data.spellName)
	end
end)