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

namespace JLGames.RocketDriver.Games.Tiledx
{
    public struct LocalIndex
    {
        public int MapIndex;
        public int MapLocalIndex;

        public override string ToString()
        {
            return $"{{MapIndex={MapIndex},LocalIndex={MapLocalIndex}}}";
        }
    }

    /// <summary>
    /// Sub map location
    /// 子Map坐标
    /// </summary>
    public struct LocalLocation
    {
        public Point2Int MapLoc;
        public Point2Int LocalLoc;

        public int MapX
        {
            get { return MapLoc.X; }
            set { MapLoc.X = value; }
        }

        public int MapY
        {
            get { return MapLoc.Y; }
            set { MapLoc.Y = value; }
        }

        public int LocalX
        {
            get { return LocalLoc.X; }
            set { LocalLoc.X = value; }
        }

        public int LocalY
        {
            get { return LocalLoc.Y; }
            set { LocalLoc.Y = value; }
        }

        public override string ToString()
        {
            return $"{{MapLoc={MapLoc},TileLoc={LocalLoc}}}";
        }
    }

    /// <summary>
    /// Sub map bound
    /// 子Map区域
    /// </summary>
    public struct MapLocalBound
    {
        public Point2Int MapLoc;
        public Bounds2Int Bounds2Int;

        public int MapX
        {
            get { return MapLoc.X; }
            set { MapLoc.X = value; }
        }

        public int MapY
        {
            get { return MapLoc.Y; }
            set { MapLoc.Y = value; }
        }

        public override string ToString()
        {
            return $"{{MapLoc={MapLoc},Bound={Bounds2Int}}}";
        }
    }

    /// <summary>
    /// Tile reference set
    /// Tile的引用集
    /// </summary>
    public class TileRef
    {
        private int m_GId;
        public List<Point2Int> Globals;
        public List<LocalLocation> Locals;

        public int GId => m_GId;

        public TileRef(int gId)
        {
            m_GId = gId;
        }

        public int AddGlobalLocation(Point2Int global)
        {
            if (Globals == null)
            {
                Globals = new List<Point2Int>();
            }

            Globals.Add(global);
            return Globals.Count;
        }

        public int AddLocalLocation(LocalLocation local)
        {
            if (Locals == null)
            {
                Locals = new List<LocalLocation>();
            }

            Locals.Add(local);
            return Locals.Count;
        }
    }

    public class TileRefSet
    {
        private int m_MaxGId;
        private TileRef[] m_Sets;

        public TileRefSet(int maxGId)
        {
            m_MaxGId = maxGId <= 0 ? 8 : maxGId;
            m_Sets = new TileRef[maxGId];
        }

        public TileRef[] TileSets => m_Sets;

        public int AddGlobalLocation(int gId, Point2Int global)
        {
            if (!InnerVerifyTileId(gId))
            {
                return m_Sets?.Length ?? 0;
            }

            if (InnerCheckOut(gId))
            {
                InnerExpandCount(m_MaxGId + (int) (m_MaxGId * 0.2));
            }

            if (m_Sets[gId] == null)
            {
                m_Sets[gId] = new TileRef(gId);
            }

//            Debug.Log($"AddGlobalLocation:gId={gId}");
            return m_Sets[gId].AddGlobalLocation(global);
        }

        public int AddLocalLocation(int gId, LocalLocation local)
        {
            if (!InnerVerifyTileId(gId))
            {
                return m_Sets?.Length ?? 0;
            }

            if (InnerCheckOut(gId))
            {
                InnerExpandCount(m_MaxGId + (int) (m_MaxGId * 0.2));
            }

            if (m_Sets[gId] == null)
            {
                m_Sets[gId] = new TileRef(gId);
            }

            return m_Sets[gId].AddLocalLocation(local);
        }

        public int AddLocalLocation(int gId, Point2Int mapLoc, Point2Int tileLoc)
        {
            return AddLocalLocation(gId, new LocalLocation {MapLoc = mapLoc, LocalLoc = tileLoc});
        }

        private bool InnerCheckOut(int gId)
        {
            return gId > m_MaxGId;
        }

        private bool InnerVerifyTileId(int gId)
        {
            return gId > 0;
        }

        private void InnerExpandCount(int newMaxGId)
        {
            if (newMaxGId <= m_MaxGId)
            {
                return;
            }

            var newSets = new TileRef[newMaxGId];
            Array.Copy(m_Sets, newSets, m_MaxGId);
            m_MaxGId = newMaxGId;
            m_Sets = newSets;
        }
    }

