﻿using System;

namespace JLGames.RocketDriver.Games.Tiledx
{
    [Serializable]
    public class Map
    {
        // Hex-formatted color (#RRGGBB or #AARRGGBB) (optional)
        public string backgroundcolor;

        // The compression level to use for tile layer data (defaults to -1, which means to use the algorithm default)
        public int compressionlevel;

        // Number of tile rows
        public int height;

        // Length of the side of a hex tile in pixels (hexagonal maps only)
        public int hexsidelength;

        // Whether the map has infinite dimensions
        public bool infinite;

        // Array of Layers
        public Layer[] layers;

        // Auto-increments for each layer
        public int nextlayerid;

        // Auto-increments for each placed object
        public int nextobjectid;

        // orthogonal, isometric, staggered or hexagonal
        public string orientation;

        // Array of Properties
        public Property[] properties;

        // right-down (the default), right-up, left-down or left-up (currently only supported for orthogonal maps)
        public string renderorder;

        // x or y (staggered / hexagonal maps only)
        public string staggeraxis;

        // odd or even (staggered / hexagonal maps only)
        public string staggerindex;

        // The Tiled version used to save the file
        public string tiledversion;

        // Map grid height
        public int tileheight;

        // Array of Tilesets
        public Tileset[] tilesets;

        // Map grid width
        public int tilewidth;

        // map (since 1.0)
        public string type;

        // The JSON format version
        public float version;

        // Number of tile columns
        public int width;

        // 外部地块链接
        private Tileset[] _linkTilesets;

        public Layer FindLayer(int localLayerId)
        {
            return TiledMapUtil.FindLayer(layers, localLayerId);
        }

        public Layer FindLayer(string name)
        {
            return TiledMapUtil.FindLayer(layers, name);
        }

        public void SetLnikTilesets(Tileset[] tilesets)
        {
            _linkTilesets = tilesets;
        }

        public Tileset FindTielset(string name)
        {
            var rs = TiledMapUtil.FindCompleteTileset(tilesets, name) ??
                     TiledMapUtil.FindCompleteTileset(_linkTilesets, name);
            return rs;
        }

        public TileDefinition FindTiel(int localTielId)
        {
            var rs = TiledMapUtil.FindTile(tilesets, localTielId) ?? TiledMapUtil.FindTile(_linkTilesets, localTielId);
            return rs;
        }

        public override string ToString()
        {
            return
                $"TieldUnit[W={width},H={height},TW={tilewidth},TH={tileheight},LayerLen={layers.Length},TieldSetLen={tilesets.Length}]";
        }
    }

    [Serializable]
    public class Layer
    {
        //Array of chunks (optional). tilelayer only.
        public Chunk[] chunks;

        // zlib, gzip, zstd (since Tiled 1.3) or empty (default). tilelayer only.
        public string compression;

        // Array of unsigned int (GIDs) or base64-encoded data. tilelayer only.
        public uint[] data;

        // topdown (default) or index. objectgroup only.
        public string draworder;

        // csv (default) or base64. tilelayer only.
        public string encoding;

        // Row count. Same as map height for fixed-size maps.
        public int height;

        // Incremental ID - unique across all layers
        public int id;

        // Image used by this layer. imagelayer only.
        public string image;

        // Array of layers. group only.
//        public Layer[] layers;

        // Name assigned to this layer
        public string name;

        // Array of objects. objectgroup only.
        public Object[] objects;

        // Horizontal layer offset in pixels (default: 0)
        public double offsetx;

        // Vertical layer offset in pixels (default: 0);
        public double offsety;

        // Value between 0 and 1
        public double opacity;

        // Array of Properties
        public Property[] properties;

        // X coordinate where layer content starts (for infinite maps)
        public int startx;

        // Y coordinate where layer content starts (for infinite maps)
        public int starty;

        // Hex-formatted color (#RRGGBB or #AARRGGBB) that is multiplied with any graphics drawn by this layer or any child layers (optional).
        public string tintcolor;

        // Hex-formatted color (#RRGGBB) (optional). imagelayer only.
        public string transparentcolor;

        // tilelayer, objectgroup, imagelayer or group
        public string type;

        // Whether layer is shown or hidden in editor
        public bool visible;

        //Column count. Same as map width for fixed-size maps.
        public int width;

        // Horizontal layer offset in tiles. Always 0.
        public int x;

        // Vertical layer offset in tiles. Always 0.
        public int y;

