// Decompiled with JetBrains decompiler
// Type: Microsoft.Iris.ViewItems.Host
// 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.Debug;
using Microsoft.Iris.Library;
using Microsoft.Iris.Markup;
using Microsoft.Iris.Markup.UIX;
using Microsoft.Iris.Render;
using Microsoft.Iris.Session;
using Microsoft.Iris.UI;
using System;

namespace Microsoft.Iris.ViewItems
{
    internal class Host : ViewItem, ISchemaInfo
    {
        private const string DefaultUI = "Default";
        private UIClassTypeSchema _typeRestriction;
        private UIClassTypeSchema _typeCurrent;
        private UIClass _childUI;
        private string _lastRequestedSource;
        private HostStatus _status;
        private bool _inputEnabled;
        private bool _dynamicHost;
        private bool _unloadable;
        private bool _newContentOnTop;
        private Vector<UIPropertyRecord> _heldUIProperties;
        private Vector<IDisposableObject> _heldInitialUIDisposables;
        private ChildFaultedInDelegate _loadNotify;
        private string _loadNotifyURI;
        private HostRequestPacket _pendingHostRequest;
        private uint _islandId;
        private static DeferredHandler s_startRequestHandler = new DeferredHandler(StartSourceRequest);

        public Host()
          : this(null, null)
        {
        }

        public Host(UIClassTypeSchema typeSchema)
          : this(typeSchema, typeSchema)
        {
        }

        public Host(UIClassTypeSchema typeRestriction, UIClassTypeSchema typeCurrent)
        {
            _status = HostStatus.Normal;
            _inputEnabled = true;
            _typeRestriction = typeRestriction;
            _typeCurrent = typeCurrent;
            if (_typeCurrent == null)
                return;
            SetChildUI(_typeCurrent.ConstructUI());
            _lastRequestedSource = SourceFromType(_typeCurrent);
        }

        private string SourceFromType(TypeSchema type) => type.Owner.Uri + "#" + type.Name;

        public TypeSchema TypeSchema => _typeCurrent;

        protected override void OnDispose()
        {
            base.OnDispose();
            Cancel();
            if (_childUI != null)
                SetChildUI(null);
            if (_heldInitialUIDisposables != null)
            {
                foreach (DisposableObject initialUiDisposable in _heldInitialUIDisposables)
                    initialUiDisposable.Dispose(this);
            }
            if (!Unloadable)
                return;
            UnloadAll();
            MarkupSystem.FreeIslandId(_islandId);
            _islandId = 0U;
        }

        protected override void OnOwnerDeclared(object owner)
        {
            base.OnOwnerDeclared(owner);
            UIClass uiClass = (UIClass)owner;
            if (ChildUI == null)
                return;
            uiClass.Children.Add(ChildUI);
        }

        public void RequestSource(string source, Vector<UIPropertyRecord> properties) => RequestSource(source, null, properties);

        public void RequestSource(TypeSchema type, Vector<UIPropertyRecord> properties) => RequestSource(null, type, properties);

        public void RequestSource(string source, TypeSchema type, Vector<UIPropertyRecord> properties)
        {
            if (type != null)
            {
                if (!HostSchema.Type.IsAssignableFrom(type))
                {
                    ErrorManager.ReportError("RequestSource failed: Referrenced type '{0}' is not a UI", type.Name);
                    return;
                }
                source = SourceFromType(type);
            }
            Cancel();
            HostRequestPacket hostRequestPacket = new HostRequestPacket();
            hostRequestPacket.Host = this;
            hostRequestPacket.Source = source;
            hostRequestPacket.Type = (UIClassTypeSchema)type;
            hostRequestPacket.Properties = properties;
            _pendingHostRequest = hostRequestPacket;
            _lastRequestedSource = source;
            DeferredCall.Post(DispatchPriority.High, s_startRequestHandler, hostRequestPacket);
        }

        public void Cancel()
        {
            if (_pendingHostRequest != null)
            {
                _pendingHostRequest.Clear();
                _pendingHostRequest = null;
            }
            RevokePendingLoadNotification();
        }

