using System;
using Xamarin.Forms;

namespace Ooui.Forms
{
    public class VisualElementPackager : IDisposable
    {
        VisualElement _element;

        bool _isDisposed;

        IElementController ElementController => _element;

        public VisualElementPackager (IVisualElementRenderer renderer) : this (renderer, null)
        {
        }

        VisualElementPackager (IVisualElementRenderer renderer, VisualElement element)
        {
            if (renderer == null)
                throw new ArgumentNullException (nameof (renderer));

            Renderer = renderer;
            renderer.ElementChanged += OnRendererElementChanged;
            SetElement (null, element ?? renderer.Element);
        }

        protected IVisualElementRenderer Renderer { get; set; }

        public void Dispose ()
        {
            Dispose (true);
        }

        public void Load ()
        {
            for (var i = 0; i < ElementController.LogicalChildren.Count; i++) {
                var child = ElementController.LogicalChildren[i] as VisualElement;
                if (child != null)
                    OnChildAdded (child);
            }
        }

        protected virtual void Dispose (bool disposing)
        {
            if (_isDisposed)
                return;

            if (disposing) {
                SetElement (_element, null);
                if (Renderer != null) {
                    Renderer.ElementChanged -= OnRendererElementChanged;
                    Renderer = null;
                }
            }

            _isDisposed = true;
        }

        protected virtual void OnChildAdded (VisualElement view)
        {
            if (_isDisposed)
                return;

            //if (CompressedLayout.GetIsHeadless (view)) {
            //    var packager = new VisualElementPackager (Renderer, view);
            //    view.IsPlatformEnabled = true;
            //    packager.Load ();
            //}
            //else {
                var viewRenderer = Platform.CreateRenderer (view);
                Platform.SetRenderer (view, viewRenderer);

                var uiview = Renderer.NativeView;
                uiview.AppendChild (viewRenderer.NativeView);

                EnsureChildrenOrder ();
            //}
        }

        protected virtual void OnChildRemoved (VisualElement view)
        {
            if (_element == null)
                return;
            
            var viewRenderer = Platform.GetRenderer (view);
            if (viewRenderer == null || viewRenderer.NativeView == null)
                return;

            var parentRenderer = Platform.GetRenderer (_element);
            if (parentRenderer == null || parentRenderer.NativeView == null)
                return;

            parentRenderer.NativeView.RemoveChild (viewRenderer.NativeView);
        }

        void EnsureChildrenOrder ()
        {
            if (ElementController.LogicalChildren.Count == 0)
                return;

            for (var z = 0; z < ElementController.LogicalChildren.Count; z++) {
                var child = ElementController.LogicalChildren[z] as VisualElement;
                if (child == null)
                    continue;
                var childRenderer = Platform.GetRenderer (child);

                if (childRenderer == null)
                    continue;

                var nativeControl = childRenderer.NativeView;
#if __MOBILE__
                Renderer.NativeView.BringSubviewToFront(nativeControl);
#endif
                nativeControl.Style.ZIndex = z * 1000;
            }
        }

        void OnChildAdded (object sender, ElementEventArgs e)
        {
            var view = e.Element as VisualElement;
            if (view != null)
                OnChildAdded (view);
        }

        void OnChildRemoved (object sender, ElementEventArgs e)
        {
            var view = e.Element as VisualElement;
            if (view != null)
                OnChildRemoved (view);
        }

        void OnRendererElementChanged (object sender, VisualElementChangedEventArgs args)
        {
            if (args.NewElement == _element)
                return;

            SetElement (_element, args.NewElement);
        }

        void SetElement (VisualElement oldElement, VisualElement newElement)
        {
            if (oldElement == newElement)
                return;

            if (oldElement != null) {
                oldElement.ChildAdded -= OnChildAdded;
                oldElement.ChildRemoved -= OnChildRemoved;
                oldElement.ChildrenReordered -= UpdateChildrenOrder;

                if (newElement != null) {
                    var pool = new RendererPool (Renderer, oldElement);
                    pool.UpdateNewElement (newElement);

                    EnsureChildrenOrder ();
                }
                else {
                    var elementController = ((IElementController)oldElement);

                    for (var i = 0; i < elementController.LogicalChildren.Count; i++) {
                        var child = elementController.LogicalChildren[i] as VisualElement;
                        if (child != null)
                            OnChildRemoved (child);
                    }
                }
            }

            _element = newElement;

            if (newElement != null) {
                newElement.ChildAdded += OnChildAdded;
                newElement.ChildRemoved += OnChildRemoved;
                newElement.ChildrenReordered += UpdateChildrenOrder;
            }
        }

        void UpdateChildrenOrder (object sender, EventArgs e)
        {
            EnsureChildrenOrder ();
        }
    }
}