        // Array of unsigned int (GIDs)
        public uint[] IdData => data;

        /// <summary>
        /// Get data
        /// 读取单个数据
        /// </summary>
        /// <param name="index">一维索引号</param>
        /// <returns>unsigned int (GIDs)</returns>
        public uint GetData(int index)
        {
            return IdData[index];
        }

        /// <summary>
        /// Get data
        /// 读取单个数据
        /// </summary>
        /// <param name="xIndex">二维索引X</param>
        /// <param name="yIndex">二维索引Y</param>
        /// <returns>unsigned int (GIDs)</returns>
        public uint GetData(int xIndex, int yIndex)
        {
            return IdData[yIndex * width + xIndex];
        }

        /// <summary>
        /// Get multi data
        /// 读取多个数据
        /// </summary>
        /// <param name="startIndex">起始一维索引号</param>
        /// <param name="len">一维数据长度</param>
        /// <returns>Array of unsigned int (GIDs)</returns>
        public uint[] GetDatas(int startIndex, int len)
        {
            var rs = new uint[len];
            Array.Copy(IdData, startIndex, rs, 0, len);
            return rs;
        }

        /// <summary>
        /// Get multi data
        /// 读取多个数据
        /// </summary>
        /// <param name="startX">起始二维索引号X</param>
        /// <param name="startY">起始二维索引号Y</param>
        /// <param name="len">一维数据长度</param>
        /// <returns>Array of unsigned int (GIDs)</returns>
        public uint[] GetDatas(int startX, int startY, int len)
        {
            return GetDatas(startY * width + startX, len);
        }

        /// <summary>
        /// Get multi data
        /// 读取多个数据
        /// </summary>
        /// <param name="startX">起始二维索引号X</param>
        /// <param name="startY">起始二维索引号Y</param>
        /// <param name="endX">结束二维索引号X</param>
        /// <param name="endY">结束二维索引号Y</param>
        /// <returns></returns>
        public uint[] GetDatas(int startX, int startY, int endX, int endY)
        {
            var sIndex = startY * width + startX;
            var eIndex = endY * width + endX;
            return GetDatas(sIndex, eIndex - sIndex);
        }

        /// <summary>
        /// Get data by bound
        /// 读取一个矩形方阵数据集
        /// </summary>
        /// <param name="startX"></param>
        /// <param name="startY"></param>
        /// <param name="sizeX"></param>
        /// <param name="sizeY"></param>
        /// <returns></returns>
        public uint[][] GetBoundDatas(int startX, int startY, int sizeX, int sizeY)
        {
            var rs = new uint[sizeY][];
            for (var yIndex = 0; yIndex < sizeY; yIndex++)
            {
                rs[yIndex] = GetDatas(startX, startY + yIndex, sizeX);
            }

            return rs;
        }

        public override string ToString()
        {
            return
                $"Layer[id={id},name={name},type={type},w={width},h={height},x={x},y={y},v={visible},opacity={opacity},dataLen={data.Length}]";
        }
    }

    [Serializable]
    public struct Chunk
    {
        // Height in tiles
        public int height;

        // Width in tiles
        public int width;

        // X coordinate in tiles
        public int x;

        // Y coordinate in tiles
        public int y;
    }

    [Serializable]
    public struct Object
    {
        // Used to mark an object as an ellipse
        public bool ellipse;

        // Global tile ID, only if object represents a tile
        public int gid;

        // Height in pixels.
        public double height;

        // Incremental ID, unique across all objects
        public int id;

        // String assigned to name field in editor
        public string name;

        // Used to mark an object as a point
        public bool point;

        // Array of Points, in case the object is a polygon
        public Point[] polygon;

        // Array of Points, in case the object is a polyline
        public Point[] polyline;

        // Array of Properties
        public Property[] properties;

        // Angle in degrees clockwise
        public double rotation;

        // Reference to a template file, in case object is a template instance
        public string template;

        // Only used for text objects
        public Text text;

        // String assigned to type field in editor
        public string type;

        // Whether object is shown in editor.
        public bool visible;

        // Width in pixels.
        public double width;

        // X coordinate in pixels
        public double x;

        // Y coordinate in pixels
        public double y;
    }

    [Serializable]
    public struct Text
    {
        // Whether to use a bold font (default: false)
        public bool bold;

        // Hex-formatted color (#RRGGBB or #AARRGGBB) (default: #000000)
        public string color;

        // Font family (default: sans-serif)
        public string fontfamily;

