using System;
using System.Collections.Generic;
using System.Text;
using CommonAI.Zone;
using CommonLang;
using CommonLang.Protocol;
using CommonAI.RTS;
using CommonLang.Vector;
using CommonLang.Log;
using CommonAI.Zone.ZoneEditor;
using CommonAI.Zone.Helper;
using CommonAI.RTS.Manhattan;

namespace CommonAI.ZoneClient
{
    partial class ZoneLayer
    {
        //-------------------------------------------------------------------------------------------

        /// <summary>
        /// 和地图做碰撞检测,是否阻挡
        /// </summary>
        /// <param name="u"></param>
        /// <returns></returns>
        public bool TouchMap(ZoneUnit u)
        {
            return TryTouchMap(u, u.X, u.Y);
        }

        /// <summary>
        /// 目标点是否阻挡
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        public bool TouchMap(float x, float y)
        {
            return path_finder.TouchMapBlock((((int)x) / Terrain.GridCellW), (((int)y) / Terrain.GridCellH));
        }

        /// <summary>
        /// 尝试用新的坐标和地图碰撞检测
        /// </summary>
        /// <param name="u"></param>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        public virtual bool TryTouchMap(ZoneUnit u, float x, float y)
        {
            int bx = (((int)x) / Terrain.GridCellW);
            int by = (((int)y) / Terrain.GridCellH);
            if (path_finder.TouchMapBlock(bx, by))
            {
                return true;
            }
            if (this.IsIgnoreTerrainTouch)
            {
                foreach (var flag in mFlags.Values)
                {
                    if (flag is ZoneEditorDecoration)
                    {
                        var deco = flag as ZoneEditorDecoration;
                        if (deco.TouchBlock(x, y))
                        {
                            return true;
                        }
                    }
                }
            }
            return false;
        }

        /**
         * 以目标点为中心,circle为半径,找可行走区域。找到了返回true;找不到返回false;
         * sx, sy 起始位置
         * dstx, dsty 目标位置
         * circle 搜寻半径
         * outx, outy修改后的目标点
         */
        public bool GetNearWay(float sx, float sy, float dstx, float dsty, float circle, ref float outx, ref float outy)
        {
            //以目标点为中心,一圈一圈向外查找可行走的块
            //起始点与终点连线方向上的点优先
            int W = Terrain.GridCellW;
            int H = Terrain.GridCellH;
            float squCircle = circle * circle;
            int dr = ((int)circle) / W;

            float vx = sx > dstx ? 1 : -1;
            float vy = sy > dsty ? 1 : -1;
            bool f = true;
            for (int r = 1; r <= dr; r++)
            {
                for (int v = 1; v <= 4; v++)
                {
                    float xx = dstx + W * r * vx;
                    float yy = dsty + H * r * vy;
                    for (int dx = f ? 0 : 1; f ? dx <= r : dx < r; dx++)
                    {
                        float x = dstx + dx * W * vx;
                        if (MathVector.getDistanceSquare(x, yy, dstx, dsty) < squCircle)
                        {
                            if (!path_finder.TouchMapBlock(((int)x) / W, ((int)yy) / H))
                            {
                                outx = x;
                                outy = yy;
                                return true;
                            }
                        }
                    }

                    for (int dy = f ? 1 : 0; f ? dy < r : dy <= r; dy++)
                    {
                        float y = dsty + dy * H * vy;
                        if (MathVector.getDistanceSquare(xx, y, dstx, dsty) < squCircle)
                        {
                            if (!path_finder.TouchMapBlock(((int)xx) / W, ((int)y) / H))
                            {
                                outx = xx;
                                outy = y;
                                return true;
                            }
                        }
                    }
                    if (f) vy *= -1;
                    else vx *= -1;
                    f = !f;
                }
            }

            outx = -1;
            outy = -1;
            return false;
        }

