using CommonAI.RTS.Manhattan;
using CommonLang;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace CommonAI.Zone.Helper
{
    public class ZoneManhattanMap : Disposable, IManhattanMap
    {
        public ZoneInfo Data { get; private set; }
        public int[,] Terrain { get; private set; }
        readonly private float TotalW, TotalH;
        readonly private HashMap<int, TerrainDefinitionMap.MapBlockBrush> Brushes;

        public ZoneManhattanMap(ZoneInfo info, TerrainDefinitionMap define)
        {
            this.Data = info;
            this.TotalW = info.TotalWidth;
            this.TotalH = info.TotalHeight;
            this.Terrain = info.TerrainMatrix;
            this.Brushes = new HashMap<int, TerrainDefinitionMap.MapBlockBrush>((define != null) ? define.Brushes.Count : 0);
            if (define != null)
            {
                foreach (var b in define.Brushes)
                {
                    Brushes.Put(b.Value, b);
                }
            }
        }
        protected override void Disposing()
        {
            Brushes.Clear();
            Data = null;
            Terrain = null;
        }

        public int XCount { get { return Data.mXCount; } }
        public int YCount { get { return Data.mYCount; } }
        public float CellW { get { return Data.mGridCellW; } }
        public float CellH { get { return Data.mGridCellH; } }
        public float TotalWidth { get { return TotalW; } }
        public float TotalHeight { get { return TotalH; } }

        public virtual bool TestBlock(int bx, int by)
        {
            TerrainDefinitionMap.MapBlockBrush brush;
            if (Terrain[bx, by] != 0)
            {
                if (Brushes.TryGetValue(Terrain[bx, by], out brush))
                {
                    return brush.IsBlock;
                }
                return true;
            }
            return false;
        }
        public bool TestBlockValue(int value)
        {
            TerrainDefinitionMap.MapBlockBrush brush;
            if (value != 0)
            {
                if (Brushes.TryGetValue(value, out brush))
                {
                    return brush.IsBlock;
                }
                return true;
            }
            return false;
        }
        public virtual int GetValue(int bx, int by)
        {
            return Terrain[bx, by];
        }
        public virtual bool SetValue(int bx, int by, int value)
        {
            if (value != Terrain[bx, by])
            {
                Terrain[bx, by] = value;
                return true;
            }
            return false;
        }

        public virtual AstarManhattan.MMapNode CreateMapNode()
        {
            return new ZoneMapNode();
        }

    }

    public class ZoneMapNode : AstarManhattan.MMapNode
    {
        private BindArea bind_area;

        public ZoneClient.ZoneEditorArea ClientArea
        {
            get { return (bind_area == null) ? null : bind_area.client_area; }
        }
        public Instance.ZoneArea ServerArea
        {
            get { return (bind_area == null) ? null : bind_area.server_area; }
        }

        public void SetClientArea(ZoneClient.ZoneEditorArea area)
        {
            if (bind_area == null) { bind_area = new BindArea(); }
            bind_area.client_area = area;
        }
        public void SetServerArea(Instance.ZoneArea area)
        {
            if (bind_area == null) { bind_area = new BindArea(); }
            bind_area.server_area = area;
        }

        public class BindArea
        {
            public ZoneClient.ZoneEditorArea client_area;
            public Instance.ZoneArea server_area;
        }
    }


    public class ManhattanMapAreaGenerator
    {
        public class MapNodeIndex
        {
            public readonly int BX;
            public readonly int BY;
            public MapNodeIndex(int bx, int by)
            {
                this.BX = bx;
                this.BY = by;
            }
            public override bool Equals(object obj)
            {
                if (obj is MapNodeIndex)
                {
                    var src = (MapNodeIndex)obj;
                    return src.BX == this.BX && src.BY == this.BY;
                }
                return base.Equals(obj);
            }
            public override int GetHashCode()
            {
                return base.GetHashCode();
            }
        }
        /// <summary>
        /// 扫描地块区域结果
        /// </summary>
        public class ContinuousMapNode : IEnumerable<MapNodeIndex>
        {
            private readonly ZoneInfo map;
            private readonly int bsx, bsy, bdx, bdy;
            private readonly int value;
            private readonly List<MapNodeIndex> nodes;
            private readonly MapNodeIndex[,] node_matrix;

            public int Value { get { return value; } }

            internal ContinuousMapNode(ZoneInfo map, int bx, int by, int xr, int yr)
            {
                this.map = map;
                this.value = map.TerrainMatrix[bx, by];
                this.nodes = new List<MapNodeIndex>();
                this.bsx = bx - xr;
                this.bsy = by - yr;
                this.bdx = bx + xr;
                this.bdy = by + yr;
                this.node_matrix = new MapNodeIndex[xr + 1 + xr, yr + 1 + yr];
            }
            public bool IsInRect(int bx, int by)
            {
                if (bx >= this.bsx && bx <= this.bdx &&
                    by >= this.bsy && by <= this.bdy)
                {
                    return true;
                }
                return false;
            }
            public bool IsInRect(float x, float y)
            {
                int bx = (int)(x / map.GridCellW);
                int by = (int)(y / map.GridCellH);
                return IsInRect(bx, by);
            }
            public MapNodeIndex GetIndex(int bx, int by)
            {
                if (IsInRect(bx, by))
                {
                    return node_matrix[bx - bsx, by - bsy];
                }
                return null;
            }
            public bool Contains(MapNodeIndex src)
            {
                return GetIndex(src.BX, src.BY) != null;
            }
            public bool TryAdd(MapNodeIndex src)
            {
                if (IsInRect(src.BX, src.BY) && (node_matrix[src.BX - bsx, src.BY - bsy] == null))
                {
                    nodes.Add(src);
                    node_matrix[src.BX - bsx, src.BY - bsy] = src;
                    return true;
                }
                return false;
            }
            public IEnumerator<MapNodeIndex> GetEnumerator()
            {
                return nodes.GetEnumerator();
            }
            IEnumerator IEnumerable.GetEnumerator()
            {
                return nodes.GetEnumerator();
            }
        }

        private static int[][] near_table_cross = new int[][]
        {
                new int[]{ 0,-1},
                new int[]{-1, 0},
                new int[]{ 1, 0},
                new int[]{ 0, 1},
        };
        private ZoneInfo map;

        public ManhattanMapAreaGenerator(ZoneInfo map)
        {
            this.map = map;
        }

        public bool TryGetValue(int bx, int by, out int value)
        {
            if (bx >= 0 && bx < map.XCount && by >= 0 && by < map.YCount)
            {
                value = map.TerrainMatrix[bx, by];
                return true;
            }
            value = 0;
            return false;
        }
        public bool TryGetValue(float x, float y, out int value)
        {
            int bx = (int)(x / map.GridCellW);
            int by = (int)(y / map.GridCellH);
            return TryGetValue(bx, by, out value);
        }
        public bool TryGetIndex(int bx, int by, int value, out MapNodeIndex index)
        {
            if (bx >= 0 && bx < map.XCount && by >= 0 && by < map.YCount && map.TerrainMatrix[bx, by] == value)
            {
                index = new MapNodeIndex(bx, by);
                return true;
            }
            index = new MapNodeIndex(-1, -1);
            return false;
        }
        public bool TryGetIndex(float x, float y, int value, out MapNodeIndex index)
        {
            int bx = (int)(x / map.GridCellW);
            int by = (int)(y / map.GridCellH);
            return TryGetIndex(bx, by, value, out index);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="x">中心点</param>
        /// <param name="y">中心点</param>
        /// <param name="xr">x半径</param>
        /// <param name="yr">y半径</param>
        /// <returns></returns>
        public ContinuousMapNode GetContinuousMapNode(float x, float y, float xr, float yr)
        {
            return GetContinuousMapNode(
                (int)(x / map.GridCellW),
                (int)(y / map.GridCellH),
                (int)(xr / map.GridCellW),
                (int)(yr / map.GridCellH));
        }
        public virtual ContinuousMapNode GetContinuousMapNode(int bx, int by, int xr, int yr)
        {
            if (bx >= 0 && bx < map.XCount && by >= 0 && by < map.YCount)
            {
                ContinuousMapNode list = new ContinuousMapNode(map, bx, by, xr, yr);
                MapNodeIndex src = new MapNodeIndex(bx, by);
                if (list.TryAdd(src))
                {
                    Stack<MapNodeIndex> stack = new Stack<MapNodeIndex>((xr * 2) * (yr * 2));
                    stack.Push(src);
                    while (stack.Count > 0)
                    {
                        MapNodeIndex cur = stack.Pop();
                        for (int i = 0; i < near_table_cross.Length; i++)
                        {
                            MapNodeIndex next;
                            if (TryGetIndex(cur.BX + near_table_cross[i][0], cur.BY + near_table_cross[i][1], list.Value, out next))
                            {
                                if (list.TryAdd(next))
                                {
                                    stack.Push(next);
                                }
                            }
                        }
                    }
                    return list;
                }
            }
            return null;
        }



    }
}