        // Horizontal alignment (center, right, justify or left (default))
        public string halign;

        // Whether to use an italic font (default: false)
        public bool italic;

        // Whether to use kerning when placing characters (default: true)
        public bool kerning;

        // Pixel size of font (default: 16)
        public int pixelsize;

        // Whether to strike out the text (default: false)
        public bool strikeout;

        // Text
        public string text;

        // Whether to underline the text (default: false)
        public bool underline;

        // Vertical alignment (center, bottom or top (default))
        public string valign;

        // Whether the text is wrapped within the object bounds (default: false)
        public bool wrap;
    }

    [Serializable]
    public class Tileset
    {
        // Hex-formatted color (#RRGGBB or #AARRGGBB) (optional)
        public string backgroundcolor;

        // The number of tile columns in the tileset
        public int columns;

        // GID corresponding to the first tile in the set
        public int firstgid;

        // (optional)
        public Grid grid;

        // Image used for tiles in this set
        public string image;

        // Height of source image in pixels
        public int imageheight;

        // Width of source image in pixels
        public int imagewidth;

        // Buffer between image edge and first tile (pixels)
        public int margin;

        // Name given to this tileset
        public string name;

        // Alignment to use for tile objects (unspecified (default), topleft, top, topright, left, center, right, bottomleft, bottom or bottomright) (since 1.4)
        public string objectalignment;

        // Array of Properties
        public Property[] properties;

        // The external file that contains this tilesets data
        public string source;

        // Spacing between adjacent tiles in image (pixels)
        public int spacing;

        // Array of Terrains (optional)
        public Terrain[] terrains;

        // The number of tiles in this tileset
        public int tilecount;

        // The Tiled version used to save the file
        public string tiledversion;

        // Maximum height of tiles in this set
        public int tileheight;

        // (optional)
        public TileOffset tileoffset;

        // Array of Tiles (optional)
        public TileDefinition[] tiles;

        // Maximum width of tiles in this set
        public int tilewidth;

        // Hex-formatted color (#RRGGBB) (optional)
        public string transparentcolor;

        // tileset (for tileset files, since 1.0)
        public string type;

        // The JSON format version
        public float version;

        // Array of Wang sets (since 1.1.5)
        public WangSet[] wangsets;

        /// <summary>
        /// 数据模式
        /// </summary>
        public TilesetDataMode DataMode
        {
            get
            {
                if (IsLinkTileset)
                {
                    return TilesetDataMode.LinkExternal;
                }

                if (IsExternalTileset)
                {
                    return TilesetDataMode.CompleteExternal;
                }

                if (IsInternalTileset)
                {
                    return TilesetDataMode.CompleteInternal;
                }

                return TilesetDataMode.Undefined;
            }
        }

        /// <summary>
        /// Whether it is an external independent parcel set information
        /// 是否为外部独立地块集信息
        /// </summary>
        public bool IsExternalTileset => type == "tileset";

        /// <summary>
        /// Whether it is embedded parcel set information
        /// 是否为内嵌地块集信息
        /// </summary>
        public bool IsInternalTileset => string.IsNullOrEmpty(type) && string.IsNullOrEmpty(source);

        /// <summary>
        /// Whether it is a detailed plot set
        /// Include (outer independent plot)
        /// 是否为详细地块集
        /// 包括(外部独立地块)
        /// </summary>
        public bool IsDetailTileset => IsExternalTileset || IsInternalTileset;

        /// <summary>
        /// Whether it is the connection plot set information
        /// 是否为连接地块集信息
        /// </summary>
        public bool IsLinkTileset => !string.IsNullOrEmpty(source);

        public string SourceName
        {
            get
            {
                if (source == "")
                {
                    return "";
                }

                var index0 = source.LastIndexOf('/');
                var index1 = source.LastIndexOf('\\');
                var endIndex = source.LastIndexOf('.');
                var startIndex = Math.Max(index0, index1) + 1;
                return source.Substring(startIndex, endIndex - startIndex);
            }
        }

        /// <summary>
        /// Whether to include the global id
        /// 是否包含全局id
        /// </summary>
        /// <param name="globalId"></param>
        /// <returns></returns>
        public bool IncludeGlobalId(int globalId)
        {
            return globalId >= firstgid && globalId < firstgid + tilecount;
        }

        /// <summary>
        /// Whether to include local id
        /// 是否包含局部id
        /// </summary>
        /// <param name="localId"></param>
        /// <returns></returns>
        public bool IncludeLocalId(int localId)
        {
            return localId >= 0 && localId < tilecount;
        }

