using Microsoft.Xna.Framework;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Xml.Linq;

namespace MonoGame.FourEngine.Tiled
{
    internal class TileLayer : LayerBase
    {
        public int[,] Data { get; private set; }
        public Point Size { get; private set; }
        public Point Offset { get; private set; }

        public new static TileLayer Parse(XElement xElement)
        {
            var xData = xElement.Descendants("data").First();
            var size = new Point(
                int.Parse(xElement.Attribute("width").Value, CultureInfo.InvariantCulture),
                int.Parse(xElement.Attribute("height").Value, CultureInfo.InvariantCulture));
            var offset = new Point(
                int.Parse(xElement.AttributeOrZero("offsetx").Value, CultureInfo.InvariantCulture),
                int.Parse(xElement.AttributeOrZero("offsety").Value, CultureInfo.InvariantCulture));
            var data = ParseCsvData(xData.Value, size);
            return new TileLayer
            {
                Data = data,
                Size = size,
                Offset = offset
            };
        }

        public new static IEnumerable<TileLayer> Parse(IEnumerable<XElement> xElements)
            => xElements.Select(x => Parse(x));

        public override IEnumerable<Entity> GetEntities(List<Tileset> tilesets, int layer, MapOrientation orientation = MapOrientation.Orthogonal)
        {
            for (var x = 0; x < Size.X; x++)
            for (var y = 0; y < Size.Y; y++)
                {
                    if (Data[x, y] == 0) continue;
                    var tileset = tilesets
                        .FirstOrDefault(t => t.ContainsIndex(Data[x, y]));
                    if (tileset == null) continue;
                    var entity = tileset.CreateEntity(x, y, Data[x, y], tileset.FirstGid, layer);
                    PositionEntity(entity, tileset, x, y, orientation);
                    yield return entity;
                }
        }

        private void PositionEntity(TiledEntity entity, Tileset tileset, int x, int y, MapOrientation orientation)
        {
            switch (orientation)
            {
                case MapOrientation.Orthogonal:
                    entity.Position = new Point(x * tileset.TileSize.X, y * tileset.TileSize.Y);
                    break;
                case MapOrientation.Isometric:
                    // for even numbers
                    if (tileset.TileSize.Y % 2 == 0)
                        entity.Position = new Point(
                            (x - y) * (tileset.TileSize.X / 2),
                            (x + y) * (tileset.TileSize.Y / 2));
                    // for odd numbers
                    else
                        entity.Position = new Point(
                            (x - y) * (tileset.TileSize.X / 2) - x + y,
                            (x + y) * (tileset.TileSize.Y / 2));
                    break;
            }
            entity.Position += Offset;
        }

        private static int[,] ParseCsvData(string csv, Point size)
        {
            var data = new int[size.X, size.Y];
            var lines = csv
                .Replace("\r\n", "\n").Split('\n')
                .Where(x => !string.IsNullOrEmpty(x))
                .ToArray();
            for (var y = 0; y < size.Y; y++)
            {
                var line = lines[y];
                var values = line.Split(',');
                for (var x = 0; x < size.X; x++)
                {
                    var value = values[x];
                    data[x, y] = int.Parse(value, CultureInfo.CurrentCulture);
                }
            }
            return data;
        }
    }
}