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

namespace JLGames.RocketDriver.Games.Tiledx
{
    public static class TiledWorldUtil
    {
        /// <summary>
        /// Calculate the map group index
        /// 计算地图组索引
        /// </summary>
        /// <param name="mapX"></param>
        /// <param name="mapY"></param>
        /// <param name="groupWidth"></param>
        /// <returns></returns>
        public static int GetGroupIndex(int mapX, int mapY, int groupWidth)
        {
            return mapY * groupWidth + mapX;
        }

        /// <summary>
        /// Global coordinates rotor map coordinates
        /// 全局坐标转子地图坐标
        /// Acyclic mode
        /// 非循环模式
        /// </summary>
        /// <param name="globalIndex"></param>
        /// <param name="sliceSize"></param>
        /// <returns></returns>
        public static LocalIndex GlobalIndexToLocal(int globalIndex, int sliceSize)
        {
            var mapIndex = globalIndex / sliceSize;
            var localIndex = globalIndex % sliceSize;
            if (localIndex < 0 && mapIndex <= 0)
            {
                mapIndex -= 1;
            }

            localIndex = localIndex < 0 ? localIndex + sliceSize : localIndex;
            return new LocalIndex {MapIndex = mapIndex, MapLocalIndex = localIndex};
        }

        /// <summary>
        /// Global coordinates rotor map coordinates
        /// 全局坐标转子地图坐标
        /// loop mode
        /// 循环模式
        /// </summary>
        /// <param name="globalIndex"></param>
        /// <param name="sliceSize"></param>
        /// <param name="cycleLen"></param>
        /// <returns></returns>
        public static LocalIndex GlobalIndexToLocal(int globalIndex, int sliceSize, int cycleLen)
        {
            var lIndex = GlobalIndexToLocal(globalIndex, sliceSize);
            if (cycleLen <= 0)
            {
                return lIndex;
            }

            if (lIndex.MapIndex < 0)
            {
                if (1 == cycleLen)
                {
                    lIndex.MapIndex = 0;
                }
                else
                {
                    var mIdx = lIndex.MapIndex % cycleLen;
                    if (mIdx < 0)
                    {
                        lIndex.MapIndex = mIdx + cycleLen;
                    }
                }
            }

            return lIndex;
        }

        /// <summary>
        /// Submap coordinates to global coordinates
        /// 子地图坐标转全局坐标
        /// </summary>
        /// <param name="mapIndex"></param>
        /// <param name="localIndex"></param>
        /// <param name="sliceSize"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public static int LocalIndexToGlobal(int mapIndex, int localIndex, int sliceSize)
        {
            if (localIndex < 0 || localIndex >= sliceSize)
            {
                throw new Exception("LocalIndex Range Error! ");
            }

            return mapIndex * sliceSize + localIndex;
        }

        /// <summary>
        /// World coordinates rotor map internal coordinates
        /// 世界坐标转子地图内部坐标
        /// </summary>
        /// <param name="globalX"></param>
        /// <param name="globalY"></param>
        /// <param name="mapWidth"></param>
        /// <param name="mapHeight"></param>
        /// <param name="cycleXMapCount">循环水平数量,<=0时关闭循环 </param>
        /// <param name="cycleYMapCount">循环垂直数量,<=0时关闭循环</param>
        /// <returns></returns>
        public static LocalLocation GlobalPointToLocal(int globalX, int globalY, int mapWidth, int mapHeight,
            int cycleXMapCount = 0, int cycleYMapCount = 0)
        {
            var localX = GlobalIndexToLocal(globalX, mapWidth, cycleXMapCount);
            var localY = GlobalIndexToLocal(globalY, mapHeight, cycleYMapCount);
            return new LocalLocation
            {
                MapX = localX.MapIndex,
                MapY = localY.MapIndex,
                LocalX = localX.MapLocalIndex,
                LocalY = localY.MapLocalIndex
            };
        }

