using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[RequireComponent(typeof(MeshRenderer))]
[RequireComponent(typeof(MeshFilter))]
public sealed class PixelFontMesh : MonoBehaviour {
    [SerializeField] private PixelFontConfig fontConfig = null;
    [SerializeField][TextArea(3, 10)] private string text = string.Empty;
    [SerializeField] private Vector2 fontSize = new Vector2(8f, 8f);
    [SerializeField] private int maxLenght = 20;
    [SerializeField] private Color fontColor = Color.white;
    [SerializeField] private bool useWorldWrap = true;
    public string Text {
        get { return text; }
        set {
            if (text != value) {
                SetText(value);
            }
        }
    }

    private sealed class MeshGenerator {
        List<Vector3> vertex = new List<Vector3>();
        List<Color> colors = new List<Color>();
        List<Vector2> texCoord = new List<Vector2>();
        List<int> faces = new List<int>();
        int quadCounter = 0;

        public void AddGlypt(Coord position, Vector2 size, Rect rect, Color color) {
            vertex.Add(new Vector3(position.x * size.x + 0, position.y * size.y + 0));
            vertex.Add(new Vector3(position.x * size.x + 0, position.y * size.y + size.y));
            vertex.Add(new Vector3(position.x * size.x + size.x, position.y * size.y + size.y));
            vertex.Add(new Vector3(position.x * size.x + size.x, position.y * size.y + 0));
            colors.Add(color);
            colors.Add(color);
            colors.Add(color);
            colors.Add(color);
            texCoord.Add(new Vector2(rect.xMin, rect.yMin));
            texCoord.Add(new Vector2(rect.xMin, rect.yMax));
            texCoord.Add(new Vector2(rect.xMax, rect.yMax));
            texCoord.Add(new Vector2(rect.xMax, rect.yMin));
            faces.Add(quadCounter * 4 + 0);
            faces.Add(quadCounter * 4 + 1);
            faces.Add(quadCounter * 4 + 2);
            faces.Add(quadCounter * 4 + 0);
            faces.Add(quadCounter * 4 + 2);
            faces.Add(quadCounter * 4 + 3);
            quadCounter += 1;
        }

        public Mesh Generate() {
            return new Mesh() { vertices = vertex.ToArray(), uv = texCoord.ToArray(), colors = colors.ToArray(), triangles = faces.ToArray() };
        }
    }

    private MeshRenderer meshRenderer;
    private MeshFilter meshFilter;

    public MeshRenderer Renderer {
        get {
            if (meshRenderer == null) {
                meshRenderer = GetComponent<MeshRenderer>();
            }
            return meshRenderer;
        }
    }
    public MeshFilter Filter {
        get {
            if (meshFilter == null) {
                meshFilter = GetComponent<MeshFilter>();
            }
            return meshFilter;
        }
    }

    private void Awake() {
        SetText(text);
    }

    public void SetText(string newText) {
        text = newText;
        Filter.mesh = GenerateTextMesh(text, fontConfig);
        Renderer.material = fontConfig.GetMaterial();
    }

    private Mesh GenerateTextMesh(string text, PixelFontConfig fontConfig) {
        var generator = new MeshGenerator();
        Coord position = new Coord();
        if (!string.IsNullOrEmpty(text)) {
            if (useWorldWrap) {
                var sentences = text.Split('\n');
                foreach (var sentence in sentences) {
                    var words = sentence.Split(' ');
                    foreach (var word in words) {
                        if (position.x + word.Length > maxLenght && word.Length <= maxLenght) {
                            position.x = 0;
                            position.y -= 1;
                        }
                        foreach (char symbol in word) {
                            var rect = fontConfig.GetRect(symbol);
                            generator.AddGlypt(position, fontSize, rect, fontColor);
                            if (position.x >= maxLenght) {
                                position.x = 0;
                                position.y -= 1;
                            }
                            position.x += 1;
                        }
                        position.x += 1;
                    }
                    position.x = 0;
                    position.y -= 1;
                }
            } else {
                foreach (char symbol in text) {
                    position.x += 1;
                    if (symbol == ' ') {
                    } else if (symbol == '\n') {
                        position.x = 0;
                        position.y -= 1;
                    } else {
                        var rect = fontConfig.GetRect(symbol);
                        generator.AddGlypt(position, fontSize, rect, fontColor);
                    }
                    if (position.x >= maxLenght) {
                        position.x = 0;
                        position.y -= 1;
                    }
                }
            }
        }
        return generator.Generate();
    }

    internal void SetColor(Color color) {
        meshRenderer.material.color = color;
    }
#if UNITY_EDITOR
    private string prevText;
    [ContextMenu("Generate text")]
    public void GenerateText() {
        meshRenderer = GetComponent<MeshRenderer>();
        meshFilter = GetComponent<MeshFilter>();
        SetText(text);
    }

    private void OnDrawGizmos() {
        if (prevText != text) {
            prevText = text;
            SetText(text);
        }
    }
#endif
}