        private static void StartSourceRequest(object args)
        {
            HostRequestPacket hostRequestPacket = (HostRequestPacket)args;
            if (hostRequestPacket.Host == null)
                return;
            Host host = hostRequestPacket.Host;
            string source = hostRequestPacket.Source;
            UIClassTypeSchema type = hostRequestPacket.Type;
            Vector<UIPropertyRecord> properties = hostRequestPacket.Properties;
            host.Cancel();
            ErrorManager.EnterContext(source);
            try
            {
                host.SetStatus(HostStatus.LoadingSource);
                LoadResult loadResult = null;
                string uiToCreate = null;
                if (type == null && source != null)
                    loadResult = MarkupSystem.Load(CrackSourceUri(source, out uiToCreate), host.InheritedIslandId);
                host.CompleteSourceRequest(source, type, properties, loadResult, uiToCreate);
            }
            catch (Exception ex)
            {
                Trace.WriteLine(TraceCategory.Markup, $"Error detected: {ex};");
            }
            finally
            {
                ErrorManager.ExitContext();
            }
        }

        private void CompleteSourceRequest(
          string requestedSource,
          UIClassTypeSchema requestedType,
          Vector<UIPropertyRecord> properties,
          LoadResult loadResult,
          string uiToCreate)
        {
            ErrorManager.EnterContext(requestedSource);
            ErrorWatermark watermark = ErrorManager.Watermark;
            bool flag = true;
            ForceContentChange();
            try
            {
                if (_childUI != null)
                {
                    if (_typeRestriction != null)
                        HoldChildUIPropertyValues();
                    SetChildUI(null);
                    _typeCurrent = _typeRestriction;
                    FireNotification(NotificationID.SourceType);
                }
                _dynamicHost = true;
                UIClassTypeSchema uiClassTypeSchema = null;
                Vector<UIPropertyRecord> vector = null;
                if (requestedSource != null)
                {
                    if (requestedType == null)
                    {
                        if (loadResult == null || loadResult.Status == LoadResultStatus.Error)
                        {
                            ErrorManager.ReportError("RequestSource failed: Unable to load '{0}'", requestedSource);
                        }
                        else
                        {
                            TypeSchema type = loadResult.FindType(uiToCreate);
                            if (type == null)
                                ErrorManager.ReportError("RequestSource failed: Unable to find '{0}' within '{1}'", uiToCreate, requestedSource);
                            else if (!HostSchema.Type.IsAssignableFrom(type))
                                ErrorManager.ReportError("RequestSource failed: Referrenced type '{0}' is not a UI", uiToCreate);
                            else
                                requestedType = (UIClassTypeSchema)type;
                        }
                    }
                    if (requestedType != null)
                    {
                        uiClassTypeSchema = requestedType;
                        if (_typeRestriction != null && !_typeRestriction.IsAssignableFrom(uiClassTypeSchema))
                            ErrorManager.ReportError("RequestSource failed: Found '{0}' within '{1}', but, it is not a '{2}'", uiToCreate, requestedSource, _typeRestriction.Name);
                        vector = NegotiateNewChildUIPropertyValues(uiClassTypeSchema, properties);
                    }
                }
                if (watermark.ErrorsDetected)
                {
                    SetStatus(HostStatus.FailureLoadingSource);
                }
                else
                {
                    if (uiClassTypeSchema != null)
                    {
                        Host host = (Host)uiClassTypeSchema.ConstructDefault();
                        UIClass childUi = host.ChildUI;
                        foreach (UIPropertyRecord uiPropertyRecord in vector)
                        {
                            object instance = host;
                            uiPropertyRecord.Schema.SetValue(ref instance, uiPropertyRecord.Value);
                        }
                        _heldUIProperties = null;
                        SetChildUI(childUi);
                        object instance1 = this;
                        uiClassTypeSchema.InitializeInstance(ref instance1);
                        UI.Children.Add(childUi);
                        _typeCurrent = uiClassTypeSchema;
                        FireNotification(NotificationID.Source);
                        FireNotification(NotificationID.SourceType);
                    }
                    SetStatus(HostStatus.Normal);
                    if (!NotifyForLatestLoad())
                        return;
                    DeferredCall.Post(DispatchPriority.LayoutSync, new SimpleCallback(DeliverLoadCompleteNotification));
                    flag = false;
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debugger.Break();
                Trace.WriteLine(TraceCategory.Markup, $"Error detected: {ex};");
            }
            finally
            {
                if (flag)
                    RevokePendingLoadNotification();
                ErrorManager.ExitContext();
            }
        }

        private bool NotifyForLatestLoad() => _loadNotify != null && InvariantString.Equals(_loadNotifyURI, Source);

        private void DeliverLoadCompleteNotification()
        {
            if (NotifyForLatestLoad() && ChildUI != null && ChildUI.RootItem != null)
                _loadNotify(this, ChildUI.RootItem);
            RevokePendingLoadNotification();
        }

        private void RevokePendingLoadNotification()
        {
            _loadNotify = null;
            _loadNotifyURI = null;
        }

        private void HoldChildUIPropertyValues()
        {
            if (_typeRestriction == null)
                return;
            _heldUIProperties = new Vector<UIPropertyRecord>();
            for (TypeSchema typeRestriction = _typeRestriction; typeRestriction != HostSchema.Type; typeRestriction = typeRestriction.Base)
            {
                foreach (PropertySchema property1 in typeRestriction.Properties)
                {
                    string name = property1.Name;
                    if (_childUI.Storage.ContainsKey(name) && !UIPropertyRecord.IsInList(_heldUIProperties, name))
                    {
                        object property2 = _childUI.GetProperty(name);
                        UIPropertyRecord.AddToList(_heldUIProperties, name, property2);
                        if (!_dynamicHost && property2 != null && property1.PropertyType.Disposable)
                        {
                            IDisposableObject disposable = (IDisposableObject)property2;
                            if (_childUI.UnregisterDisposable(ref disposable))
                            {
                                if (_heldInitialUIDisposables == null)
                                    _heldInitialUIDisposables = new Vector<IDisposableObject>();
                                _heldInitialUIDisposables.Add(disposable);
                                disposable.TransferOwnership(this);
                            }
                        }
                    }
                }
            }
        }

        private Vector<UIPropertyRecord> NegotiateNewChildUIPropertyValues(
          TypeSchema replacementType,
          Vector<UIPropertyRecord> specifiedProperties)
        {
            Vector<UIPropertyRecord> list = specifiedProperties ?? new Vector<UIPropertyRecord>();
            if (_typeRestriction != null)
            {
                foreach (UIPropertyRecord heldUiProperty in _heldUIProperties)
                {
                    if (!UIPropertyRecord.IsInList(list, heldUiProperty.Name))
                        list.Add(heldUiProperty);
                }
            }
            foreach (UIPropertyRecord uiPropertyRecord in list)
            {
                uiPropertyRecord.Schema = replacementType.FindPropertyDeep(uiPropertyRecord.Name);
                if (uiPropertyRecord.Schema == null)
                    ErrorManager.ReportError("Runtime UI replacement to '{0}' failed since a property named '{1}' was specified but doesn't exist on '{0}'", replacementType.Name, uiPropertyRecord.Name);
                else if (!uiPropertyRecord.Schema.PropertyType.IsAssignableFrom(uiPropertyRecord.Value))
                    ErrorManager.ReportError("Runtime UI replacement to '{0}' failed since the value specified ({1}) for the '{2}' property is incompatible (type expected is '{3}')", replacementType.Name, uiPropertyRecord.Value, uiPropertyRecord.Name, uiPropertyRecord.Schema.PropertyType.Name);
            }
            foreach (string name in replacementType.FindRequiredPropertyNamesDeep())
            {
                if (!UIPropertyRecord.IsInList(list, name))
                    ErrorManager.ReportError("Runtime UI replacement to '{0}' failed since required property '{1}' was never provided a value", replacementType.Name, name);
            }
            return list;
        }

        protected override ViewItemID IDForChild(ViewItem childItem) => new ViewItemID(Source);

        protected override FindChildResult ChildForID(
          ViewItemID part,
          out ViewItem resultItem)
        {
            resultItem = null;
            FindChildResult findChildResult = FindChildResult.Failure;
            if (part.StringPartValid && !part.IDValid)
            {
                if (InvariantString.Equals(Source, part.StringPart) && ChildUI != null && ChildUI.RootItem != null)
                {
                    resultItem = ChildUI.RootItem;
                    findChildResult = FindChildResult.Success;
                }
                else if (_status == HostStatus.LoadingSource)
                    findChildResult = FindChildResult.PotentiallyFaultIn;
            }
            return findChildResult;
        }

        internal override void FaultInChild(ViewItemID childID, ChildFaultedInDelegate handler)
        {
            _loadNotify = handler;
            _loadNotifyURI = childID.StringPart;
        }

        public string Source => _lastRequestedSource;

        public TypeSchema SourceType => _typeCurrent;

        public bool Unloadable
        {
            get => _unloadable;
            set
            {
                if (_unloadable == value)
                    return;
                _unloadable = value;
                if (!_unloadable)
                    return;
                _islandId = MarkupSystem.AllocateIslandId();
                if (_islandId != 0U)
                    return;
                ErrorManager.ReportError("Maximum number of Unloadable Hosts reached");
            }
        }

        public bool UnloadAll() => UnloadAll(true);

        public bool UnloadAll(bool requestSourceToNull)
        {
            if (Unloadable)
            {
                MarkupSystem.UnloadIsland(_islandId);
                if (requestSourceToNull)
                    RequestSource(null, null, null);
                return true;
            }
            ErrorManager.ReportError("UnloadAll may only be called on Unloadable hosts");
            return false;
        }

        protected uint InheritedIslandId
        {
            get
            {
                if (_islandId == 0U)
                    _islandId = UI == null || UI.Host == null ? MarkupSystem.RootIslandId : UI.Host.InheritedIslandId;
                return _islandId;
            }
        }

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

        public void ForceRefresh(bool unloadMarkup)
        {
            if (unloadMarkup)
            {
                string lastRequestedSource = _lastRequestedSource;
                RequestSource(null, null, null);
                DeferredCall.Post(DispatchPriority.High, new SimpleCallback(DeferredUnloadAll));
                DeferredCall.Post(DispatchPriority.High, new DeferredHandler(DeferredRequestSource), lastRequestedSource);
            }
            else
                RequestSource(_lastRequestedSource, null);
        }

        private void DeferredUnloadAll() => UnloadAll(false);

        private void DeferredRequestSource(object objLastRequestedSource) => RequestSource((string)objLastRequestedSource, null);

        public HostStatus Status => _status;

        private void SetStatus(HostStatus status)
        {
            if (_status == status)
                return;
            _status = status;
            FireNotification(NotificationID.Status);
        }

        public bool InputEnabled
        {
            get => _inputEnabled;
            set
            {
                if (_inputEnabled == value)
                    return;
                _inputEnabled = value;
                if (_childUI != null)
                    _childUI.OnInputEnabledChanged();
                FireNotification(NotificationID.InputEnabled);
            }
        }

        public UIClass ChildUI => _childUI;

        private void SetChildUI(UIClass childUI)
        {
            LoadResult loadResult = null;
            if (_childUI != null)
            {
                loadResult = _childUI.TypeSchema.Owner;
                _childUI.Dispose(this);
            }
            if (_dynamicHost)
            {
                loadResult?.UnregisterUsage(this);
                childUI?.TypeSchema.Owner.RegisterUsage(this);
            }
            _childUI = childUI;
        }

        protected override void CreateVisualContainer(IRenderSession renderSession)
        {
            base.CreateVisualContainer(renderSession);
            if (_childUI == null)
                return;
            _childUI.OnHostVisibilityChanged();
        }

        protected static string CrackSourceUri(string source, out string uiToCreate)
        {
            string str = source;
            uiToCreate = "Default";
            int length = source.LastIndexOf('#');
            if (length != -1)
            {
                str = source.Substring(0, length);
                uiToCreate = source.Substring(length + 1, source.Length - length - 1);
            }
            return str;
        }

        public void NotifyChildUIScriptErrors() => SetStatus(HostStatus.FailureRunningScript);

        public object GetChildUIProperty(string name) => _childUI == null ? UIPropertyRecord.FindInList(_heldUIProperties, name).Value : _childUI.GetProperty(name);

        public void SetChildUIProperty(string name, object value)
        {
            if (_childUI != null)
            {
                _childUI.SetProperty(name, value);
            }
            else
            {
                UIPropertyRecord inList = UIPropertyRecord.FindInList(_heldUIProperties, name);
                if (Utility.IsEqual(inList.Value, value))
                    return;
                inList.Value = value;
                FireNotification(name);
            }
        }

        public void FireChildUINotification(string id) => FireNotification(id);

        public bool NewContentOnTop
        {
            get => _newContentOnTop;
            set
            {
                if (_newContentOnTop == value)
                    return;
                _newContentOnTop = value;
                FireNotification(NotificationID.NewContentOnTop);
            }
        }

        protected override VisualOrder GetVisualOrder() => !NewContentOnTop ? VisualOrder.Last : VisualOrder.First;

        public override string ToString() => base.ToString() + " ('" + _lastRequestedSource + "')";
    }
}
