using System;
using UnityEngine;
using FairyGUI.Utils;

namespace FairyGUI
{
    public class GObject : EventDispatcher
    {
        /// <summary>
        /// GObject的id,仅作为内部使用。与name不同,id值是不会相同的。
        /// id is for internal use only.
        /// </summary>
        public string id { get; private set; }

        /// <summary>
        /// Name of the object.
        /// </summary>
        public string name;

        /// <summary>
        /// User defined data. 
        /// </summary>
        public object data;

        /// <summary>
        /// The source width of the object.
        /// </summary>
        public int sourceWidth;

        /// <summary>
        /// The source height of the object.
        /// </summary>
        public int sourceHeight;

        /// <summary>
        /// The initial width of the object.
        /// </summary>
        public int initWidth;

        /// <summary>
        /// The initial height of the object.
        /// </summary>
        public int initHeight;

        /// <summary>
        /// 
        /// </summary>
        public int minWidth;

        /// <summary>
        /// 
        /// </summary>
        public int maxWidth;

        /// <summary>
        /// 
        /// </summary>
        public int minHeight;

        /// <summary>
        /// 
        /// </summary>
        public int maxHeight;

        /// <summary>
        /// Relations Object.
        /// </summary>
        public Relations relations { get; private set; }

        /// <summary>
        /// Restricted range of dragging.
        /// </summary>
        public Rect? dragBounds;

        /// <summary>
        /// Parent object.
        /// </summary>
        public GComponent parent { get; private set; }

        /// <summary>
        /// Lowlevel display object.
        /// </summary>
        public DisplayObject displayObject { get; protected set; }

        /// <summary>
        /// 当前全局正在被拖动的对象
        /// </summary>
        public static GObject draggingObject { get; private set; }

        /// <summary>
        /// 
        /// </summary>
        public PackageItem packageItem;

        float _x;
        float _y;
        float _z;
        float _pivotX;
        float _pivotY;
        bool _pivotAsAnchor;
        float _alpha;
        float _rotation;
        float _rotationX;
        float _rotationY;
        bool _visible;
        bool _internalVisible;
        bool _handlingController;
        bool _touchable;
        bool _grayed;
        bool _draggable;
        float _scaleX;
        float _scaleY;
        int _sortingOrder;
        string _tooltips;
        GGroup _group;

        GearBase[] _gears;

        EventListener _onClick;
        EventListener _onRightClick;
        EventListener _onTouchBegin;
        EventListener _onTouchMove;
        EventListener _onTouchEnd;
        EventListener _onRollOver;
        EventListener _onRollOut;
        EventListener _onAddedToStage;
        EventListener _onRemovedFromStage;
        EventListener _onKeyDown;
        EventListener _onClickLink;
        EventListener _onPositionChanged;
        EventListener _onSizeChanged;
        EventListener _onDragStart;
        EventListener _onDragMove;
        EventListener _onDragEnd;
        EventListener _onGearStop;
        EventListener _onFocusIn;
        EventListener _onFocusOut;

        internal protected bool underConstruct;
        internal float _width;
        internal float _height;
        internal float _rawWidth;
        internal float _rawHeight;
        internal bool _gearLocked;
        internal float _sizePercentInGroup;
        internal bool _disposed;
        internal GTreeNode _treeNode;

        internal static uint _gInstanceCounter;

        public GObject()
        {
            _width = 0;
            _height = 0;
            _alpha = 1;
            _visible = true;
            _touchable = true;
            _scaleX = 1;
            _scaleY = 1;
            _internalVisible = true;
            id = "_n" + _gInstanceCounter++;
            name = string.Empty;

            CreateDisplayObject();

            relations = new Relations(this);
            _gears = new GearBase[10];
        }

        /// <summary>
        /// Dispatched when the object or its child was clicked.
        /// </summary>
        public EventListener onClick
        {
            get { return _onClick ?? (_onClick = new EventListener(this, "onClick")); }
        }

        /// <summary>
        /// Dispatched when the object or its child was clicked by right mouse button. Web only.
        /// </summary>
        public EventListener onRightClick
        {
            get { return _onRightClick ?? (_onRightClick = new EventListener(this, "onRightClick")); }
        }

        /// <summary>
        /// Dispatched when the finger touched the object or its child just now.
        /// </summary>
        public EventListener onTouchBegin
        {
            get { return _onTouchBegin ?? (_onTouchBegin = new EventListener(this, "onTouchBegin")); }
        }

        /// <summary>
        /// 
        /// </summary>
        public EventListener onTouchMove
        {
            get { return _onTouchMove ?? (_onTouchMove = new EventListener(this, "onTouchMove")); }
        }

        /// <summary>
        /// Dispatched when the finger was lifted from the screen or from the mouse button. 
        /// </summary>
        public EventListener onTouchEnd
        {
            get { return _onTouchEnd ?? (_onTouchEnd = new EventListener(this, "onTouchEnd")); }
        }

        /// <summary>
        /// The cursor or finger hovers over an object.
        /// </summary>
        public EventListener onRollOver
        {
            get { return _onRollOver ?? (_onRollOver = new EventListener(this, "onRollOver")); }
        }

        /// <summary>
        /// The cursor or finger leave an object.
        /// </summary>
        public EventListener onRollOut
        {
            get { return _onRollOut ?? (_onRollOut = new EventListener(this, "onRollOut")); }
        }

        /// <summary>
        /// Dispatched when the object was added to the stage.
        /// </summary>
        public EventListener onAddedToStage
        {
            get { return _onAddedToStage ?? (_onAddedToStage = new EventListener(this, "onAddedToStage")); }
        }

        /// <summary>
        /// Dispatched when the object was removed from the stage.
        /// </summary>
        public EventListener onRemovedFromStage
        {
            get { return _onRemovedFromStage ?? (_onRemovedFromStage = new EventListener(this, "onRemovedFromStage")); }
        }

        /// <summary>
        /// Dispatched on key pressed when the object is in focus.
        /// </summary>
        public EventListener onKeyDown
        {
            get { return _onKeyDown ?? (_onKeyDown = new EventListener(this, "onKeyDown")); }
        }

