﻿using System;
using JLGames.RocketDriver.CSharp.Mathx;

namespace JLGames.RocketDriver.Games.Tiledx
{
    public sealed class WalkableMap
    {
        private readonly int[][] m_WalkableData;
        private Bounds2Int m_WalkableBound;

        public WalkableMap(int[][] walkableData)
        {
            m_WalkableData = walkableData;
            m_WalkableBound = new Bounds2Int(0, 0, walkableData[0].Length, walkableData.Length);
        }

        public Bounds2Int WalkableBound => m_WalkableBound;

        /// <summary>
        /// 取值
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        public int GetWalkableValue(int x, int y)
        {
            if (!m_WalkableBound.ContainsX(x) || !m_WalkableBound.ContainsY(y))
            {
                return -1;
            }

            return m_WalkableData[y][x];
        }

        /// <summary>
        /// 取附近值为value的点坐标
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="value"></param>
        /// <param name="max">最大搜索范围</param>
        /// <returns></returns>
        public Point2Int? GetNearValue(int x, int y, int value, int max)
        {
            for (var addY = 0; addY < max; addY++)
            {
                var y1 = y + addY;
                var y2 = y - addY;
                for (var addX = 0; addX <= addY; addX++)
                {
                    var x1 = x + addX;
                    var x2 = x - addX;

                    if (CheckValue(x1, y1, value))
                    {
                        return new Point2Int(x1, y1);
                    }

                    if (CheckValue(x2, y1, value))
                    {
                        return new Point2Int(x2, y1);
                    }

                    if (CheckValue(x1, y2, value))
                    {
                        return new Point2Int(x1, y2);
                    }

                    if (CheckValue(x2, y2, value))
                    {
                        return new Point2Int(x2, y2);
                    }
                }
            }

            return null;
        }

        /// <summary>
        /// 值检查
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public bool CheckValue(int x, int y, int value)
        {
            if (!m_WalkableBound.ContainsX(x) || !m_WalkableBound.ContainsY(y))
            {
                return false;
            }

            return m_WalkableData[y][x] == value;
        }

        /// <summary>
        /// 取全部数据
        /// </summary>
        /// <returns></returns>
        public int[][] GetFullData()
        {
            return m_WalkableData;
        }

        /// <summary>
        /// 取区域数据
        /// </summary>
        /// <param name="bound"></param>
        /// <returns></returns>
        public int[][] GetDataAtBound(Bounds2Int bound)
        {
            return bound.Equals(m_WalkableBound)
                ? m_WalkableData
                : GetDataAtBound(bound.XMin, bound.YMin, bound.XMax, bound.YMax);
        }

        /// <summary>
        /// 取区域数据
        /// 非连续扩展
        /// </summary>
        /// <param name="xMin"></param>
        /// <param name="yMin"></param>
        /// <param name="xMax"></param>
        /// <param name="yMax"></param>
        /// <returns></returns>
        public int[][] GetDataAtBound(int xMin, int yMin, int xMax, int yMax)
        {
            var bounds = m_WalkableBound.Crop2(xMin, yMin, xMax, yMax);
            if (bounds.IsNone) return null;

            var ySize = bounds.YSize;
            var xSize = bounds.XSize;
            var rs = new int[ySize][];
            var y = 0;
            for (var yIndex = bounds.YMin; yIndex < bounds.YMax; yIndex++)
            {
                rs[y] = new int[xSize];
                Array.Copy(m_WalkableData[yIndex], bounds.XMin, rs[y], 0, xSize);
                y++;
            }

            return rs;
        }
    }
}