        /// <summary>
        /// 2个单位是否碰撞
        /// </summary>
        /// <param name="s"></param>
        /// <param name="d"></param>
        /// <returns></returns>
        public virtual bool TouchObject2(ZoneObject s, ZoneObject d)
        {
            if (s == d) return false;
            float w = s.X - d.X;
            float h = s.Y - d.Y;
            float r = s.RadiusSize + d.RadiusSize;
            if (w * w + h * h <= r * r)
            {
                return true;
            }
            return false;
        }
        /// <summary>
        /// 尝试用新的坐标碰撞检测
        /// </summary>
        /// <param name="s"></param>
        /// <param name="sx"></param>
        /// <param name="sy"></param>
        /// <param name="d"></param>
        /// <returns></returns>
        public virtual bool TryTouchObject2(ZoneObject s, float sx, float sy, ZoneObject d)
        {
            if (s == d) return false;
            float w = sx - d.X;
            float h = sy - d.Y;
            float r = s.RadiusSize + d.RadiusSize;
            if (w * w + h * h <= r * r)
            {
                return true;
            }
            return false;
        }
        /// <summary>
        /// 获得和当前单位碰撞的单位
        /// </summary>
        /// <param name="src"></param>
        /// <returns></returns>
        public ZoneUnit TouchUnit(ZoneUnit src)
        {
            ZoneUnit ret = null;
            ForEachNearObjects(src.X, src.Y, src.BodySize, (ZoneObject o, ref bool cancel) =>
            {
                if ((o != src) && (o is ZoneUnit))
                {
                    ZoneUnit u = o as ZoneUnit;
                    if ((u.TouchObj) && TouchObject2(src, u))
                    {
                        ret = u;
                        cancel = true;
                    }
                }
            });
            return ret;
        }
        /// <summary>
        /// 获得和当前单位碰撞的建筑
        /// </summary>
        /// <param name="src"></param>
        /// <returns></returns>
        public ZoneUnit TouchBuilding(ZoneUnit src)
        {
            ZoneUnit ret = null;
            ForEachNearObjects(src.X, src.Y, src.BodySize, (ZoneObject o, ref bool cancel) =>
            {
                if ((o != src) && (o is ZoneUnit))
                {
                    ZoneUnit u = o as ZoneUnit;
                    if (u.IsStaticBlockable)
                    {
                        if (u.TouchObj && TouchObject2(src, u))
                        {
                            ret = u;
                            cancel = true;
                        }
                    }
                }
            });
            return ret;
        }
        public ZoneUnit TryTouchUnit(ZoneUnit src, float x, float y)
        {
            ZoneUnit ret = null;
            ForEachNearObjects(x, y, src.BodySize, (ZoneObject o, ref bool cancel) =>
            {
                if ((o != src) && (o is ZoneUnit))
                {
                    ZoneUnit u = o as ZoneUnit;
                    if ((u.TouchObj) && TryTouchObject2(src, x, y, u))
                    {
                        ret = u;
                        cancel = true;
                    }
                }
            });
            return ret;
        }
        public ZoneUnit TryTouchBuilding(ZoneUnit src, float x, float y)
        {
            ZoneUnit ret = null;
            ForEachNearObjects(x, y, src.BodySize, (ZoneObject o, ref bool cancel) =>
            {
                if ((o != src) && (o is ZoneUnit))
                {
                    ZoneUnit u = o as ZoneUnit;
                    if (u.IsStaticBlockable)
                    {
                        if (u.TouchObj && TryTouchObject2(src, x, y, u))
                        {
                            ret = u;
                            cancel = true;
                        }
                    }
                }
            });
            return ret;
        }
        //-------------------------------------------------------------------------------------------
        /// <summary>
        /// 获取最近的一个单位
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="range"></param>
        /// <param name="select"></param>
        /// <returns></returns>
        public virtual T GetNearUnit<T>(float x, float y, float range, Predicate<T> select) where T : ZoneObject
        {
            T min = null;
            float min_len = float.MaxValue;
            float r2 = range * range;
            ForEachNearObjects<T>(x, y, range, (T u, ref bool cancel) =>
            {
                if (select(u))
                {
                    float len = MathVector.getDistanceSquare(u.X, u.Y, x, y);
                    if (len <= r2 && min_len > len)
                    {
                        min_len = len;
                        min = u;
                    }
                }
            });
            return min;
        }

        /// <summary>
        /// 获取最近的一个单位
        /// </summary>
        /// <param name="owner"></param>
        /// <param name="range"></param>
        /// <param name="expect"></param>
        /// <returns></returns>
        public virtual ZoneUnit GetNearTarget(ZoneActor owner, float range, SkillTemplate.CastTarget expect = SkillTemplate.CastTarget.Enemy)
        {
            return GetNearUnit<ZoneUnit>(owner.X, owner.Y, range, (u) => { return IsAttackable(owner, u, expect); });
        }

        /// <summary>
        /// 获取最近可检取单位
        /// </summary>
        /// <param name="owner"></param>
        /// <returns></returns>
        public ZoneItem GetNearPickableItem(ZoneActor owner)
        {
            if (mSpaceDiv != null)
            {
                return mSpaceDiv.GetNearPickableItem(owner);
            }
            else if (mObjectes != null)
            {
                return mObjectes.GetNearPickableItem(owner);
            }
            else
            {
                return null;
            }
        }