        /// <summary>
        /// Dispatched when links in the object or its child was clicked.
        /// </summary>
        public EventListener onClickLink
        {
            get { return _onClickLink ?? (_onClickLink = new EventListener(this, "onClickLink")); }
        }

        /// <summary>
        /// Dispatched when the object was moved.
        /// </summary>
        public EventListener onPositionChanged
        {
            get { return _onPositionChanged ?? (_onPositionChanged = new EventListener(this, "onPositionChanged")); }
        }

        /// <summary>
        /// Dispatched when the object was resized.
        /// </summary>
        public EventListener onSizeChanged
        {
            get { return _onSizeChanged ?? (_onSizeChanged = new EventListener(this, "onSizeChanged")); }
        }

        /// <summary>
        /// Dispatched when drag start. 
        /// </summary>
        public EventListener onDragStart
        {
            get { return _onDragStart ?? (_onDragStart = new EventListener(this, "onDragStart")); }
        }

        /// <summary>
        /// Dispatched when dragging.
        /// </summary>
        public EventListener onDragMove
        {
            get { return _onDragMove ?? (_onDragMove = new EventListener(this, "onDragMove")); }
        }

        /// <summary>
        /// Dispatched when drag end.
        /// </summary>
        public EventListener onDragEnd
        {
            get { return _onDragEnd ?? (_onDragEnd = new EventListener(this, "onDragEnd")); }
        }

        /// <summary>
        /// 
        /// </summary>
        public EventListener onGearStop
        {
            get { return _onGearStop ?? (_onGearStop = new EventListener(this, "onGearStop")); }
        }

        /// <summary>
        /// 
        /// </summary>
        public EventListener onFocusIn
        {
            get { return _onFocusIn ?? (_onFocusIn = new EventListener(this, "onFocusIn")); }
        }

        /// <summary>
        /// 
        /// </summary>
        public EventListener onFocusOut
        {
            get { return _onFocusOut ?? (_onFocusOut = new EventListener(this, "onFocusOut")); }
        }

        /// <summary>
        /// The x coordinate of the object relative to the local coordinates of the parent.
        /// </summary>
        public float x
        {
            get { return _x; }
            set
            {
                SetPosition(value, _y, _z);
            }
        }

        /// <summary>
        /// The y coordinate of the object relative to the local coordinates of the parent.
        /// </summary>
        public float y
        {
            get { return _y; }
            set
            {
                SetPosition(_x, value, _z);
            }
        }

        /// <summary>
        /// The z coordinate of the object relative to the local coordinates of the parent.
        /// </summary>
        public float z
        {
            get { return _z; }
            set
            {
                SetPosition(_x, _y, value);
            }
        }

        /// <summary>
        /// The x and y coordinates of the object relative to the local coordinates of the parent.
        /// </summary>
        public Vector2 xy
        {
            get { return new Vector2(_x, _y); }
            set { SetPosition(value.x, value.y, _z); }
        }

        /// <summary>
        /// The x,y,z coordinates of the object relative to the local coordinates of the parent.
        /// </summary>
        public Vector3 position
        {
            get { return new Vector3(_x, _y, _z); }
            set { SetPosition(value.x, value.y, value.z); }
        }

