using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace FairyGUI
{
    /// <summary>
    /// 
    /// </summary>
    public class Container : DisplayObject
    {
        /// <summary>
        /// 
        /// </summary>
        public RenderMode renderMode;

        /// <summary>
        /// 
        /// </summary>
        public Camera renderCamera;

        /// <summary>
        /// 
        /// </summary>
        public bool opaque;

        /// <summary>
        /// 
        /// </summary>
        public Vector4? clipSoftness;

        /// <summary>
        /// 
        /// </summary>
        public IHitTest hitArea;

        /// <summary>
        /// 
        /// </summary>
        public bool touchChildren;

        /// <summary>
        /// 
        /// </summary>
        public event Action onUpdate;

        /// <summary>
        /// 
        /// </summary>
        public bool reversedMask;

        List<DisplayObject> _children;
        DisplayObject _mask;
        Rect? _clipRect;
        List<DisplayObject> _descendants;

        internal int _panelOrder;
        internal DisplayObject _lastFocus;

        /// <summary>
        /// 
        /// </summary>
        public Container()
            : base()
        {
            CreateGameObject("Container");
            Init();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="gameObjectName"></param>
        public Container(string gameObjectName)
            : base()
        {
            CreateGameObject(gameObjectName);
            Init();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="attachTarget"></param>
        public Container(GameObject attachTarget)
            : base()
        {
            SetGameObject(attachTarget);
            Init();
        }

        void Init()
        {
            _children = new List<DisplayObject>();
            touchChildren = true;
        }

        /// <summary>
        /// 
        /// </summary>
        public int numChildren
        {
            get { return _children.Count; }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="child"></param>
        /// <returns></returns>
        public DisplayObject AddChild(DisplayObject child)
        {
            AddChildAt(child, _children.Count);
            return child;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="child"></param>
        /// <param name="index"></param>
        /// <returns></returns>
        public DisplayObject AddChildAt(DisplayObject child, int index)
        {
            int count = _children.Count;
            if (index >= 0 && index <= count)
            {
                if (child.parent == this)
                {
                    SetChildIndex(child, index);
                }
                else
                {
                    child.RemoveFromParent();
                    if (index == count)
                        _children.Add(child);
                    else
                        _children.Insert(index, child);
                    child.InternalSetParent(this);

                    if (stage != null)
                    {
                        if (child is Container)
                            child.BroadcastEvent("onAddedToStage", null);
                        else
                            child.DispatchEvent("onAddedToStage", null);
                    }

                    InvalidateBatchingState(true);
                }
                return child;
            }
            else
            {
                throw new Exception("Invalid child index");
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="child"></param>
        /// <returns></returns>
        public bool Contains(DisplayObject child)
        {
            return _children.Contains(child);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public DisplayObject GetChildAt(int index)
        {
            return _children[index];
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public DisplayObject GetChild(string name)
        {
            int cnt = _children.Count;
            for (int i = 0; i < cnt; ++i)
            {
                if (_children[i].name == name)
                    return _children[i];
            }

            return null;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public DisplayObject[] GetChildren()
        {
            return _children.ToArray();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="child"></param>
        /// <returns></returns>
        public int GetChildIndex(DisplayObject child)
        {
            return _children.IndexOf(child);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="child"></param>
        /// <returns></returns>
        public DisplayObject RemoveChild(DisplayObject child)
        {
            return RemoveChild(child, false);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="child"></param>
        /// <param name="dispose"></param>
        /// <returns></returns>
        public DisplayObject RemoveChild(DisplayObject child, bool dispose)
        {
            if (child.parent != this)
                throw new Exception("obj is not a child");

            int i = _children.IndexOf(child);
            if (i >= 0)
                return RemoveChildAt(i, dispose);
            else
                return null;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public DisplayObject RemoveChildAt(int index)
        {
            return RemoveChildAt(index, false);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="index"></param>
        /// <param name="dispose"></param>
        /// <returns></returns>
        public DisplayObject RemoveChildAt(int index, bool dispose)
        {
            if (index >= 0 && index < _children.Count)
            {
                DisplayObject child = _children[index];

                if (stage != null && (child._flags & Flags.Disposed) == 0)
                {
                    if (child is Container)
                    {
                        child.BroadcastEvent("onRemovedFromStage", null);
                        if (child == Stage.inst.focus || ((Container)child).IsAncestorOf(Stage.inst.focus))
                            Stage.inst._OnFocusRemoving(this);
                    }
                    else
                    {
                        child.DispatchEvent("onRemovedFromStage", null);
                        if (child == Stage.inst.focus)
                            Stage.inst._OnFocusRemoving(this);
                    }
                }
                _children.Remove(child);
                InvalidateBatchingState(true);
                if (!dispose)
                    child.InternalSetParent(null);
                else
                    child.Dispose();

                return child;
            }
            else
                throw new Exception("Invalid child index");
        }

        /// <summary>
        /// 
        /// </summary>
        public void RemoveChildren()
        {
            RemoveChildren(0, int.MaxValue, false);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="beginIndex"></param>
        /// <param name="endIndex"></param>
        /// <param name="dispose"></param>
        public void RemoveChildren(int beginIndex, int endIndex, bool dispose)
        {
            if (endIndex < 0 || endIndex >= numChildren)
                endIndex = numChildren - 1;

            for (int i = beginIndex; i <= endIndex; ++i)
                RemoveChildAt(beginIndex, dispose);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="child"></param>
        /// <param name="index"></param>
        public void SetChildIndex(DisplayObject child, int index)
        {
            int oldIndex = _children.IndexOf(child);
            if (oldIndex == index) return;
            if (oldIndex == -1) throw new ArgumentException("Not a child of this container");
            _children.RemoveAt(oldIndex);
            if (index >= _children.Count)
                _children.Add(child);
            else
                _children.Insert(index, child);
            InvalidateBatchingState(true);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="child1"></param>
        /// <param name="child2"></param>
        public void SwapChildren(DisplayObject child1, DisplayObject child2)
        {
            int index1 = _children.IndexOf(child1);
            int index2 = _children.IndexOf(child2);
            if (index1 == -1 || index2 == -1)
                throw new Exception("Not a child of this container");
            SwapChildrenAt(index1, index2);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="index1"></param>
        /// <param name="index2"></param>
        public void SwapChildrenAt(int index1, int index2)
        {
            DisplayObject obj1 = _children[index1];
            DisplayObject obj2 = _children[index2];
            _children[index1] = obj2;
            _children[index2] = obj1;
            InvalidateBatchingState(true);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="indice"></param>
        /// <param name="objs"></param>
        public void ChangeChildrenOrder(IList<int> indice, IList<DisplayObject> objs)
        {
            int cnt = objs.Count;
            for (int i = 0; i < cnt; i++)
            {
                DisplayObject obj = objs[i];
                if (obj.parent != this)
                    throw new Exception("Not a child of this container");

                _children[indice[i]] = obj;
            }
            InvalidateBatchingState(true);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public IEnumerator<DisplayObject> GetDescendants(bool backward)
        {
            return new DescendantsEnumerator(this, backward);
        }

        /// <summary>
        /// 
        /// </summary>
        public Rect? clipRect
        {
            get { return _clipRect; }
            set
            {
                if (_clipRect != value)
                {
                    _clipRect = value;
                    UpdateBatchingFlags();
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public DisplayObject mask
        {
            get { return _mask; }
            set
            {
                if (_mask != value)
                {
                    _mask = value;
                    UpdateBatchingFlags();
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public void CreateGraphics()
        {
            if (graphics == null)
            {
                graphics = new NGraphics(this.gameObject);
                graphics.texture = NTexture.Empty;
            }
        }

        public override Rect GetBounds(DisplayObject targetSpace)
        {
            if (_clipRect != null)
                return TransformRect((Rect)_clipRect, targetSpace);

            int count = _children.Count;

            Rect rect;
            if (count == 0)
            {
                Vector2 v = TransformPoint(Vector2.zero, targetSpace);
                rect = Rect.MinMaxRect(v.x, v.y, 0, 0);
            }
            else if (count == 1)
            {
                rect = _children[0].GetBounds(targetSpace);
            }
            else
            {
                float minX = float.MaxValue, maxX = float.MinValue;
                float minY = float.MaxValue, maxY = float.MinValue;

                for (int i = 0; i < count; ++i)
                {
                    rect = _children[i].GetBounds(targetSpace);
                    minX = minX < rect.xMin ? minX : rect.xMin;
                    maxX = maxX > rect.xMax ? maxX : rect.xMax;
                    minY = minY < rect.yMin ? minY : rect.yMin;
                    maxY = maxY > rect.yMax ? maxY : rect.yMax;
                }

                rect = Rect.MinMaxRect(minX, minY, maxX, maxY);
            }

            return rect;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public Camera GetRenderCamera()
        {
            if (renderMode == RenderMode.ScreenSpaceOverlay)
                return StageCamera.main;
            else
            {
                Camera cam = this.renderCamera;
                if (cam == null)
                {
                    if (HitTestContext.cachedMainCamera != null)
                        cam = HitTestContext.cachedMainCamera;
                    else
                    {
                        cam = Camera.main;
                        if (cam == null)
                            cam = StageCamera.main;
                    }
                }
                return cam;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="stagePoint"></param>
        /// <param name="forTouch"></param>
        /// <param name="displayIndex"></param>
        /// <returns></returns>
        public DisplayObject HitTest(Vector2 stagePoint, bool forTouch)
        {
            if (StageCamera.main == null)
            {
                if (this is Stage)
                    return this;
                else
                    return null;
            }

            HitTestContext.screenPoint = new Vector3(stagePoint.x, Screen.height - stagePoint.y, 0);
            if (Display.displays.Length > 1)
            {
                Vector3 p = Display.RelativeMouseAt(HitTestContext.screenPoint);
                if (p != Vector3.zero)
                    HitTestContext.screenPoint = p;
            }
            HitTestContext.worldPoint = StageCamera.main.ScreenToWorldPoint(HitTestContext.screenPoint);
            HitTestContext.direction = Vector3.back;
            HitTestContext.forTouch = forTouch;
            HitTestContext.camera = StageCamera.main;

            DisplayObject ret = HitTest();
            if (ret != null)
                return ret;
            else if (this is Stage)
                return this;
            else
                return null;
        }

        override protected DisplayObject HitTest()
        {
            if ((_flags & Flags.UserGameObject) != 0 && !gameObject.activeInHierarchy)
                return null;

            if (this.cachedTransform.localScale.x == 0 || this.cachedTransform.localScale.y == 0)
                return null;

            Camera savedCamera = HitTestContext.camera;
            Vector3 savedWorldPoint = HitTestContext.worldPoint;
            Vector3 savedDirection = HitTestContext.direction;
            DisplayObject target;

            if (renderMode != RenderMode.ScreenSpaceOverlay || (_flags & Flags.UserGameObject) != 0)
            {
                Camera cam = GetRenderCamera();
                if (cam.targetDisplay != HitTestContext.screenPoint.z)
                    return null;

                HitTestContext.camera = cam;
                if (renderMode == RenderMode.WorldSpace)
                {
                    Vector3 screenPoint = HitTestContext.camera.WorldToScreenPoint(this.cachedTransform.position); //only for query z value
                    screenPoint.x = HitTestContext.screenPoint.x;
                    screenPoint.y = HitTestContext.screenPoint.y;

                    //获得本地z轴在世界坐标的方向
                    HitTestContext.worldPoint = HitTestContext.camera.ScreenToWorldPoint(screenPoint);
                    Ray ray = HitTestContext.camera.ScreenPointToRay(screenPoint);
                    HitTestContext.direction = Vector3.zero - ray.direction;
                }
                else if (renderMode == RenderMode.ScreenSpaceCamera)
                {
                    HitTestContext.worldPoint = HitTestContext.camera.ScreenToWorldPoint(HitTestContext.screenPoint);
                }
            }
            else
            {
                if (HitTestContext.camera.targetDisplay != HitTestContext.screenPoint.z && !(this is Stage))
                    return null;
            }

            target = HitTest_Container();

            HitTestContext.camera = savedCamera;
            HitTestContext.worldPoint = savedWorldPoint;
            HitTestContext.direction = savedDirection;

            return target;
        }

        DisplayObject HitTest_Container()
        {
            Vector2 localPoint = WorldToLocal(HitTestContext.worldPoint, HitTestContext.direction);
            if (_vertexMatrix != null)
                HitTestContext.worldPoint = this.cachedTransform.TransformPoint(new Vector2(localPoint.x, -localPoint.y));

            if (hitArea != null)
            {
                if (!hitArea.HitTest(_contentRect, localPoint))
                    return null;

                if (hitArea is MeshColliderHitTest)
                    localPoint = ((MeshColliderHitTest)hitArea).lastHit;
            }
            else
            {
                if (_clipRect != null && !((Rect)_clipRect).Contains(localPoint))
                    return null;
            }

            if (_mask != null)
            {
                DisplayObject tmp = _mask.InternalHitTestMask();
                if (!reversedMask && tmp == null || reversedMask && tmp != null)
                    return null;
            }

            DisplayObject target = null;
            if (touchChildren)
            {
                int count = _children.Count;
                for (int i = count - 1; i >= 0; --i) // front to back!
                {
                    DisplayObject child = _children[i];
                    if ((child._flags & Flags.GameObjectDisposed) != 0)
                    {
                        child.DisplayDisposedWarning();
                        continue;
                    }

                    if (child == _mask || (child._flags & Flags.TouchDisabled) != 0)
                        continue;

                    target = child.InternalHitTest();
                    if (target != null)
                        break;
                }
            }

            if (target == null && opaque && (hitArea != null || _contentRect.Contains(localPoint)))
                target = this;

            return target;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public bool IsAncestorOf(DisplayObject obj)
        {
            if (obj == null)
                return false;

            Container p = obj.parent;
            while (p != null)
            {
                if (p == this)
                    return true;

                p = p.parent;
            }
            return false;
        }

        /// <summary>
        /// 
        /// </summary>
        public bool fairyBatching
        {
            get { return (_flags & Flags.FairyBatching) != 0; }
            set
            {
                bool oldValue = (_flags & Flags.FairyBatching) != 0;
                if (oldValue != value)
                {
                    if (value)
                        _flags |= Flags.FairyBatching;
                    else
                        _flags &= ~Flags.FairyBatching;
                    UpdateBatchingFlags();
                }
            }
        }

        internal void UpdateBatchingFlags()
        {
            bool oldValue = (_flags & Flags.BatchingRoot) != 0;
            bool newValue = (_flags & Flags.FairyBatching) != 0 || _clipRect != null || _mask != null || _paintingMode > 0;
            if (newValue)
                _flags |= Flags.BatchingRoot;
            else
                _flags &= ~Flags.BatchingRoot;
            if (oldValue != newValue)
            {
                if (newValue)
                    _flags |= Flags.BatchingRequested;
                else if (_descendants != null)
                    _descendants.Clear();

                InvalidateBatchingState();
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="childrenChanged"></param>
        public void InvalidateBatchingState(bool childrenChanged)
        {
            if (childrenChanged && (_flags & Flags.BatchingRoot) != 0)
                _flags |= Flags.BatchingRequested;
            else
            {
                Container p = this.parent;
                while (p != null)
                {
                    if ((p._flags & Flags.BatchingRoot) != 0)
                    {
                        p._flags |= Flags.BatchingRequested;
                        break;
                    }

                    p = p.parent;
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        public void SetChildrenLayer(int value)
        {
            int cnt = _children.Count;
            for (int i = 0; i < cnt; i++)
            {
                DisplayObject child = _children[i];
                child._SetLayerDirect(value);
                if ((child is Container) && child._paintingMode == 0)
                    ((Container)child).SetChildrenLayer(value);
            }
        }

        override public void Update(UpdateContext context)
        {
            if ((_flags & Flags.UserGameObject) != 0 && !gameObject.activeInHierarchy)
                return;

            base.Update(context);

            if (_paintingMode != 0)
            {
                if ((_flags & Flags.CacheAsBitmap) != 0 && _paintingInfo.flag == 2)
                {
                    if (onUpdate != null)
                        onUpdate();
                    return;
                }

                context.EnterPaintingMode();
            }

            if (_mask != null)
            {
                context.EnterClipping(this.id, reversedMask);
                if (_mask.graphics != null)
                    _mask.graphics._PreUpdateMask(context, _mask.id);
            }
            else if (_clipRect != null)
                context.EnterClipping(this.id, this.TransformRect((Rect)_clipRect, null), clipSoftness);

            float savedAlpha = context.alpha;
            context.alpha *= this.alpha;
            bool savedGrayed = context.grayed;
            context.grayed = context.grayed || this.grayed;

            if ((_flags & Flags.FairyBatching) != 0)
                context.batchingDepth++;

            if (context.batchingDepth > 0)
            {
                int cnt = _children.Count;
                for (int i = 0; i < cnt; i++)
                {
                    DisplayObject child = _children[i];
                    if ((child._flags & Flags.GameObjectDisposed) != 0)
                    {
                        child.DisplayDisposedWarning();
                        continue;
                    }

                    if (child.visible)
                        child.Update(context);
                }
            }
            else
            {
                if (_mask != null)
                    _mask.renderingOrder = context.renderingOrder++;

                int cnt = _children.Count;
                for (int i = 0; i < cnt; i++)
                {
                    DisplayObject child = _children[i];
                    if ((child._flags & Flags.GameObjectDisposed) != 0)
                    {
                        child.DisplayDisposedWarning();
                        continue;
                    }

                    if (child.visible)
                    {
                        if (!(child.graphics != null && child.graphics._maskFlag == 1)) //if not a mask
                            child.renderingOrder = context.renderingOrder++;

                        child.Update(context);
                    }
                }

                if (_mask != null)
                {
                    if (_mask.graphics != null)
                        _mask.graphics._SetStencilEraserOrder(context.renderingOrder++);
                }
            }

            if ((_flags & Flags.FairyBatching) != 0)
            {
                if (context.batchingDepth == 1)
                    SetRenderingOrder(context);
                context.batchingDepth--;
            }

            context.alpha = savedAlpha;
            context.grayed = savedGrayed;

            if (_clipRect != null || _mask != null)
                context.LeaveClipping();

            if (_paintingMode != 0)
            {
                context.LeavePaintingMode();
                UpdateContext.OnEnd += _paintingInfo.captureDelegate;
            }

            if (onUpdate != null)
                onUpdate();
        }

        private void SetRenderingOrder(UpdateContext context)
        {
            if ((_flags & Flags.BatchingRequested) != 0)
                DoFairyBatching();

            if (_mask != null)
                _mask.renderingOrder = context.renderingOrder++;

            int cnt = _descendants.Count;
            for (int i = 0; i < cnt; i++)
            {
                DisplayObject child = _descendants[i];
                if (!(child.graphics != null && child.graphics._maskFlag == 1))
                    child.renderingOrder = context.renderingOrder++;

                if ((child._flags & Flags.BatchingRoot) != 0)
                    ((Container)child).SetRenderingOrder(context);
            }

            if (_mask != null)
            {
                if (_mask.graphics != null)
                    _mask.graphics._SetStencilEraserOrder(context.renderingOrder++);
            }
        }

        private void DoFairyBatching()
        {
            _flags &= ~Flags.BatchingRequested;

            if (_descendants == null)
                _descendants = new List<DisplayObject>();
            else
                _descendants.Clear();
            CollectChildren(this, false);

            int cnt = _descendants.Count;

            int i, j, k, m;
            object curMat, testMat, lastMat;
            DisplayObject current, test;
            float[] bound;
            for (i = 0; i < cnt; i++)
            {
                current = _descendants[i];
                bound = current._batchingBounds;
                curMat = current.material;
                if (curMat == null || (current._flags & Flags.SkipBatching) != 0)
                    continue;

                k = -1;
                lastMat = null;
                m = i;
                for (j = i - 1; j >= 0; j--)
                {
                    test = _descendants[j];
                    if ((test._flags & Flags.SkipBatching) != 0)
                        break;

                    testMat = test.material;
                    if (testMat != null)
                    {
                        if (lastMat != testMat)
                        {
                            lastMat = testMat;
                            m = j + 1;
                        }

                        if (curMat == testMat)
                            k = m;
                    }

                    if ((bound[0] > test._batchingBounds[0] ? bound[0] : test._batchingBounds[0])
                        <= (bound[2] < test._batchingBounds[2] ? bound[2] : test._batchingBounds[2])
                        && (bound[1] > test._batchingBounds[1] ? bound[1] : test._batchingBounds[1])
                        <= (bound[3] < test._batchingBounds[3] ? bound[3] : test._batchingBounds[3]))
                    {
                        if (k == -1)
                            k = m;
                        break;
                    }
                }
                if (k != -1 && i != k)
                {
                    _descendants.RemoveAt(i);
                    _descendants.Insert(k, current);
                }
            }

            //Debug.Log("DoFairyBatching " + cnt + "," + this.cachedTransform.GetInstanceID());
        }

        private void CollectChildren(Container initiator, bool outlineChanged)
        {
            int count = _children.Count;
            for (int i = 0; i < count; i++)
            {
                DisplayObject child = _children[i];
                if (!child.visible)
                    continue;

                if (child._batchingBounds == null)
                    child._batchingBounds = new float[4];

                if (child is Container)
                {
                    Container container = (Container)child;
                    if ((container._flags & Flags.BatchingRoot) != 0)
                    {
                        initiator._descendants.Add(container);
                        if (outlineChanged || (container._flags & Flags.OutlineChanged) != 0)
                        {
                            Rect rect = container.GetBounds(initiator);
                            container._batchingBounds[0] = rect.xMin;
                            container._batchingBounds[1] = rect.yMin;
                            container._batchingBounds[2] = rect.xMax;
                            container._batchingBounds[3] = rect.yMax;
                        }
                        if ((container._flags & Flags.BatchingRequested) != 0)
                            container.DoFairyBatching();
                    }
                    else
                        container.CollectChildren(initiator, outlineChanged || (container._flags & Flags.OutlineChanged) != 0);
                }
                else if (child != initiator._mask)
                {
                    if (outlineChanged || (child._flags & Flags.OutlineChanged) != 0)
                    {
                        Rect rect = child.GetBounds(initiator);
                        child._batchingBounds[0] = rect.xMin;
                        child._batchingBounds[1] = rect.yMin;
                        child._batchingBounds[2] = rect.xMax;
                        child._batchingBounds[3] = rect.yMax;
                    }
                    initiator._descendants.Add(child);
                }

                child._flags &= ~Flags.OutlineChanged;
            }
        }

        public override void Dispose()
        {
            if ((_flags & Flags.Disposed) != 0)
                return;

            base.Dispose(); //Destroy GameObject tree first, avoid destroying each seperately;

            int numChildren = _children.Count;
            for (int i = numChildren - 1; i >= 0; --i)
            {
                DisplayObject obj = _children[i];
                obj.InternalSetParent(null); //Avoid RemoveParent call
                obj.Dispose();
            }
        }

        /// <summary>
        /// If true, when the container is focused, tab navigation is lock inside it.
        /// </summary>
        public bool tabStopChildren
        {
            get { return (_flags & Flags.TabStopChildren) != 0; }
            set
            {
                if (value)
                    _flags |= Flags.TabStopChildren;
                else
                    _flags &= ~Flags.TabStopChildren;
            }
        }

        struct DescendantsEnumerator : IEnumerator<DisplayObject>
        {
            Container _root;
            Container _com;
            DisplayObject _current;
            int _index;
            bool _forward;

            public DescendantsEnumerator(Container root, bool backward)
            {
                _root = root;
                _com = _root;
                _current = null;
                _forward = !backward;
                if (_forward)
                    _index = 0;
                else
                    _index = _com._children.Count - 1;
            }

            public DisplayObject Current
            {
                get { return _current; }
            }

            object IEnumerator.Current
            {
                get { return _current; }
            }

            public bool MoveNext()
            {
                if (_forward)
                {
                    if (_index >= _com._children.Count)
                    {
                        if (_com == _root)
                        {
                            _current = null;
                            return false;
                        }

                        _current = _com;
                        _com = _com.parent;
                        _index = _com.GetChildIndex(_current) + 1;
                        return true;
                    }
                    else
                    {
                        DisplayObject obj = _com._children[_index];
                        if (obj is Container)
                        {
                            _com = (Container)obj;
                            _index = 0;
                            return MoveNext();
                        }
                        _index++;
                        _current = obj;
                        return true;
                    }
                }
                else
                {
                    if (_index < 0)
                    {
                        if (_com == _root)
                        {
                            _current = null;
                            return false;
                        }

                        _current = _com;
                        _com = _com.parent;
                        _index = _com.GetChildIndex(_current) - 1;
                        return true;
                    }
                    else
                    {
                        DisplayObject obj = _com._children[_index];
                        if (obj is Container)
                        {
                            _com = (Container)obj;
                            _index = _com._children.Count - 1;
                            return MoveNext();
                        }
                        _index--;
                        _current = obj;
                        return true;
                    }
                }
            }

            public void Reset()
            {
                _com = _root;
                _current = null;
                _index = 0;
            }

            public void Dispose()
            {
            }
        }
    }
}