local Keyboard, GetCurrentKeyBoardFocus, bitbor = ConsolePortKeyboard, GetCurrentKeyBoardFocus, bit.bor;
local IsShiftKeyDown, IsControlKeyDown, IsAltKeyDown = IsShiftKeyDown, IsControlKeyDown, IsAltKeyDown;
local Observer, _, env = CreateFrame('Frame'), ...;

function Observer:UpdateFocus()
	local focus = self.forceFrame;
	if focus == nil then
		focus = GetCurrentKeyBoardFocus()
	end

	local valid = focus
		and not focus:IsForbidden()
		and not focus:IsAnchoringRestricted()
		and not focus:GetAttribute(env.Attributes.HideKeyboard);
	local changed = focus ~= self.focusFrame;
	if changed then
		self.focusFrame = focus;
	end

	return focus, changed, valid;
end

function Observer:OnUpdate(elapsed)
	local focus, changed, valid = self:UpdateFocus()
	if changed then
		Keyboard:OnFocusChanged(valid and focus)
	end
	if not valid or not Keyboard:IsShown() then return end
	Keyboard:SetState(1 + bitbor(
		IsShiftKeyDown()   and 0x1 or 0,
		IsControlKeyDown() and 0x2 or 0,
		IsAltKeyDown()     and 0x4 or 0)
	);
	local text, pos = focus:GetText(), focus:GetUTF8CursorPosition()
	if text ~= self.focusText or pos ~= self.focusPos then
		self.focusText, self.focusPos = text, pos;
		Keyboard:OnTextChanged(text, pos, focus)
	end
end

function Keyboard:ForceFocus(frame)
	Observer.forceFrame = frame;
end

function env:ToggleObserver(enabled)
	Observer:SetScript('OnUpdate', enabled and Observer.OnUpdate or nil)
	if not enabled then
		Observer.focusFrame = nil;
	end
end