using System;
using System.Collections.Generic;
using UnityEngine;
using FairyGUI.Utils;
#if FAIRYGUI_TOLUA
using LuaInterface;
#endif
namespace FairyGUI
{
    /// 
    /// Component
    /// 
    public class GComponent : GObject
    {
        /// 
        /// Root container.
        /// 
        public Container rootContainer { get; private set; }
        /// 
        /// Content container. If the component is not clipped, then container==rootContainer.
        /// 
        public Container container { get; protected set; }
        /// 
        /// ScrollPane of the component. If the component is not scrollable, the value is null.
        /// 
        public ScrollPane scrollPane { get; private set; }
        internal List _children;
        internal List _controllers;
        internal List _transitions;
        internal bool _buildingDisplayList;
        protected Margin _margin;
        protected bool _trackBounds;
        protected bool _boundsChanged;
        protected ChildrenRenderOrder _childrenRenderOrder;
        protected int _apexIndex;
        internal Vector2 _alignOffset;
        Vector2 _clipSoftness;
        int _sortingChildCount;
        Action _buildDelegate;
        Controller _applyingController;
        EventListener _onDrop;
        public GComponent()
        {
            _children = new List();
            _controllers = new List();
            _transitions = new List();
            _margin = new Margin();
            _buildDelegate = BuildNativeDisplayList;
        }
        override protected void CreateDisplayObject()
        {
            rootContainer = new Container("GComponent");
            rootContainer.gOwner = this;
            rootContainer.onUpdate += OnUpdate;
            container = rootContainer;
            displayObject = rootContainer;
        }
        override public void Dispose()
        {
            int cnt = _transitions.Count;
            for (int i = 0; i < cnt; ++i)
            {
                Transition trans = _transitions[i];
                trans.Dispose();
            }
            cnt = _controllers.Count;
            for (int i = 0; i < cnt; ++i)
            {
                Controller c = _controllers[i];
                c.Dispose();
            }
            if (scrollPane != null)
                scrollPane.Dispose();
            base.Dispose(); //Dispose native tree first, avoid DisplayObject.RemoveFromParent call
            cnt = _children.Count;
            for (int i = cnt - 1; i >= 0; --i)
            {
                GObject obj = _children[i];
                obj.InternalSetParent(null); //Avoid GObject.RemoveParent call
                obj.Dispose();
            }
#if FAIRYGUI_TOLUA
            if (_peerTable != null)
            {
                _peerTable.Dispose();
                _peerTable = null;
            }
#endif
        }
        /// 
        /// Dispatched when an object was dragged and dropped to this component.
        /// 
        public EventListener onDrop
        {
            get { return _onDrop ?? (_onDrop = new EventListener(this, "onDrop")); }
        }
        /// 
        /// Draw call optimization switch.
        /// 
        public bool fairyBatching
        {
            get { return rootContainer.fairyBatching; }
            set { rootContainer.fairyBatching = value; }
        }
        /// 
        /// 
        /// 
        /// 
        public void InvalidateBatchingState(bool childChanged)
        {
            if (childChanged)
                container.InvalidateBatchingState(childChanged);
            else
                rootContainer.InvalidateBatchingState();
        }
        /// 
        /// If true, mouse/touch events cannot pass through the empty area of the component. Default is true.
        /// 
        public bool opaque
        {
            get { return rootContainer.opaque; }
            set { rootContainer.opaque = value; }
        }
        /// 
        /// 
        /// 
        /// 
        public Margin margin
        {
            get { return _margin; }
            set
            {
                _margin = value;
                if (rootContainer.clipRect != null && scrollPane == null) //如果scrollPane不为空,则HandleSizeChanged里面的处理会促使ScrollPane处理
                    container.SetXY(_margin.left + _alignOffset.x, _margin.top + _alignOffset.y);
                HandleSizeChanged();
            }
        }
        /// 
        /// 
        /// 
        public ChildrenRenderOrder childrenRenderOrder
        {
            get { return _childrenRenderOrder; }
            set
            {
                if (_childrenRenderOrder != value)
                {
                    _childrenRenderOrder = value;
                    BuildNativeDisplayList();
                }
            }
        }
        /// 
        /// 
        /// 
        public int apexIndex
        {
            get { return _apexIndex; }
            set
            {
                if (_apexIndex != value)
                {
                    _apexIndex = value;
                    if (_childrenRenderOrder == ChildrenRenderOrder.Arch)
                        BuildNativeDisplayList();
                }
            }
        }
        /// 
        /// If true, children can be navigated by TAB from first to last, and repeat
        /// 
        public bool tabStopChildren
        {
            get { return rootContainer.tabStopChildren; }
            set { rootContainer.tabStopChildren = value; }
        }
        /// 
        /// Add a child to the component. It will be at the frontmost position.
        /// 
        /// A child object
        /// GObject
        public GObject AddChild(GObject child)
        {
            AddChildAt(child, _children.Count);
            return child;
        }
        /// 
        /// Adds a child to the component at a certain index.
        /// 
        /// A child object
        /// Index
        /// GObject
        virtual public GObject AddChildAt(GObject child, int index)
        {
            if (index >= 0 && index <= _children.Count)
            {
                if (child.parent == this)
                {
                    SetChildIndex(child, index);
                }
                else
                {
                    child.RemoveFromParent();
                    child.InternalSetParent(this);
                    int cnt = _children.Count;
                    if (child.sortingOrder != 0)
                    {
                        _sortingChildCount++;
                        index = GetInsertPosForSortingChild(child);
                    }
                    else if (_sortingChildCount > 0)
                    {
                        if (index > (cnt - _sortingChildCount))
                            index = cnt - _sortingChildCount;
                    }
                    if (index == cnt)
                        _children.Add(child);
                    else
                        _children.Insert(index, child);
                    ChildStateChanged(child);
                    SetBoundsChangedFlag();
                }
                return child;
            }
            else
            {
                throw new Exception("Invalid child index: " + index + ">" + _children.Count);
            }
        }
        int GetInsertPosForSortingChild(GObject target)
        {
            int cnt = _children.Count;
            int i;
            for (i = 0; i < cnt; i++)
            {
                GObject child = _children[i];
                if (child == target)
                    continue;
                if (target.sortingOrder < child.sortingOrder)
                    break;
            }
            return i;
        }
        /// 
        /// Removes a child from the component. If the object is not a child, nothing happens. 
        /// 
        /// A child object
        /// GObject
        public GObject RemoveChild(GObject child)
        {
            return RemoveChild(child, false);
        }
        /// 
        /// Removes a child from the component. If the object is not a child, nothing happens. 
        /// 
        /// A child object
        /// If true, the child will be disposed right away.
        /// GObject
        public GObject RemoveChild(GObject child, bool dispose)
        {
            int childIndex = _children.IndexOf(child);
            if (childIndex != -1)
            {
                RemoveChildAt(childIndex, dispose);
            }
            return child;
        }
        /// 
        /// Removes a child at a certain index. Children above the child will move down.
        /// 
        /// Index
        /// GObject
        public GObject RemoveChildAt(int index)
        {
            return RemoveChildAt(index, false);
        }
        /// 
        /// Removes a child at a certain index. Children above the child will move down.
        /// 
        /// Index
        /// If true, the child will be disposed right away.
        /// GObject
        virtual public GObject RemoveChildAt(int index, bool dispose)
        {
            if (index >= 0 && index < numChildren)
            {
                GObject child = _children[index];
                child.InternalSetParent(null);
                if (child.sortingOrder != 0)
                    _sortingChildCount--;
                _children.RemoveAt(index);
                child.group = null;
                if (child.inContainer)
                {
                    container.RemoveChild(child.displayObject);
                    if (_childrenRenderOrder == ChildrenRenderOrder.Arch)
                    {
                        UpdateContext.OnBegin -= _buildDelegate;
                        UpdateContext.OnBegin += _buildDelegate;
                    }
                }
                if (dispose)
                    child.Dispose();
                SetBoundsChangedFlag();
                return child;
            }
            else
                throw new Exception("Invalid child index: " + index + ">" + numChildren);
        }
        /// 
        /// Remove all children.
        /// 
        public void RemoveChildren()
        {
            RemoveChildren(0, -1, false);
        }
        /// 
        /// Removes a range of children from the container (endIndex included). 
        /// 
        /// Begin index.
        /// End index.(Included).
        /// If true, the child will be disposed right away.
        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);
        }
        /// 
        /// Returns a child object at a certain index. If index out of bounds, exception raised.
        /// 
        /// Index
        /// A child object.
        public GObject GetChildAt(int index)
        {
            if (index >= 0 && index < numChildren)
                return _children[index];
            else
                throw new Exception("Invalid child index: " + index + ">" + numChildren);
        }
        /// 
        /// Returns a child object with a certain name.
        /// 
        /// Name
        /// A child object. Null if not found.
        public GObject GetChild(string name)
        {
            int cnt = _children.Count;
            for (int i = 0; i < cnt; ++i)
            {
                if (_children[i].name == name)
                    return _children[i];
            }
            return null;
        }
        public GObject GetChildByPath(string path)
        {
            string[] arr = path.Split('.');
            int cnt = arr.Length;
            GComponent gcom = this;
            GObject obj = null;
            for (int i = 0; i < cnt; ++i)
            {
                obj = gcom.GetChild(arr[i]);
                if (obj == null)
                    break;
                if (i != cnt - 1)
                {
                    if (!(obj is GComponent))
                    {
                        obj = null;
                        break;
                    }
                    else
                        gcom = (GComponent)obj;
                }
            }
            return obj;
        }
        /// 
        /// Returns a visible child object with a certain name.
        /// 
        /// Name
        /// A child object. Null if not found.
        public GObject GetVisibleChild(string name)
        {
            int cnt = _children.Count;
            for (int i = 0; i < cnt; ++i)
            {
                GObject child = _children[i];
                if (child.internalVisible && child.internalVisible2 && child.name == name)
                    return child;
            }
            return null;
        }
        /// 
        /// Returns a child object belong to a group with a certain name.
        /// 
        /// A group object
        /// Name
        /// A child object. Null if not found.
        public GObject GetChildInGroup(GGroup group, string name)
        {
            int cnt = _children.Count;
            for (int i = 0; i < cnt; ++i)
            {
                GObject child = _children[i];
                if (child.group == group && child.name == name)
                    return child;
            }
            return null;
        }
        internal GObject GetChildById(string id)
        {
            int cnt = _children.Count;
            for (int i = 0; i < cnt; ++i)
            {
                if (_children[i].id == id)
                    return _children[i];
            }
            return null;
        }
        /// 
        /// Returns a copy of all children with an array.
        /// 
        /// An array contains all children
        public GObject[] GetChildren()
        {
            return _children.ToArray();
        }
        /// 
        /// Returns the index of a child within the container, or "-1" if it is not found.
        /// 
        /// A child object
        /// Index of the child. -1 If not found.
        public int GetChildIndex(GObject child)
        {
            return _children.IndexOf(child);
        }
        /// 
        /// Moves a child to a certain index. Children at and after the replaced position move up.
        /// 
        /// A Child
        /// Index
        public void SetChildIndex(GObject child, int index)
        {
            int oldIndex = _children.IndexOf(child);
            if (oldIndex == -1)
                throw new ArgumentException("Not a child of this container");
            if (child.sortingOrder != 0) //no effect
                return;
            if (_sortingChildCount > 0)
            {
                int cnt = _children.Count;
                if (index > (cnt - _sortingChildCount - 1))
                    index = cnt - _sortingChildCount - 1;
            }
            _SetChildIndex(child, oldIndex, index);
        }
        /// 
        /// Moves a child to a certain position which is in front of the child previously at given index.
        /// 与SetChildIndex不同的是,如果child原来在index的前面,那么child插入的位置是index-1,即保证排在原来占据index的对象的前面。
        /// 
        /// 
        /// 
        public int SetChildIndexBefore(GObject child, int index)
        {
            int oldIndex = _children.IndexOf(child);
            if (oldIndex == -1)
                throw new ArgumentException("Not a child of this container");
            if (child.sortingOrder != 0) //no effect
                return oldIndex;
            int cnt = _children.Count;
            if (_sortingChildCount > 0)
            {
                if (index > (cnt - _sortingChildCount - 1))
                    index = cnt - _sortingChildCount - 1;
            }
            if (oldIndex < index)
                return _SetChildIndex(child, oldIndex, index - 1);
            else
                return _SetChildIndex(child, oldIndex, index);
        }
        int _SetChildIndex(GObject child, int oldIndex, int index)
        {
            int cnt = _children.Count;
            if (index > cnt)
                index = cnt;
            if (oldIndex == index)
                return oldIndex;
            _children.RemoveAt(oldIndex);
            if (index >= cnt)
                _children.Add(child);
            else
                _children.Insert(index, child);
            if (child.inContainer)
            {
                int displayIndex = 0;
                if (_childrenRenderOrder == ChildrenRenderOrder.Ascent)
                {
                    for (int i = 0; i < index; i++)
                    {
                        GObject g = _children[i];
                        if (g.inContainer)
                            displayIndex++;
                    }
                    container.SetChildIndex(child.displayObject, displayIndex);
                }
                else if (_childrenRenderOrder == ChildrenRenderOrder.Descent)
                {
                    for (int i = cnt - 1; i > index; i--)
                    {
                        GObject g = _children[i];
                        if (g.inContainer)
                            displayIndex++;
                    }
                    container.SetChildIndex(child.displayObject, displayIndex);
                }
                else
                {
                    UpdateContext.OnBegin -= _buildDelegate;
                    UpdateContext.OnBegin += _buildDelegate;
                }
                SetBoundsChangedFlag();
            }
            return index;
        }
        /// 
        /// Swaps the indexes of two children. 
        /// 
        /// A child object
        /// A child object
        public void SwapChildren(GObject child1, GObject 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);
        }
        /// 
        ///  Swaps the indexes of two children.
        /// 
        /// index of first child
        /// index of second child
        public void SwapChildrenAt(int index1, int index2)
        {
            GObject child1 = _children[index1];
            GObject child2 = _children[index2];
            SetChildIndex(child1, index2);
            SetChildIndex(child2, index1);
        }
        /// 
        /// The number of children of this component.
        /// 
        public int numChildren
        {
            get { return _children.Count; }
        }
        /// 
        /// 
        /// 
        /// 
        /// 
        public bool IsAncestorOf(GObject obj)
        {
            if (obj == null)
                return false;
            GComponent p = obj.parent;
            while (p != null)
            {
                if (p == this)
                    return true;
                p = p.parent;
            }
            return false;
        }
        /// 
        /// 
        /// 
        /// 
        public void ChangeChildrenOrder(IList objs)
        {
            int cnt = objs.Count;
            for (int i = 0; i < cnt; i++)
            {
                GObject obj = objs[i];
                if (obj.parent != this)
                    throw new Exception("Not a child of this container");
                _children[i] = obj;
            }
            BuildNativeDisplayList();
            SetBoundsChangedFlag();
        }
        /// 
        /// Adds a controller to the container.
        /// 
        /// Controller object
        public void AddController(Controller controller)
        {
            _controllers.Add(controller);
            controller.parent = this;
            ApplyController(controller);
        }
        /// 
        /// Returns a controller object  at a certain index.
        /// 
        /// Index
        /// Controller object.
        public Controller GetControllerAt(int index)
        {
            return _controllers[index];
        }
        /// 
        /// Returns a controller object with a certain name.
        /// 
        /// Name
        /// Controller object. Null if not found.
        public Controller GetController(string name)
        {
            int cnt = _controllers.Count;
            for (int i = 0; i < cnt; ++i)
            {
                Controller c = _controllers[i];
                if (c.name == name)
                    return c;
            }
            return null;
        }
        /// 
        /// Removes a controller from the container. 
        /// 
        /// Controller object.
        public void RemoveController(Controller c)
        {
            int index = _controllers.IndexOf(c);
            if (index == -1)
                throw new Exception("controller not exists: " + c.name);
            c.parent = null;
            _controllers.RemoveAt(index);
            int cnt = _children.Count;
            for (int i = 0; i < cnt; ++i)
            {
                GObject child = _children[i];
                child.HandleControllerChanged(c);
            }
        }
        /// 
        /// Returns controller list.
        /// 
        /// Controller list
        public List Controllers
        {
            get { return _controllers; }
        }
        /// 
        /// Returns a transition object  at a certain index.
        /// 
        /// Index
        /// transition object.
        public Transition GetTransitionAt(int index)
        {
            return _transitions[index];
        }
        /// 
        /// Returns a transition object at a certain name. 
        /// 
        /// Name
        /// Transition Object
        public Transition GetTransition(string name)
        {
            int cnt = _transitions.Count;
            for (int i = 0; i < cnt; ++i)
            {
                Transition trans = _transitions[i];
                if (trans.name == name)
                    return trans;
            }
            return null;
        }
        internal void ChildStateChanged(GObject child)
        {
            if (_buildingDisplayList)
                return;
            int cnt = _children.Count;
            if (child is GGroup)
            {
                for (int i = 0; i < cnt; ++i)
                {
                    GObject g = _children[i];
                    if (g.group == child)
                        ChildStateChanged(g);
                }
                return;
            }
            if (child.displayObject == null)
                return;
            if (child.internalVisible)
            {
                if (child.displayObject.parent == null)
                {
                    if (_childrenRenderOrder == ChildrenRenderOrder.Ascent)
                    {
                        int index = 0;
                        for (int i = 0; i < cnt; i++)
                        {
                            GObject g = _children[i];
                            if (g == child)
                                break;
                            if (g.displayObject != null && g.displayObject.parent != null)
                                index++;
                        }
                        container.AddChildAt(child.displayObject, index);
                    }
                    else if (_childrenRenderOrder == ChildrenRenderOrder.Descent)
                    {
                        int index = 0;
                        for (int i = cnt - 1; i >= 0; i--)
                        {
                            GObject g = _children[i];
                            if (g == child)
                                break;
                            if (g.displayObject != null && g.displayObject.parent != null)
                                index++;
                        }
                        container.AddChildAt(child.displayObject, index);
                    }
                    else
                    {
                        container.AddChild(child.displayObject);
                        UpdateContext.OnBegin -= _buildDelegate;
                        UpdateContext.OnBegin += _buildDelegate;
                    }
                }
            }
            else
            {
                if (child.displayObject.parent != null)
                {
                    container.RemoveChild(child.displayObject);
                    if (_childrenRenderOrder == ChildrenRenderOrder.Arch)
                    {
                        UpdateContext.OnBegin -= _buildDelegate;
                        UpdateContext.OnBegin += _buildDelegate;
                    }
                }
            }
        }
        void BuildNativeDisplayList()
        {
            if (displayObject == null || displayObject.isDisposed)
                return;
            int cnt = _children.Count;
            if (cnt == 0)
                return;
            switch (_childrenRenderOrder)
            {
                case ChildrenRenderOrder.Ascent:
                    {
                        for (int i = 0; i < cnt; i++)
                        {
                            GObject child = _children[i];
                            if (child.displayObject != null && child.internalVisible)
                                container.AddChild(child.displayObject);
                        }
                    }
                    break;
                case ChildrenRenderOrder.Descent:
                    {
                        for (int i = cnt - 1; i >= 0; i--)
                        {
                            GObject child = _children[i];
                            if (child.displayObject != null && child.internalVisible)
                                container.AddChild(child.displayObject);
                        }
                    }
                    break;
                case ChildrenRenderOrder.Arch:
                    {
                        int apex = Mathf.Clamp(_apexIndex, 0, cnt);
                        for (int i = 0; i < apex; i++)
                        {
                            GObject child = _children[i];
                            if (child.displayObject != null && child.internalVisible)
                                container.AddChild(child.displayObject);
                        }
                        for (int i = cnt - 1; i >= apex; i--)
                        {
                            GObject child = _children[i];
                            if (child.displayObject != null && child.internalVisible)
                                container.AddChild(child.displayObject);
                        }
                    }
                    break;
            }
        }
        internal void ApplyController(Controller c)
        {
            _applyingController = c;
            int cnt = _children.Count;
            for (int i = 0; i < cnt; ++i)
            {
                GObject child = _children[i];
                child.HandleControllerChanged(c);
            }
            _applyingController = null;
            c.RunActions();
        }
        void ApplyAllControllers()
        {
            int cnt = _controllers.Count;
            for (int i = 0; i < cnt; ++i)
            {
                Controller controller = _controllers[i];
                ApplyController(controller);
            }
        }
        internal void AdjustRadioGroupDepth(GObject obj, Controller c)
        {
            int cnt = _children.Count;
            int i;
            GObject child;
            int myIndex = -1, maxIndex = -1;
            for (i = 0; i < cnt; i++)
            {
                child = _children[i];
                if (child == obj)
                {
                    myIndex = i;
                }
                else if ((child is GButton)
                    && ((GButton)child).relatedController == c)
                {
                    if (i > maxIndex)
                        maxIndex = i;
                }
            }
            if (myIndex < maxIndex)
            {
                if (_applyingController != null)
                    _children[maxIndex].HandleControllerChanged(_applyingController);
                this.SwapChildrenAt(myIndex, maxIndex);
            }
        }
        /// 
        /// If clipping softness is set, clipped containers will have soft border effect.
        /// 
        public Vector2 clipSoftness
        {
            get { return _clipSoftness; }
            set
            {
                _clipSoftness = value;
                if (scrollPane != null)
                    scrollPane.UpdateClipSoft();
                else if (_clipSoftness.x > 0 || _clipSoftness.y > 0)
                    rootContainer.clipSoftness = new Vector4(value.x, value.y, value.x, value.y);
                else
                    rootContainer.clipSoftness = null;
            }
        }
        /// 
        /// The mask of the component.
        /// 
        public DisplayObject mask
        {
            get { return container.mask; }
            set
            {
                container.mask = value;
                if (value != null && value.parent != container)
                    container.AddChild(value);
            }
        }
        /// 
        /// 
        /// 
        public bool reversedMask
        {
            get { return container.reversedMask; }
            set { container.reversedMask = value; }
        }
        /// 
        /// 
        /// 
        public string baseUserData
        {
            get
            {
                ByteBuffer buffer = packageItem.rawData;
                buffer.Seek(0, 4);
                return buffer.ReadS();
            }
        }
        /// 
        /// Test if a child is in view.
        /// 
        /// A child object
        /// True if in view
        public bool IsChildInView(GObject child)
        {
            if (scrollPane != null)
            {
                return scrollPane.IsChildInView(child);
            }
            else if (rootContainer.clipRect != null)
            {
                return child.x + child.width >= 0 && child.x <= this.width
                    && child.y + child.height >= 0 && child.y <= this.height;
            }
            else
                return true;
        }
        virtual public int GetFirstChildInView()
        {
            int cnt = _children.Count;
            for (int i = 0; i < cnt; ++i)
            {
                GObject child = _children[i];
                if (IsChildInView(child))
                    return i;
            }
            return -1;
        }
        protected void SetupScroll(ByteBuffer buffer)
        {
            if (rootContainer == container)
            {
                container = new Container();
                rootContainer.AddChild(container);
            }
            scrollPane = new ScrollPane(this);
            scrollPane.Setup(buffer);
        }
        protected void SetupOverflow(OverflowType overflow)
        {
            if (overflow == OverflowType.Hidden)
            {
                if (rootContainer == container)
                {
                    container = new Container();
                    rootContainer.AddChild(container);
                }
                UpdateClipRect();
                container.SetXY(_margin.left, _margin.top);
            }
            else if (_margin.left != 0 || _margin.top != 0)
            {
                if (rootContainer == container)
                {
                    container = new Container();
                    rootContainer.AddChild(container);
                }
                container.SetXY(_margin.left, _margin.top);
            }
        }
        void UpdateClipRect()
        {
            if (scrollPane == null)
            {
                float w = this.width - (_margin.left + _margin.right);
                float h = this.height - (_margin.top + _margin.bottom);
                rootContainer.clipRect = new Rect(_margin.left, _margin.top, w, h);
            }
            else
                rootContainer.clipRect = new Rect(0, 0, this.width, this.height);
        }
        override protected void HandleSizeChanged()
        {
            base.HandleSizeChanged();
            if (scrollPane != null)
                scrollPane.OnOwnerSizeChanged();
            if (rootContainer.clipRect != null)
                UpdateClipRect();
        }
        override protected void HandleGrayedChanged()
        {
            Controller cc = GetController("grayed");
            if (cc != null)
                cc.selectedIndex = this.grayed ? 1 : 0;
            else
                base.HandleGrayedChanged();
        }
        override public void HandleControllerChanged(Controller c)
        {
            base.HandleControllerChanged(c);
            if (scrollPane != null)
                scrollPane.HandleControllerChanged(c);
        }
        /// 
        /// Notify the component the bounds should recaculate.
        /// 
        public void SetBoundsChangedFlag()
        {
            if (scrollPane == null && !_trackBounds)
                return;
            _boundsChanged = true;
        }
        /// 
        /// Make sure the bounds of the component is correct. 
        /// Bounds of the component is not updated on every changed. For example, you add a new child to the list, children in the list will be rearranged in next frame.
        /// If you want to access the correct child position immediatelly, call this function first.
        /// 
        public void EnsureBoundsCorrect()
        {
            if (_boundsChanged)
                UpdateBounds();
        }
        virtual protected void UpdateBounds()
        {
            float ax, ay, aw, ah;
            if (_children.Count > 0)
            {
                ax = int.MaxValue;
                ay = int.MaxValue;
                float ar = int.MinValue, ab = int.MinValue;
                float tmp;
                int cnt = _children.Count;
                for (int i = 0; i < cnt; ++i)
                {
                    GObject child = _children[i];
                    tmp = child.x;
                    if (tmp < ax)
                        ax = tmp;
                    tmp = child.y;
                    if (tmp < ay)
                        ay = tmp;
                    tmp = child.x + child.actualWidth;
                    if (tmp > ar)
                        ar = tmp;
                    tmp = child.y + child.actualHeight;
                    if (tmp > ab)
                        ab = tmp;
                }
                aw = ar - ax;
                ah = ab - ay;
            }
            else
            {
                ax = 0;
                ay = 0;
                aw = 0;
                ah = 0;
            }
            SetBounds(ax, ay, aw, ah);
        }
        protected void SetBounds(float ax, float ay, float aw, float ah)
        {
            _boundsChanged = false;
            if (scrollPane != null)
                scrollPane.SetContentSize(Mathf.RoundToInt(ax + aw), Mathf.RoundToInt(ay + ah));
        }
        /// 
        /// Viwe port width of the container.
        /// 
        public float viewWidth
        {
            get
            {
                if (scrollPane != null)
                    return scrollPane.viewWidth;
                else
                    return this.width - _margin.left - _margin.right;
            }
            set
            {
                if (scrollPane != null)
                    scrollPane.viewWidth = value;
                else
                    this.width = value + _margin.left + _margin.right;
            }
        }
        /// 
        /// View port height of the container.
        /// 
        public float viewHeight
        {
            get
            {
                if (scrollPane != null)
                    return scrollPane.viewHeight;
                else
                    return this.height - _margin.top - _margin.bottom;
            }
            set
            {
                if (scrollPane != null)
                    scrollPane.viewHeight = value;
                else
                    this.height = value + _margin.top + _margin.bottom;
            }
        }
        virtual protected internal void GetSnappingPosition(ref float xValue, ref float yValue)
        {
            int cnt = _children.Count;
            if (cnt == 0)
                return;
            EnsureBoundsCorrect();
            GObject obj = null;
            int i = 0;
            if (yValue != 0)
            {
                for (; i < cnt; i++)
                {
                    obj = _children[i];
                    if (yValue < obj.y)
                    {
                        if (i == 0)
                        {
                            yValue = 0;
                            break;
                        }
                        else
                        {
                            GObject prev = _children[i - 1];
                            if (yValue < prev.y + prev.height / 2) //top half part
                                yValue = prev.y;
                            else //bottom half part
                                yValue = obj.y;
                            break;
                        }
                    }
                }
                if (i == cnt)
                    yValue = obj.y;
            }
            if (xValue != 0)
            {
                if (i > 0)
                    i--;
                for (; i < cnt; i++)
                {
                    obj = _children[i];
                    if (xValue < obj.x)
                    {
                        if (i == 0)
                        {
                            xValue = 0;
                            break;
                        }
                        else
                        {
                            GObject prev = _children[i - 1];
                            if (xValue < prev.x + prev.width / 2) // top half part
                                xValue = prev.x;
                            else//bottom half part
                                xValue = obj.x;
                            break;
                        }
                    }
                }
                if (i == cnt)
                    xValue = obj.x;
            }
        }
        internal void ChildSortingOrderChanged(GObject child, int oldValue, int newValue)
        {
            if (newValue == 0)
            {
                _sortingChildCount--;
                SetChildIndex(child, _children.Count);
            }
            else
            {
                if (oldValue == 0)
                    _sortingChildCount++;
                int oldIndex = _children.IndexOf(child);
                int index = GetInsertPosForSortingChild(child);
                if (oldIndex < index)
                    _SetChildIndex(child, oldIndex, index - 1);
                else
                    _SetChildIndex(child, oldIndex, index);
            }
        }
        /// 
        /// 每帧调用的一个回调。如果你要override,请记住以下两点:
        /// 1、记得调用base.onUpdate;
        /// 2、不要在方法里进行任何会更改显示列表的操作,例如AddChild、RemoveChild、visible等。
        /// 
        virtual protected void OnUpdate()
        {
            if (_boundsChanged)
                UpdateBounds();
        }
        override public void ConstructFromResource()
        {
            ConstructFromResource(null, 0);
        }
        internal void ConstructFromResource(List objectPool, int poolIndex)
        {
            this.gameObjectName = packageItem.name;
            PackageItem contentItem = packageItem.getBranch();
            if (!contentItem.translated)
            {
                contentItem.translated = true;
                TranslationHelper.TranslateComponent(contentItem);
            }
            ByteBuffer buffer = contentItem.rawData;
            buffer.Seek(0, 0);
            underConstruct = true;
            sourceWidth = buffer.ReadInt();
            sourceHeight = buffer.ReadInt();
            initWidth = sourceWidth;
            initHeight = sourceHeight;
            SetSize(sourceWidth, sourceHeight);
            if (buffer.ReadBool())
            {
                minWidth = buffer.ReadInt();
                maxWidth = buffer.ReadInt();
                minHeight = buffer.ReadInt();
                maxHeight = buffer.ReadInt();
            }
            if (buffer.ReadBool())
            {
                float f1 = buffer.ReadFloat();
                float f2 = buffer.ReadFloat();
                SetPivot(f1, f2, buffer.ReadBool());
            }
            if (buffer.ReadBool())
            {
                _margin.top = buffer.ReadInt();
                _margin.bottom = buffer.ReadInt();
                _margin.left = buffer.ReadInt();
                _margin.right = buffer.ReadInt();
            }
            OverflowType overflow = (OverflowType)buffer.ReadByte();
            if (overflow == OverflowType.Scroll)
            {
                int savedPos = buffer.position;
                buffer.Seek(0, 7);
                SetupScroll(buffer);
                buffer.position = savedPos;
            }
            else
                SetupOverflow(overflow);
            if (buffer.ReadBool())
            {
                int i1 = buffer.ReadInt();
                int i2 = buffer.ReadInt();
                this.clipSoftness = new Vector2(i1, i2);
            }
            _buildingDisplayList = true;
            buffer.Seek(0, 1);
            int controllerCount = buffer.ReadShort();
            for (int i = 0; i < controllerCount; i++)
            {
                int nextPos = buffer.ReadShort();
                nextPos += buffer.position;
                Controller controller = new Controller();
                _controllers.Add(controller);
                controller.parent = this;
                controller.Setup(buffer);
                buffer.position = nextPos;
            }
            buffer.Seek(0, 2);
            GObject child;
            int childCount = buffer.ReadShort();
            for (int i = 0; i < childCount; i++)
            {
                int dataLen = buffer.ReadShort();
                int curPos = buffer.position;
                if (objectPool != null)
                    child = objectPool[poolIndex + i];
                else
                {
                    buffer.Seek(curPos, 0);
                    ObjectType type = (ObjectType)buffer.ReadByte();
                    string src = buffer.ReadS();
                    string pkgId = buffer.ReadS();
                    PackageItem pi = null;
                    if (src != null)
                    {
                        UIPackage pkg;
                        if (pkgId != null)
                            pkg = UIPackage.GetById(pkgId);
                        else
                            pkg = contentItem.owner;
                        pi = pkg != null ? pkg.GetItem(src) : null;
                    }
                    if (pi != null)
                    {
                        child = UIObjectFactory.NewObject(pi);
                        child.ConstructFromResource();
                    }
                    else
                        child = UIObjectFactory.NewObject(type);
                }
                child.underConstruct = true;
                child.Setup_BeforeAdd(buffer, curPos);
                child.InternalSetParent(this);
                _children.Add(child);
                buffer.position = curPos + dataLen;
            }
            buffer.Seek(0, 3);
            this.relations.Setup(buffer, true);
            buffer.Seek(0, 2);
            buffer.Skip(2);
            for (int i = 0; i < childCount; i++)
            {
                int nextPos = buffer.ReadShort();
                nextPos += buffer.position;
                buffer.Seek(buffer.position, 3);
                _children[i].relations.Setup(buffer, false);
                buffer.position = nextPos;
            }
            buffer.Seek(0, 2);
            buffer.Skip(2);
            for (int i = 0; i < childCount; i++)
            {
                int nextPos = buffer.ReadShort();
                nextPos += buffer.position;
                child = _children[i];
                child.Setup_AfterAdd(buffer, buffer.position);
                child.underConstruct = false;
                if (child.displayObject != null)
                    child.displayObject.cachedTransform.SetParent(this.displayObject.cachedTransform, false);
                buffer.position = nextPos;
            }
            buffer.Seek(0, 4);
            buffer.Skip(2); //customData
            this.opaque = buffer.ReadBool();
            int maskId = buffer.ReadShort();
            if (maskId != -1)
            {
                container.mask = GetChildAt(maskId).displayObject;
                if (buffer.ReadBool())
                    container.reversedMask = true;
            }
            {
                string hitTestId = buffer.ReadS();
                int i1 = buffer.ReadInt();
                int i2 = buffer.ReadInt();
                if (hitTestId != null)
                {
                    PackageItem pi = contentItem.owner.GetItem(hitTestId);
                    if (pi != null && pi.pixelHitTestData != null)
                        rootContainer.hitArea = new PixelHitTest(pi.pixelHitTestData, i1, i2, sourceWidth, sourceHeight);
                }
                else if (i1 != 0 && i2 != -1)
                {
                    rootContainer.hitArea = new ShapeHitTest(this.GetChildAt(i2).displayObject);
                }
            }
            buffer.Seek(0, 5);
            int transitionCount = buffer.ReadShort();
            for (int i = 0; i < transitionCount; i++)
            {
                int nextPos = buffer.ReadShort();
                nextPos += buffer.position;
                Transition trans = new Transition(this);
                trans.Setup(buffer);
                _transitions.Add(trans);
                buffer.position = nextPos;
            }
            if (_transitions.Count > 0)
            {
                this.onAddedToStage.Add(__addedToStage);
                this.onRemovedFromStage.Add(__removedFromStage);
            }
            ApplyAllControllers();
            _buildingDisplayList = false;
            underConstruct = false;
            BuildNativeDisplayList();
            SetBoundsChangedFlag();
            if (contentItem.objectType != ObjectType.Component)
                ConstructExtension(buffer);
            ConstructFromXML(null);
#if FAIRYGUI_TOLUA
            CallLua("ctor");
#endif
        }
        virtual protected void ConstructExtension(ByteBuffer buffer)
        {
        }
        /// 
        /// Method for extensions to override
        /// 
        /// 
        virtual public void ConstructFromXML(XML xml)
        {
        }
        public override void Setup_AfterAdd(ByteBuffer buffer, int beginPos)
        {
            base.Setup_AfterAdd(buffer, beginPos);
            buffer.Seek(beginPos, 4);
            int pageController = buffer.ReadShort();
            if (pageController != -1 && scrollPane != null && scrollPane.pageMode)
                scrollPane.pageController = parent.GetControllerAt(pageController);
            int cnt = buffer.ReadShort();
            for (int i = 0; i < cnt; i++)
            {
                Controller cc = GetController(buffer.ReadS());
                string pageId = buffer.ReadS();
                if (cc != null)
                    cc.selectedPageId = pageId;
            }
            if (buffer.version >= 2)
            {
                cnt = buffer.ReadShort();
                for (int i = 0; i < cnt; i++)
                {
                    string target = buffer.ReadS();
                    int propertyId = buffer.ReadShort();
                    string value = buffer.ReadS();
                    GObject obj = this.GetChildByPath(target);
                    if (obj != null)
                    {
                        if (propertyId == 0)
                            obj.text = value;
                        else if (propertyId == 1)
                            obj.icon = value;
                    }
                }
            }
        }
        void __addedToStage()
        {
            int cnt = _transitions.Count;
            for (int i = 0; i < cnt; ++i)
                _transitions[i].OnOwnerAddedToStage();
        }
        void __removedFromStage()
        {
            int cnt = _transitions.Count;
            for (int i = 0; i < cnt; ++i)
                _transitions[i].OnOwnerRemovedFromStage();
        }
#if FAIRYGUI_TOLUA
        internal LuaTable _peerTable;
        public void SetLuaPeer(LuaTable peerTable)
        {
            _peerTable = peerTable;
        }
        [NoToLua]
        public bool CallLua(string funcName)
        {
            if (_peerTable != null)
            {
                LuaFunction ctor = _peerTable.GetLuaFunction(funcName);
                if (ctor != null)
                {
                    try
                    {
                        ctor.Call(this);
                    }
                    catch (Exception err)
                    {
                        Debug.LogError(err);
                    }
                    
                    ctor.Dispose();
                    return true;
                }
            }
            return false;
        }
#endif
    }
}