        public override string ToString()
        {
            return
                $"TieldSet[name={name},fid={firstgid},image={image},iw={imagewidth},ih={imageheight},columns={columns},margin={margin},spacing={spacing},tw={tilewidth},th={tileheight},tcount={tilecount}]";
        }
    }

    [Serializable]
    public struct Grid
    {
        // orthogonal (default) or isometric
        public string orientation;

        // Cell width of tile grid
        public int width;

        // Cell height of tile grid
        public int height;

        public override string ToString()
        {
            return $"Grid{{width={width},height={height}}}";
        }
    }

    [Serializable]
    public struct TileOffset
    {
        // Horizontal offset in pixels
        public int x;

        // Vertical offset in pixels (positive is down)
        public int y;

        public override string ToString()
        {
            return $"TileOffset{{x={x},y={y}}}";
        }
    }

    [Serializable]
    public class TileDefinition
    {
        // Array of Frames
        public Frame[] animation;

        // Local ID of the tile
        public int id;

        // Image representing this tile (optional)
        public string image;

        // Height of the tile image in pixels
        public int imageheight;

        // Width of the tile image in pixels
        public int imagewidth;

        // Layer with type objectgroup, when collision shapes are specified (optional)
        public Layer objectgroup;

        // Percentage chance this tile is chosen when competing with others in the editor (optional)
        public double probability;

        // Array of Properties
        public Property[] properties;

        // Index of terrain for each corner of tile (optional)
        public int[] terrain;

        // The type of the tile (optional)
        public string type;
    }

    [Serializable]
    public struct Frame
    {
        // Frame duration in milliseconds
        public int duration;

        // Local tile ID representing this frame
        public int tileid;
    }

    [Serializable]
    public struct Terrain
    {
        // Name of terrain
        public string name;

        // Array of Properties
        public Property[] properties;

        // Local ID of tile representing terrain
        public int tile;
    }

    [Serializable]
    public struct WangSet
    {
        // Array of Wang colors
        public WangColor[] cornercolors;

        // Array of Wang colors
        public WangColor[] edgecolors;

        // Name of the Wang set
        public string name;

        // Array of Properties
        public Property[] properties;

        // Local ID of tile representing the Wang set
        public int tile;

        // Array of Wang tiles
        public WangTile[] wangtiles;
    }

    [Serializable]
    public struct WangColor
    {
        // Hex-formatted color (#RRGGBB or #AARRGGBB)
        public string color;

        // Name of the Wang color
        public string name;

        // Probability used when randomizing
        public double probability;

        // Local ID of tile representing the Wang color
        public int tile;
    }

    [Serializable]
    public struct WangTile
    {
        // Tile is flipped diagonally (default: false)
        public bool dflip;

        // Tile is flipped horizontally (default: false)
        public bool hflip;

        // Local ID of tile
        public int tileid;

        // Tile is flipped vertically (default: false)
        public bool vflip;

        // Array of Wang color indexes (uchar[8])
        public uint[] wangid;
    }

    [Serializable]
    // A point on a polygon or a polyline, relative to the position of the object.
    public struct Point
    {
        // X coordinate in pixels
        public double x;

        // Y coordinate in pixels
        public double y;

        public override string ToString()
        {
            return $"Point{{x={x},y={y}}}";
        }
    }

    [Serializable]
    public struct Property
    {
        private static readonly Property _empty = new Property();

        public static Property Empty => _empty;

        // Name of the property
        public string name;

        // Type of the property (string (default), int, float, bool, color or file (since 0.16, with color and file added in 0.17))
        public string type;

        // Value of the property
        public int value;

        public string StringValue => value.ToString();
        public string ColorValue => value.ToString();
        public string FileValue => value.ToString();

        public int IntValue
        {
            get
            {
                if (TiledConst.PropertyTypeInt == type)
                {
                    return (int) value;
                }

                return 0;
            }
        }

        public float FloatValue
        {
            get
            {
                if (TiledConst.PropertyTypeFloat == type)
                {
                    return (float) value;
                }

                return 0f;
            }
        }

        public bool BoolValue
        {
            get
            {
                if (TiledConst.PropertyTypeBool == type)
                {
                    return value == 1;
                }

                return false;
            }
        }

        public override string ToString()
        {
            return $"Property{{name={name},type={type},value={value}}}";
        }
    }
}