        /// <summary>
        /// Submap internal coordinates to world coordinates
        /// 子地图内部坐标转世界坐标
        /// </summary>
        /// <param name="mapLoc"></param>
        /// <param name="local"></param>
        /// <param name="mapWidth"></param>
        /// <param name="mapHeight"></param>
        /// <returns></returns>
        public static Point2Int LocalPointToGlobal(Point2Int mapLoc, Point2Int local, int mapWidth, int mapHeight)
        {
            return new Point2Int
            {
                X = LocalIndexToGlobal(mapLoc.X, local.X, mapWidth),
                Y = LocalIndexToGlobal(mapLoc.Y, local.Y, mapHeight)
            };
        }

        /// <summary>
        /// slice
        /// 一维切块
        /// </summary>
        /// <param name="start"></param>
        /// <param name="size"></param>
        /// <param name="sliceSize"></param>
        /// <returns></returns>
        public static int[] SliceGlobalLine(int start, int size, int sliceSize)
        {
            if (size <= 0)
            {
                return null;
            }

            if (size == 1)
            {
                return new[] {start};
            }

            var end = start + size;

            var startIndex = GlobalIndexToLocal(start, sliceSize);
            var endIndex = GlobalIndexToLocal(end, sliceSize);

            if (startIndex.MapIndex == endIndex.MapIndex)
            {
                return new int[] {start, end};
            }

            var ln = endIndex.MapIndex - startIndex.MapIndex + (endIndex.MapLocalIndex == 0 ? 1 : 2);
            var rs = new int[ln];
            rs[0] = start;
            rs[ln - 1] = end;
            for (var i = 1; i < ln - 1; i++)
            {
                rs[i] = sliceSize * (i + startIndex.MapIndex);
            }

            return rs;
        }

        /// <summary>
        /// Dice a rectangular area
        /// 对矩形区域切块
        /// </summary>
        /// <param name="globalBound"></param>
        /// <param name="sizeX"></param>
        /// <param name="sizeY"></param>
        /// <returns></returns>
        public static MapLocalBound[][] CutBoundByCycleZero(Bounds2Int globalBound, int sizeX, int sizeY)
        {
            var xLine = new Line1Int {Start = globalBound.XMin, End = globalBound.XMax};
            var yLine = new Line1Int {Start = globalBound.YMin, End = globalBound.YMax};
            var xLines = xLine.SliceAtZero(sizeX);
            var yLines = yLine.SliceAtZero(sizeY);
//            DebugUtil.Log(xLines.ToStringText());
//            DebugUtil.Log(yLines.ToStringText());
            if (xLines == null || yLines == null || xLines.Length == 0 || yLines.Length == 0)
            {
                return null;
            }

            var rs = new MapLocalBound[yLines.Length][];
            for (var yIndex = 0; yIndex < yLines.Length; yIndex++)
            {
                rs[yIndex] = new MapLocalBound[xLines.Length];
                var startY = GlobalIndexToLocal(yLines[yIndex].Start, (int) sizeY);
                var endY = GlobalIndexToLocal(yLines[yIndex].End, (int) sizeY);
//                DebugUtil.Log("StartY:", startY);
//                DebugUtil.Log("EndY:", endY);
                for (var xIndex = 0; xIndex < xLines.Length; xIndex++)
                {
                    var startX = GlobalIndexToLocal(xLines[xIndex].Start, (int) sizeX);
                    var endX = GlobalIndexToLocal(xLines[xIndex].End, (int) sizeX);
//                    DebugUtil.Log("\tStartX:", startX);
//                    DebugUtil.Log("\tEndX:", endX);
                    var mlb = new MapLocalBound
                    {
                        MapX = startX.MapIndex,
                        MapY = startY.MapIndex,
                        Bounds2Int = new Bounds2Int
                        {
                            XMin = startX.MapLocalIndex,
                            YMin = startY.MapLocalIndex,
                            XMax = endX.MapLocalIndex == 0 ? (int) sizeX : endX.MapLocalIndex,
                            YMax = endY.MapLocalIndex == 0 ? (int) sizeY : endY.MapLocalIndex
                        }
                    };
//                    DebugUtil.Log("结果：", mlb);
                    rs[yIndex][xIndex] = mlb;
                }
            }

            return rs;
        }
    }
}