        public ZoneItem GetNearPopPanelItem(ZoneActor owner)
        {
            if (mSpaceDiv != null)
            {
                return mSpaceDiv.GetNearPopPanelItem(owner);
            }
            else if(mObjectes != null)
            {
                return mObjectes.GetNearPopPanelItem(owner);
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// 获取掉落单位单位
        /// </summary>
        /// <param name="owner"></param>
        /// <returns></returns>
        public ZoneItem GetPickableItem()
        {
            return mObjectes.GetDropItem();
        }

        /// <summary>
        /// 判断是否可检取
        /// </summary>
        /// <param name="owner"></param>
        /// <param name="item"></param>
        /// <param name="no_touch">不碰撞检测</param>
        /// <returns></returns>
        public virtual bool IsPickableItem(ZoneActor owner, ZoneItem item, bool no_touch = false)
        {
            if (no_touch || CMath.intersectRound(item.X, item.Y, item.Info.BodySize, owner.X, owner.Y, owner.Info.BodySize))
            {
                if (item.Info.Pickable && (item.Info.DropForAll || item.Force == owner.Force))
                {
                    return true;
                }
            }
            return false;
        }


        public virtual bool IsCanPickItem(ZoneActor owner, ZoneItem item)
        {
            return IsPickableItem(owner, item);
        }

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

        [Obsolete]
        public virtual List<ZoneUnit> GetSkillAttackableUnits(ZoneUnit src, SkillTemplate skill)
        {
            List<ZoneUnit> list = new List<ZoneUnit>();
            GetSkillAttackableUnits(src, skill, list);
            return list;
        }

        /// <summary>
        /// 获取技能可打到的单位
        /// </summary>
        /// <param name="src"></param>
        /// <param name="skill"></param>
        /// <param name="list"></param>
        public virtual void GetSkillAttackableUnits(ZoneUnit src, SkillTemplate skill, List<ZoneUnit> list)
        {
            float range = src.GetSkillAttackRange(skill);
            ForEachNearObjects<ZoneUnit>(src.X, src.Y, range, (ZoneUnit u, ref bool cancel) =>
            {
                if (IsAttackable(src, u, skill.ExpectTarget))
                {
                    if (CMath.intersectRound(src.X, src.Y, range, u.X, u.Y, u.Info.BodyHitSize))
                    {
                        list.Add(u);
                    }
                }
            });
        }

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

        #region TERRAIN


        private ZoneManhattanMap path_terrain_data;
        private AstarManhattan path_finder;
        private ManhattanMapAreaGenerator path_terrain_area_gen;

        protected virtual void InitTerrain(ClientEnterScene msg, out ZoneManhattanMap terrain_data, out AstarManhattan path_finder, out ManhattanMapAreaGenerator area_gen)
        {
            if (msg.sender is Zone.Instance.InstanceZone)
            {
                var zone = msg.sender as Zone.Instance.InstanceZone;
                terrain_data = zone.PathFinderTerrain;
                path_finder = zone.PathFinder;
                area_gen = zone.TerrainAreaGenerator;
                this.IsShareTerrain = true;
                this.IsIgnoreTerrainTouch = false;
            }
            else
            {
                var data = mTerrainDataSrc.Clone() as ZoneInfo;
                terrain_data = new ZoneManhattanMap(data, Templates.TerrainDefinition);
                path_finder = new AstarManhattan(msg.sceneID, terrain_data, true, msg.spaceDiv);
                area_gen = new ManhattanMapAreaGenerator(terrain_data.Data);
                this.IsShareTerrain = false;
                this.IsIgnoreTerrainTouch = false;
            }
            //mData.Terrain = null;
        }
        protected virtual void DisposeTerrain()
        {
            if (!IsShareTerrain)
            {
                if (path_finder != null)
                {
                    path_finder.Dispose();
                }
                if (path_terrain_data != null)
                {
                    path_terrain_data.Dispose();
                }
            }
            this.path_finder = null;
            this.path_terrain_data = null;
            this.path_terrain_area_gen = null;
        }


        public AstarManhattan PathFinder
        {
            get { return path_finder; }
        }
        public ZoneManhattanMap PathFinderTerrain
        {
            get { return path_terrain_data; }
        }
        public ManhattanMapAreaGenerator TerrainAreaGenerator
        {
            get { return path_terrain_area_gen; }
        }
        /// <summary>
        /// 是否标记为共享寻路数据,表示多个场景共享一个PathFinderTerrain数据;
        /// 一般客户端多开或者共享服务端Terrain打开此功能。
        /// </summary>
        public bool IsShareTerrain
        {
            get; protected set;
        }
        /// <summary>
        /// 是否忽略本地Decoration碰撞
        /// </summary>
        public bool IsIgnoreTerrainTouch
        {
            get; protected set;
        }

        /// <summary>
        /// 寻路
        /// </summary>
        /// <param name="sx"></param>
        /// <param name="sy"></param>
        /// <param name="dx"></param>
        /// <param name="dy"></param>
        /// <returns></returns>
        public virtual AstarManhattan.MWayPoint FindPath(float sx, float sy, float dx, float dy)
        {
            AstarManhattan.MWayPoint wp;
            if (path_finder.findPath(sx, sy, dx, dy, out wp, true) == AstarManhattan.FindPathResult.Cross)
            {
                return wp;
            }
            return null;
        }
        public virtual AstarManhattan.FindPathResult FindPathResult(float sx, float sy, float dx, float dy, out AstarManhattan.MWayPoint wp)
        {
            return path_finder.findPath(sx, sy, dx, dy, out wp, true);
        }

        /// <summary>
        /// 获取位置所在的编辑器Area
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        public ZoneEditorArea GetArea(float x, float y)
        {
            if (PathFinder == null)
            {
                return null;
            }

            var node = PathFinder.GetMapNodeByPos(x, y) as ZoneMapNode;
            if (node != null)
            {
                return node.ClientArea;
            }
            return null;
        }

        #endregion

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

        #region SPACE_DIVISION

        private SpaceDivision mSpaceDiv;

        public int SpaceDivSize { get; private set; }
        public int SpaceXCount { get { return (mSpaceDiv != null) ? mSpaceDiv.SpaceXCount : 0; } }
        public int SpaceYCount { get { return (mSpaceDiv != null) ? mSpaceDiv.SpaceYCount : 0; } }

        internal void SwapSpace(ZoneObject gu)
        {
            if (mSpaceDiv != null)
            {
                mSpaceDiv.SwapSpace(gu.mCurrentCellNode, gu.X, gu.Y, false);
            }
        }

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

        /// <summary>
        /// 获取当前坐标附近的所有单位
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="indexer"></param>
        /// <returns>is cancel</returns>
        public bool ForEachNearObjects<T>(float x, float y, SpaceDivision.ObjectForEachAction<T> indexer) where T : ZoneObject
        {
            if (mSpaceDiv != null)
            {
                return mSpaceDiv.ForEachNearObjects(x, y, indexer);
            }
            else
            {
                var action = new Predicate<ZoneObject>((obj) =>
                {
                    if (obj is T)
                    {
                        bool cancel = false;
                        indexer(obj as T, ref cancel);
                        return cancel;
                    }
                    return false;
                });
                return mObjectes.ForEachObjects(action);
            }
        }

        /// <summary>
        /// 获取当前坐标附近的所有单位
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="r"></param>
        /// <param name="indexer"></param>
        /// <returns>is cancel</returns>
        public bool ForEachNearObjects<T>(float x, float y, float r, SpaceDivision.ObjectForEachAction<T> indexer) where T : ZoneObject
        {
            if (mSpaceDiv != null)
            {
                return mSpaceDiv.ForEachNearObjects(x, y, r, indexer);
            }
            else
            {
                var action = new Predicate<ZoneObject>((obj) =>
                {
                    if (obj is T)
                    {
                        bool cancel = false;
                        indexer(obj as T, ref cancel);
                        return cancel;
                    }
                    return false;
                });
                return mObjectes.ForEachObjects(action);
            }
        }

        /// <summary>
        /// 获取当前坐标附近的所有单位
        /// </summary>
        /// <param name="x1"></param>
        /// <param name="y1"></param>
        /// <param name="x2"></param>
        /// <param name="y2"></param>
        /// <param name="indexer"></param>
        /// <returns>is cancel</returns>
        public bool ForEachNearObjectsRect<T>(float x1, float y1, float x2, float y2, SpaceDivision.ObjectForEachAction<T> indexer) where T : ZoneObject
        {
            if (mSpaceDiv != null)
            {
                return mSpaceDiv.ForEachNearObjectsRect(x1, y1, x2, y2, indexer);
            }
            else
            {
                var action = new Predicate<ZoneObject>((obj) =>
                {
                    if (obj is T)
                    {
                        bool cancel = false;
                        indexer(obj as T, ref cancel);
                        return cancel;
                    }
                    return false;
                });
                return mObjectes.ForEachObjects(action);
            }
        }


        #endregion
    }
}