// Decompiled with JetBrains decompiler
// Type: Microsoft.Iris.UI.UIClass
// Assembly: UIX, Version=4.8.0.0, Culture=neutral, PublicKeyToken=ddd0da4d3e678217
// MVID: A56C6C9D-B7F6-46A9-8BDE-B3D9B8D60B11
// Assembly location: C:\Program Files\Zune\UIX.dll

using Microsoft.Iris.Accessibility;
using Microsoft.Iris.Animations;
using Microsoft.Iris.Debug;
using Microsoft.Iris.Drawing;
using Microsoft.Iris.Input;
using Microsoft.Iris.InputHandlers;
using Microsoft.Iris.Layout;
using Microsoft.Iris.Library;
using Microsoft.Iris.Markup;
using Microsoft.Iris.Navigation;
using Microsoft.Iris.Render;
using Microsoft.Iris.RenderAPI.Drawing;
using Microsoft.Iris.Session;
using Microsoft.Iris.ViewItems;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;

namespace Microsoft.Iris.UI
{
    public class UIClass :
      Microsoft.Iris.Library.TreeNode,
      ICookedInputSite,
      IMarkupTypeBase,
      IDisposableOwner,
      INotifyObject,
      ISchemaInfo
    {
        public const string UIStateReservedSymbolName = "UI";
        private BitVector32 _bits;
        private Host _ownerHost;
        private int _uiIDFactory;
        private ViewItem _rootItem;
        private InputHandlerList _inputHandlers;
        private UIClassTypeSchema _typeSchema;
        private Dictionary<object, object> _storage;
        private Vector<IDisposableObject> _disposables;
        private MarkupListeners _listeners;
        private NotifyService _notifier = new NotifyService();
        private ScriptRunScheduler _scriptRunScheduler = new ScriptRunScheduler();
        private static DeferredHandler s_executePendingScriptsHandler = new DeferredHandler(ExecutePendingScripts);
        private static readonly FocusStateHandler s_updateMouseFocusStates = new FocusStateHandler(UpdateMouseFocusStates);
        private static readonly FocusStateHandler s_updateKeyFocusStates = new FocusStateHandler(UpdateKeyFocusStates);
        private static readonly DataCookie s_cursorProperty = DataCookie.ReserveSlot();
        private static readonly DataCookie s_cursorOverrideProperty = DataCookie.ReserveSlot();
        private static readonly DataCookie s_accProxyProperty = DataCookie.ReserveSlot();
        private static readonly DataCookie s_pendingFocusRestoreProperty = DataCookie.ReserveSlot();
        private static readonly DataCookie s_focusInterestTargetProperty = DataCookie.ReserveSlot();
        private static readonly DataCookie s_focusInterestTargetMarginsProperty = DataCookie.ReserveSlot();
        private static readonly EventCookie s_descendantKeyFocusChangedEvent = EventCookie.ReserveSlot();
        private static readonly EventCookie s_descendantMouseFocusChangedEvent = EventCookie.ReserveSlot();
        private EventContext _eventContext;

        public UIClass(UIClassTypeSchema type)
        {
            _typeSchema = type;
            _storage = new Dictionary<object, object>(type.TotalPropertiesAndLocalsCount);
            _bits = new BitVector32();
            SetBit(Bits.Flippable, true);
            SetBit(Bits.CreateInterestOnFocus, true);
            SetBit(Bits.KeyFocusOnMouseDown, true);
            SetBit(Bits.AllowDoubleClicks, true);
            SetBit(Bits.Enabled, true);
            SetBit(Bits.ScriptEnabled, true);
        }

        public bool Initialized => GetBit(Bits.Initialized);

        public void RegisterDisposable(IDisposableObject disposable)
        {
            if (disposable is ViewItem || disposable is InputHandler)
                return;
            if (_disposables == null)
                _disposables = new Vector<IDisposableObject>();
            _disposables.Add(disposable);
        }

        public bool UnregisterDisposable(ref IDisposableObject disposable)
        {
            if (!(disposable is ViewItem) && !(disposable is InputHandler) && _disposables != null)
            {
                int index = _disposables.IndexOf(disposable);
                if (index != -1)
                {
                    disposable = _disposables[index];
                    _disposables.RemoveAt(index);
                    return true;
                }
            }
            return false;
        }

        protected override void OnDispose()
        {
            _typeSchema.RunFinalEvaluates(this);
            base.OnDispose();
            if (Initialized)
                AccessibleProxy.NotifyDestroyed(this);
            SetBit(Bits.ScriptEnabled, false);
            if (_listeners != null)
            {
                _listeners.Dispose(this);
                _listeners = null;
            }
            _notifier.ClearListeners();
            if (_rootItem != null)
            {
                DisposeViewItemTree(_rootItem);
                _rootItem = null;
            }
            DisposeInputHandlers();
            _storage.Clear();
            if (_disposables != null)
            {
                for (int index = 0; index < _disposables.Count; ++index)
                    _disposables[index].Dispose(this);
            }
            RemoveEventHandlers(s_descendantMouseFocusChangedEvent);
            RemoveEventHandlers(s_descendantKeyFocusChangedEvent);
        }

        private void DisposeInputHandlers()
        {
            if (_inputHandlers == null)
                return;
            foreach (DisposableObject inputHandler in _inputHandlers)
                inputHandler.Dispose(this);
        }

        internal void DisposeViewItemTree(ViewItem item)
        {
            if (item.UI != this)
                return;
            ViewItem nextSibling;
            for (ViewItem viewItem = (ViewItem)item.FirstChild; viewItem != null; viewItem = nextSibling)
            {
                nextSibling = (ViewItem)viewItem.NextSibling;
                DisposeViewItemTree(viewItem);
            }
            item.Dispose(this);
        }

        internal void DestroyVisualTree(ViewItem subjectItem) => DestroyVisualTree(subjectItem, false);

        internal void DestroyVisualTree(ViewItem subjectItem, bool markLayoutOutputDirty) => DestroyVisualTree(subjectItem, true, markLayoutOutputDirty);

        private void DestroyVisualTree(
          ViewItem subjectItem,
          bool allowAnimations,
          bool markLayoutOutputDirty)
        {
            OrphanedVisualCollection orphans = new OrphanedVisualCollection(UISession.AnimationManager);
            DestroyVisualTreeWorker(subjectItem, orphans, allowAnimations, markLayoutOutputDirty);
            orphans.OnLayoutApplyComplete();
        }

        private void DestroyVisualTreeWorker(
          ViewItem subjectItem,
          OrphanedVisualCollection orphans,
          bool allowAnimations,
          bool markLayoutOutputDirty)
        {
            subjectItem.RendererVisual.MouseOptions = MouseOptions.None;
            foreach (ViewItem child in subjectItem.Children)
            {
                if (child.HasVisual)
                    DestroyVisualTreeWorker(child, orphans, allowAnimations, markLayoutOutputDirty);
            }
            if (markLayoutOutputDirty)
                subjectItem.MarkLayoutOutputDirty(false);
            if (allowAnimations)
                AnimateDestroyedVisual(subjectItem, orphans);
            if (subjectItem == subjectItem.UI.RootItem && subjectItem.UI.RootItem.HasVisual)
                subjectItem.UI.RevalidateUsage(true, true);
            subjectItem.OrphanVisuals(orphans);
        }

        [Conditional("DEBUG")]
        private static void DEBUG_VerifyVisualTreeDestroyed(ViewItem vi)
        {
            foreach (ViewItem child in vi.Children)
                ;
        }

        public int AllocateUIID(ViewItem item) => _uiIDFactory++;

        public override bool IsRoot => Zone.RootUI == this;

        public UIClass Parent => (UIClass)base.Parent;

        protected override void OnZoneAttached()
        {
            base.OnZoneAttached();
            RevalidateUsage(true, false);
            bool enabledFlag = true;
            UIClass parent = Parent;
            if (parent != null)
                enabledFlag = parent.FullyEnabled;
            DeliverFullyEnabled(enabledFlag);
            if (_inputHandlers != null)
            {
                foreach (InputHandler inputHandler in _inputHandlers)
                    inputHandler.OnZoneAttached();
            }
            AccessibleProxy.NotifyTreeChanged(this);
        }

        protected override void OnZoneDetached()
        {
            base.OnZoneDetached();
            ChangeBit(Bits.AppFullyEnabled, false);
            RevalidateUsage(true, true);
            if (_inputHandlers != null)
            {
                foreach (InputHandler inputHandler in _inputHandlers)
                    inputHandler.OnZoneDetached();
            }
            if (_rootItem == null || !_rootItem.HasVisual)
                return;
            DestroyVisualTree(_rootItem);
        }

        protected override void OnChildrenChanged()
        {
            base.OnChildrenChanged();
            if (!IsZoned)
                return;
            AccessibleProxy.NotifyTreeChanged(this);
        }

        public bool HasAccessibleProxy => GetBit(Bits.HasAccProxy);

        protected virtual AccessibleProxy OnCreateAccessibleProxy(
          UIClass ui,
          Accessible data)
        {
            return new AccessibleProxy(ui, data);
        }

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        public AccessibleProxy AccessibleProxy
        {
            get
            {
                AccessibleProxy accessibleProxy;
                if (HasAccessibleProxy)
                {
                    accessibleProxy = (AccessibleProxy)GetData(s_accProxyProperty);
                }
                else
                {
                    Accessible data = null;
                    foreach (KeyValuePair<object, object> keyValuePair in _storage)
                    {
                        if (keyValuePair.Value is Accessible accessible)
                        {
                            data = accessible;
                            break;
                        }
                    }
                    if (data == null)
                        data = new Accessible();
                    accessibleProxy = OnCreateAccessibleProxy(this, data);
                    SetData(s_accProxyProperty, accessibleProxy);
                    SetBit(Bits.HasAccProxy, true);
                }
                return accessibleProxy;
            }
        }

        internal static bool ShouldPlayAnimation(IAnimationProvider provider)
        {
            if (provider == null)
                return false;
            bool flag = true;
            if (provider is NonIntensiveAnimation)
                flag = false;
            return AnimationSystem.Enabled || !flag;
        }

        private void AnimateNewVisual(
          ViewItem vi,
          Vector3 startPosVector,
          Vector2 startSizeVector,
          Vector3 startScaleVector,
          Rotation startRotation)
        {
            vi.VisualPosition = startPosVector;
            vi.VisualSize = startSizeVector;
            vi.VisualScale = startScaleVector;
            vi.VisualRotation = startRotation;
            AnimateNewVisual(vi);
        }

        private void AnimateNewVisual(ViewItem vi)
        {
            vi.PlayShowAnimation();
            if (!DirectKeyFocus)
                return;
            vi.PlayAnimation(AnimationEventType.GainFocus);
        }

        private void AnimateDestroyedVisual(ViewItem vi, OrphanedVisualCollection orphans) => vi.PlayHideAnimation(orphans);

        private void AnimateVisual(
          ViewItem vi,
          Vector3 newPosVector,
          Vector2 newSizeVector,
          Vector3 newScaleVector,
          Rotation newRotation)
        {
            Vector3 visualPosition = vi.VisualPosition;
            Vector2 visualSize = vi.VisualSize;
            Vector3 visualScale = vi.VisualScale;
            Rotation visualRotation = vi.VisualRotation;
            float visualAlpha = vi.VisualAlpha;
            AnimationArgs args = new AnimationArgs(vi, visualPosition, visualSize, visualScale, visualRotation, visualAlpha, newPosVector, newSizeVector, newScaleVector, newRotation, visualAlpha);
            if (visualPosition != newPosVector)
                vi.ApplyAnimatableValue(AnimationEventType.Move, ref args);
            if (visualSize != newSizeVector)
                vi.ApplyAnimatableValue(AnimationEventType.Size, ref args);
            if (visualScale != newScaleVector)
                vi.ApplyAnimatableValue(AnimationEventType.Scale, ref args);
            if (!(visualRotation != newRotation))
                return;
            vi.ApplyAnimatableValue(AnimationEventType.Rotate, ref args);
        }

        private void TryToPlayAnimationOnTree(ViewItem vi, AnimationEventType type)
        {
            if (!vi.HasVisual)
                return;
            vi.PlayAnimation(type);
            foreach (ViewItem child in vi.Children)
            {
                if (child.UI == this)
                    TryToPlayAnimationOnTree(child, type);
            }
        }

        internal object SaveKeyFocus()
        {
            if (KeyFocus)
            {
                UIClass keyFocusDescendant = KeyFocusDescendant;
                if (keyFocusDescendant != null && keyFocusDescendant.RootItem != null)
                    return new UIClass.SavedFocusState(keyFocusDescendant.RootItem);
            }
            return null;
        }

        internal void RestoreKeyFocus(object obj) => RestoreKeyFocus(obj, true);

        internal void RestoreKeyFocus(object obj, bool allowDeferralFlag)
        {
            if (!(obj is UIClass.SavedFocusState focusState))
                return;
            ViewItem resultItem;
            ViewItemID failedComponent;
            switch (RootItem.FindChildFromPath(focusState.Path, out resultItem, out failedComponent))
            {
                case FindChildResult.Success:
                    if (resultItem == null)
                        break;
                    UIClass ui = resultItem.UI;
                    if (!HasDescendant(ui) || !ui.IsKeyFocusable())
                        break;
                    resultItem.NavigateInto(focusState.FocusIsDefault);
                    break;
                case FindChildResult.PotentiallyFaultIn:
                    if (!allowDeferralFlag)
                        break;
                    new UIClass.DeferredKeyFocusRestoreHelper(this, focusState).FaultInChild(resultItem, failedComponent);
                    break;
            }
        }

        private UIClass.DeferredKeyFocusRestoreHelper PendingFocusRestore
        {
            get => GetData(s_pendingFocusRestoreProperty) as UIClass.DeferredKeyFocusRestoreHelper;
            set => SetData(s_pendingFocusRestoreProperty, value);
        }

        private static bool CheckHandled(InputInfo info, InputHandler inputHandler) => info.Handled;

        public void SetAreaOfInterest(AreaOfInterestID id) => SetAreaOfInterest(id, RootItem, Inset.Zero);

        public void SetAreaOfInterest(AreaOfInterestID id, ViewItem target, Inset margins) => target.SetAreaOfInterest(id, margins);

        public void ClearAreaOfInterest(AreaOfInterestID id) => ClearAreaOfInterest(id, RootItem);

        public void ClearAreaOfInterest(AreaOfInterestID id, ViewItem target) => target.ClearAreaOfInterest(id);

        internal void UpdateMouseHandling(ViewItem childItem)
        {
            if (childItem == null)
            {
                childItem = RootItem;
                if (childItem == null)
                    return;
            }
            if (!childItem.HasVisual)
                return;
            MouseOptions mouseOptions = MouseOptions.None;
            if (!childItem.IsOffscreen && childItem.AllowMouseInput && (childItem != RootItem || IsInputBranchEnabled() && InputEnabled))
                mouseOptions |= MouseOptions.Traversable;
            if (childItem.MouseInteractive && MouseInteractive)
                mouseOptions |= MouseOptions.Hittable;
            if (childItem == RootItem)
                mouseOptions |= MouseOptions.Returnable;
            if (childItem.ClipMouse)
                mouseOptions |= MouseOptions.ClipChildren;
            childItem.RendererVisual.MouseOptions = mouseOptions;
        }

        internal void OnHostVisibilityChanged()
        {
            OnInputEnabledChanged();
            AccessibleProxy.NotifyVisibilityChange(this, Visible);
        }

        public IList EnsureInputHandlerStorage()
        {
            if (_inputHandlers == null)
                _inputHandlers = new InputHandlerList();
            return _inputHandlers;
        }

        internal bool Visible => IsRoot || _ownerHost.HasVisual;

        internal bool InputEnabled
        {
            get
            {
                if (IsRoot)
                    return true;
                return Visible && _ownerHost.InputEnabled;
            }
        }

        public bool Enabled
        {
            get => GetBit(Bits.Enabled);
            set
            {
                if (!ChangeBit(Bits.Enabled, value))
                    return;
                UpdateMouseHandling(null);
                RevalidateUsage(true, !value);
                FireNotification(NotificationID.Enabled);
                if (!IsZoned || Parent != null && !Parent.FullyEnabled)
                    return;
                NotifyFullyEnabledChange();
            }
        }

        private bool HostEnabled => _ownerHost == null ? IsValid : _ownerHost.InputEnabled;

        public bool FullyEnabled => GetBit(Bits.AppFullyEnabled);

        public bool DirectMouseFocus => GetBit(Bits.DirectMouseFocus);

        public bool MouseFocus => GetBit(Bits.MouseFocus);

        public bool DirectKeyFocus => GetBit(Bits.DirectKeyFocus);

        public bool KeyFocus => GetBit(Bits.KeyFocus);

        public UIClass KeyFocusDescendant
        {
            get
            {
                //uiClass = (UIClass)null;
                if (KeyFocus && UISession.InputManager.RawInstantaneousKeyFocus is UIClass uiClass)
                {
                    if (uiClass.IsDisposed)
                        return null;
                    else
                        return uiClass;
                }
                else
                {
                    return null;
                }
            }
        }

        public uint PaintOrder
        {
            get => _ownerHost.Layer;
            set => _ownerHost.Layer = value;
        }

        public bool CreateInterestOnFocus
        {
            get => GetBit(Bits.CreateInterestOnFocus);
            set
            {
                if (!ChangeBit(Bits.CreateInterestOnFocus, value))
                    return;
                FireNotification(NotificationID.CreateInterestOnFocus);
            }
        }

        public ViewItem FocusInterestTarget
        {
            get => (ViewItem)GetData(s_focusInterestTargetProperty);
            set
            {
                if (FocusInterestTarget == value)
                    return;
                SetData(s_focusInterestTargetProperty, value);
                FireNotification(NotificationID.FocusInterestTarget);
            }
        }

        public Inset FocusInterestTargetMargins
        {
            get
            {
                object data = GetData(s_focusInterestTargetMarginsProperty);
                return data != null ? (Inset)data : Inset.Zero;
            }
            set
            {
                if (!(FocusInterestTargetMargins != value))
                    return;
                SetData(s_focusInterestTargetMarginsProperty, value);
                FireNotification(NotificationID.FocusInterestTargetMargins);
            }
        }

        public CursorID Cursor
        {
            get
            {
                CursorID cursorId = CursorID.NotSpecified;
                object data = GetData(s_cursorProperty);
                if (data != null)
                    cursorId = (CursorID)data;
                return cursorId;
            }
            set
            {
                if (Cursor == value)
                    return;
                SetData(s_cursorProperty, value);
                FireNotification(NotificationID.Cursor);
                if (!IsZoned || OverrideCursor != CursorID.NotSpecified)
                    return;
                Zone.UpdateCursor(this);
            }
        }

        private CursorID OverrideCursor
        {
            get
            {
                CursorID cursorId = CursorID.NotSpecified;
                object data = GetData(s_cursorOverrideProperty);
                if (data != null)
                    cursorId = (CursorID)data;
                return cursorId;
            }
            set
            {
                CursorID overrideCursor = OverrideCursor;
                if (overrideCursor == value)
                    return;
                SetData(s_cursorOverrideProperty, value);
                if (!IsZoned || overrideCursor == CursorID.NotSpecified && value == Cursor)
                    return;
                Zone.UpdateCursor(this);
            }
        }

        public CursorID EffectiveCursor
        {
            get
            {
                CursorID cursorId = OverrideCursor;
                if (cursorId == CursorID.NotSpecified)
                    cursorId = Cursor;
                return cursorId;
            }
        }

        public void UpdateCursor()
        {
            CursorID cursorId = CursorID.NotSpecified;
            if (_inputHandlers != null)
            {
                foreach (InputHandler inputHandler in _inputHandlers)
                {
                    if (inputHandler.Enabled)
                    {
                        cursorId = inputHandler.GetCursor();
                        if (cursorId != CursorID.NotSpecified)
                            break;
                    }
                }
            }
            OverrideCursor = cursorId;
        }

        public bool KeyFocusOnMouseEnter
        {
            get => GetBit(Bits.KeyFocusOnMouseEnter);
            set => SetBit(Bits.KeyFocusOnMouseEnter, value);
        }

        public bool KeyFocusOnMouseDown
        {
            get => GetBit(Bits.KeyFocusOnMouseDown);
            set => SetBit(Bits.KeyFocusOnMouseDown, value);
        }

        public bool AllowDoubleClicks
        {
            get => GetBit(Bits.AllowDoubleClicks);
            set => SetBit(Bits.AllowDoubleClicks, value);
        }

        internal void NotifyFullyEnabledChange() => Zone.ScheduleFullyEnabledChangeNotifications();

        internal void DeliverFullyEnabled(bool enabledFlag)
        {
            if (!Enabled || !HostEnabled)
                enabledFlag = false;
            if (ChangeBit(Bits.AppFullyEnabled, enabledFlag))
                FireNotification(NotificationID.FullyEnabled);
            foreach (UIClass child in Children)
                child.DeliverFullyEnabled(enabledFlag);
        }

        public void EnableRawInput(bool enableFlag)
        {
            if (!ChangeBit(Bits.RawInputDisabled, !enableFlag))
                return;
            UpdateMouseHandling(null);
            RevalidateUsage(true, !enableFlag);
        }

        public bool IsInputBranchEnabled() => GetBit(Bits.Enabled) && !GetBit(Bits.RawInputDisabled);

        public bool IsEligibleForInput() => IsEligibleForInput(out UIClass _);

        public bool IsEligibleForInput(out UIClass failurePoint)
        {
            failurePoint = null;
            UIClass uiClass = this;
            while (true)
            {
                failurePoint = uiClass;
                if (uiClass.IsInputBranchEnabled() && uiClass.InputEnabled)
                {
                    UIClass parent = uiClass.Parent;
                    if (parent != null)
                        uiClass = parent;
                    else
                        goto label_5;
                }
                else
                    break;
            }
            return false;
        label_5:
            failurePoint = null;
            return IsZoned;
        }

        public bool MouseInteractive
        {
            get => GetBit(Bits.MouseInteractive);
            set => SetMouseInteractive(value, false);
        }

        public void SetMouseInteractive(bool value, bool fromScript)
        {
            if (fromScript)
                SetBit(Bits.MouseInteractiveSet, true);
            if (!fromScript && GetBit(Bits.MouseInteractiveSet) || !ChangeBit(Bits.MouseInteractive, value))
                return;
            if (value && _rootItem != null && !HasMouseInteractiveContent())
                _rootItem.MouseInteractive = true;
            UpdateMouseHandling(null);
            RevalidateUsage(false, !value);
            FireNotification(NotificationID.MouseInteractive);
            if (!DebugOutlines.Enabled)
                return;
            DebugOutlines.NotifyInteractivityChange(Host);
        }

        public bool IsMouseFocusable() => MouseInteractive && IsEligibleForInput();

        public bool KeyInteractive
        {
            get => GetBit(Bits.KeyInteractive);
            set
            {
                if (!ChangeBit(Bits.KeyInteractive, value))
                    return;
                RevalidateUsage(false, !value);
                FireNotification(NotificationID.KeyInteractive);
                if (!DebugOutlines.Enabled)
                    return;
                DebugOutlines.NotifyInteractivityChange(Host);
            }
        }

        public event SessionInputHandler SessionInput
        {
            add => Zone.SessionInput += value;
            remove => Zone.SessionInput -= value;
        }

        public bool HostActivated => Zone.Form.ActivationState;

        public bool IsKeyFocusable() => KeyInteractive && IsEligibleForInput();

        public UIClass FindKeyFocusableAncestor()
        {
            if (!IsValid)
                return null;
            UIClass uiClass;
            for (uiClass = this; uiClass != null; uiClass = uiClass.Parent)
            {
                UIClass failurePoint = null;
                if (uiClass.KeyInteractive && uiClass.IsEligibleForInput(out failurePoint))
                    return uiClass;
                if (failurePoint != null)
                    uiClass = failurePoint;
            }
            return uiClass;
        }

        public void NavigateInto() => NavigateInto(false);

        public void NavigateInto(bool isDefault) => RequestKeyFocus(isDefault ? KeyFocusReason.Default : KeyFocusReason.Other);

        public void RequestKeyFocus() => RequestKeyFocus(KeyFocusReason.Other);

        private void RequestKeyFocus(KeyFocusReason keyfocusReason)
        {
            if (!IsKeyFocusable())
                return;
            UISession.InputManager.Queue.RequestKeyFocus(this, keyfocusReason);
        }

        public bool IsValid => !IsDisposed;

        public static FocusStateHandler GetFocusUpdateProc(InputDeviceType focusType)
        {
            switch (focusType)
            {
                case InputDeviceType.Keyboard:
                    return s_updateKeyFocusStates;
                case InputDeviceType.Mouse:
                    return s_updateMouseFocusStates;
                default:
                    return null;
            }
        }

        private void RevalidateUsage(bool recursiveFlag, bool knownDisabledFlag)
        {
            if (!IsZoned)
                return;
            UISession.InputManager.RevalidateInputSiteUsage(this, recursiveFlag, knownDisabledFlag);
        }

        internal void OnInputEnabledChanged()
        {
            if (!IsZoned)
                return;
            RevalidateUsage(true, !InputEnabled);
            UpdateMouseHandling(null);
            if (!Enabled || Parent != null && !Parent.FullyEnabled)
                return;
            NotifyFullyEnabledChange();
        }

        IRawInputSite ICookedInputSite.RawInputSource
        {
            get
            {
                IRawInputSite rawInputSite = null;
                if (_rootItem != null)
                    rawInputSite = _rootItem.RendererVisual;
                return rawInputSite;
            }
        }

        public void DeliverInput(InputInfo info, EventRouteStages stage)
        {
            if (stage != EventRouteStages.Unhandled)
            {
                switch (info.EventType)
                {
                    case InputEventType.CommandDown:
                    case InputEventType.CommandUp:
                    case InputEventType.KeyDown:
                    case InputEventType.KeyUp:
                    case InputEventType.KeyCharacter:
                    case InputEventType.MouseMove:
                    case InputEventType.MousePrimaryUp:
                    case InputEventType.MouseSecondaryUp:
                    case InputEventType.MouseWheel:
                    case InputEventType.DragEnter:
                    case InputEventType.DragOver:
                    case InputEventType.DragLeave:
                    case InputEventType.DragDropped:
                    case InputEventType.DragComplete:
                        DeliverInputEvent(info, stage);
                        break;
                    case InputEventType.GainKeyFocus:
                        DeliverGainKeyFocus(info, stage);
                        break;
                    case InputEventType.LoseKeyFocus:
                        DeliverLoseKeyFocus(info, stage);
                        break;
                    case InputEventType.GainMouseFocus:
                        DeliverGainMouseFocus(info, stage);
                        break;
                    case InputEventType.LoseMouseFocus:
                        DeliverFocusChange(info, stage);
                        break;
                    case InputEventType.MousePrimaryDown:
                    case InputEventType.MouseSecondaryDown:
                    case InputEventType.MouseDoubleClick:
                        DeliverMouseButtonDown(info, stage);
                        break;
                }
            }
            else
            {
                if (!(info is KeyStateInfo keyStateInfo) || keyStateInfo.Action != KeyAction.Down || (keyStateInfo.SystemKey || !KeyNavigate(keyStateInfo.Key, keyStateInfo.Modifiers)))
                    return;
                keyStateInfo.MarkHandled();
            }
        }

        private void DeliverInputEvent(InputInfo info, EventRouteStages stage)
        {
            if (info.Handled)
                return;
            ForwardEventToInputHandlers(info, stage);
        }

        private void ForwardEventToInputHandlers(InputInfo info, EventRouteStages stage)
        {
            if (_inputHandlers == null)
                return;
            foreach (InputHandler inputHandler in _inputHandlers)
            {
                inputHandler.DeliverInput(this, info, stage);
                if (CheckHandled(info, inputHandler))
                    break;
            }
        }

        private void DeliverFocusChange(InputInfo info, EventRouteStages stage)
        {
            if (stage == EventRouteStages.Routed)
            {
                int num = info.Handled ? 1 : 0;
                DeliverCodeNotifications(info);
            }
            ForwardEventToInputHandlers(info, stage);
        }

        private void DeliverGainKeyFocus(InputInfo info, EventRouteStages stage)
        {
            if (stage == EventRouteStages.Direct)
                OnGainKeyFocus();
            DeliverFocusChange(info, stage);
        }

        private void DeliverLoseKeyFocus(InputInfo info, EventRouteStages stage)
        {
            if (stage == EventRouteStages.Direct)
                OnLoseKeyFocus();
            DeliverFocusChange(info, stage);
        }

        private void DeliverGainMouseFocus(InputInfo info, EventRouteStages stage)
        {
            if (stage == EventRouteStages.Direct && GetBit(Bits.KeyFocusOnMouseEnter))
                RequestKeyFocus(KeyFocusReason.MouseEnter);
            DeliverFocusChange(info, stage);
        }

        private void DeliverMouseButtonDown(InputInfo args, EventRouteStages stage)
        {
            if (stage == EventRouteStages.Direct && GetBit(Bits.KeyFocusOnMouseDown))
                RequestKeyFocus(KeyFocusReason.MouseDown);
            DeliverInputEvent(args, stage);
        }

        private void OnGainKeyFocus()
        {
            NavigationServices.SeedDefaultFocus(RootItem);
            if (CreateInterestOnFocus)
            {
                SetAreaOfInterest(AreaOfInterestID.Focus);
                ViewItem focusInterestTarget = FocusInterestTarget;
                if (focusInterestTarget != null && focusInterestTarget != RootItem)
                    SetAreaOfInterest(AreaOfInterestID.FocusOverride, focusInterestTarget, FocusInterestTargetMargins);
            }
            TryToPlayAnimationOnTree(RootItem, AnimationEventType.GainFocus);
        }

        private void OnLoseKeyFocus()
        {
            if (RootItem == null)
                return;
            if (CreateInterestOnFocus)
            {
                ClearAreaOfInterest(AreaOfInterestID.Focus);
                ViewItem focusInterestTarget = FocusInterestTarget;
                if (focusInterestTarget != null && focusInterestTarget != RootItem)
                    ClearAreaOfInterest(AreaOfInterestID.FocusOverride, focusInterestTarget);
            }
            TryToPlayAnimationOnTree(RootItem, AnimationEventType.LoseFocus);
        }

        private void OnGainDeepKeyFocus()
        {
            if (_inputHandlers == null)
                return;
            foreach (InputHandler inputHandler in _inputHandlers)
                inputHandler.NotifyGainDeepKeyFocus();
        }

        private void OnLoseDeepKeyFocus()
        {
            if (_inputHandlers == null)
                return;
            foreach (InputHandler inputHandler in _inputHandlers)
                inputHandler.NotifyLoseDeepKeyFocus();
        }

        private static void UpdateKeyFocusStates(
          UIClass recipient,
          bool deepFocusFlag,
          bool directFocusFlag)
        {
            if (recipient.ChangeBit(Bits.KeyFocus, deepFocusFlag))
            {
                recipient.FireNotification(NotificationID.KeyFocus);
                if (deepFocusFlag)
                    recipient.OnGainDeepKeyFocus();
                else
                    recipient.OnLoseDeepKeyFocus();
            }
            if (!recipient.ChangeBit(Bits.DirectKeyFocus, directFocusFlag))
                return;
            recipient.FireNotification(NotificationID.DirectKeyFocus);
            if (DebugOutlines.Enabled)
                DebugOutlines.NotifyInteractivityChange(recipient.Host);
            if (!directFocusFlag)
                return;
            AccessibleProxy.NotifyFocus(recipient);
        }

        public event InputEventHandler DescendentKeyFocusChange
        {
            add => AddEventHandler(s_descendantKeyFocusChangedEvent, value);
            remove => RemoveEventHandler(s_descendantKeyFocusChangedEvent, value);
        }

        private static void UpdateMouseFocusStates(
          UIClass recipient,
          bool deepFocusFlag,
          bool directFocusFlag)
        {
            if (recipient.ChangeBit(Bits.MouseFocus, deepFocusFlag))
                recipient.FireNotification(NotificationID.MouseFocus);
            if (!recipient.ChangeBit(Bits.DirectMouseFocus, directFocusFlag))
                return;
            recipient.FireNotification(NotificationID.DirectMouseFocus);
            if (!DebugOutlines.Enabled)
                return;
            DebugOutlines.NotifyInteractivityChange(recipient.Host);
        }

        public event InputEventHandler DescendentMouseFocusChange
        {
            add => AddEventHandler(s_descendantMouseFocusChangedEvent, value);
            remove => RemoveEventHandler(s_descendantMouseFocusChangedEvent, value);
        }

        private void DeliverCodeNotifications(InputInfo info)
        {
            EventCookie focusChangedEvent = EventCookie.NULL;
            switch (info.EventType)
            {
                case InputEventType.GainKeyFocus:
                case InputEventType.LoseKeyFocus:
                    focusChangedEvent = s_descendantKeyFocusChangedEvent;
                    break;
                case InputEventType.GainMouseFocus:
                case InputEventType.LoseMouseFocus:
                    focusChangedEvent = s_descendantMouseFocusChangedEvent;
                    break;
            }
            if (!(focusChangedEvent != EventCookie.NULL) || !(GetEventHandler(focusChangedEvent) is InputEventHandler eventHandler))
                return;
            eventHandler(this, info);
        }

        public void ApplyLayoutOutput(
          ViewItem subjectItem,
          bool parentFullyVisible,
          bool parentOffscreen,
          bool allowAnimations,
          out bool visibilityChange,
          out bool offscreenChange)
        {
            bool flag1 = subjectItem.LayoutVisible && parentFullyVisible;
            bool hasVisual = subjectItem.HasVisual;
            if (!flag1 && !hasVisual)
            {
                visibilityChange = false;
                offscreenChange = false;
            }
            else
            {
                visibilityChange = flag1 != hasVisual;
                bool flag2 = visibilityChange && hasVisual;
                bool flag3 = visibilityChange && !hasVisual;
                if (flag3)
                    subjectItem.CreateVisual(Zone.Session.RenderSession);
                Rectangle layoutBounds = subjectItem.LayoutBounds;
                Vector2 vector2 = new Vector2(layoutBounds.Width, layoutBounds.Height);
                Vector3 vector3_1 = new Vector3(layoutBounds.X, layoutBounds.Y, 0.0f);
                Vector3 vector3_2 = subjectItem.LayoutScale * subjectItem.Scale;
                Rotation layoutRotation = subjectItem.LayoutRotation;
                Vector3 oldScaleVector;
                if (!hasVisual)
                {
                    AnimateNewVisual(subjectItem, vector3_1, vector2, vector3_2, layoutRotation);
                    oldScaleVector = vector3_2;
                }
                else
                {
                    Vector2 visualSize = subjectItem.VisualSize;
                    oldScaleVector = subjectItem.VisualScale;
                }
                bool flag4 = subjectItem.LayoutOffscreen || parentOffscreen;
                offscreenChange = flag4 != subjectItem.IsOffscreen;
                if (offscreenChange)
                    subjectItem.IsOffscreen = flag4;
                if (offscreenChange || flag3)
                    UpdateMouseHandling(subjectItem);
                bool flag5 = hasVisual;
                if (flag5 && flag2)
                    flag5 = false;
                if (flag5)
                    AnimateVisual(subjectItem, vector3_1, vector2, vector3_2, layoutRotation);
                if (flag2)
                {
                    if (subjectItem.IsRoot)
                        return;
                    DestroyVisualTree(subjectItem, allowAnimations, false);
                }
                else
                {
                    if (!(oldScaleVector != vector3_2) && hasVisual)
                        return;
                    subjectItem.OnScaleChange(oldScaleVector, vector3_2);
                }
            }
        }

        public bool FindNextFocusablePeer(
          Direction searchDirection,
          RectangleF startRectangleF,
          out UIClass resultUI)
        {
            if (_rootItem != null)
                return FindNextFocusablePeerWorker(_rootItem, searchDirection, startRectangleF, out resultUI);
            resultUI = null;
            return false;
        }

        private bool FindNextFocusablePeerWorker(
          ViewItem startItem,
          Direction searchDirection,
          RectangleF startRectangleF,
          out UIClass resultUI)
        {
            INavigationSite resultSite;
            bool nextPeer = NavigationServices.FindNextPeer(startItem, searchDirection, startRectangleF, out resultSite);
            resultUI = null;
            if (resultSite != null && resultSite is ViewItem viewItem)
                resultUI = viewItem.UI;
            return nextPeer;
        }

        internal bool FindNextFocusableWithin(
          Direction searchDirection,
          RectangleF startRectangleF,
          out UIClass resultUI)
        {
            INavigationSite resultSite;
            bool nextWithin = NavigationServices.FindNextWithin(_rootItem, searchDirection, startRectangleF, out resultSite);
            resultUI = null;
            if (resultSite != null && resultSite is ViewItem viewItem)
                resultUI = viewItem.UI;
            return nextWithin;
        }

        internal NavigationClass GetNavigability(ViewItem subjectItem)
        {
            NavigationClass navigationClass = NavigationClass.None;
            if (subjectItem == _rootItem && IsKeyFocusable())
                navigationClass = NavigationClass.Direct;
            return navigationClass;
        }

        public void NotifyNavigationDestination(KeyFocusReason keyFocusReason) => RequestKeyFocus(keyFocusReason);

        public bool InboundKeyNavigation(
          Direction searchDirection,
          RectangleF startRectangleF,
          bool defaultFlag)
        {
            UIClass resultUI;
            if (!FindNextFocusableWithin(searchDirection, startRectangleF, out resultUI) || resultUI == null)
                return false;
            resultUI.NotifyNavigationDestination(defaultFlag ? KeyFocusReason.Default : KeyFocusReason.Other);
            return true;
        }

        public bool NavigateDirection(Direction direction, KeyFocusReason reason)
        {
            UIClass resultUI;
            if (!FindNextFocusablePeer(direction, RectangleF.Zero, out resultUI) || resultUI == null)
                return false;
            UIClass uiClass = this;
            resultUI.NotifyNavigationDestination(reason);
            return true;
        }

        private bool KeyNavigate(Keys key, InputModifiers modifiers)
        {
            InputHandlerModifiers modifiers1 = InputHandler.GetModifiers(modifiers);
            KeyHandler.TranslateKey(ref key, ref modifiers1);
            KeyFocusReason reason = KeyFocusReason.Directional;
            Direction direction;
            switch (key)
            {
                case Keys.Tab:
                    direction = (modifiers1 & InputHandlerModifiers.Shift) != InputHandlerModifiers.Shift ? Direction.Next : Direction.Previous;
                    reason = KeyFocusReason.Tab;
                    break;
                case Keys.Left:
                    direction = Direction.West;
                    break;
                case Keys.Up:
                    direction = Direction.North;
                    break;
                case Keys.Right:
                    direction = Direction.East;
                    break;
                case Keys.Down:
                    direction = Direction.South;
                    break;
                default:
                    return false;
            }
            return NavigateDirection(direction, reason);
        }

        public TypeSchema TypeSchema => _typeSchema;

        public void NotifyInitialized()
        {
            if (_inputHandlers != null)
            {
                foreach (InputHandler inputHandler in _inputHandlers)
                    inputHandler.NotifyUIInitialized();
            }
            if (_rootItem != null)
            {
                if (MouseInteractive)
                {
                    if (!HasMouseInteractiveContent())
                        _rootItem.MouseInteractive = true;
                }
                else if (HasMouseInteractiveContent())
                    MouseInteractive = true;
            }
            SetBit(Bits.Initialized, true);
            AccessibleProxy.NotifyCreated(this);
        }

        public object ReadSymbol(SymbolReference symbolRef)
        {
            object obj = null;
            switch (symbolRef.Origin)
            {
                case SymbolOrigin.Properties:
                case SymbolOrigin.Locals:
                    obj = _storage[symbolRef.Symbol];
                    break;
                case SymbolOrigin.Input:
                    if (_inputHandlers != null)
                    {
                        foreach (InputHandler inputHandler in _inputHandlers)
                        {
                            if (inputHandler.Name != null && ReferenceEquals(inputHandler.Name, symbolRef.Symbol))
                            {
                                obj = inputHandler;
                                break;
                            }
                        }
                        break;
                    }
                    break;
                case SymbolOrigin.Content:
                    obj = FindViewItemByName(RootItem, symbolRef.Symbol);
                    break;
                case SymbolOrigin.Reserved:
                    if (symbolRef.Symbol == "UI")
                    {
                        obj = this;
                        break;
                    }
                    break;
            }
            return obj;
        }

        public ViewItem FindViewItemByName(ViewItem item, string name)
        {
            if (ReferenceEquals(item.Name, name))
                return item;
            if (item.HideNamedChildren)
                return null;
            foreach (ViewItem child in item.Children)
            {
                if (child.UI == this)
                {
                    ViewItem viewItemByName = FindViewItemByName(child, name);
                    if (viewItemByName != null)
                        return viewItemByName;
                }
            }
            return null;
        }

        public void WriteSymbol(SymbolReference symbolRef, object value)
        {
            string symbol = symbolRef.Symbol;
            if (_storage.ContainsKey(symbol) && Utility.IsEqual(_storage[symbol], value))
                return;
            _storage[symbol] = value;
            if (symbolRef.Origin != SymbolOrigin.Properties && symbolRef.Origin != SymbolOrigin.Locals)
                return;
            bool surfaceViaHost = symbolRef.Origin == SymbolOrigin.Properties;
            FireNotification(symbol, surfaceViaHost);
        }

        public object GetProperty(string name) => _storage[name];

        public void SetProperty(string name, object value)
        {
            if (_storage.ContainsKey(name) && Utility.IsEqual(_storage[name], value))
                return;
            _storage[name] = value;
            FireNotification(name, true);
        }

        public Dictionary<object, object> Storage => _storage;

        public MarkupListeners Listeners
        {
            get => _listeners;
            set => _listeners = value;
        }

        public void ScheduleScriptRun(uint scriptId, bool ignoreErrors)
        {
            if (!_scriptRunScheduler.Pending)
                DeferredCall.Post(DispatchPriority.Script, s_executePendingScriptsHandler, this);
            _scriptRunScheduler.ScheduleRun(scriptId, ignoreErrors);
        }

        private static void ExecutePendingScripts(object args)
        {
            UIClass uiClass = (UIClass)args;
            uiClass._scriptRunScheduler.Execute(uiClass);
        }

        public object RunScript(uint scriptId, bool ignoreErrors, ParameterContext parameterContext) => _typeSchema.Run(this, scriptId, ignoreErrors, parameterContext);

        public void NotifyScriptErrors()
        {
            SetBit(Bits.ScriptEnabled, false);
            _ownerHost.NotifyChildUIScriptErrors();
            ErrorManager.ReportWarning("Script runtime failure: Scripting has been disabled for '{0}' due to runtime scripting errors", _typeSchema.Name);
        }

        public bool ScriptEnabled => GetBit(Bits.ScriptEnabled);

        protected void FireNotification(string id) => FireNotification(id, false);

        private void FireNotification(string id, bool surfaceViaHost)
        {
            _notifier.Fire(id);
            if (!surfaceViaHost || _ownerHost == null)
                return;
            _ownerHost.FireChildUINotification(id);
        }

        void INotifyObject.AddListener(Listener listener) => _notifier.AddListener(listener);

        public ViewItem ConstructNamedContent(
          string contentName,
          ParameterContext parameterContext)
        {
            return _typeSchema.ConstructNamedContent(contentName, this, parameterContext);
        }

        private bool GetBit(UIClass.Bits lookupBit) => _bits[(int)lookupBit];

        private void SetBit(UIClass.Bits changeBit, bool value) => _bits[(int)changeBit] = value;

        private bool ChangeBit(UIClass.Bits bit, bool value)
        {
            if (_bits[(int)bit] == value)
                return false;
            _bits[(int)bit] = value;
            return true;
        }

        public ViewItem RootItem => _rootItem;

        public string ID => _ownerHost.Name;

        public Host Host => _ownerHost;

        public void DeclareHost(Host ownerHost) => _ownerHost = ownerHost;

        public bool Flippable
        {
            get => GetBit(Bits.Flippable);
            set => SetBit(Bits.Flippable, value);
        }

        internal void SetRootItem(ViewItem newRootItem) => _rootItem = newRootItem;

        internal bool HasMouseInteractiveContent() => _rootItem != null && HasMouseInteractiveContentWorker(_rootItem);

        private bool HasMouseInteractiveContentWorker(ViewItem checkItem)
        {
            if (checkItem.MouseInteractive)
                return true;
            foreach (ViewItem child in checkItem.Children)
            {
                if (child.UI == this && HasMouseInteractiveContentWorker(child))
                    return true;
            }
            return false;
        }

        public object GetEventContext() => _eventContext != null ? _eventContext.Value : null;

        internal void SetEventContext(EventContext eventContext) => _eventContext = eventContext;

        public override string ToString() => $"UI[{_typeSchema.Name}]";

        private class SavedFocusState
        {
            private ViewItemID[] _path;
            private bool _focusIsDefault;

            public SavedFocusState(ViewItem item)
            {
                _path = item.IDPath;
                _focusIsDefault = item.UISession.InputManager.RawKeyFocusIsDefault;
            }

            public ViewItemID[] Path => _path;

            public bool FocusIsDefault => _focusIsDefault;
        }

        private class DeferredKeyFocusRestoreHelper
        {
            private UIClass _ui;
            private UIClass.SavedFocusState _focusState;
            private ICookedInputSite _lastFocus;

            public DeferredKeyFocusRestoreHelper(UIClass ui, UIClass.SavedFocusState focusState)
            {
                _ui = ui;
                _focusState = focusState;
                _lastFocus = ui.UISession.InputManager.RawKeyFocus;
            }

            public void FaultInChild(ViewItem bottleneck, ViewItemID item)
            {
                _ui.PendingFocusRestore = this;
                bottleneck.FaultInChild(item, new ChildFaultedInDelegate(ChildFaultedIn));
            }

            private void ChildFaultedIn(ViewItem bottleneckItem, ViewItem faultedInItem)
            {
                if (!_ui.IsZoned || _ui.PendingFocusRestore != this)
                    return;
                _ui.PendingFocusRestore = null;
                InputManager inputManager = _ui.UISession.InputManager;
                if (!inputManager.RawKeyFocusIsDefault && inputManager.RawKeyFocus != _lastFocus || (faultedInItem == null || faultedInItem.IsDisposed))
                    return;
                _ui.RestoreKeyFocus(_focusState);
            }
        }

        private enum Bits
        {
            Initialized = 1,
            IsDisposed = 2,
            CreateInterestOnFocus = 4,
            Flippable = 8,
            KeyInteractive = 16, // 0x00000010
            MouseInteractive = 32, // 0x00000020
            MouseInteractiveSet = 64, // 0x00000040
            Enabled = 128, // 0x00000080
            AppFullyEnabled = 256, // 0x00000100
            RawInputDisabled = 512, // 0x00000200
            DirectKeyFocus = 1024, // 0x00000400
            DirectMouseFocus = 2048, // 0x00000800
            KeyFocusOnMouseEnter = 4096, // 0x00001000
            KeyFocusOnMouseDown = 8192, // 0x00002000
            AllowDoubleClicks = 16384, // 0x00004000
            KeyFocus = 32768, // 0x00008000
            MouseFocus = 65536, // 0x00010000
            HasAccProxy = 131072, // 0x00020000
            ScriptEnabled = 262144, // 0x00040000
            HasAutomationInstanceId = 524288, // 0x00080000
        }
    }
}
