using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TMPro;
using UnityEditor;
using UnityEngine;

public static class TmpFallbackFontTool {
    private const string FallbackFontAssetPath =
            "Assets/TextMesh Pro/Resources/Fonts & Materials/LiberationSans SDF - Fallback.asset";

    [MenuItem("Eagle0/Fonts/Bake TMP Fallback Font")]
    public static void BakeFallbackFont() {
        var fontAsset = AssetDatabase.LoadAssetAtPath<TMP_FontAsset>(FallbackFontAssetPath);
        if (fontAsset == null) {
            throw new InvalidOperationException(
                    $"Could not load TMP fallback font at {FallbackFontAssetPath}");
        }

        var unicodes = CuratedFallbackUnicodes();
        var characters = UnicodeString(unicodes);

        fontAsset.atlasPopulationMode = AtlasPopulationMode.Dynamic;
        fontAsset.TryAddCharacters(unicodes, out _);
        fontAsset.atlasPopulationMode = AtlasPopulationMode.Static;

        EditorUtility.SetDirty(fontAsset);
        AssetDatabase.SaveAssets();

        if (fontAsset.HasCharacters(characters, out uint[] missingUnicodes)) {
            Debug.Log(
                    $"Baked {unicodes.Length} curated glyphs into {FallbackFontAssetPath} and set it to Static.");
            return;
        }

        Debug.LogWarning(
                $"Baked TMP fallback font, but the source font does not contain {missingUnicodes.Length} curated glyphs: " +
                String.Join(", ", missingUnicodes.Select(DescribeUnicode)));
    }

    private static uint[] CuratedFallbackUnicodes() {
        var unicodes = new SortedSet<uint>();

        AddRange(unicodes, 0x0020, 0x007E);  // Basic Latin, printable ASCII
        AddRange(unicodes, 0x00A0, 0x00FF);  // Latin-1 Supplement
        AddRange(unicodes, 0x0100, 0x017F);  // Latin Extended-A
        AddRange(unicodes, 0x0180, 0x024F);  // Latin Extended-B

        uint[] commonSymbols = {
            0x2013,  // EN DASH
            0x2014,  // EM DASH
            0x2018,  // LEFT SINGLE QUOTATION MARK
            0x2019,  // RIGHT SINGLE QUOTATION MARK
            0x201C,  // LEFT DOUBLE QUOTATION MARK
            0x201D,  // RIGHT DOUBLE QUOTATION MARK
            0x2020,  // DAGGER
            0x2021,  // DOUBLE DAGGER
            0x2022,  // BULLET
            0x2026,  // HORIZONTAL ELLIPSIS
            0x2030,  // PER MILLE SIGN
            0x2032,  // PRIME
            0x2033,  // DOUBLE PRIME
            0x2039,  // SINGLE LEFT-POINTING ANGLE QUOTATION MARK
            0x203A,  // SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
            0x2044,  // FRACTION SLASH
            0x20AC,  // EURO SIGN
            0x2122,  // TRADE MARK SIGN
            0x2190,  // LEFTWARDS ARROW
            0x2191,  // UPWARDS ARROW
            0x2192,  // RIGHTWARDS ARROW
            0x2193,  // DOWNWARDS ARROW
            0x2194,  // LEFT RIGHT ARROW
            0x2195,  // UP DOWN ARROW
            0x2211,  // N-ARY SUMMATION
            0x2212,  // MINUS SIGN
            0x221A,  // SQUARE ROOT
            0x221E,  // INFINITY
            0x221F,  // RIGHT ANGLE
            0x2229,  // INTERSECTION
            0x222B,  // INTEGRAL
            0x2248,  // ALMOST EQUAL TO
            0x2260,  // NOT EQUAL TO
            0x2261,  // IDENTICAL TO
            0x2264,  // LESS-THAN OR EQUAL TO
            0x2265,  // GREATER-THAN OR EQUAL TO
            0x25A0,  // BLACK SQUARE
            0x25A1,  // WHITE SQUARE
            0x25B2,  // BLACK UP-POINTING TRIANGLE
            0x25BA,  // BLACK RIGHT-POINTING POINTER
            0x25BC,  // BLACK DOWN-POINTING TRIANGLE
            0x25C4,  // BLACK LEFT-POINTING POINTER
            0x25CA,  // LOZENGE
            0x25CB,  // WHITE CIRCLE
            0x25CF,  // BLACK CIRCLE
            0x25E6,  // WHITE BULLET
        };

        foreach (var unicode in commonSymbols) { unicodes.Add(unicode); }

        return unicodes.ToArray();
    }

    private static void AddRange(ISet<uint> unicodes, uint first, uint last) {
        for (var unicode = first; unicode <= last; unicode++) { unicodes.Add(unicode); }
    }

    private static string UnicodeString(IEnumerable<uint> unicodes) {
        var builder = new StringBuilder();
        foreach (var unicode in unicodes) { builder.Append(Char.ConvertFromUtf32((int)unicode)); }
        return builder.ToString();
    }

    private static string DescribeUnicode(uint unicode) {
        return $"U+{unicode:X4} '{Char.ConvertFromUtf32((int)unicode)}'";
    }
}