        /// <summary>
        /// change the x and y coordinates of the object relative to the local coordinates of the parent.
        /// </summary>
        /// <param name="xv">x value.</param>
        /// <param name="yv">y value.</param>
        public void SetXY(float xv, float yv)
        {
            SetPosition(xv, yv, _z);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="xv"></param>
        /// <param name="yv"></param>
        /// <param name="topLeft"></param>
        public void SetXY(float xv, float yv, bool topLeftValue)
        {
            if (_pivotAsAnchor)
                SetPosition(xv + _pivotX * _width, yv + _pivotY * _height, _z);
            else
                SetPosition(xv, yv, _z);
        }

        /// <summary>
        /// change the x,y,z coordinates of the object relative to the local coordinates of the parent.
        /// </summary>
        /// <param name="xv">x value.</param>
        /// <param name="yv">y value.</param>
        /// <param name="zv">z value.</param>
        public void SetPosition(float xv, float yv, float zv)
        {
            if (_x != xv || _y != yv || _z != zv)
            {
                float dx = xv - _x;
                float dy = yv - _y;
                _x = xv;
                _y = yv;
                _z = zv;

                HandlePositionChanged();

                if (this is GGroup)
                    ((GGroup)this).MoveChildren(dx, dy);

                UpdateGear(1);

                if (parent != null && !(parent is GList))
                {
                    parent.SetBoundsChangedFlag();
                    if (_group != null)
                        _group.SetBoundsChangedFlag(true);
                    DispatchEvent("onPositionChanged", null);
                }

                if (draggingObject == this && !sUpdateInDragging)
                    sGlobalRect = this.LocalToGlobal(new Rect(0, 0, this.width, this.height));
            }
        }

        [Obsolete("Use UIConfig.makePixelPerfect or DisplayObject.pixelPerfect")]
        public bool pixelSnapping
        {
            get { return false; }
            set { }
        }

        /// <summary>
        /// Set the object in middle of the parent or GRoot if the parent is not set.
        /// </summary>
        public void Center()
        {
            Center(false);
        }

        /// <summary>
        /// Set the object in middle of the parent or GRoot if the parent is not set.
        /// </summary>
        /// <param name="restraint">Add relations to maintain the center state.</param>
        public void Center(bool restraint)
        {
            GComponent r;
            if (parent != null)
                r = parent;
            else
                r = this.root;

            this.SetXY((int)((r.width - this.width) / 2), (int)((r.height - this.height) / 2), true);
            if (restraint)
            {
                this.AddRelation(r, RelationType.Center_Center);
                this.AddRelation(r, RelationType.Middle_Middle);
            }
        }

        /// <summary>
        /// 设置对象为全屏大小(逻辑屏幕)。
        /// </summary>
        public void MakeFullScreen()
        {
            this.SetSize(GRoot.inst.width, GRoot.inst.height);
        }

        /// <summary>
        /// The width of the object in pixels.
        /// </summary>
        public float width
        {
            get
            {
                return _width;
            }
            set
            {
                SetSize(value, _rawHeight);
            }
        }

        /// <summary>
        /// The height of the object in pixels.
        /// </summary>
        public float height
        {
            get
            {
                return _height;
            }
            set
            {
                SetSize(_rawWidth, value);
            }
        }

        /// <summary>
        /// The size of the object in pixels.
        /// </summary>
        public Vector2 size
        {
            get { return new Vector2(width, height); }
            set { SetSize(value.x, value.y); }
        }

        /// <summary>
        /// actualWidth = width * scalex
        /// </summary>
        public float actualWidth
        {
            get { return this.width * _scaleX; }
        }

        /// <summary>
        /// actualHeight = height * scaleY
        /// </summary>
        public float actualHeight
        {
            get { return this.height * _scaleY; }
        }

        /// <summary>
        /// Change size.
        /// </summary>
        /// <param name="wv">Width value.</param>
        /// <param name="hv">Height value.</param>
        public void SetSize(float wv, float hv)
        {
            SetSize(wv, hv, false);
        }

        /// <summary>
        /// Change size.
        /// </summary>
        /// <param name="wv">Width value.</param>
        /// <param name="hv">Height value.</param>
        /// <param name="ignorePivot">If pivot is set, the object's positon will change when its size change. Set ignorePivot=true to keep the position.</param>
        public void SetSize(float wv, float hv, bool ignorePivot)
        {
            if (_rawWidth != wv || _rawHeight != hv)
            {
                _rawWidth = wv;
                _rawHeight = hv;
                if (wv < minWidth)
                    wv = minWidth;
                else if (maxWidth > 0 && wv > maxWidth)
                    wv = maxWidth;
                if (hv < minHeight)
                    hv = minHeight;
                else if (maxHeight > 0 && hv > maxHeight)
                    hv = maxHeight;
                float dWidth = wv - _width;
                float dHeight = hv - _height;
                _width = wv;
                _height = hv;

                HandleSizeChanged();

                if (_pivotX != 0 || _pivotY != 0)
                {
                    if (!_pivotAsAnchor)
                    {
                        if (!ignorePivot)
                            SetXY(_x - _pivotX * dWidth, _y - _pivotY * dHeight);
                        else
                            HandlePositionChanged();
                    }
                    else
                        HandlePositionChanged();
                }

                if (this is GGroup)
                    ((GGroup)this).ResizeChildren(dWidth, dHeight);

                UpdateGear(2);

                if (parent != null)
                {
                    relations.OnOwnerSizeChanged(dWidth, dHeight, _pivotAsAnchor || !ignorePivot);
                    parent.SetBoundsChangedFlag();
                    if (_group != null)
                        _group.SetBoundsChangedFlag();
                }

                DispatchEvent("onSizeChanged", null);
            }
        }

        protected void SetSizeDirectly(float wv, float hv)
        {
            _rawWidth = wv;
            _rawHeight = hv;
            if (wv < 0)
                wv = 0;
            if (hv < 0)
                hv = 0;
            _width = wv;
            _height = hv;
        }

        /// <summary>
        /// 
        /// </summary>
        public float xMin
        {
            get
            {
                return _pivotAsAnchor ? (_x - _width * _pivotX) : _x;
            }
            set
            {
                if (_pivotAsAnchor)
                    SetPosition(value + _width * _pivotX, _y, _z);
                else
                    SetPosition(value, _y, _z);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public float yMin
        {
            get
            {
                return _pivotAsAnchor ? (_y - _height * _pivotY) : _y;
            }
            set
            {
                if (_pivotAsAnchor)
                    SetPosition(_x, value + _height * _pivotY, _z);
                else
                    SetPosition(_x, value, _z);
            }
        }

        /// <summary>
        /// The horizontal scale factor. '1' means no scale, cannt be negative.
        /// </summary>
        public float scaleX
        {
            get { return _scaleX; }
            set
            {
                SetScale(value, _scaleY);
            }
        }

        /// <summary>
        /// The vertical scale factor. '1' means no scale, cannt be negative.
        /// </summary>
        public float scaleY
        {
            get { return _scaleY; }
            set
            {
                SetScale(_scaleX, value);
            }
        }

        /// <summary>
        /// The scale factor.
        /// </summary>
        public Vector2 scale
        {
            get { return new Vector2(_scaleX, _scaleY); }
            set { SetScale(value.x, value.y); }
        }

        /// <summary>
        /// Change the scale factor.
        /// </summary>
        /// <param name="wv">The horizontal scale factor.</param>
        /// <param name="hv">The vertical scale factor</param>
        public void SetScale(float wv, float hv)
        {
            if (_scaleX != wv || _scaleY != hv)
            {
                _scaleX = wv;
                _scaleY = hv;
                HandleScaleChanged();

                UpdateGear(2);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public Vector2 skew
        {
            get
            {
                if (displayObject != null)
                    return displayObject.skew;
                else
                    return Vector2.zero;
            }

            set
            {
                if (displayObject != null)
                    displayObject.skew = value;
            }
        }

        /// <summary>
        /// The x coordinate of the object's origin in its own coordinate space.
        /// </summary>
        public float pivotX
        {
            get { return _pivotX; }
            set
            {
                SetPivot(value, _pivotY, _pivotAsAnchor);
            }
        }

        /// <summary>
        /// The y coordinate of the object's origin in its own coordinate space.
        /// </summary>
        public float pivotY
        {
            get { return _pivotY; }
            set
            {
                SetPivot(_pivotX, value, _pivotAsAnchor);
            }
        }

        /// <summary>
        /// The x and y coordinates of the object's origin in its own coordinate space.
        /// </summary>
        public Vector2 pivot
        {
            get { return new Vector2(_pivotX, _pivotY); }
            set { SetPivot(value.x, value.y, _pivotAsAnchor); }
        }

        public bool pivotAsAnchor
        {
            get { return _pivotAsAnchor; }
            set
            {
                SetPivot(_pivotX, _pivotY, value);
            }
        }

        /// <summary>
        /// Change the x and y coordinates of the object's origin in its own coordinate space.
        /// </summary>
        /// <param name="xv">x value in ratio</param>
        /// <param name="yv">y value in ratio</param>
        public void SetPivot(float xv, float yv)
        {
            SetPivot(xv, yv, false);
        }

        /// <summary>
        ///  Change the x and y coordinates of the object's origin in its own coordinate space.
        /// </summary>
        /// <param name="xv">x value in ratio</param>
        /// <param name="yv">y value in ratio</param>
        /// <param name="asAnchor">If use the pivot as the anchor position</param>
        public void SetPivot(float xv, float yv, bool asAnchor)
        {
            if (_pivotX != xv || _pivotY != yv || _pivotAsAnchor != asAnchor)
            {
                _pivotX = xv;
                _pivotY = yv;
                _pivotAsAnchor = asAnchor;
                if (displayObject != null)
                    displayObject.pivot = new Vector2(_pivotX, _pivotY);
                HandlePositionChanged();
            }
        }

        /// <summary>
        /// If the object can touch or click. GImage/GTextField is not touchable even it is true.
        /// </summary>
        public bool touchable
        {
            get
            {
                return _touchable;
            }
            set
            {
                if (_touchable != value)
                {
                    _touchable = value;
                    UpdateGear(3);

                    if (displayObject != null)
                        displayObject.touchable = _touchable;
                }
            }
        }

        /// <summary>
        /// If true, apply a grayed effect on this object.
        /// </summary>
        public bool grayed
        {
            get
            {
                return _grayed;
            }
            set
            {
                if (_grayed != value)
                {
                    _grayed = value;
                    HandleGrayedChanged();
                    UpdateGear(3);
                }
            }
        }

        /// <summary>
        /// Enabled is shortcut for grayed and !touchable combination.
        /// </summary>
        public bool enabled
        {
            get
            {
                return !_grayed && _touchable;
            }
            set
            {
                this.grayed = !value;
                this.touchable = value;
            }
        }

        /// <summary>
        /// The rotation around the z axis of the object in degrees.
        /// </summary>
        public float rotation
        {
            get
            {
                return _rotation;
            }
            set
            {
                _rotation = value;
                if (displayObject != null)
                    displayObject.rotation = _rotation;
                UpdateGear(3);
            }
        }

        /// <summary>
        /// The rotation around the x axis of the object in degrees.
        /// </summary>
        public float rotationX
        {
            get
            {
                return _rotationX;
            }
            set
            {
                _rotationX = value;
                if (displayObject != null)
                    displayObject.rotationX = _rotationX;
            }
        }

        /// <summary>
        /// The rotation around the y axis of the object in degrees.
        /// </summary>
        public float rotationY
        {
            get
            {
                return _rotationY;
            }
            set
            {
                _rotationY = value;
                if (displayObject != null)
                    displayObject.rotationY = _rotationY;
            }
        }

        /// <summary>
        /// The opacity of the object. 0 = transparent, 1 = opaque.
        /// </summary>
        public float alpha
        {

            get
            {
                return _alpha;
            }

            set
            {
                _alpha = value;
                HandleAlphaChanged();
                UpdateGear(3);
            }
        }

        /// <summary>
        /// The visibility of the object. An invisible object will be untouchable.
        /// </summary>
        public bool visible
        {
            get
            {
                return _visible;
            }

            set
            {
                if (_visible != value)
                {
                    _visible = value;
                    HandleVisibleChanged();
                    if (parent != null)
                        parent.SetBoundsChangedFlag();
                    if (_group != null && _group.excludeInvisibles)
                        _group.SetBoundsChangedFlag();
                }
            }
        }

        internal bool internalVisible
        {
            get
            {
                return _internalVisible && (group == null || group.internalVisible);
            }
        }

        internal bool internalVisible2
        {
            get
            {
                return _visible && (group == null || group.internalVisible2);
            }
        }

        internal bool internalVisible3
        {
            get
            {
                return _visible && _internalVisible;
            }
        }

        /// <summary>
        /// By default(when sortingOrder==0), object added to component is arrange by the added roder. 
        /// The bigger is the sorting order, the object is more in front.
        /// </summary>
        public int sortingOrder
        {
            get { return _sortingOrder; }
            set
            {
                if (value < 0)
                    value = 0;
                if (_sortingOrder != value)
                {
                    int old = _sortingOrder;
                    _sortingOrder = value;
                    if (parent != null)
                        parent.ChildSortingOrderChanged(this, old, _sortingOrder);
                }
            }
        }

        /// <summary>
        /// If the object can be focused?
        /// </summary>
        public bool focusable
        {
            get { return displayObject != null && displayObject.focusable; }
            set { if (displayObject != null) displayObject.focusable = value; }
        }

        /// <summary>
        /// If the object can be navigated by TAB?
        /// </summary>
        public bool tabStop
        {
            get { return displayObject != null && displayObject.tabStop; }
            set { if (displayObject != null) displayObject.tabStop = value; }
        }

        /// <summary>
        /// If the object is focused. 
        /// </summary>
        public bool focused
        {
            get { return displayObject != null && displayObject.focused; }
        }

        /// <summary>
        /// Request focus on this object.
        /// </summary>
        public void RequestFocus()
        {
            if (displayObject != null)
                Stage.inst.SetFocus(displayObject, false);
        }

        /// <summary>
        /// Request focus on this object.
        /// </summary>
        public void RequestFocus(bool byKey)
        {
            if (displayObject != null)
                Stage.inst.SetFocus(displayObject, byKey);
        }

        /// <summary>
        /// Tooltips of this object. UIConfig.tooltipsWin must be set first.
        /// </summary>
        public string tooltips
        {
            get { return _tooltips; }
            set
            {
                if (!string.IsNullOrEmpty(_tooltips))
                {
                    this.onRollOver.Remove(__rollOver);
                    this.onRollOut.Remove(__rollOut);
                }

                _tooltips = value;
                if (!string.IsNullOrEmpty(_tooltips))
                {
                    this.onRollOver.Add(__rollOver);
                    this.onRollOut.Add(__rollOut);
                }
            }
        }


        /// <summary>
        /// 
        /// </summary>
        /// <value></value>
        public string cursor
        {
            get { return displayObject != null ? displayObject.cursor : null; }
            set
            {
                if (displayObject != null)
                    displayObject.cursor = value;
            }
        }

        private void __rollOver()
        {
            this.root.ShowTooltips(tooltips);
        }

        private void __rollOut()
        {
            this.root.HideTooltips();
        }

        /// <summary>
        /// 
        /// </summary>
        virtual public IFilter filter
        {
            get { return displayObject != null ? displayObject.filter : null; }
            set { if (displayObject != null) displayObject.filter = value; }
        }

        /// <summary>
        /// 
        /// </summary>
        virtual public BlendMode blendMode
        {
            get { return displayObject != null ? displayObject.blendMode : BlendMode.None; }
            set { if (displayObject != null) displayObject.blendMode = value; }
        }

        /// <summary>
        /// 设定GameObject的名称
        /// </summary>
        public string gameObjectName
        {
            get
            {
                if (displayObject != null)
                    return displayObject.gameObject.name;
                else
                    return null;
            }

            set
            {
                if (displayObject != null)
                    displayObject.gameObject.name = value;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="obj"></param>
        public void SetHome(GObject obj)
        {
            if (obj != null && displayObject != null && obj.displayObject != null)
                displayObject.home = obj.displayObject.cachedTransform;
        }

        /// <summary>
        /// If the object has lowlevel displayobject and the displayobject has a container parent?
        /// </summary>
        public bool inContainer
        {
            get
            {
                return displayObject != null && displayObject.parent != null;
            }
        }

        /// <summary>
        /// If the object is on stage.
        /// </summary>
        public bool onStage
        {
            get
            {
                return displayObject != null && displayObject.stage != null;
            }
        }

        /// <summary>
        /// Resource url of this object.
        /// </summary>
        public string resourceURL
        {
            get
            {
                if (packageItem != null)
                    return UIPackage.URL_PREFIX + packageItem.owner.id + packageItem.id;
                else
                    return null;
            }
        }

        /// <summary>
        /// Gear to xy controller.
        /// </summary>
        public GearXY gearXY
        {
            get
            {
                return (GearXY)GetGear(1);
            }
        }

        /// <summary>
        /// Gear to size controller.
        /// </summary>
        public GearSize gearSize
        {
            get
            {
                return (GearSize)GetGear(2);
            }
        }

        /// <summary>
        /// Gear to look controller.
        /// </summary>
        public GearLook gearLook
        {
            get
            {
                return (GearLook)GetGear(3);
            }
        }

        public GearBase GetGear(int index)
        {
            GearBase gear = _gears[index];
            if (gear == null)
            {
                switch (index)
                {
                    case 0:
                        gear = new GearDisplay(this);
                        break;
                    case 1:
                        gear = new GearXY(this);
                        break;
                    case 2:
                        gear = new GearSize(this);
                        break;
                    case 3:
                        gear = new GearLook(this);
                        break;
                    case 4:
                        gear = new GearColor(this);
                        break;
                    case 5:
                        gear = new GearAnimation(this);
                        break;
                    case 6:
                        gear = new GearText(this);
                        break;
                    case 7:
                        gear = new GearIcon(this);
                        break;
                    case 8:
                        gear = new GearDisplay2(this);
                        break;
                    case 9:
                        gear = new GearFontSize(this);
                        break;
                    default:
                        throw new System.Exception("FairyGUI: invalid gear index!");
                }
                _gears[index] = gear;
            }
            return gear;
        }

        protected void UpdateGear(int index)
        {
            if (underConstruct || _gearLocked)
                return;

            GearBase gear = _gears[index];
            if (gear != null && gear.controller != null)
                gear.UpdateState();
        }

        internal bool CheckGearController(int index, Controller c)
        {
            return _gears[index] != null && _gears[index].controller == c;
        }

        internal void UpdateGearFromRelations(int index, float dx, float dy)
        {
            if (_gears[index] != null)
                _gears[index].UpdateFromRelations(dx, dy);
        }

        internal uint AddDisplayLock()
        {
            GearDisplay gearDisplay = (GearDisplay)_gears[0];
            if (gearDisplay != null && gearDisplay.controller != null)
            {
                uint ret = gearDisplay.AddLock();
                CheckGearDisplay();

                return ret;
            }
            else
                return 0;
        }

        internal void ReleaseDisplayLock(uint token)
        {
            GearDisplay gearDisplay = (GearDisplay)_gears[0];
            if (gearDisplay != null && gearDisplay.controller != null)
            {
                gearDisplay.ReleaseLock(token);
                CheckGearDisplay();
            }
        }

        void CheckGearDisplay()
        {
            if (_handlingController)
                return;

            bool connected = _gears[0] == null || ((GearDisplay)_gears[0]).connected;
            if (_gears[8] != null)
                connected = ((GearDisplay2)_gears[8]).Evaluate(connected);

            if (connected != _internalVisible)
            {
                _internalVisible = connected;
                if (parent != null)
                    parent.ChildStateChanged(this);
                if (_group != null && _group.excludeInvisibles)
                    _group.SetBoundsChangedFlag();
            }
        }

        /// <summary>
        /// Mark the fairy batching state is invalid. 
        /// </summary>
        public void InvalidateBatchingState()
        {
            if (displayObject != null)
                displayObject.InvalidateBatchingState();
            else if ((this is GGroup) && parent != null)
                parent.container.InvalidateBatchingState(true);
        }

        virtual public void HandleControllerChanged(Controller c)
        {
            _handlingController = true;
            for (int i = 0; i < 10; i++)
            {
                GearBase gear = _gears[i];
                if (gear != null && gear.controller == c)
                    gear.Apply();
            }
            _handlingController = false;

            CheckGearDisplay();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="target"></param>
        /// <param name="relationType"></param>
        public void AddRelation(GObject target, RelationType relationType)
        {
            AddRelation(target, relationType, false);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="target"></param>
        /// <param name="relationType"></param>
        /// <param name="usePercent"></param>
        public void AddRelation(GObject target, RelationType relationType, bool usePercent)
        {
            relations.Add(target, relationType, usePercent);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="target"></param>
        /// <param name="relationType"></param>
        public void RemoveRelation(GObject target, RelationType relationType)
        {
            relations.Remove(target, relationType);
        }

        /// <summary>
        /// 
        /// </summary>
        public void RemoveFromParent()
        {
            if (parent != null)
                parent.RemoveChild(this);
        }

        /// <summary>
        ///  Group belonging to.
        /// </summary>
        public GGroup group
        {
            get { return _group; }
            set
            {
                if (_group != value)
                {
                    if (_group != null)
                        _group.SetBoundsChangedFlag();
                    _group = value;
                    if (_group != null)
                        _group.SetBoundsChangedFlag();
                    HandleVisibleChanged();
                    if (parent != null)
                        parent.ChildStateChanged(this);
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public GRoot root
        {
            get
            {
                GObject p = this;
                while (p.parent != null)
                    p = p.parent;

                if (p is GRoot)
                    return (GRoot)p;

                if (p.displayObject != null && p.displayObject.parent != null)
                {
                    DisplayObject d = p.displayObject.parent.GetChild("GRoot");
                    if (d != null && (d.gOwner is GRoot))
                        return (GRoot)d.gOwner;
                }

                return GRoot.inst;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        virtual public string text
        {
            get { return null; }
            set { /*override in child*/}
        }

        /// <summary>
        /// 
        /// </summary>
        virtual public string icon
        {
            get { return null; }
            set { /*override in child*/}
        }

        /// <summary>
        /// 
        /// </summary>
        public bool draggable
        {
            get { return _draggable; }
            set
            {
                if (_draggable != value)
                {
                    _draggable = value;
                    InitDrag();
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public void StartDrag()
        {
            StartDrag(-1);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="touchId"></param>
        public void StartDrag(int touchId)
        {
            if (displayObject.stage == null)
                return;

            DragBegin(touchId);
        }

        /// <summary>
        /// 
        /// </summary>
        public void StopDrag()
        {
            DragEnd();
        }

        /// <summary>
        /// 
        /// </summary>
        public bool dragging
        {
            get { return draggingObject == this; }
        }

        /// <summary>
        /// Transforms a point from the local coordinate system to global (Stage) coordinates.
        /// </summary>
        /// <param name="pt"></param>
        /// <returns></returns>
        public Vector2 LocalToGlobal(Vector2 pt)
        {
            if (_pivotAsAnchor)
            {
                pt.x += _width * _pivotX;
                pt.y += _height * _pivotY;
            }
            return displayObject.LocalToGlobal(pt);
        }

        /// <summary>
        /// Transforms a point from global (Stage) coordinates to the local coordinate system.
        /// </summary>
        /// <param name="pt"></param>
        /// <returns></returns>
        public Vector2 GlobalToLocal(Vector2 pt)
        {
            pt = displayObject.GlobalToLocal(pt);
            if (_pivotAsAnchor)
            {
                pt.x -= _width * _pivotX;
                pt.y -= _height * _pivotY;
            }
            return pt;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="rect"></param>
        /// <returns></returns>
        public Rect LocalToGlobal(Rect rect)
        {
            Rect ret = new Rect();
            Vector2 v = this.LocalToGlobal(new Vector2(rect.xMin, rect.yMin));
            ret.xMin = v.x;
            ret.yMin = v.y;
            v = this.LocalToGlobal(new Vector2(rect.xMax, rect.yMax));
            ret.xMax = v.x;
            ret.yMax = v.y;
            return ret;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="rect"></param>
        /// <returns></returns>
        public Rect GlobalToLocal(Rect rect)
        {
            Rect ret = new Rect();
            Vector2 v = this.GlobalToLocal(new Vector2(rect.xMin, rect.yMin));
            ret.xMin = v.x;
            ret.yMin = v.y;
            v = this.GlobalToLocal(new Vector2(rect.xMax, rect.yMax));
            ret.xMax = v.x;
            ret.yMax = v.y;
            return ret;
        }

        /// <summary>
        /// Transforms a point from the local coordinate system to GRoot coordinates.
        /// </summary>
        /// <param name="pt"></param>
        /// <param name="r"></param>
        /// <returns></returns>
        public Vector2 LocalToRoot(Vector2 pt, GRoot r)
        {
            pt = LocalToGlobal(pt);
            if (r == null || r == GRoot.inst)
            {
                //fast
                pt.x /= UIContentScaler.scaleFactor;
                pt.y /= UIContentScaler.scaleFactor;
                return pt;
            }
            else
                return r.GlobalToLocal(pt);
        }

        /// <summary>
        /// Transforms a point from the GRoot coordinate  to local coordinates system.
        /// </summary>
        /// <param name="pt"></param>
        /// <param name="r"></param>
        /// <returns></returns>
        public Vector2 RootToLocal(Vector2 pt, GRoot r)
        {
            if (r == null || r == GRoot.inst)
            {
                //fast
                pt.x *= UIContentScaler.scaleFactor;
                pt.y *= UIContentScaler.scaleFactor;
            }
            else
                pt = r.LocalToGlobal(pt);
            return GlobalToLocal(pt);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="pt"></param>
        /// <returns></returns>
        public Vector2 WorldToLocal(Vector3 pt)
        {
            return WorldToLocal(pt, HitTestContext.cachedMainCamera);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="pt"></param>
        /// <param name="camera"></param>
        /// <returns></returns>
        public Vector2 WorldToLocal(Vector3 pt, Camera camera)
        {
            Vector3 v = camera.WorldToScreenPoint(pt);
            v.y = Screen.height - v.y;
            v.z = 0;
            return GlobalToLocal(v);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="point"></param>
        /// <param name="targetSpace"></param>
        /// <returns></returns>
        public Vector2 TransformPoint(Vector2 pt, GObject targetSpace)
        {
            if (_pivotAsAnchor)
            {
                pt.x += _width * _pivotX;
                pt.y += _height * _pivotY;
            }
            return this.displayObject.TransformPoint(pt, targetSpace != null ? targetSpace.displayObject : Stage.inst);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="rect"></param>
        /// <param name="targetSpace"></param>
        /// <returns></returns>
        public Rect TransformRect(Rect rect, GObject targetSpace)
        {
            if (_pivotAsAnchor)
            {
                rect.x += _width * _pivotX;
                rect.y += _height * _pivotY;
            }
            return this.displayObject.TransformRect(rect, targetSpace != null ? targetSpace.displayObject : Stage.inst);
        }

        /// <summary>
        /// 
        /// </summary>
        public bool isDisposed
        {
            get { return _disposed; }
        }

        /// <summary>
        /// 
        /// </summary>
        virtual public void Dispose()
        {
            if (_disposed)
                return;

            _disposed = true;

            RemoveFromParent();
            RemoveEventListeners();
            relations.Dispose();
            relations = null;
            for (int i = 0; i < 10; i++)
            {
                GearBase gear = _gears[i];
                if (gear != null)
                    gear.Dispose();
            }
            if (displayObject != null)
            {
                displayObject.gOwner = null;
                displayObject.Dispose();
            }
            data = null;
        }

        /// <summary>
        /// 
        /// </summary>
        public GImage asImage
        {
            get { return this as GImage; }
        }

        /// <summary>
        /// 
        /// </summary>
        public GComponent asCom
        {
            get { return this as GComponent; }
        }

        /// <summary>
        /// 
        /// </summary>
        public GButton asButton
        {
            get { return this as GButton; }
        }

        /// <summary>
        /// 
        /// </summary>
        public GLabel asLabel
        {
            get { return this as GLabel; }
        }

        /// <summary>
        /// 
        /// </summary>
        public GProgressBar asProgress
        {
            get { return this as GProgressBar; }
        }

        /// <summary>
        /// 
        /// </summary>
        public GSlider asSlider
        {
            get { return this as GSlider; }
        }

        /// <summary>
        /// 
        /// </summary>
        public GComboBox asComboBox
        {
            get { return this as GComboBox; }
        }

        /// <summary>
        /// 
        /// </summary>
        public GTextField asTextField
        {
            get { return this as GTextField; }
        }

        /// <summary>
        /// 
        /// </summary>
        public GRichTextField asRichTextField
        {
            get { return this as GRichTextField; }
        }

        /// <summary>
        /// 
        /// </summary>
        public GTextInput asTextInput
        {
            get { return this as GTextInput; }
        }

        /// <summary>
        /// 
        /// </summary>
        public GLoader asLoader
        {
            get { return this as GLoader; }
        }

        /// <summary>
        /// 
        /// </summary>
        public GLoader3D asLoader3D
        {
            get { return this as GLoader3D; }
        }

        /// <summary>
        /// 
        /// </summary>
        public GList asList
        {
            get { return this as GList; }
        }

        /// <summary>
        /// 
        /// </summary>
        public GGraph asGraph
        {
            get { return this as GGraph; }
        }

        /// <summary>
        /// 
        /// </summary>
        public GGroup asGroup
        {
            get { return this as GGroup; }
        }

        /// <summary>
        /// 
        /// </summary>
        public GMovieClip asMovieClip
        {
            get { return this as GMovieClip; }
        }

        /// <summary>
        /// 
        /// </summary>
        public GTree asTree
        {
            get { return this as GTree; }
        }

        /// <summary>
        /// 
        /// </summary>
        public GTreeNode treeNode
        {
            get { return _treeNode; }
        }

        virtual protected void CreateDisplayObject()
        {
        }

        internal void InternalSetParent(GComponent value)
        {
            parent = value;
        }

        virtual protected void HandlePositionChanged()
        {
            if (displayObject != null)
            {
                float xv = _x;
                float yv = _y;
                if (!_pivotAsAnchor)
                {
                    xv += _width * _pivotX;
                    yv += _height * _pivotY;
                }
                displayObject.location = new Vector3(xv, yv, _z);
            }
        }

        virtual protected void HandleSizeChanged()
        {
            if (displayObject != null)
                displayObject.SetSize(_width, _height);
        }

        virtual protected void HandleScaleChanged()
        {
            if (displayObject != null)
                displayObject.SetScale(_scaleX, _scaleY);
        }

        virtual protected void HandleGrayedChanged()
        {
            if (displayObject != null)
                displayObject.grayed = _grayed;
        }

        virtual protected void HandleAlphaChanged()
        {
            if (displayObject != null)
                displayObject.alpha = _alpha;
        }

        virtual internal protected void HandleVisibleChanged()
        {
            if (displayObject != null)
                displayObject.visible = internalVisible2;
        }

        virtual public void ConstructFromResource()
        {
        }

        virtual public void Setup_BeforeAdd(ByteBuffer buffer, int beginPos)
        {
            buffer.Seek(beginPos, 0);
            buffer.Skip(5);

            id = buffer.ReadS();
            name = buffer.ReadS();
            float f1 = buffer.ReadInt();
            float f2 = buffer.ReadInt();
            SetXY(f1, f2);

            if (buffer.ReadBool())
            {
                initWidth = buffer.ReadInt();
                initHeight = buffer.ReadInt();
                SetSize(initWidth, initHeight, true);
            }

            if (buffer.ReadBool())
            {
                minWidth = buffer.ReadInt();
                maxWidth = buffer.ReadInt();
                minHeight = buffer.ReadInt();
                maxHeight = buffer.ReadInt();
            }

            if (buffer.ReadBool())
            {
                f1 = buffer.ReadFloat();
                f2 = buffer.ReadFloat();
                SetScale(f1, f2);
            }

            if (buffer.ReadBool())
            {
                f1 = buffer.ReadFloat();
                f2 = buffer.ReadFloat();
                this.skew = new Vector2(f1, f2);
            }

            if (buffer.ReadBool())
            {
                f1 = buffer.ReadFloat();
                f2 = buffer.ReadFloat();
                SetPivot(f1, f2, buffer.ReadBool());
            }

            f1 = buffer.ReadFloat();
            if (f1 != 1)
                this.alpha = f1;

            f1 = buffer.ReadFloat();
            if (f1 != 0)
                this.rotation = f1;

            if (!buffer.ReadBool())
                this.visible = false;
            if (!buffer.ReadBool())
                this.touchable = false;
            if (buffer.ReadBool())
                this.grayed = true;
            this.blendMode = (BlendMode)buffer.ReadByte();

            int filter = buffer.ReadByte();
            if (filter == 1)
            {
                ColorFilter cf = new ColorFilter();
                this.filter = cf;

                cf.AdjustBrightness(buffer.ReadFloat());
                cf.AdjustContrast(buffer.ReadFloat());
                cf.AdjustSaturation(buffer.ReadFloat());
                cf.AdjustHue(buffer.ReadFloat());
            }

            string str = buffer.ReadS();
            if (str != null)
                this.data = str;
        }

        virtual public void Setup_AfterAdd(ByteBuffer buffer, int beginPos)
        {
            buffer.Seek(beginPos, 1);

            string str = buffer.ReadS();
            if (str != null)
                this.tooltips = str;

            int groupId = buffer.ReadShort();
            if (groupId >= 0)
                group = parent.GetChildAt(groupId) as GGroup;

            buffer.Seek(beginPos, 2);

            int cnt = buffer.ReadShort();
            for (int i = 0; i < cnt; i++)
            {
                int nextPos = buffer.ReadUshort();
                nextPos += buffer.position;

                GearBase gear = GetGear(buffer.ReadByte());
                gear.Setup(buffer);

                buffer.position = nextPos;
            }
        }

        #region Drag support
        Vector2 _dragTouchStartPos;
        bool _dragTesting;

        static Vector2 sGlobalDragStart = new Vector2();
        static Rect sGlobalRect = new Rect();
        static bool sUpdateInDragging = false;

        private void InitDrag()
        {
            if (_draggable)
            {
                onTouchBegin.Add(__touchBegin);
                onTouchMove.Add(__touchMove);
                onTouchEnd.Add(__touchEnd);
            }
            else
            {
                onTouchBegin.Remove(__touchBegin);
                onTouchMove.Remove(__touchMove);
                onTouchEnd.Remove(__touchEnd);
            }
        }

        private void DragBegin(int touchId)
        {
            if (DispatchEvent("onDragStart", touchId))
                return;

            if (draggingObject != null)
            {
                GObject tmp = draggingObject;
                draggingObject.StopDrag();
                draggingObject = null;
                tmp.DispatchEvent("onDragEnd", null);
            }

            onTouchMove.Add(__touchMove);
            onTouchEnd.Add(__touchEnd);

            sGlobalDragStart = Stage.inst.GetTouchPosition(touchId);
            sGlobalRect = this.LocalToGlobal(new Rect(0, 0, this.width, this.height));
            _dragTesting = false;

            draggingObject = this;
            Stage.inst.AddTouchMonitor(touchId, this);
        }

        private void DragEnd()
        {
            if (draggingObject == this)
            {
                _dragTesting = false;
                draggingObject = null;
            }
        }

        private void __touchBegin(EventContext context)
        {
            if ((Stage.inst.focus is InputTextField) && ((InputTextField)Stage.inst.focus).editable)
            {
                _dragTesting = false;
                return;
            }

            InputEvent evt = context.inputEvent;
            _dragTouchStartPos = evt.position;
            _dragTesting = true;
            context.CaptureTouch();
        }

        private void __touchMove(EventContext context)
        {
            InputEvent evt = context.inputEvent;

            if (_dragTesting && draggingObject != this)
            {
                int sensitivity;
                if (Stage.touchScreen)
                    sensitivity = UIConfig.touchDragSensitivity;
                else
                    sensitivity = UIConfig.clickDragSensitivity;
                if (Mathf.Abs(_dragTouchStartPos.x - evt.x) < sensitivity
                    && Mathf.Abs(_dragTouchStartPos.y - evt.y) < sensitivity)
                    return;

                _dragTesting = false;
                DragBegin(evt.touchId);
            }

            if (draggingObject == this)
            {
                float xx = evt.x - sGlobalDragStart.x + sGlobalRect.x;
                float yy = evt.y - sGlobalDragStart.y + sGlobalRect.y;

                if (dragBounds != null)
                {
                    Rect rect = GRoot.inst.LocalToGlobal((Rect)dragBounds);
                    if (xx < rect.x)
                        xx = rect.x;
                    else if (xx + sGlobalRect.width > rect.xMax)
                    {
                        xx = rect.xMax - sGlobalRect.width;
                        if (xx < rect.x)
                            xx = rect.x;
                    }

                    if (yy < rect.y)
                        yy = rect.y;
                    else if (yy + sGlobalRect.height > rect.yMax)
                    {
                        yy = rect.yMax - sGlobalRect.height;
                        if (yy < rect.y)
                            yy = rect.y;
                    }
                }

                Vector2 pt = this.parent.GlobalToLocal(new Vector2(xx, yy));
                if (float.IsNaN(pt.x))
                    return;

                sUpdateInDragging = true;
                this.SetXY(Mathf.RoundToInt(pt.x), Mathf.RoundToInt(pt.y));
                sUpdateInDragging = false;

                DispatchEvent("onDragMove", null);
            }
        }

        private void __touchEnd(EventContext context)
        {
            if (draggingObject == this)
            {
                draggingObject = null;
                DispatchEvent("onDragEnd", null);
            }
        }
        #endregion

        #region Tween Helpers
        public GTweener TweenMove(Vector2 endValue, float duration)
        {
            return GTween.To(this.xy, endValue, duration).SetTarget(this, TweenPropType.XY);
        }

        public GTweener TweenMoveX(float endValue, float duration)
        {
            return GTween.To(_x, endValue, duration).SetTarget(this, TweenPropType.X);
        }

        public GTweener TweenMoveY(float endValue, float duration)
        {
            return GTween.To(_y, endValue, duration).SetTarget(this, TweenPropType.Y);
        }

        public GTweener TweenScale(Vector2 endValue, float duration)
        {
            return GTween.To(this.scale, endValue, duration).SetTarget(this, TweenPropType.Scale);
        }

        public GTweener TweenScaleX(float endValue, float duration)
        {
            return GTween.To(_scaleX, endValue, duration).SetTarget(this, TweenPropType.ScaleX);
        }

        public GTweener TweenScaleY(float endValue, float duration)
        {
            return GTween.To(_scaleY, endValue, duration).SetTarget(this, TweenPropType.ScaleY);
        }

        public GTweener TweenResize(Vector2 endValue, float duration)
        {
            return GTween.To(this.size, endValue, duration).SetTarget(this, TweenPropType.Size);
        }

        public GTweener TweenFade(float endValue, float duration)
        {
            return GTween.To(_alpha, endValue, duration).SetTarget(this, TweenPropType.Alpha);
        }

        public GTweener TweenRotate(float endValue, float duration)
        {
            return GTween.To(_rotation, endValue, duration).SetTarget(this, TweenPropType.Rotation);
        }
        #endregion
    }
}