    // right-down (the default), right-up, left-down or left-up (currently only supported for orthogonal maps)
    public enum RenderOrder
    {
        // default
        RightDown,
        RightUp,

        // only supported for orthogonal maps
        LeftDown,

        // only supported for orthogonal maps
        LeftUp
    }

    // x or y (staggered / hexagonal maps only)
    public enum StaggerAxis
    {
        X,
        Y
    }

    // odd or even (staggered / hexagonal maps only)
    public enum StaggerIndex
    {
        Add,
        Even
    }

    // zlib, gzip, zstd (since Tiled 1.3) or empty (default). tilelayer only.
    public enum Compression
    {
        // default
        Empty,
        ZLib,
        GZip,
        ZStd
    }

    // csv (default) or base64. tilelayer only.
    public enum Encoding
    {
        CSV,
        Base64
    }

    // Horizontal alignment (center, right, justify or left (default))
    public enum HorizontalAlignment
    {
        // default
        Left,
        Center,
        Right,
        Justify
    }

    // Vertical alignment (center, bottom or top (default))
    public enum VerticalAlignment
    {
        // default
        Top,
        Center,
        Bottom
    }

    // Alignment to use for tile objects (unspecified (default), topleft, top, topright, left, center, right, bottomleft, bottom or bottomright) (since 1.4)
    public enum ObjectAlignment
    {
        // default
        Unspecified,
        TopLeft,
        Top,
        TopRight,
        Left,
        Center,
        Right,
        BottomLeft,
        Bottom,
        BottomRight
    }

    // orthogonal (default) or isometric
    public enum Orientation
    {
        // default
        Orthogonal,
        Isometric
    }

    // Type of the property (string (default), int, float, bool, color or file (since 0.16, with color and file added in 0.17))
    public enum PropertyType
    {
        // default
        String,
        Int,
        Float,
        Bool,
        Color,
        File
    }

    /// <summary>
    /// Tile set data mode
    /// 地块集数据模式
    /// </summary>
    public enum TilesetDataMode
    {
        // 未定义数据信息
        Undefined,

        // 外部链接信息
        LinkExternal,

        // 完成内嵌信息
        CompleteInternal,

        // 完成外部独立信息
        CompleteExternal
    }

    public static class TiledConst
    {
        // RenderOrder
        public static string RenderOrderRightDown = "right-down";
        public static string RenderOrderRightUp = "right-up";
        public static string RenderOrderLeftDown = "left-down";

        public static string RenderOrderLeftUp = "left-up";

        // StaggerAxis
        public static string StaggerAxisX = "x";

        public static string StaggerAxisY = "y";

        // StaggerIndex
        public static string StaggerIndexAdd = "add";

        public static string StaggerIndexEven = "even";

        // Compression
        public static string CompressionEmpty = "empty";
        public static string CompressionZLib = "zlib";
        public static string CompressionGZip = "gzip";

        public static string CompressionZStd = "zstd";

        // Encoding
        public static string EncodingCSV = "csv";

        public static string EncodingBase = "base64";

        // HorizontalAlignment
        public static string HorizontalAlignmentLeft = "left";
        public static string HorizontalAlignmentRight = "right";
        public static string HorizontalAlignmentCenter = "center";

        public static string HorizontalAlignmentJustify = "justify";

        // VerticalAlignment
        public static string VerticalAlignmentTop = "top";
        public static string VerticalAlignmentCenter = "center";

        public static string VerticalAlignmentBottom = "bottom";

        // ObjectAlignment
        public static string ObjectAlignmentUnspecified = "unspecified";
        public static string ObjectAlignmentTopLeft = "topleft";
        public static string ObjectAlignmentTop = "top";
        public static string ObjectAlignmentTopRight = "topright";
        public static string ObjectAlignmentLeft = "left";
        public static string ObjectAlignmentCenter = "center";
        public static string ObjectAlignmentRight = "right";
        public static string ObjectAlignmentBottomLeft = "bottomleft";
        public static string ObjectAlignmentBottom = "bottom";

        public static string ObjectAlignmentBottomRight = "bottomright";

        // Orientation
        public static string OrientationOrthogonal = "orthogonal";

        public static string OrientationIsometric = "isometric";

        // PropertyType
        public static string PropertyTypeString = "string";
        public static string PropertyTypeInt = "int";
        public static string PropertyTypeFloat = "float";
        public static string PropertyTypeBool = "bool";
        public static string PropertyTypeColor = "color";
        public static string PropertyTypeFile = "file";
    }
}