using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Text;

namespace Igdc.Fonts
{
    public abstract class FontBase : IDisposable
    {
        public abstract int Height { get; }

        protected abstract Dictionary<int, string> WidthMap { get; }
        protected abstract int Columns { get; }
        protected abstract string FontName { get; }
        protected abstract string Alphabet { get; }
        protected abstract int MaxWidth { get; }

        private Texture2D _texture;
        private Point _size;
        private Dictionary<char, int> _width;
        private Dictionary<char, Point> _positions;

        public FontBase(GraphicsDevice device)
        {
            _texture = device.ReadTexture2D(FontName);
            _size = new Point(MaxWidth, Height);
            _width = new Dictionary<char, int>();
            foreach (var mapping in WidthMap)
                foreach (var c in mapping.Value)
                    _width[c] = mapping.Key;
            var col = 0;
            var row = 0;
            _positions = new Dictionary<char, Point>();
            foreach (var c in Alphabet)
            {
                _positions.Add(c, new Point(col * MaxWidth, row * Height));
                col++;
                if (col == Columns)
                {
                    col = 0;
                    row++;
                }
            }
        }

        public void DrawText(SpriteBatch spriteBatch, string text, Vector2 position, Color color)
        {
            var offset = 0;
            foreach (var c in text.Replace("\r", string.Empty))
            {
                if (c == ' ')
                {
                    offset += _width['_'];
                    continue;
                }
                var pos = _positions[c];
                spriteBatch.Draw(_texture,
                    new Vector2(position.X + offset, position.Y),
                    new Rectangle(pos, _size),
                    color);
                offset += _width[c];
            }
        }

        public List<string> PrepareLines(string text, int targetWidth)
        {
            var nlines = text.Split('\n');
            var lines = new List<string>();
            foreach (var nline in nlines)
            {
                var widthLeft = targetWidth;
                var space = MeasureChar(' ');
                var words = nline.Split(' ');
                var builder = new StringBuilder();
                foreach (var word in words)
                {
                    var width = MeasureString(word);
                    if (width > widthLeft || widthLeft <= 0)
                    {
                        lines.Add(builder.ToString());
                        builder = new StringBuilder();
                        widthLeft = targetWidth;
                    }
                    widthLeft -= width;
                    widthLeft -= space;
                    builder.Append(word);
                    builder.Append(' ');
                }
                lines.Add(builder.ToString());
            }
            return lines;
        }

        public int MeasureString(string text)
        {
            var width = 0;
            foreach (var c in text)
                width += MeasureChar(c);
            return width;
        }

        public int MeasureChar(char c) => _width.ContainsKey(c) ? _width[c] : _width['_'];

        public void Dispose() => _texture?.Dispose();
    }

    public static class SpriteBatchExt
    {
        public static void DrawText(this SpriteBatch spriteBatch, FontBase font, string text, Vector2 position, Color color)
            => font.DrawText(spriteBatch, text, position, color);
    }
}