using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CommonLang.Geometry.SceneGraph2D
{
    public class DisplayNode : IDisposable
    {
        public string Name { get; set; }

        public DisplayNode()
        {
        }

        //-------------------------------------------------------------------------------------------------------------

        #region Tint

        public float Alpha { get; set; }
        /// <summary>
        /// Color RGB
        /// </summary>
        public uint Color { get; set; }


        #endregion

        //-------------------------------------------------------------------------------------------------------------

        #region Transform

        private ITransform transform = Factory.Instance.CreateTransform();
        private Vector2[] temp_pts = new Vector2[1];

        public Vector2 Translation
        {
            get { return transform.Translation; }
            set { transform.Translation = value; }
        }
        public float Rotation
        {
            get { return transform.Rotation; }
            set { transform.Rotation = value; }
        }
        public Vector2 Scale
        {
            get { return transform.Scale; }
            set { transform.Scale = value; }
        }
        
        public Vector2 LocalToParent(Vector2 p)
        {
            temp_pts[0] = p;
            transform.LocalToParent(temp_pts);
            return temp_pts[0];
        }
        public Vector2 ParentToLocal(Vector2 p)
        {
            temp_pts[0] = p;
            transform.ParentToLocal(temp_pts);
            return temp_pts[0];
        }       
        public Vector2 LocalToRoot(Vector2 p)
        {
            temp_pts[0] = p;
            this.TransformLocalToRoot(temp_pts);
            return temp_pts[0];
        }
        public Vector2 RootToLocal(Vector2 p)
        {
            temp_pts[0] = p;
            this.TransformRootToLocal(temp_pts);
            return temp_pts[0];
        }

        /// <summary>
        /// 本地坐标转换为父节点坐标
        /// </summary>
        public void TransformLocalToParent(Vector2[] pts)
        {
            transform.LocalToParent(pts);
        }
        /// <summary>
        /// 父节点坐标转换为本地坐标
        /// </summary>
        public void TransformParentToLocal(Vector2[] pts)
        {
            transform.ParentToLocal(pts);
        }

        /// <summary>
        /// 屏幕坐标转换为本地坐标
        /// </summary>
        public void TransformRootToLocal(Vector2[] pts)
        {
            if (parent != null)
            {
                using (var path = ListObjectPool<DisplayNode>.AllocAutoRelease())
                {
                    SceneGraphTreePath(path);
                    foreach (var node in path)
                    {
                        node.transform.ParentToLocal(pts);
                    }
                }
            }
        }
        /// <summary>
        /// 本地坐标转换为屏幕坐标
        /// </summary>
        public void TransformLocalToRoot(Vector2[] pts)
        {
            if (parent != null)
            {
                var curnode = this;
                do
                {
                    curnode.transform.LocalToParent(pts);
                    curnode = curnode.Parent;
                }
                while (curnode != null);
            }
        }

        #endregion

        //-------------------------------------------------------------------------------------------------------------

        #region IDisposable

        private bool disposedValue = false; // 要检测冗余调用

        protected virtual void Dispose(bool disposing)
        {
            if (!disposedValue)
            {
                if (disposing)
                {
                    // TODO: 释放托管状态(托管对象)。
                }

                // TODO: 释放未托管的资源(未托管的对象)并在以下内容中替代终结器。
                // TODO: 将大型字段设置为 null。

                disposedValue = true;
            }
        }

        // TODO: 仅当以上 Dispose(bool disposing) 拥有用于释放未托管资源的代码时才替代终结器。
        // ~DisplayNode() {
        //   // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。
        //   Dispose(false);
        // }

        // 添加此代码以正确实现可处置模式。
        public void Dispose()
        {
            // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。
            Dispose(true);
            // TODO: 如果在以上内容中替代了终结器,则取消注释以下行。
            // GC.SuppressFinalize(this);
        }
        #endregion

        //-------------------------------------------------------------------------------------------------------------

        #region Children

        private readonly List<DisplayNode> children = new List<DisplayNode>();
        internal DisplayNode parent;
        public DisplayNode Parent { get { return parent; } }
        public int NumChildren { get { return children.Count; } }
        public DisplayRoot Root
        {
            get
            {
                var currentObject = this;
                do
                {
                    if (currentObject is DisplayRoot)
                    {
                        return currentObject as DisplayRoot;
                    }
                    currentObject = currentObject.parent;
                }
                while (currentObject != null);
                return null;
            }
        }

        internal void InternalSetParent(DisplayNode node)
        {
            // check for a recursion
            DisplayNode ancestor = node;
            while (ancestor != this && ancestor != null)
                ancestor = ancestor.parent;
            if (ancestor == this)
                throw new Exception("An object cannot be added as a child to itself or one of its children (or children's children, etc.)");
            else
                parent = node;
        }
        internal void InternalAddChild(DisplayNode child, int index)
        {
            child.RemoveFromParent();
            child.InternalSetParent(this);
            children.Insert(index, child);
        }
        internal void InternalRemoveChild(DisplayNode child, bool dispose)
        {
            children.Remove(child);
            child.parent = null;
        }

        public bool ContainsChild(DisplayNode child)
        {
            while (child != null)
            {
                if (child == this)
                    return true;
                else
                    child = child.Parent;
            }
            return false;
        }
        public void AddChildAt(DisplayNode child, int index)
        {
            if (child == null || index < 0)
            {
                //LogError("AddChildAt Error :Child can not be null or index < 0");
                return;
            }
            if (child.Parent == this)
            {
                SetChildIndex(child, index);
            }
            else
            {
                InternalAddChild(child, index);
            }
        }

        public void AddChild(DisplayNode child)
        {
            AddChildAt(child, NumChildren);
        }

        public void RemoveChildByName(string name, bool dispose = true)
        {
            foreach (DisplayNode child in children)
            {
                if (child.Name.Equals(name))
                {
                    InternalRemoveChild(child, dispose);
                    break;
                }
            }
        }

        public void RemoveChild(DisplayNode child, bool dispose = true)
        {
            int result = children.IndexOf(child);
            if (result != -1)
            {
                RemoveChildAt(result, dispose);
            }
        }

        public void RemoveChildAt(int index, bool dispose = true)
        {
            if (index >= 0 && index < children.Count)
            {
                DisplayNode child = children[index];
                if (child.parent == this)
                {
                    InternalRemoveChild(child, dispose);
                }
            }
            else
            {
                throw new Exception("RemoveChild Error :: mChildren Out of Bounds");
            }
        }

        public void RemoveChildren(int beginIndex, int endIndex, bool dispose = true)
        {
            if (endIndex < 0 || endIndex >= NumChildren)
                endIndex = NumChildren - 1;

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

        public void RemoveAllChildren(bool dispose = true)
        {
            RemoveChildren(0, -1, dispose);
        }

        public void RemoveFromParent(bool dispose = true)
        {
            if (parent != null)
            {
                parent.RemoveChild(this, dispose);
            }
        }

        public void SetChildIndex(DisplayNode child, int index)
        {
            int oldIndex = GetChildIndex(child);
            if (oldIndex == -1)
            {
                //LogError("SetChildIndex Error: oldIndex = -1");
                return;
            }
            //logic list.
            children.RemoveAt(oldIndex);
            if (index > children.Count)
            {
                index = children.Count;
            }
            children.Insert(index, child);
        }

        public int GetChildIndex(DisplayNode child)
        {
            if (child == null)
            {
                //LogError("UIBase GetChildIndex() child == null");
                return -1;
            }
            return children.IndexOf(child);
        }

        public DisplayNode GetChildAt(int index)
        {
            if (index >= 0 && index < NumChildren)
                return children[index];
            else
                throw new Exception("Invalid child index");
        }

        public void SwapChildren(DisplayNode child1, DisplayNode child2)
        {
            int index1 = GetChildIndex(child1);
            int index2 = GetChildIndex(child2);
            if (index1 == -1 || index2 == -1)
                throw new Exception("Not a child of this container");
            SwapChildrenAt(index1, index2);
        }

        public void SwapChildrenAt(int index1, int index2)
        {
            DisplayNode child1 = GetChildAt(index1);
            DisplayNode child2 = GetChildAt(index2);

            if (child1 != null && child2 != null)
            {
                children[index1] = child2;
                children[index2] = child1;
            }
        }

        /// <summary>
        /// 获取所有节点
        /// </summary>
        /// <param name="list"></param>
        /// <param name="recursion"></param>
        public void GetChildren(List<DisplayNode> list, bool recursion = false)
        {
            list.AddRange(children);
            if (recursion)
            {
                foreach (var c in children)
                {
                    c.GetChildren(list, recursion);
                }
            }
        }

        /// <summary>
        /// 获取场景数路径,从当前节点一直到根节点
        /// </summary>
        public void SceneGraphTreePath(List<DisplayNode> ret)
        {
            var curnode = this;
            do
            {
                ret.Insert(0, curnode);
                curnode = curnode.Parent;
            }
            while (curnode != null);
        }


        #endregion

        //-------------------------------------------------------------------------------------------------------------

        #region Visit

        /// <summary>
        /// 遍历所有节点
        /// </summary>
        /// <param name="action">return true for break</param>
        /// <param name="recursion"></param>
        public bool ForEachChildren(Predicate<DisplayNode> action, bool recursion = false)
        {
            using (var list = ListObjectPool<DisplayNode>.AllocAutoRelease())
            {
                list.AddRange(children);
                for (int i = 0; i < list.Count; ++i)
                {
                    if (action(list[i])) { return true; }
                }
                if (recursion)
                {
                    for (int i = 0; i < list.Count; ++i)
                    {
                        if (list[i].ForEachChildren(action, recursion))
                        {
                            return true;
                        }
                    }
                }
            }
            return false;
        }


        public virtual void Visit(IGraphics g)
        {
            g.PushTransform();
            try
            {
                g.Transform(this.transform);
                OnDrawBegin(g);
                for (int i = 0; i < children.Count; ++i)
                {
                    children[i].Visit(g);
                }
                OnDrawAfter(g);
            }
            finally
            {
                g.PopTransform();
            }
        }


        /// <summary>
        /// 子节点前渲染
        /// </summary>
        /// <param name="g"></param>
        protected virtual void OnDrawBegin(IGraphics g) { }

        /// <summary>
        /// 子节点后渲染
        /// </summary>
        /// <param name="g"></param>
        protected virtual void OnDrawAfter(IGraphics g) { }

        public virtual DisplayNode HitTest(Vector2 point)
        {
            for (int i = children.Count - 1; i >= 0; --i)
            {
                var child = children[i];
                temp_pts[0] = point;
                var local = child.ParentToLocal(point);
                var hit = child.HitTest(local);
                if (hit != null)
                {
                    return hit;
                }
            }
            if (OnHitTest(ref point))
            {
                return this;
            }
            return null;
        }

        protected virtual bool OnHitTest(ref Vector2 localPoint) { return false; }


        #endregion
        
    }
}