using UnityEngine;
using UnityEditor;
using UnityEditorInternal;
using System.Collections.Generic;
using JetBrains.Annotations;
using UnityEngine.TextCore;
using UnityEngine.TextCore.LowLevel;
using UnityEditor.TextCore.LowLevel;


namespace TMPro.EditorUtilities
{

    [CustomPropertyDrawer(typeof(TMP_FontWeightPair))]
    public class FontWeightDrawer : PropertyDrawer
    {
        public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
        {
            SerializedProperty prop_regular = property.FindPropertyRelative("regularTypeface");
            SerializedProperty prop_italic = property.FindPropertyRelative("italicTypeface");

            float width = position.width;

            position.width = EditorGUIUtility.labelWidth;
            EditorGUI.LabelField(position, label);

            int oldIndent = EditorGUI.indentLevel;
            EditorGUI.indentLevel = 0;

            // NORMAL TYPEFACE
            if (label.text[0] == '4') GUI.enabled = false;
            position.x += position.width; position.width = (width - position.width) / 2;
            EditorGUI.PropertyField(position, prop_regular, GUIContent.none);

            // ITALIC TYPEFACE
            GUI.enabled = true;
            position.x += position.width;
            EditorGUI.PropertyField(position, prop_italic, GUIContent.none);

            EditorGUI.indentLevel = oldIndent;
        }
    }

    [CustomEditor(typeof(TMP_FontAsset))]
    public class TMP_FontAssetEditor : Editor
    {
        private struct UI_PanelState
        {
            public static bool faceInfoPanel = true;
            public static bool generationSettingsPanel = true;
            public static bool fontAtlasInfoPanel = true;
            public static bool fontWeightPanel = true;
            public static bool fallbackFontAssetPanel = true;
            public static bool glyphTablePanel = false;
            public static bool characterTablePanel = false;
            public static bool LigatureSubstitutionTablePanel;
            public static bool PairAdjustmentTablePanel = false;
            public static bool MarkToBaseTablePanel = false;
            public static bool MarkToMarkTablePanel = false;
        }

        private struct GenerationSettings
        {
            public Font sourceFont;
            public int faceIndex;
            public GlyphRenderMode glyphRenderMode;
            public int pointSize;
            public int padding;
            public int atlasWidth;
            public int atlasHeight;
        }

        /// <summary>
        /// Material used to display SDF glyphs in the Character and Glyph tables.
        /// </summary>
        internal static Material internalSDFMaterial
        {
            get
            {
                if (s_InternalSDFMaterial == null)
                {
                    Shader shader = Shader.Find("Hidden/TMP/Internal/Editor/Distance Field SSD");

                    if (shader != null)
                        s_InternalSDFMaterial = new Material(shader);
                }

                return s_InternalSDFMaterial;
            }
        }
        static Material s_InternalSDFMaterial;

        /// <summary>
        /// Material used to display Bitmap glyphs in the Character and Glyph tables.
        /// </summary>
        internal static Material internalBitmapMaterial
        {
            get
            {
                if (s_InternalBitmapMaterial == null)
                {
                    Shader shader = Shader.Find("Hidden/Internal-GUITextureClipText");

                    if (shader != null)
                        s_InternalBitmapMaterial = new Material(shader);
                }

                return s_InternalBitmapMaterial;
            }
        }
        static Material s_InternalBitmapMaterial;

        /// <summary>
        /// Material used to display color glyphs in the Character and Glyph tables.
        /// </summary>
        internal static Material internalRGBABitmapMaterial
        {
            get
            {
                if (s_Internal_Bitmap_RGBA_Material == null)
                {
                    Shader shader = Shader.Find("Hidden/Internal-GUITextureClip");

                    if (shader != null)
                        s_Internal_Bitmap_RGBA_Material = new Material(shader);
                }

                return s_Internal_Bitmap_RGBA_Material;
            }
        }
        static Material s_Internal_Bitmap_RGBA_Material;



        private static string[] s_UiStateLabel = new string[] { "<i>(Click to collapse)</i> ", "<i>(Click to expand)</i> " };
        public static readonly GUIContent getFontFeaturesLabel = new GUIContent("Get Font Features", "Determines if OpenType font features should be retrieved from the source font file as new characters and glyphs are added to the font asset.");
        private GUIContent[] m_AtlasResolutionLabels = { new GUIContent("8"), new GUIContent("16"), new GUIContent("32"), new GUIContent("64"), new GUIContent("128"), new GUIContent("256"), new GUIContent("512"), new GUIContent("1024"), new GUIContent("2048"), new GUIContent("4096"), new GUIContent("8192") };
        private int[] m_AtlasResolutions = { 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192 };

        private struct Warning
        {
            public bool isEnabled;
            public double expirationTime;
        }

        private int m_CurrentGlyphPage = 0;
        private int m_CurrentCharacterPage = 0;
        private int m_CurrentLigaturePage = 0;
        private int m_CurrentAdjustmentPairPage = 0;
        private int m_CurrentMarkToBasePage = 0;
        private int m_CurrentMarkToMarkPage = 0;

        private int m_SelectedGlyphRecord = -1;
        private int m_SelectedCharacterRecord = -1;
        private int m_SelectedLigatureRecord = -1;
        private int m_SelectedAdjustmentRecord = -1;
        private int m_SelectedMarkToBaseRecord = -1;
        private int m_SelectedMarkToMarkRecord = -1;

        enum RecordSelectionType { CharacterRecord, GlyphRecord, LigatureSubstitutionRecord, AdjustmentPairRecord, MarkToBaseRecord, MarkToMarkRecord }

        private string m_dstGlyphID;
        private string m_dstUnicode;
        private const string k_placeholderUnicodeHex = "<i>New Unicode (Hex)</i>";
        private string m_unicodeHexLabel = k_placeholderUnicodeHex;
        private const string k_placeholderGlyphID = "<i>New Glyph ID</i>";
        private string m_GlyphIDLabel = k_placeholderGlyphID;

        private Warning m_AddGlyphWarning;
        private Warning m_AddCharacterWarning;
        private bool m_DisplayDestructiveChangeWarning;
        private GenerationSettings m_GenerationSettings;
        private bool m_MaterialPresetsRequireUpdate;

        private static readonly string[] k_InvalidFontFaces = { string.Empty };
        private string[] m_FontFaces;
        private bool m_FaceInfoDirty;

        private string m_GlyphSearchPattern;
        private List<int> m_GlyphSearchList;

        private string m_CharacterSearchPattern;
        private List<int> m_CharacterSearchList;

        private string m_LigatureTableSearchPattern;
        private List<int> m_LigatureTableSearchList;

        private string m_KerningTableSearchPattern;
        private List<int> m_KerningTableSearchList;

        private string m_MarkToBaseTableSearchPattern;
        private List<int> m_MarkToBaseTableSearchList;

        private string m_MarkToMarkTableSearchPattern;
        private List<int> m_MarkToMarkTableSearchList;

        private HashSet<uint> m_GlyphsToAdd;

        private bool m_isSearchDirty;

        private const string k_UndoRedo = "UndoRedoPerformed";

        private SerializedProperty m_AtlasPopulationMode_prop;
        private SerializedProperty font_atlas_prop;
        private SerializedProperty font_material_prop;

        private SerializedProperty m_FontFaceIndex_prop;
        private SerializedProperty m_AtlasRenderMode_prop;
        private SerializedProperty m_SamplingPointSize_prop;
        private SerializedProperty m_AtlasPadding_prop;
        private SerializedProperty m_AtlasWidth_prop;
        private SerializedProperty m_AtlasHeight_prop;
        private SerializedProperty m_IsMultiAtlasTexturesEnabled_prop;
        private SerializedProperty m_ClearDynamicDataOnBuild_prop;
        private SerializedProperty m_GetFontFeatures_prop;

        private SerializedProperty fontWeights_prop;

        //private SerializedProperty fallbackFontAssets_prop;
        private ReorderableList m_FallbackFontAssetList;

        private SerializedProperty font_normalStyle_prop;
        private SerializedProperty font_normalSpacing_prop;

        private SerializedProperty font_boldStyle_prop;
        private SerializedProperty font_boldSpacing_prop;

        private SerializedProperty font_italicStyle_prop;
        private SerializedProperty font_tabSize_prop;

        private SerializedProperty m_FaceInfo_prop;
        private SerializedProperty m_GlyphTable_prop;
        private SerializedProperty m_CharacterTable_prop;

        private TMP_FontFeatureTable m_FontFeatureTable;
        private SerializedProperty m_FontFeatureTable_prop;
        private SerializedProperty m_LigatureSubstitutionRecords_prop;
        private SerializedProperty m_GlyphPairAdjustmentRecords_prop;
        private SerializedProperty m_MarkToBaseAdjustmentRecords_prop;
        private SerializedProperty m_MarkToMarkAdjustmentRecords_prop;

        private TMP_SerializedPropertyHolder m_SerializedPropertyHolder;
        private SerializedProperty m_EmptyGlyphPairAdjustmentRecord_prop;
        private SerializedProperty m_FirstCharacterUnicode_prop;
        private SerializedProperty m_SecondCharacterUnicode_prop;

        private TMP_FontAsset m_fontAsset;

        private Material[] m_materialPresets;

        private bool isAssetDirty = false;
        private bool m_IsFallbackGlyphCacheDirty;

        private int errorCode;

        private System.DateTime timeStamp;


        public void OnEnable()
        {
            m_FaceInfo_prop = serializedObject.FindProperty("m_FaceInfo");

            font_atlas_prop = serializedObject.FindProperty("m_AtlasTextures").GetArrayElementAtIndex(0);
            font_material_prop = serializedObject.FindProperty("m_Material");

            m_FontFaceIndex_prop = m_FaceInfo_prop.FindPropertyRelative("m_FaceIndex");
            m_AtlasPopulationMode_prop = serializedObject.FindProperty("m_AtlasPopulationMode");
            m_AtlasRenderMode_prop = serializedObject.FindProperty("m_AtlasRenderMode");
            m_SamplingPointSize_prop = m_FaceInfo_prop.FindPropertyRelative("m_PointSize");
            m_AtlasPadding_prop = serializedObject.FindProperty("m_AtlasPadding");
            m_AtlasWidth_prop = serializedObject.FindProperty("m_AtlasWidth");
            m_AtlasHeight_prop = serializedObject.FindProperty("m_AtlasHeight");
            m_IsMultiAtlasTexturesEnabled_prop = serializedObject.FindProperty("m_IsMultiAtlasTexturesEnabled");
            m_ClearDynamicDataOnBuild_prop = serializedObject.FindProperty("m_ClearDynamicDataOnBuild");
            m_GetFontFeatures_prop = serializedObject.FindProperty("m_GetFontFeatures");

            fontWeights_prop = serializedObject.FindProperty("m_FontWeightTable");

            m_FallbackFontAssetList = PrepareReorderableList(serializedObject.FindProperty("m_FallbackFontAssetTable"), "Fallback Font Assets");

            // Clean up fallback list in the event if contains null elements.
            CleanFallbackFontAssetTable();

            font_normalStyle_prop = serializedObject.FindProperty("normalStyle");
            font_normalSpacing_prop = serializedObject.FindProperty("normalSpacingOffset");

            font_boldStyle_prop = serializedObject.FindProperty("boldStyle");
            font_boldSpacing_prop = serializedObject.FindProperty("boldSpacing");

            font_italicStyle_prop = serializedObject.FindProperty("italicStyle");
            font_tabSize_prop = serializedObject.FindProperty("tabSize");

            m_CharacterTable_prop = serializedObject.FindProperty("m_CharacterTable");
            m_GlyphTable_prop = serializedObject.FindProperty("m_GlyphTable");

            m_FontFeatureTable_prop = serializedObject.FindProperty("m_FontFeatureTable");
            m_LigatureSubstitutionRecords_prop = m_FontFeatureTable_prop.FindPropertyRelative("m_LigatureSubstitutionRecords");
            m_GlyphPairAdjustmentRecords_prop = m_FontFeatureTable_prop.FindPropertyRelative("m_GlyphPairAdjustmentRecords");
            m_MarkToBaseAdjustmentRecords_prop = m_FontFeatureTable_prop.FindPropertyRelative("m_MarkToBaseAdjustmentRecords");
            m_MarkToMarkAdjustmentRecords_prop = m_FontFeatureTable_prop.FindPropertyRelative("m_MarkToMarkAdjustmentRecords");

            m_fontAsset = target as TMP_FontAsset;
            m_FontFeatureTable = m_fontAsset.fontFeatureTable;

            // Get Font Faces and Styles
            m_FontFaces = GetFontFaces();

            // Upgrade Font Feature Table if necessary
            if (m_fontAsset.m_KerningTable != null && m_fontAsset.m_KerningTable.kerningPairs != null && m_fontAsset.m_KerningTable.kerningPairs.Count > 0)
                m_fontAsset.ReadFontAssetDefinition();

            // Create serialized object to allow us to use a serialized property of an empty kerning pair.
            m_SerializedPropertyHolder = CreateInstance<TMP_SerializedPropertyHolder>();
            m_SerializedPropertyHolder.fontAsset = m_fontAsset;
            SerializedObject internalSerializedObject = new SerializedObject(m_SerializedPropertyHolder);
            m_FirstCharacterUnicode_prop = internalSerializedObject.FindProperty("firstCharacter");
            m_SecondCharacterUnicode_prop = internalSerializedObject.FindProperty("secondCharacter");
            m_EmptyGlyphPairAdjustmentRecord_prop = internalSerializedObject.FindProperty("glyphPairAdjustmentRecord");

            m_materialPresets = TMP_EditorUtility.FindMaterialReferences(m_fontAsset);

            m_GlyphSearchList = new List<int>();
            m_KerningTableSearchList = new List<int>();

            // Sort Font Asset Tables
            m_fontAsset.SortAllTables();

            // Clear glyph proxy lookups
            TMP_PropertyDrawerUtilities.ClearGlyphProxyLookups();
        }

        private ReorderableList PrepareReorderableList(SerializedProperty property, string label)
        {
            SerializedObject so = property.serializedObject;

            ReorderableList list = new ReorderableList(so, property, true, true, true, true);

            list.drawHeaderCallback = rect =>
            {
                EditorGUI.LabelField(rect, label);
            };

            list.drawElementCallback = (rect, index, isActive, isFocused) =>
            {
                var element = list.serializedProperty.GetArrayElementAtIndex(index);
                rect.y += 2;
                EditorGUI.PropertyField(new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight), element, GUIContent.none);
            };

            list.onChangedCallback = itemList => { };

            return list;
        }


        public void OnDisable()
        {
            // Revert changes if user closes or changes selection without having made a choice.
            if (m_DisplayDestructiveChangeWarning)
            {
                m_DisplayDestructiveChangeWarning = false;
                RestoreGenerationSettings();
                GUIUtility.keyboardControl = 0;

                serializedObject.ApplyModifiedProperties();
            }
        }


        public override void OnInspectorGUI()
        {
            //Debug.Log("OnInspectorGUI Called.");

            Event currentEvent = Event.current;

            serializedObject.Update();

            Rect rect = EditorGUILayout.GetControlRect(false, 24);
            float labelWidth = EditorGUIUtility.labelWidth;
            float fieldWidth = EditorGUIUtility.fieldWidth;

            // FACE INFO PANEL
            #region Face info
            GUI.Label(rect, new GUIContent("<b>Face Info</b> - v" + m_fontAsset.version), TMP_UIStyleManager.sectionHeader);

            rect.x += rect.width - 132f;
            rect.y += 2;
            rect.width = 130f;
            rect.height = 18f;
            if (GUI.Button(rect, new GUIContent("Update Atlas Texture")))
            {
                TMPro_FontAssetCreatorWindow.ShowFontAtlasCreatorWindow(target as TMP_FontAsset);
            }

            EditorGUI.indentLevel = 1;
            GUI.enabled = false; // Lock UI

            // TODO : Consider creating a property drawer for these.
            EditorGUILayout.PropertyField(m_FaceInfo_prop.FindPropertyRelative("m_FamilyName"));
            EditorGUILayout.PropertyField(m_FaceInfo_prop.FindPropertyRelative("m_StyleName"));
            EditorGUILayout.PropertyField(m_FaceInfo_prop.FindPropertyRelative("m_PointSize"));

            GUI.enabled = true;

            EditorGUILayout.PropertyField(m_FaceInfo_prop.FindPropertyRelative("m_Scale"));
            EditorGUILayout.PropertyField(m_FaceInfo_prop.FindPropertyRelative("m_LineHeight"));

            EditorGUILayout.PropertyField(m_FaceInfo_prop.FindPropertyRelative("m_AscentLine"));
            EditorGUILayout.PropertyField(m_FaceInfo_prop.FindPropertyRelative("m_CapLine"));
            EditorGUILayout.PropertyField(m_FaceInfo_prop.FindPropertyRelative("m_MeanLine"));
            EditorGUILayout.PropertyField(m_FaceInfo_prop.FindPropertyRelative("m_Baseline"));
            EditorGUILayout.PropertyField(m_FaceInfo_prop.FindPropertyRelative("m_DescentLine"));
            EditorGUILayout.PropertyField(m_FaceInfo_prop.FindPropertyRelative("m_UnderlineOffset"));
            EditorGUILayout.PropertyField(m_FaceInfo_prop.FindPropertyRelative("m_UnderlineThickness"));
            EditorGUILayout.PropertyField(m_FaceInfo_prop.FindPropertyRelative("m_StrikethroughOffset"));
            //EditorGUILayout.PropertyField(m_FaceInfo_prop.FindPropertyRelative("strikethroughThickness"));
            EditorGUILayout.PropertyField(m_FaceInfo_prop.FindPropertyRelative("m_SuperscriptOffset"));
            EditorGUILayout.PropertyField(m_FaceInfo_prop.FindPropertyRelative("m_SuperscriptSize"));
            EditorGUILayout.PropertyField(m_FaceInfo_prop.FindPropertyRelative("m_SubscriptOffset"));
            EditorGUILayout.PropertyField(m_FaceInfo_prop.FindPropertyRelative("m_SubscriptSize"));
            EditorGUILayout.PropertyField(m_FaceInfo_prop.FindPropertyRelative("m_TabWidth"));
            // TODO : Add clamping for some of these values.
            //subSize_prop.floatValue = Mathf.Clamp(subSize_prop.floatValue, 0.25f, 1f);

            EditorGUILayout.Space();
            #endregion

            // GENERATION SETTINGS
            #region Generation Settings
            rect = EditorGUILayout.GetControlRect(false, 24);

            if (GUI.Button(rect, new GUIContent("<b>Generation Settings</b>"), TMP_UIStyleManager.sectionHeader))
                UI_PanelState.generationSettingsPanel = !UI_PanelState.generationSettingsPanel;

            GUI.Label(rect, (UI_PanelState.generationSettingsPanel ? "" : s_UiStateLabel[1]), TMP_UIStyleManager.rightLabel);

            if (UI_PanelState.generationSettingsPanel)
            {
                EditorGUI.indentLevel = 1;

                EditorGUI.BeginChangeCheck();
                Font sourceFont = (Font)EditorGUILayout.ObjectField("Source Font File", m_fontAsset.SourceFont_EditorRef, typeof(Font), false);
                if (EditorGUI.EndChangeCheck())
                {
                    m_GenerationSettings.sourceFont = m_fontAsset.SourceFont_EditorRef;
                    m_fontAsset.SourceFont_EditorRef = sourceFont;
                    m_FontFaces = GetFontFaces(0);
                    m_FaceInfoDirty = true;
                    m_DisplayDestructiveChangeWarning = true;
                    //m_MaterialPresetsRequireUpdate = true;
                }

                EditorGUI.BeginDisabledGroup(sourceFont == null);
                {
                    EditorGUI.BeginChangeCheck();
                    m_FontFaceIndex_prop.intValue = EditorGUILayout.Popup(new GUIContent("Font Face"), m_FontFaceIndex_prop.intValue, m_FontFaces);
                    if (EditorGUI.EndChangeCheck())
                    {
                        m_MaterialPresetsRequireUpdate = true;
                        m_DisplayDestructiveChangeWarning = true;
                        m_FaceInfoDirty = true;
                    }

                    EditorGUI.BeginChangeCheck();
                    EditorGUILayout.PropertyField(m_AtlasPopulationMode_prop, new GUIContent("Atlas Population Mode"));
                    if (EditorGUI.EndChangeCheck())
                    {
                        serializedObject.ApplyModifiedProperties();

                        bool isDatabaseRefreshRequired = false;

                        // Static font asset
                        if (m_AtlasPopulationMode_prop.intValue == 0)
                        {
                            m_fontAsset.sourceFontFile = null;

                            //Set atlas textures to non readable.
                            for (int i = 0; i < m_fontAsset.atlasTextures.Length; i++)
                            {
                                Texture2D tex = m_fontAsset.atlasTextures[i];

                                if (tex != null && tex.isReadable)
                                    FontEngineEditorUtilities.SetAtlasTextureIsReadable(tex, false);
                            }

                            //Debug.Log("Atlas Population mode set to [Static].");
                        }
                        else // Dynamic font asset
                        {
                            if (m_fontAsset.m_SourceFontFile_EditorRef.dynamic == false)
                            {
                                Debug.LogWarning("Please set the [" + m_fontAsset.name + "] font to dynamic mode as this is required for Dynamic SDF support.", m_fontAsset.m_SourceFontFile_EditorRef);
                                m_AtlasPopulationMode_prop.intValue = 0;

                                serializedObject.ApplyModifiedProperties();
                            }
                            else
                            {
                                m_fontAsset.sourceFontFile = m_fontAsset.m_SourceFontFile_EditorRef;

                                // Set atlas textures to non readable.
                                for (int i = 0; i < m_fontAsset.atlasTextures.Length; i++)
                                {
                                    Texture2D tex = m_fontAsset.atlasTextures[i];

                                    if (tex != null && tex.isReadable == false)
                                        FontEngineEditorUtilities.SetAtlasTextureIsReadable(tex, true);
                                }

                                //Debug.Log("Atlas Population mode set to [" + (m_AtlasPopulationMode_prop.intValue == 1 ? "Dynamic" : "Dynamic OS") + "].");
                            }

                            // Dynamic OS font asset
                            if (m_AtlasPopulationMode_prop.intValue == 2)
                                m_fontAsset.sourceFontFile = null;
                        }

                        if (isDatabaseRefreshRequired)
                            AssetDatabase.Refresh();

                        serializedObject.Update();
                        isAssetDirty = true;
                    }

                    // Save state of atlas settings
                    if (m_DisplayDestructiveChangeWarning == false)
                    {
                        SavedGenerationSettings();
                        //Undo.RegisterCompleteObjectUndo(m_fontAsset, "Font Asset Changes");
                    }

                    EditorGUI.BeginDisabledGroup(m_AtlasPopulationMode_prop.intValue == (int)AtlasPopulationMode.Static);
                    {
                        EditorGUI.BeginChangeCheck();
                        // TODO: Switch shaders depending on GlyphRenderMode.
                        EditorGUILayout.PropertyField(m_AtlasRenderMode_prop);
                        EditorGUILayout.PropertyField(m_SamplingPointSize_prop, new GUIContent("Sampling Point Size"));
                        if (EditorGUI.EndChangeCheck())
                        {
                            m_DisplayDestructiveChangeWarning = true;
                        }

                        // Changes to these properties require updating Material Presets for this font asset.
                        EditorGUI.BeginChangeCheck();
                        EditorGUILayout.PropertyField(m_AtlasPadding_prop, new GUIContent("Padding"));
                        EditorGUILayout.IntPopup(m_AtlasWidth_prop, m_AtlasResolutionLabels, m_AtlasResolutions, new GUIContent("Atlas Width"));
                        EditorGUILayout.IntPopup(m_AtlasHeight_prop, m_AtlasResolutionLabels, m_AtlasResolutions, new GUIContent("Atlas Height"));
                        EditorGUILayout.PropertyField(m_IsMultiAtlasTexturesEnabled_prop, new GUIContent("Multi Atlas Textures", "Determines if the font asset will store glyphs in multiple atlas textures."));
                        if (EditorGUI.EndChangeCheck())
                        {
                            if (m_AtlasPadding_prop.intValue < 0)
                            {
                                m_AtlasPadding_prop.intValue = 0;
                                serializedObject.ApplyModifiedProperties();
                            }

                            m_MaterialPresetsRequireUpdate = true;
                            m_DisplayDestructiveChangeWarning = true;
                        }
                        EditorGUILayout.PropertyField(m_ClearDynamicDataOnBuild_prop, new GUIContent("Clear Dynamic Data On Build", "Clears all dynamic data restoring the font asset back to its default creation and empty state."));
                        EditorGUILayout.PropertyField(m_GetFontFeatures_prop, getFontFeaturesLabel);

                        EditorGUILayout.Space();

                        if (m_DisplayDestructiveChangeWarning)
                        {
                            bool guiEnabledState = GUI.enabled;
                            GUI.enabled = true;

                            // These changes are destructive on the font asset
                            rect = EditorGUILayout.GetControlRect(false, 60);
                            rect.x += 15;
                            rect.width -= 15;
                            EditorGUI.HelpBox(rect, "Changing these settings will clear the font asset's character, glyph and texture data.", MessageType.Warning);

                            if (GUI.Button(new Rect(rect.width - 140, rect.y + 36, 80, 18), new GUIContent("Apply")))
                            {
                                m_DisplayDestructiveChangeWarning = false;

                                // Update face info if sampling point size was changed.
#if UNITY_2023_3_OR_NEWER
                                if (m_GenerationSettings.pointSize != m_SamplingPointSize_prop.floatValue || m_FaceInfoDirty)
                                {
                                    LoadFontFace((int)m_SamplingPointSize_prop.floatValue, m_FontFaceIndex_prop.intValue);
#else
                                if (m_GenerationSettings.pointSize != m_SamplingPointSize_prop.intValue || m_FaceInfoDirty)
                                {
                                    LoadFontFace(m_SamplingPointSize_prop.intValue, m_FontFaceIndex_prop.intValue);
#endif
                                    m_fontAsset.faceInfo = FontEngine.GetFaceInfo();
                                    m_FaceInfoDirty = false;
                                }

                                Material mat = m_fontAsset.material;

                                // Update material
                                mat.SetFloat(ShaderUtilities.ID_TextureWidth, m_AtlasWidth_prop.intValue);
                                mat.SetFloat(ShaderUtilities.ID_TextureHeight, m_AtlasHeight_prop.intValue);

                                if (mat.HasProperty(ShaderUtilities.ID_GradientScale))
                                    mat.SetFloat(ShaderUtilities.ID_GradientScale, m_AtlasPadding_prop.intValue + 1);

                                // Update material presets if any of the relevant properties have been changed.
                                if (m_MaterialPresetsRequireUpdate)
                                {
                                    m_MaterialPresetsRequireUpdate = false;

                                    Material[] materialPresets = TMP_EditorUtility.FindMaterialReferences(m_fontAsset);
                                    for (int i = 0; i < materialPresets.Length; i++)
                                    {
                                        mat = materialPresets[i];

                                        mat.SetFloat(ShaderUtilities.ID_TextureWidth, m_AtlasWidth_prop.intValue);
                                        mat.SetFloat(ShaderUtilities.ID_TextureHeight, m_AtlasHeight_prop.intValue);

                                        if (mat.HasProperty(ShaderUtilities.ID_GradientScale))
                                            mat.SetFloat(ShaderUtilities.ID_GradientScale, m_AtlasPadding_prop.intValue + 1);
                                    }
                                }

                                m_fontAsset.UpdateFontAssetData();
                                GUIUtility.keyboardControl = 0;
                                isAssetDirty = true;

                                // Update Font Asset Creation Settings to reflect new changes.
                                UpdateFontAssetCreationSettings();

                                // TODO: Clear undo buffers.
                                //Undo.ClearUndo(m_fontAsset);
                            }

                            if (GUI.Button(new Rect(rect.width - 56, rect.y + 36, 80, 18), new GUIContent("Revert")))
                            {
                                m_DisplayDestructiveChangeWarning = false;
                                RestoreGenerationSettings();
                                GUIUtility.keyboardControl = 0;

                                // TODO: Clear undo buffers.
                                //Undo.ClearUndo(m_fontAsset);
                            }

                            GUI.enabled = guiEnabledState;
                        }
                    }
                    EditorGUI.EndDisabledGroup();
                }
                EditorGUI.EndDisabledGroup();
                EditorGUILayout.Space();
            }
            #endregion

            // ATLAS & MATERIAL PANEL
            #region Atlas & Material
            rect = EditorGUILayout.GetControlRect(false, 24);

            if (GUI.Button(rect, new GUIContent("<b>Atlas & Material</b>"), TMP_UIStyleManager.sectionHeader))
                UI_PanelState.fontAtlasInfoPanel = !UI_PanelState.fontAtlasInfoPanel;

            GUI.Label(rect, (UI_PanelState.fontAtlasInfoPanel ? "" : s_UiStateLabel[1]), TMP_UIStyleManager.rightLabel);

            if (UI_PanelState.fontAtlasInfoPanel)
            {
                EditorGUI.indentLevel = 1;

                GUI.enabled = false;
                EditorGUILayout.PropertyField(font_atlas_prop, new GUIContent("Font Atlas"));
                EditorGUILayout.PropertyField(font_material_prop, new GUIContent("Font Material"));
                GUI.enabled = true;
                EditorGUILayout.Space();
            }
            #endregion

            string evt_cmd = Event.current.commandName; // Get Current Event CommandName to check for Undo Events

            // FONT WEIGHT PANEL
            #region Font Weights
            rect = EditorGUILayout.GetControlRect(false, 24);

            if (GUI.Button(rect, new GUIContent("<b>Font Weights</b>", "The Font Assets that will be used for different font weights and the settings used to simulate a typeface when no asset is available."), TMP_UIStyleManager.sectionHeader))
                UI_PanelState.fontWeightPanel = !UI_PanelState.fontWeightPanel;

            GUI.Label(rect, (UI_PanelState.fontWeightPanel ? "" : s_UiStateLabel[1]), TMP_UIStyleManager.rightLabel);

            if (UI_PanelState.fontWeightPanel)
            {
                EditorGUIUtility.labelWidth *= 0.75f;
                EditorGUIUtility.fieldWidth *= 0.25f;

                EditorGUILayout.BeginVertical();
                EditorGUI.indentLevel = 1;
                rect = EditorGUILayout.GetControlRect(true);
                rect.x += EditorGUIUtility.labelWidth;
                rect.width = (rect.width - EditorGUIUtility.labelWidth) / 2f;
                GUI.Label(rect, "Regular Typeface", EditorStyles.label);
                rect.x += rect.width;
                GUI.Label(rect, "Italic Typeface", EditorStyles.label);

                EditorGUI.indentLevel = 1;

                EditorGUILayout.PropertyField(fontWeights_prop.GetArrayElementAtIndex(1), new GUIContent("100 - Thin"));
                EditorGUILayout.PropertyField(fontWeights_prop.GetArrayElementAtIndex(2), new GUIContent("200 - Extra-Light"));
                EditorGUILayout.PropertyField(fontWeights_prop.GetArrayElementAtIndex(3), new GUIContent("300 - Light"));
                EditorGUILayout.PropertyField(fontWeights_prop.GetArrayElementAtIndex(4), new GUIContent("400 - Regular"));
                EditorGUILayout.PropertyField(fontWeights_prop.GetArrayElementAtIndex(5), new GUIContent("500 - Medium"));
                EditorGUILayout.PropertyField(fontWeights_prop.GetArrayElementAtIndex(6), new GUIContent("600 - Semi-Bold"));
                EditorGUILayout.PropertyField(fontWeights_prop.GetArrayElementAtIndex(7), new GUIContent("700 - Bold"));
                EditorGUILayout.PropertyField(fontWeights_prop.GetArrayElementAtIndex(8), new GUIContent("800 - Heavy"));
                EditorGUILayout.PropertyField(fontWeights_prop.GetArrayElementAtIndex(9), new GUIContent("900 - Black"));

                EditorGUILayout.EndVertical();

                EditorGUILayout.Space();

                EditorGUILayout.BeginVertical();

                EditorGUILayout.BeginHorizontal();
                EditorGUILayout.PropertyField(font_normalStyle_prop, new GUIContent("Normal Weight"));
                font_normalStyle_prop.floatValue = Mathf.Clamp(font_normalStyle_prop.floatValue, -3.0f, 3.0f);
                if (GUI.changed || evt_cmd == k_UndoRedo)
                {
                    GUI.changed = false;

                    // Modify the material property on matching material presets.
                    for (int i = 0; i < m_materialPresets.Length; i++)
                        m_materialPresets[i].SetFloat("_WeightNormal", font_normalStyle_prop.floatValue);
                }

                EditorGUILayout.PropertyField(font_boldStyle_prop, new GUIContent("Bold Weight"));
                font_boldStyle_prop.floatValue = Mathf.Clamp(font_boldStyle_prop.floatValue, -3.0f, 3.0f);
                if (GUI.changed || evt_cmd == k_UndoRedo)
                {
                    GUI.changed = false;

                    // Modify the material property on matching material presets.
                    for (int i = 0; i < m_materialPresets.Length; i++)
                        m_materialPresets[i].SetFloat("_WeightBold", font_boldStyle_prop.floatValue);
                }
                EditorGUILayout.EndHorizontal();

                EditorGUILayout.BeginHorizontal();
                EditorGUILayout.PropertyField(font_normalSpacing_prop, new GUIContent("Spacing Offset"));
                font_normalSpacing_prop.floatValue = Mathf.Clamp(font_normalSpacing_prop.floatValue, -100, 100);
                if (GUI.changed || evt_cmd == k_UndoRedo)
                {
                    GUI.changed = false;
                }

                EditorGUILayout.PropertyField(font_boldSpacing_prop, new GUIContent("Bold Spacing"));
                font_boldSpacing_prop.floatValue = Mathf.Clamp(font_boldSpacing_prop.floatValue, 0, 100);
                if (GUI.changed || evt_cmd == k_UndoRedo)
                {
                    GUI.changed = false;
                }
                EditorGUILayout.EndHorizontal();

                EditorGUILayout.BeginHorizontal();
                EditorGUILayout.PropertyField(font_italicStyle_prop, new GUIContent("Italic Style"));
                font_italicStyle_prop.intValue = Mathf.Clamp(font_italicStyle_prop.intValue, 15, 60);

                EditorGUILayout.PropertyField(font_tabSize_prop, new GUIContent("Tab Multiple"));
                EditorGUILayout.EndHorizontal();
                EditorGUILayout.EndVertical();
                EditorGUILayout.Space();
            }

            EditorGUIUtility.labelWidth = 0;
            EditorGUIUtility.fieldWidth = 0;
            #endregion

            // FALLBACK FONT ASSETS
            #region Fallback Font Asset
            rect = EditorGUILayout.GetControlRect(false, 24);
            EditorGUI.indentLevel = 0;
            if (GUI.Button(rect, new GUIContent("<b>Fallback Font Assets</b>", "Select the Font Assets that will be searched and used as fallback when characters are missing from this font asset."), TMP_UIStyleManager.sectionHeader))
                UI_PanelState.fallbackFontAssetPanel = !UI_PanelState.fallbackFontAssetPanel;

            GUI.Label(rect, (UI_PanelState.fallbackFontAssetPanel ? "" : s_UiStateLabel[1]), TMP_UIStyleManager.rightLabel);

            if (UI_PanelState.fallbackFontAssetPanel)
            {
                EditorGUIUtility.labelWidth = 120;
                EditorGUI.indentLevel = 0;
                EditorGUI.BeginChangeCheck();
                m_FallbackFontAssetList.DoLayoutList();
                if (EditorGUI.EndChangeCheck())
                {
                    m_IsFallbackGlyphCacheDirty = true;
                }
                EditorGUILayout.Space();
            }
            #endregion

            // CHARACTER TABLE TABLE
            #region Character Table
            EditorGUIUtility.labelWidth = labelWidth;
            EditorGUIUtility.fieldWidth = fieldWidth;
            EditorGUI.indentLevel = 0;
            rect = EditorGUILayout.GetControlRect(false, 24);

            int characterCount = m_fontAsset.characterTable.Count;

            if (GUI.Button(rect, new GUIContent("<b>Character Table</b>   [" + characterCount + "]" + (rect.width > 320 ? " Characters" : ""), "List of characters contained in this font asset."), TMP_UIStyleManager.sectionHeader))
                UI_PanelState.characterTablePanel = !UI_PanelState.characterTablePanel;

            GUI.Label(rect, (UI_PanelState.characterTablePanel ? "" : s_UiStateLabel[1]), TMP_UIStyleManager.rightLabel);

            if (UI_PanelState.characterTablePanel)
            {
                int arraySize = m_CharacterTable_prop.arraySize;
                int itemsPerPage = 15;

                // Display Glyph Management Tools
                EditorGUILayout.BeginVertical(EditorStyles.helpBox);
                {
                    // Search Bar implementation
                    #region DISPLAY SEARCH BAR
                    EditorGUILayout.BeginHorizontal();
                    {
                        EditorGUIUtility.labelWidth = 130f;
                        EditorGUI.BeginChangeCheck();
                        string searchPattern = EditorGUILayout.TextField("Character Search", m_CharacterSearchPattern, "SearchTextField");
                        if (EditorGUI.EndChangeCheck() || m_isSearchDirty)
                        {
                            if (string.IsNullOrEmpty(searchPattern) == false)
                            {
                                m_CharacterSearchPattern = searchPattern;

                                // Search Character Table for potential matches
                                SearchCharacterTable(m_CharacterSearchPattern, ref m_CharacterSearchList);
                            }
                            else
                                m_CharacterSearchPattern = null;

                            m_isSearchDirty = false;
                        }

                        string styleName = string.IsNullOrEmpty(m_CharacterSearchPattern) ? "SearchCancelButtonEmpty" : "SearchCancelButton";
                        if (GUILayout.Button(GUIContent.none, styleName))
                        {
                            GUIUtility.keyboardControl = 0;
                            m_CharacterSearchPattern = string.Empty;
                        }
                    }
                    EditorGUILayout.EndHorizontal();
                    #endregion

                    // Display Page Navigation
                    if (!string.IsNullOrEmpty(m_CharacterSearchPattern))
                        arraySize = m_CharacterSearchList.Count;

                    DisplayPageNavigation(ref m_CurrentCharacterPage, arraySize, itemsPerPage);
                }
                EditorGUILayout.EndVertical();

                // Display Character Table Elements
                if (arraySize > 0)
                {
                    // Display each character entry using the CharacterPropertyDrawer.
                    for (int i = itemsPerPage * m_CurrentCharacterPage; i < arraySize && i < itemsPerPage * (m_CurrentCharacterPage + 1); i++)
                    {
                        // Define the start of the selection region of the element.
                        Rect elementStartRegion = GUILayoutUtility.GetRect(0f, 0f, GUILayout.ExpandWidth(true));

                        int elementIndex = i;
                        if (!string.IsNullOrEmpty(m_CharacterSearchPattern))
                            elementIndex = m_CharacterSearchList[i];

                        SerializedProperty characterProperty = m_CharacterTable_prop.GetArrayElementAtIndex(elementIndex);

                        EditorGUILayout.BeginVertical(EditorStyles.helpBox);

                        EditorGUI.BeginDisabledGroup(i != m_SelectedCharacterRecord);
                        {
                            EditorGUILayout.PropertyField(characterProperty);
                        }
                        EditorGUI.EndDisabledGroup();

                        EditorGUILayout.EndVertical();

                        // Define the end of the selection region of the element.
                        Rect elementEndRegion = GUILayoutUtility.GetRect(0f, 0f, GUILayout.ExpandWidth(true));

                        // Check for Item selection
                        Rect selectionArea = new Rect(elementStartRegion.x, elementStartRegion.y, elementEndRegion.width, elementEndRegion.y - elementStartRegion.y);
                        if (DoSelectionCheck(selectionArea))
                        {
                            if (m_SelectedCharacterRecord == i)
                                m_SelectedCharacterRecord = -1;
                            else
                            {
                                m_SelectedCharacterRecord = i;
                                m_AddCharacterWarning.isEnabled = false;
                                m_unicodeHexLabel = k_placeholderUnicodeHex;
                                GUIUtility.keyboardControl = 0;
                            }
                        }

                        // Draw Selection Highlight and Glyph Options
                        if (m_SelectedCharacterRecord == i)
                        {
                            // Reset other selections
                            ResetSelections(RecordSelectionType.CharacterRecord);

                            TMP_EditorUtility.DrawBox(selectionArea, 2f, new Color32(40, 192, 255, 255));

                            // Draw Glyph management options
                            Rect controlRect = EditorGUILayout.GetControlRect(true, EditorGUIUtility.singleLineHeight * 1f);
                            float optionAreaWidth = controlRect.width * 0.6f;
                            float btnWidth = optionAreaWidth / 3;

                            Rect position = new Rect(controlRect.x + controlRect.width * .4f, controlRect.y, btnWidth, controlRect.height);

                            // Copy Selected Glyph to Target Glyph ID
                            GUI.enabled = !string.IsNullOrEmpty(m_dstUnicode);
                            if (GUI.Button(position, new GUIContent("Copy to")))
                            {
                                GUIUtility.keyboardControl = 0;

                                // Convert Hex Value to Decimal
                                int dstGlyphID = TMP_TextUtilities.StringHexToInt(m_dstUnicode);

                                //Add new glyph at target Unicode hex id.
                                if (!AddNewCharacter(elementIndex, dstGlyphID))
                                {
                                    m_AddCharacterWarning.isEnabled = true;
                                    m_AddCharacterWarning.expirationTime = EditorApplication.timeSinceStartup + 1;
                                }

                                m_dstUnicode = string.Empty;
                                m_isSearchDirty = true;

                                TMPro_EventManager.ON_FONT_PROPERTY_CHANGED(true, m_fontAsset);
                            }

                            // Target Glyph ID
                            GUI.enabled = true;
                            position.x += btnWidth;

                            GUI.SetNextControlName("CharacterID_Input");
                            m_dstUnicode = EditorGUI.TextField(position, m_dstUnicode);

                            // Placeholder text
                            EditorGUI.LabelField(position, new GUIContent(m_unicodeHexLabel, "The Unicode (Hex) ID of the duplicated Character"), TMP_UIStyleManager.label);

                            // Only filter the input when the destination glyph ID text field has focus.
                            if (GUI.GetNameOfFocusedControl() == "CharacterID_Input")
                            {
                                m_unicodeHexLabel = string.Empty;

                                //Filter out unwanted characters.
                                char chr = Event.current.character;
                                if ((chr < '0' || chr > '9') && (chr < 'a' || chr > 'f') && (chr < 'A' || chr > 'F'))
                                {
                                    Event.current.character = '\0';
                                }
                            }
                            else
                            {
                                m_unicodeHexLabel = k_placeholderUnicodeHex;
                                //m_dstUnicode = string.Empty;
                            }


                            // Remove Glyph
                            position.x += btnWidth;
                            if (GUI.Button(position, "Remove"))
                            {
                                GUIUtility.keyboardControl = 0;

                                RemoveCharacterFromList(elementIndex);

                                isAssetDirty = true;
                                m_SelectedCharacterRecord = -1;
                                m_isSearchDirty = true;
                                break;
                            }

                            if (m_AddCharacterWarning.isEnabled && EditorApplication.timeSinceStartup < m_AddCharacterWarning.expirationTime)
                            {
                                EditorGUILayout.HelpBox("The Destination Character ID already exists", MessageType.Warning);
                            }
                        }
                    }
                }

                DisplayPageNavigation(ref m_CurrentCharacterPage, arraySize, itemsPerPage);

                EditorGUILayout.Space();
            }
            #endregion

            // GLYPH TABLE
            #region Glyph Table
            EditorGUIUtility.labelWidth = labelWidth;
            EditorGUIUtility.fieldWidth = fieldWidth;
            EditorGUI.indentLevel = 0;
            rect = EditorGUILayout.GetControlRect(false, 24);

            GUIStyle glyphPanelStyle = new GUIStyle(EditorStyles.helpBox);

            int glyphRecordCount = m_fontAsset.glyphTable.Count;

            if (GUI.Button(rect, new GUIContent("<b>Glyph Table</b>   [" + glyphRecordCount + "]" + (rect.width > 275 ? " Glyphs" : ""), "List of glyphs contained in this font asset."), TMP_UIStyleManager.sectionHeader))
                UI_PanelState.glyphTablePanel = !UI_PanelState.glyphTablePanel;

            GUI.Label(rect, (UI_PanelState.glyphTablePanel ? "" : s_UiStateLabel[1]), TMP_UIStyleManager.rightLabel);

            if (UI_PanelState.glyphTablePanel)
            {
                int arraySize = m_GlyphTable_prop.arraySize;
                int itemsPerPage = 15;

                // Display Glyph Management Tools
                EditorGUILayout.BeginVertical(EditorStyles.helpBox);
                {
                    // Search Bar implementation
                    #region DISPLAY SEARCH BAR
                    EditorGUILayout.BeginHorizontal();
                    {
                        EditorGUIUtility.labelWidth = 130f;
                        EditorGUI.BeginChangeCheck();
                        string searchPattern = EditorGUILayout.TextField("Glyph Search", m_GlyphSearchPattern, "SearchTextField");
                        if (EditorGUI.EndChangeCheck() || m_isSearchDirty)
                        {
                            if (string.IsNullOrEmpty(searchPattern) == false)
                            {
                                m_GlyphSearchPattern = searchPattern;

                                // Search Glyph Table for potential matches
                                SearchGlyphTable(m_GlyphSearchPattern, ref m_GlyphSearchList);
                            }
                            else
                                m_GlyphSearchPattern = null;

                            m_isSearchDirty = false;
                        }

                        string styleName = string.IsNullOrEmpty(m_GlyphSearchPattern) ? "SearchCancelButtonEmpty" : "SearchCancelButton";
                        if (GUILayout.Button(GUIContent.none, styleName))
                        {
                            GUIUtility.keyboardControl = 0;
                            m_GlyphSearchPattern = string.Empty;
                        }
                    }
                    EditorGUILayout.EndHorizontal();
                    #endregion

                    // Display Page Navigation
                    if (!string.IsNullOrEmpty(m_GlyphSearchPattern))
                        arraySize = m_GlyphSearchList.Count;

                    DisplayPageNavigation(ref m_CurrentGlyphPage, arraySize, itemsPerPage);
                }
                EditorGUILayout.EndVertical();

                // Display Glyph Table Elements

                if (arraySize > 0)
                {
                    // Display each GlyphInfo entry using the GlyphInfo property drawer.
                    for (int i = itemsPerPage * m_CurrentGlyphPage; i < arraySize && i < itemsPerPage * (m_CurrentGlyphPage + 1); i++)
                    {
                        // Define the start of the selection region of the element.
                        Rect elementStartRegion = GUILayoutUtility.GetRect(0f, 0f, GUILayout.ExpandWidth(true));

                        int elementIndex = i;
                        if (!string.IsNullOrEmpty(m_GlyphSearchPattern))
                            elementIndex = m_GlyphSearchList[i];

                        SerializedProperty glyphProperty = m_GlyphTable_prop.GetArrayElementAtIndex(elementIndex);

                        EditorGUILayout.BeginVertical(glyphPanelStyle);

                        using (new EditorGUI.DisabledScope(i != m_SelectedGlyphRecord))
                        {
                            EditorGUILayout.PropertyField(glyphProperty);
                        }

                        EditorGUILayout.EndVertical();

                        // Define the end of the selection region of the element.
                        Rect elementEndRegion = GUILayoutUtility.GetRect(0f, 0f, GUILayout.ExpandWidth(true));

                        // Check for Item selection
                        Rect selectionArea = new Rect(elementStartRegion.x, elementStartRegion.y, elementEndRegion.width, elementEndRegion.y - elementStartRegion.y);
                        if (DoSelectionCheck(selectionArea))
                        {
                            if (m_SelectedGlyphRecord == i)
                                m_SelectedGlyphRecord = -1;
                            else
                            {
                                m_SelectedGlyphRecord = i;
                                m_AddGlyphWarning.isEnabled = false;
                                m_unicodeHexLabel = k_placeholderUnicodeHex;
                                GUIUtility.keyboardControl = 0;
                            }
                        }

                        // Draw Selection Highlight and Glyph Options
                        if (m_SelectedGlyphRecord == i)
                        {
                            // Reset other selections
                            ResetSelections(RecordSelectionType.GlyphRecord);

                            TMP_EditorUtility.DrawBox(selectionArea, 2f, new Color32(40, 192, 255, 255));

                            // Draw Glyph management options
                            Rect controlRect = EditorGUILayout.GetControlRect(true, EditorGUIUtility.singleLineHeight * 1f);
                            float optionAreaWidth = controlRect.width * 0.6f;
                            float btnWidth = optionAreaWidth / 3;

                            Rect position = new Rect(controlRect.x + controlRect.width * .4f, controlRect.y, btnWidth, controlRect.height);

                            // Copy Selected Glyph to Target Glyph ID
                            GUI.enabled = !string.IsNullOrEmpty(m_dstGlyphID);
                            if (GUI.Button(position, new GUIContent("Copy to")))
                            {
                                GUIUtility.keyboardControl = 0;
                                int dstGlyphID;

                                // Convert Hex Value to Decimal
                                int.TryParse(m_dstGlyphID, out dstGlyphID);

                                //Add new glyph at target Unicode hex id.
                                if (!AddNewGlyph(elementIndex, dstGlyphID))
                                {
                                    m_AddGlyphWarning.isEnabled = true;
                                    m_AddGlyphWarning.expirationTime = EditorApplication.timeSinceStartup + 1;
                                }

                                m_dstGlyphID = string.Empty;
                                m_isSearchDirty = true;

                                TMPro_EventManager.ON_FONT_PROPERTY_CHANGED(true, m_fontAsset);
                            }

                            // Target Glyph ID
                            GUI.enabled = true;
                            position.x += btnWidth;

                            GUI.SetNextControlName("GlyphID_Input");
                            m_dstGlyphID = EditorGUI.TextField(position, m_dstGlyphID);

                            // Placeholder text
                            EditorGUI.LabelField(position, new GUIContent(m_GlyphIDLabel, "The Glyph ID of the duplicated Glyph"), TMP_UIStyleManager.label);

                            // Only filter the input when the destination glyph ID text field has focus.
                            if (GUI.GetNameOfFocusedControl() == "GlyphID_Input")
                            {
                                m_GlyphIDLabel = string.Empty;

                                //Filter out unwanted characters.
                                char chr = Event.current.character;
                                if ((chr < '0' || chr > '9'))
                                {
                                    Event.current.character = '\0';
                                }
                            }
                            else
                            {
                                m_GlyphIDLabel = k_placeholderGlyphID;
                                //m_dstGlyphID = string.Empty;
                            }

                            // Remove Glyph
                            position.x += btnWidth;
                            if (GUI.Button(position, "Remove"))
                            {
                                GUIUtility.keyboardControl = 0;

                                RemoveGlyphFromList(elementIndex);

                                isAssetDirty = true;
                                m_SelectedGlyphRecord = -1;
                                m_isSearchDirty = true;
                                break;
                            }

                            if (m_AddGlyphWarning.isEnabled && EditorApplication.timeSinceStartup < m_AddGlyphWarning.expirationTime)
                            {
                                EditorGUILayout.HelpBox("The Destination Glyph ID already exists", MessageType.Warning);
                            }
                        }
                    }
                }

                //DisplayAddRemoveButtons(m_GlyphTable_prop, m_SelectedGlyphRecord, glyphRecordCount);

                DisplayPageNavigation(ref m_CurrentGlyphPage, arraySize, itemsPerPage);

                EditorGUILayout.Space();
            }
            #endregion

            // FONT FEATURE TABLES

            // LIGATURE SUBSTITUTION TABLE
            #region LIGATURE
            EditorGUIUtility.labelWidth = labelWidth;
            EditorGUIUtility.fieldWidth = fieldWidth;
            EditorGUI.indentLevel = 0;
            rect = EditorGUILayout.GetControlRect(false, 24);

            int ligatureSubstitutionRecordCount = m_fontAsset.fontFeatureTable.ligatureRecords.Count;

            if (GUI.Button(rect, new GUIContent("<b>Ligature Table</b>   [" + ligatureSubstitutionRecordCount + "]" + (rect.width > 340 ? " Records" : ""), "List of Ligature substitution records."), TMP_UIStyleManager.sectionHeader))
                UI_PanelState.LigatureSubstitutionTablePanel = !UI_PanelState.LigatureSubstitutionTablePanel;

            GUI.Label(rect, (UI_PanelState.LigatureSubstitutionTablePanel ? "" : s_UiStateLabel[1]), TMP_UIStyleManager.rightLabel);

            if (UI_PanelState.LigatureSubstitutionTablePanel)
            {
                int arraySize = m_LigatureSubstitutionRecords_prop.arraySize;
                int itemsPerPage = 20;

                // Display Mark Adjust Records Management Tools
                EditorGUILayout.BeginVertical(EditorStyles.helpBox);
                {
                    // Search Bar implementation
                    #region DISPLAY SEARCH BAR
                    EditorGUILayout.BeginHorizontal();
                    {
                        EditorGUIUtility.labelWidth = 150f;
                        EditorGUI.BeginChangeCheck();
                        string searchPattern = EditorGUILayout.TextField("Ligature Search", m_LigatureTableSearchPattern, "SearchTextField");
                        if (EditorGUI.EndChangeCheck() || m_isSearchDirty)
                        {
                            if (string.IsNullOrEmpty(searchPattern) == false)
                            {
                                m_LigatureTableSearchPattern = searchPattern;

                                // Search Glyph Table for potential matches
                                SearchLigatureTable(m_LigatureTableSearchPattern, ref m_LigatureTableSearchList);
                            }
                            else
                                m_LigatureTableSearchPattern = null;

                            m_isSearchDirty = false;
                        }

                        string styleName = string.IsNullOrEmpty(m_LigatureTableSearchPattern) ? "SearchCancelButtonEmpty" : "SearchCancelButton";
                        if (GUILayout.Button(GUIContent.none, styleName))
                        {
                            GUIUtility.keyboardControl = 0;
                            m_LigatureTableSearchPattern = string.Empty;
                        }
                    }
                    EditorGUILayout.EndHorizontal();
                    #endregion

                    // Display Page Navigation
                    if (!string.IsNullOrEmpty(m_LigatureTableSearchPattern))
                        arraySize = m_LigatureTableSearchList.Count;

                    DisplayPageNavigation(ref m_CurrentLigaturePage, arraySize, itemsPerPage);
                }
                EditorGUILayout.EndVertical();

                if (arraySize > 0)
                {
                    // Display each GlyphInfo entry using the GlyphInfo property drawer.
                    for (int i = itemsPerPage * m_CurrentLigaturePage; i < arraySize && i < itemsPerPage * (m_CurrentLigaturePage + 1); i++)
                    {
                        // Define the start of the selection region of the element.
                        Rect elementStartRegion = GUILayoutUtility.GetRect(0f, 0f, GUILayout.ExpandWidth(true));

                        int elementIndex = i;
                        if (!string.IsNullOrEmpty(m_LigatureTableSearchPattern))
                            elementIndex = m_LigatureTableSearchList[i];

                        SerializedProperty ligaturePropertyRecord = m_LigatureSubstitutionRecords_prop.GetArrayElementAtIndex(elementIndex);

                        EditorGUILayout.BeginVertical(EditorStyles.helpBox);

                        using (new EditorGUI.DisabledScope(i != m_SelectedLigatureRecord))
                        {
                            EditorGUI.BeginChangeCheck();
                            EditorGUILayout.PropertyField(ligaturePropertyRecord);

                            if (EditorGUI.EndChangeCheck())
                            {
                                UpdateLigatureSubstitutionRecordLookup(ligaturePropertyRecord);
                            }
                        }

                        EditorGUILayout.EndVertical();

                        // Define the end of the selection region of the element.
                        Rect elementEndRegion = GUILayoutUtility.GetRect(0f, 0f, GUILayout.ExpandWidth(true));

                        // Check for Item selection
                        Rect selectionArea = new Rect(elementStartRegion.x, elementStartRegion.y, elementEndRegion.width, elementEndRegion.y - elementStartRegion.y);
                        if (DoSelectionCheck(selectionArea))
                        {
                            if (m_SelectedLigatureRecord == i)
                            {
                                m_SelectedLigatureRecord = -1;
                            }
                            else
                            {
                                m_SelectedLigatureRecord = i;
                                GUIUtility.keyboardControl = 0;
                            }
                        }

                        // Draw Selection Highlight and Kerning Pair Options
                        if (m_SelectedLigatureRecord == i)
                        {
                            // Reset other selections
                            ResetSelections(RecordSelectionType.LigatureSubstitutionRecord);

                            TMP_EditorUtility.DrawBox(selectionArea, 2f, new Color32(40, 192, 255, 255));

                            // Draw Glyph management options
                            Rect controlRect = EditorGUILayout.GetControlRect(true, EditorGUIUtility.singleLineHeight * 1f);
                            float optionAreaWidth = controlRect.width;
                            float btnWidth = optionAreaWidth / 4;

                            Rect position = new Rect(controlRect.x + controlRect.width - btnWidth, controlRect.y, btnWidth, controlRect.height);

                            // Move record up
                            bool guiEnabled = GUI.enabled;
                            if (m_SelectedLigatureRecord == 0) { GUI.enabled = false; }
                            if (GUI.Button(new Rect(controlRect.x, controlRect.y, btnWidth, controlRect.height), "Up"))
                            {
                                SwapCharacterElements(m_LigatureSubstitutionRecords_prop, m_SelectedLigatureRecord, m_SelectedLigatureRecord - 1);
                                serializedObject.ApplyModifiedProperties();
                                m_SelectedLigatureRecord -= 1;
                                isAssetDirty = true;
                                m_isSearchDirty = true;
                                m_fontAsset.InitializeLigatureSubstitutionLookupDictionary();
                            }
                            GUI.enabled = guiEnabled;

                            // Move record down
                            if (m_SelectedLigatureRecord == arraySize - 1) { GUI.enabled = false; }
                            if (GUI.Button(new Rect(controlRect.x + btnWidth, controlRect.y, btnWidth, controlRect.height), "Down"))
                            {
                                SwapCharacterElements(m_LigatureSubstitutionRecords_prop, m_SelectedLigatureRecord, m_SelectedLigatureRecord + 1);
                                serializedObject.ApplyModifiedProperties();
                                m_SelectedLigatureRecord += 1;
                                isAssetDirty = true;
                                m_isSearchDirty = true;
                                m_fontAsset.InitializeLigatureSubstitutionLookupDictionary();
                            }
                            GUI.enabled = guiEnabled;

                            // Remove record
                            GUI.enabled = true;
                            if (GUI.Button(position, "Remove"))
                            {
                                GUIUtility.keyboardControl = 0;

                                RemoveRecord(m_LigatureSubstitutionRecords_prop, m_SelectedLigatureRecord);

                                isAssetDirty = true;
                                m_SelectedLigatureRecord = -1;
                                m_isSearchDirty = true;
                                break;
                            }
                        }
                    }
                }

                DisplayAddRemoveButtons(m_LigatureSubstitutionRecords_prop, m_SelectedLigatureRecord, ligatureSubstitutionRecordCount);

                DisplayPageNavigation(ref m_CurrentLigaturePage, arraySize, itemsPerPage);

                GUILayout.Space(5);
            }
            #endregion

            // PAIR ADJUSTMENT TABLE
            #region Pair Adjustment Table
            EditorGUIUtility.labelWidth = labelWidth;
            EditorGUIUtility.fieldWidth = fieldWidth;
            EditorGUI.indentLevel = 0;
            rect = EditorGUILayout.GetControlRect(false, 24);

            int adjustmentPairCount = m_fontAsset.fontFeatureTable.glyphPairAdjustmentRecords.Count;

            if (GUI.Button(rect, new GUIContent("<b>Glyph Adjustment Table</b>   [" + adjustmentPairCount + "]" + (rect.width > 340 ? " Records" : ""), "List of glyph adjustment / advanced kerning pairs."), TMP_UIStyleManager.sectionHeader))
                UI_PanelState.PairAdjustmentTablePanel = !UI_PanelState.PairAdjustmentTablePanel;

            GUI.Label(rect, (UI_PanelState.PairAdjustmentTablePanel ? "" : s_UiStateLabel[1]), TMP_UIStyleManager.rightLabel);

            if (UI_PanelState.PairAdjustmentTablePanel)
            {
                int arraySize = m_GlyphPairAdjustmentRecords_prop.arraySize;
                int itemsPerPage = 20;

                // Display Kerning Pair Management Tools
                EditorGUILayout.BeginVertical(EditorStyles.helpBox);
                {
                    // Search Bar implementation
                    #region DISPLAY SEARCH BAR
                    EditorGUILayout.BeginHorizontal();
                    {
                        EditorGUIUtility.labelWidth = 150f;
                        EditorGUI.BeginChangeCheck();
                        string searchPattern = EditorGUILayout.TextField("Adjustment Pair Search", m_KerningTableSearchPattern, "SearchTextField");
                        if (EditorGUI.EndChangeCheck() || m_isSearchDirty)
                        {
                            if (string.IsNullOrEmpty(searchPattern) == false)
                            {
                                m_KerningTableSearchPattern = searchPattern;

                                // Search Glyph Table for potential matches
                                SearchKerningTable(m_KerningTableSearchPattern, ref m_KerningTableSearchList);
                            }
                            else
                                m_KerningTableSearchPattern = null;

                            m_isSearchDirty = false;
                        }

                        string styleName = string.IsNullOrEmpty(m_KerningTableSearchPattern) ? "SearchCancelButtonEmpty" : "SearchCancelButton";
                        if (GUILayout.Button(GUIContent.none, styleName))
                        {
                            GUIUtility.keyboardControl = 0;
                            m_KerningTableSearchPattern = string.Empty;
                        }
                    }
                    EditorGUILayout.EndHorizontal();
                    #endregion

                    // Display Page Navigation
                    if (!string.IsNullOrEmpty(m_KerningTableSearchPattern))
                        arraySize = m_KerningTableSearchList.Count;

                    DisplayPageNavigation(ref m_CurrentAdjustmentPairPage, arraySize, itemsPerPage);
                }
                EditorGUILayout.EndVertical();

                if (arraySize > 0)
                {
                    // Display each GlyphInfo entry using the GlyphInfo property drawer.
                    for (int i = itemsPerPage * m_CurrentAdjustmentPairPage; i < arraySize && i < itemsPerPage * (m_CurrentAdjustmentPairPage + 1); i++)
                    {
                        // Define the start of the selection region of the element.
                        Rect elementStartRegion = GUILayoutUtility.GetRect(0f, 0f, GUILayout.ExpandWidth(true));

                        int elementIndex = i;
                        if (!string.IsNullOrEmpty(m_KerningTableSearchPattern))
                            elementIndex = m_KerningTableSearchList[i];

                        SerializedProperty pairAdjustmentRecordProperty = m_GlyphPairAdjustmentRecords_prop.GetArrayElementAtIndex(elementIndex);

                        EditorGUILayout.BeginVertical(EditorStyles.helpBox);

                        using (new EditorGUI.DisabledScope(i != m_SelectedAdjustmentRecord))
                        {
                            EditorGUI.BeginChangeCheck();
                            EditorGUILayout.PropertyField(pairAdjustmentRecordProperty, new GUIContent("Selectable"));
                            if (EditorGUI.EndChangeCheck())
                            {
                                UpdatePairAdjustmentRecordLookup(pairAdjustmentRecordProperty);
                            }
                        }

                        EditorGUILayout.EndVertical();

                        // Define the end of the selection region of the element.
                        Rect elementEndRegion = GUILayoutUtility.GetRect(0f, 0f, GUILayout.ExpandWidth(true));

                        // Check for Item selection
                        Rect selectionArea = new Rect(elementStartRegion.x, elementStartRegion.y, elementEndRegion.width, elementEndRegion.y - elementStartRegion.y);
                        if (DoSelectionCheck(selectionArea))
                        {
                            if (m_SelectedAdjustmentRecord == i)
                            {
                                m_SelectedAdjustmentRecord = -1;
                            }
                            else
                            {
                                m_SelectedAdjustmentRecord = i;
                                GUIUtility.keyboardControl = 0;
                            }
                        }

                        // Draw Selection Highlight and Kerning Pair Options
                        if (m_SelectedAdjustmentRecord == i)
                        {
                            // Reset other selections
                            ResetSelections(RecordSelectionType.AdjustmentPairRecord);

                            TMP_EditorUtility.DrawBox(selectionArea, 2f, new Color32(40, 192, 255, 255));

                            // Draw Glyph management options
                            Rect controlRect = EditorGUILayout.GetControlRect(true, EditorGUIUtility.singleLineHeight * 1f);
                            float optionAreaWidth = controlRect.width;
                            float btnWidth = optionAreaWidth / 4;

                            Rect position = new Rect(controlRect.x + controlRect.width - btnWidth, controlRect.y, btnWidth, controlRect.height);

                            // Remove Kerning pair
                            GUI.enabled = true;
                            if (GUI.Button(position, "Remove"))
                            {
                                GUIUtility.keyboardControl = 0;

                                RemoveRecord(m_GlyphPairAdjustmentRecords_prop, i);

                                isAssetDirty = true;
                                m_SelectedAdjustmentRecord = -1;
                                m_isSearchDirty = true;
                                break;
                            }
                        }
                    }
                }

                DisplayPageNavigation(ref m_CurrentAdjustmentPairPage, arraySize, itemsPerPage);

                GUILayout.Space(5);

                // Add new kerning pair
                EditorGUILayout.BeginVertical(EditorStyles.helpBox);
                {
                    EditorGUI.BeginChangeCheck();
                    EditorGUILayout.PropertyField(m_EmptyGlyphPairAdjustmentRecord_prop);
                    if (EditorGUI.EndChangeCheck())
                    {
                        SetPropertyHolderGlyphIndexes();
                    }
                }
                EditorGUILayout.EndVertical();

                if (GUILayout.Button("Add New Glyph Adjustment Record"))
                {
                    SerializedProperty firstAdjustmentRecordProperty = m_EmptyGlyphPairAdjustmentRecord_prop.FindPropertyRelative("m_FirstAdjustmentRecord");
                    SerializedProperty secondAdjustmentRecordProperty = m_EmptyGlyphPairAdjustmentRecord_prop.FindPropertyRelative("m_SecondAdjustmentRecord");

                    uint firstGlyphIndex = (uint)firstAdjustmentRecordProperty.FindPropertyRelative("m_GlyphIndex").intValue;
                    uint secondGlyphIndex = (uint)secondAdjustmentRecordProperty.FindPropertyRelative("m_GlyphIndex").intValue;

                    GlyphValueRecord firstValueRecord = GetValueRecord(firstAdjustmentRecordProperty.FindPropertyRelative("m_GlyphValueRecord"));
                    GlyphValueRecord secondValueRecord = GetValueRecord(secondAdjustmentRecordProperty.FindPropertyRelative("m_GlyphValueRecord"));

                    errorCode = -1;
                    uint pairKey = secondGlyphIndex << 16 | firstGlyphIndex;
                    if (m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookup.ContainsKey(pairKey) == false)
                    {
                        GlyphPairAdjustmentRecord adjustmentRecord = new GlyphPairAdjustmentRecord(new GlyphAdjustmentRecord(firstGlyphIndex, firstValueRecord), new GlyphAdjustmentRecord(secondGlyphIndex, secondValueRecord));
                        m_FontFeatureTable.m_GlyphPairAdjustmentRecords.Add(adjustmentRecord);
                        m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookup.Add(pairKey, adjustmentRecord);
                        errorCode = 0;
                    }

                    // Add glyphs and characters
                    TMP_Character character;

                    uint firstCharacter = m_SerializedPropertyHolder.firstCharacter;
                    if (!m_fontAsset.characterLookupTable.ContainsKey(firstCharacter))
                        m_fontAsset.TryAddCharacterInternal(firstCharacter, out character);

                    uint secondCharacter = m_SerializedPropertyHolder.secondCharacter;
                    if (!m_fontAsset.characterLookupTable.ContainsKey(secondCharacter))
                        m_fontAsset.TryAddCharacterInternal(secondCharacter, out character);

                    // Sort Kerning Pairs & Reload Font Asset if new kerning pair was added.
                    if (errorCode != -1)
                    {
                        m_FontFeatureTable.SortGlyphPairAdjustmentRecords();
                        serializedObject.ApplyModifiedProperties();
                        isAssetDirty = true;
                        m_isSearchDirty = true;
                    }
                    else
                    {
                        timeStamp = System.DateTime.Now.AddSeconds(5);
                    }

                    // Clear Add Kerning Pair Panel
                    // TODO
                }

                if (errorCode == -1)
                {
                    GUILayout.BeginHorizontal();
                    GUILayout.FlexibleSpace();
                    GUILayout.Label("Kerning Pair already <color=#ffff00>exists!</color>", TMP_UIStyleManager.label);
                    GUILayout.FlexibleSpace();
                    GUILayout.EndHorizontal();

                    if (System.DateTime.Now > timeStamp)
                        errorCode = 0;
                }
            }
            #endregion

            // MARK TO BASE Font Feature Table
            #region MARK TO BASE
            EditorGUIUtility.labelWidth = labelWidth;
            EditorGUIUtility.fieldWidth = fieldWidth;
            EditorGUI.indentLevel = 0;
            rect = EditorGUILayout.GetControlRect(false, 24);

            int markToBaseAdjustmentRecordCount = m_fontAsset.fontFeatureTable.MarkToBaseAdjustmentRecords.Count;

            if (GUI.Button(rect, new GUIContent("<b>Mark To Base Adjustment Table</b>   [" + markToBaseAdjustmentRecordCount + "]" + (rect.width > 340 ? " Records" : ""), "List of Mark to Base adjustment records."), TMP_UIStyleManager.sectionHeader))
                UI_PanelState.MarkToBaseTablePanel = !UI_PanelState.MarkToBaseTablePanel;

            GUI.Label(rect, (UI_PanelState.MarkToBaseTablePanel ? "" : s_UiStateLabel[1]), TMP_UIStyleManager.rightLabel);

            if (UI_PanelState.MarkToBaseTablePanel)
            {
                int arraySize = m_MarkToBaseAdjustmentRecords_prop.arraySize;
                int itemsPerPage = 20;

                // Display Mark Adjust Records Management Tools
                EditorGUILayout.BeginVertical(EditorStyles.helpBox);
                {
                    // Search Bar implementation
                    #region DISPLAY SEARCH BAR
                    EditorGUILayout.BeginHorizontal();
                    {
                        EditorGUIUtility.labelWidth = 150f;
                        EditorGUI.BeginChangeCheck();
                        string searchPattern = EditorGUILayout.TextField("Mark to Base Search", m_MarkToBaseTableSearchPattern, "SearchTextField");
                        if (EditorGUI.EndChangeCheck() || m_isSearchDirty)
                        {
                            if (string.IsNullOrEmpty(searchPattern) == false)
                            {
                                m_MarkToBaseTableSearchPattern = searchPattern;

                                // Search Glyph Table for potential matches
                                SearchMarkToBaseTable(m_MarkToBaseTableSearchPattern, ref m_MarkToBaseTableSearchList);
                            }
                            else
                                m_MarkToBaseTableSearchPattern = null;

                            m_isSearchDirty = false;
                        }

                        string styleName = string.IsNullOrEmpty(m_MarkToBaseTableSearchPattern) ? "SearchCancelButtonEmpty" : "SearchCancelButton";
                        if (GUILayout.Button(GUIContent.none, styleName))
                        {
                            GUIUtility.keyboardControl = 0;
                            m_MarkToBaseTableSearchPattern = string.Empty;
                        }
                    }
                    EditorGUILayout.EndHorizontal();
                    #endregion

                    // Display Page Navigation
                    if (!string.IsNullOrEmpty(m_MarkToBaseTableSearchPattern))
                        arraySize = m_MarkToBaseTableSearchList.Count;

                    DisplayPageNavigation(ref m_CurrentMarkToBasePage, arraySize, itemsPerPage);
                }
                EditorGUILayout.EndVertical();

                if (arraySize > 0)
                {
                    // Display each GlyphInfo entry using the GlyphInfo property drawer.
                    for (int i = itemsPerPage * m_CurrentMarkToBasePage; i < arraySize && i < itemsPerPage * (m_CurrentMarkToBasePage + 1); i++)
                    {
                        // Define the start of the selection region of the element.
                        Rect elementStartRegion = GUILayoutUtility.GetRect(0f, 0f, GUILayout.ExpandWidth(true));

                        int elementIndex = i;
                        if (!string.IsNullOrEmpty(m_MarkToBaseTableSearchPattern))
                            elementIndex = m_MarkToBaseTableSearchList[i];

                        SerializedProperty markToBasePropertyRecord = m_MarkToBaseAdjustmentRecords_prop.GetArrayElementAtIndex(elementIndex);

                        EditorGUILayout.BeginVertical(EditorStyles.helpBox);

                        using (new EditorGUI.DisabledScope(i != m_SelectedMarkToBaseRecord))
                        {
                            EditorGUI.BeginChangeCheck();
                            EditorGUILayout.PropertyField(markToBasePropertyRecord, new GUIContent("Selectable"));

                            if (EditorGUI.EndChangeCheck())
                            {
                                UpdateMarkToBaseAdjustmentRecordLookup(markToBasePropertyRecord);
                            }
                        }

                        EditorGUILayout.EndVertical();

                        // Define the end of the selection region of the element.
                        Rect elementEndRegion = GUILayoutUtility.GetRect(0f, 0f, GUILayout.ExpandWidth(true));

                        // Check for Item selection
                        Rect selectionArea = new Rect(elementStartRegion.x, elementStartRegion.y, elementEndRegion.width, elementEndRegion.y - elementStartRegion.y);
                        if (DoSelectionCheck(selectionArea))
                        {
                            if (m_SelectedMarkToBaseRecord == i)
                            {
                                m_SelectedMarkToBaseRecord = -1;
                            }
                            else
                            {
                                m_SelectedMarkToBaseRecord = i;
                                GUIUtility.keyboardControl = 0;
                            }
                        }

                        // Draw Selection Highlight and Kerning Pair Options
                        if (m_SelectedMarkToBaseRecord == i)
                        {
                            // Reset other selections
                            ResetSelections(RecordSelectionType.MarkToBaseRecord);

                            TMP_EditorUtility.DrawBox(selectionArea, 2f, new Color32(40, 192, 255, 255));

                            // Draw Glyph management options
                            Rect controlRect = EditorGUILayout.GetControlRect(true, EditorGUIUtility.singleLineHeight * 1f);
                            float optionAreaWidth = controlRect.width;
                            float btnWidth = optionAreaWidth / 4;

                            Rect position = new Rect(controlRect.x + controlRect.width - btnWidth, controlRect.y, btnWidth, controlRect.height);

                            // Remove Mark to Base Record
                            GUI.enabled = true;
                            if (GUI.Button(position, "Remove"))
                            {
                                GUIUtility.keyboardControl = 0;

                                RemoveRecord(m_MarkToBaseAdjustmentRecords_prop, i);

                                isAssetDirty = true;
                                m_SelectedMarkToBaseRecord = -1;
                                m_isSearchDirty = true;
                                break;
                            }
                        }
                    }
                }

                DisplayAddRemoveButtons(m_MarkToBaseAdjustmentRecords_prop, m_SelectedMarkToBaseRecord, markToBaseAdjustmentRecordCount);

                DisplayPageNavigation(ref m_CurrentMarkToBasePage, arraySize, itemsPerPage);

                GUILayout.Space(5);
            }
            #endregion

            // MARK TO MARK Font Feature Table
            #region MARK TO MARK
            EditorGUIUtility.labelWidth = labelWidth;
            EditorGUIUtility.fieldWidth = fieldWidth;
            EditorGUI.indentLevel = 0;
            rect = EditorGUILayout.GetControlRect(false, 24);

            int markToMarkAdjustmentRecordCount = m_fontAsset.fontFeatureTable.MarkToMarkAdjustmentRecords.Count;

            if (GUI.Button(rect, new GUIContent("<b>Mark To Mark Adjustment Table</b>   [" + markToMarkAdjustmentRecordCount + "]" + (rect.width > 340 ? " Records" : ""), "List of Mark to Mark adjustment records."), TMP_UIStyleManager.sectionHeader))
                UI_PanelState.MarkToMarkTablePanel = !UI_PanelState.MarkToMarkTablePanel;

            GUI.Label(rect, (UI_PanelState.MarkToMarkTablePanel ? "" : s_UiStateLabel[1]), TMP_UIStyleManager.rightLabel);

            if (UI_PanelState.MarkToMarkTablePanel)
            {
                int arraySize = m_MarkToMarkAdjustmentRecords_prop.arraySize;
                int itemsPerPage = 20;

                // Display Kerning Pair Management Tools
                EditorGUILayout.BeginVertical(EditorStyles.helpBox);
                {
                    // Search Bar implementation
                    #region DISPLAY SEARCH BAR
                    EditorGUILayout.BeginHorizontal();
                    {
                        EditorGUIUtility.labelWidth = 150f;
                        EditorGUI.BeginChangeCheck();
                        string searchPattern = EditorGUILayout.TextField("Mark to Mark Search", m_MarkToMarkTableSearchPattern, "SearchTextField");
                        if (EditorGUI.EndChangeCheck() || m_isSearchDirty)
                        {
                            if (string.IsNullOrEmpty(searchPattern) == false)
                            {
                                m_MarkToMarkTableSearchPattern = searchPattern;

                                // Search Glyph Table for potential matches
                                SearchMarkToMarkTable(m_MarkToMarkTableSearchPattern, ref m_MarkToMarkTableSearchList);
                            }
                            else
                                m_MarkToMarkTableSearchPattern = null;

                            m_isSearchDirty = false;
                        }

                        string styleName = string.IsNullOrEmpty(m_MarkToMarkTableSearchPattern) ? "SearchCancelButtonEmpty" : "SearchCancelButton";
                        if (GUILayout.Button(GUIContent.none, styleName))
                        {
                            GUIUtility.keyboardControl = 0;
                            m_MarkToMarkTableSearchPattern = string.Empty;
                        }
                    }
                    EditorGUILayout.EndHorizontal();
                    #endregion

                    // Display Page Navigation
                    if (!string.IsNullOrEmpty(m_MarkToMarkTableSearchPattern))
                        arraySize = m_MarkToMarkTableSearchList.Count;

                    DisplayPageNavigation(ref m_CurrentMarkToMarkPage, arraySize, itemsPerPage);
                }
                EditorGUILayout.EndVertical();

                if (arraySize > 0)
                {
                    // Display each GlyphInfo entry using the GlyphInfo property drawer.
                    for (int i = itemsPerPage * m_CurrentMarkToMarkPage; i < arraySize && i < itemsPerPage * (m_CurrentMarkToMarkPage + 1); i++)
                    {
                        // Define the start of the selection region of the element.
                        Rect elementStartRegion = GUILayoutUtility.GetRect(0f, 0f, GUILayout.ExpandWidth(true));

                        int elementIndex = i;
                        if (!string.IsNullOrEmpty(m_MarkToMarkTableSearchPattern))
                            elementIndex = m_MarkToMarkTableSearchList[i];

                        SerializedProperty markToMarkPropertyRecord = m_MarkToMarkAdjustmentRecords_prop.GetArrayElementAtIndex(elementIndex);

                        EditorGUILayout.BeginVertical(EditorStyles.helpBox);

                        using (new EditorGUI.DisabledScope(i != m_SelectedMarkToMarkRecord))
                        {
                            EditorGUI.BeginChangeCheck();
                            EditorGUILayout.PropertyField(markToMarkPropertyRecord, new GUIContent("Selectable"));

                            if (EditorGUI.EndChangeCheck())
                            {
                                UpdateMarkToMarkAdjustmentRecordLookup(markToMarkPropertyRecord);
                            }
                        }

                        EditorGUILayout.EndVertical();

                        // Define the end of the selection region of the element.
                        Rect elementEndRegion = GUILayoutUtility.GetRect(0f, 0f, GUILayout.ExpandWidth(true));

                        // Check for Item selection
                        Rect selectionArea = new Rect(elementStartRegion.x, elementStartRegion.y, elementEndRegion.width, elementEndRegion.y - elementStartRegion.y);
                        if (DoSelectionCheck(selectionArea))
                        {
                            if (m_SelectedMarkToMarkRecord == i)
                            {
                                m_SelectedMarkToMarkRecord = -1;
                            }
                            else
                            {
                                m_SelectedMarkToMarkRecord = i;
                                GUIUtility.keyboardControl = 0;
                            }
                        }

                        // Draw Selection Highlight and Kerning Pair Options
                        if (m_SelectedMarkToMarkRecord == i)
                        {
                            // Reset other selections
                            ResetSelections(RecordSelectionType.MarkToMarkRecord);

                            TMP_EditorUtility.DrawBox(selectionArea, 2f, new Color32(40, 192, 255, 255));

                            // Draw Glyph management options
                            Rect controlRect = EditorGUILayout.GetControlRect(true, EditorGUIUtility.singleLineHeight * 1f);
                            float optionAreaWidth = controlRect.width;
                            float btnWidth = optionAreaWidth / 4;

                            Rect position = new Rect(controlRect.x + controlRect.width - btnWidth, controlRect.y, btnWidth, controlRect.height);

                            // Remove Mark to Base Record
                            GUI.enabled = true;
                            if (GUI.Button(position, "Remove"))
                            {
                                GUIUtility.keyboardControl = 0;

                                RemoveRecord(m_MarkToMarkAdjustmentRecords_prop, i);

                                isAssetDirty = true;
                                m_SelectedMarkToMarkRecord = -1;
                                m_isSearchDirty = true;
                                break;
                            }
                        }
                    }
                }

                DisplayAddRemoveButtons(m_MarkToMarkAdjustmentRecords_prop, m_SelectedMarkToMarkRecord, markToMarkAdjustmentRecordCount);

                DisplayPageNavigation(ref m_CurrentMarkToMarkPage, arraySize, itemsPerPage);

                GUILayout.Space(5);
            }
            #endregion

            if (serializedObject.ApplyModifiedProperties() || evt_cmd == k_UndoRedo || isAssetDirty || m_IsFallbackGlyphCacheDirty)
            {
                // Delay callback until user has decided to Apply or Revert the changes.
                if (m_DisplayDestructiveChangeWarning == false)
                {
                    TMP_ResourceManager.RebuildFontAssetCache();
                    TMPro_EventManager.ON_FONT_PROPERTY_CHANGED(true, m_fontAsset);
                    m_IsFallbackGlyphCacheDirty = false;
                }

                if (m_fontAsset.IsFontAssetLookupTablesDirty || evt_cmd == k_UndoRedo)
                    m_fontAsset.ReadFontAssetDefinition();

                isAssetDirty = false;
                EditorUtility.SetDirty(target);
            }


            // Clear selection if mouse event was not consumed.
            GUI.enabled = true;
            if (currentEvent.type == EventType.MouseDown && currentEvent.button == 0)
            {
                m_SelectedAdjustmentRecord = -1;
                m_SelectedMarkToBaseRecord = -1;
                m_SelectedMarkToMarkRecord = -1;
            }
        }

        /// <summary>
        /// Overrided method from the Editor class.
        /// </summary>
        /// <returns></returns>
        public override bool HasPreviewGUI()
        {
            return true;
        }

        /// <summary>
        /// Overrided method to implement custom preview inspector.
        /// </summary>
        /// <param name="rect"></param>
        /// <param name="background"></param>
        public override void OnPreviewGUI(Rect rect, GUIStyle background)
        {
            if (m_SelectedMarkToBaseRecord != -1)
                DrawMarkToBasePreview(m_SelectedMarkToBaseRecord, rect);

            if (m_SelectedMarkToMarkRecord != -1)
                DrawMarkToMarkPreview(m_SelectedMarkToMarkRecord, rect);

        }

        void ResetSelections(RecordSelectionType type)
        {
            switch (type)
            {
             case RecordSelectionType.CharacterRecord:
                 m_SelectedGlyphRecord = -1;
                 m_SelectedLigatureRecord = -1;
                 m_SelectedAdjustmentRecord = -1;
                 m_SelectedMarkToBaseRecord = -1;
                 m_SelectedMarkToMarkRecord = -1;
                 break;
             case RecordSelectionType.GlyphRecord:
                 m_SelectedCharacterRecord = -1;
                 m_SelectedLigatureRecord = -1;
                 m_SelectedAdjustmentRecord = -1;
                 m_SelectedMarkToBaseRecord = -1;
                 m_SelectedMarkToMarkRecord = -1;
                 break;
             case RecordSelectionType.LigatureSubstitutionRecord:
                 m_SelectedCharacterRecord = -1;
                 m_SelectedGlyphRecord = -1;
                 m_SelectedAdjustmentRecord = -1;
                 m_SelectedMarkToBaseRecord = -1;
                 m_SelectedMarkToMarkRecord = -1;
                 break;
             case RecordSelectionType.AdjustmentPairRecord:
                 m_SelectedCharacterRecord = -1;
                 m_SelectedGlyphRecord = -1;
                 m_SelectedLigatureRecord = -1;
                 m_SelectedMarkToBaseRecord = -1;
                 m_SelectedMarkToMarkRecord = -1;
                 break;
             case RecordSelectionType.MarkToBaseRecord:
                 m_SelectedCharacterRecord = -1;
                 m_SelectedGlyphRecord = -1;
                 m_SelectedLigatureRecord = -1;
                 m_SelectedAdjustmentRecord = -1;
                 m_SelectedMarkToMarkRecord = -1;
                 break;
             case RecordSelectionType.MarkToMarkRecord:
                 m_SelectedCharacterRecord = -1;
                 m_SelectedGlyphRecord = -1;
                 m_SelectedLigatureRecord = -1;
                 m_SelectedAdjustmentRecord = -1;
                 m_SelectedMarkToBaseRecord = -1;
                 break;
            }
        }


        string[] GetFontFaces()
        {
            return GetFontFaces(m_FontFaceIndex_prop.intValue);
        }

        string[] GetFontFaces(int faceIndex)
        {
#if UNITY_2023_3_OR_NEWER
            if (LoadFontFace((int)m_SamplingPointSize_prop.floatValue, faceIndex) == FontEngineError.Success)
#else
            if (LoadFontFace(m_SamplingPointSize_prop.intValue, faceIndex) == FontEngineError.Success)
#endif
                return FontEngine.GetFontFaces();

            return k_InvalidFontFaces;
        }

        FontEngineError LoadFontFace(int pointSize, int faceIndex)
        {
            if (m_fontAsset.SourceFont_EditorRef != null)
            {
                if (FontEngine.LoadFontFace(m_fontAsset.SourceFont_EditorRef, pointSize, faceIndex) == FontEngineError.Success)
                    return FontEngineError.Success;
            }

            // Requires Unity 2018.4.35f1
            return FontEngine.LoadFontFace(m_fontAsset.faceInfo.familyName, m_fontAsset.faceInfo.styleName, pointSize);
        }


        void CleanFallbackFontAssetTable()
        {
            SerializedProperty m_FallbackFontAsseTable = serializedObject.FindProperty("m_FallbackFontAssetTable");

            bool isListDirty = false;

            int elementCount = m_FallbackFontAsseTable.arraySize;

            for (int i = 0; i < elementCount; i++)
            {
                SerializedProperty element = m_FallbackFontAsseTable.GetArrayElementAtIndex(i);
                if (element.objectReferenceValue == null)
                {
                    m_FallbackFontAsseTable.DeleteArrayElementAtIndex(i);
                    elementCount -= 1;
                    i -= 1;

                    isListDirty = true;
                }
            }

            if (isListDirty)
            {
                serializedObject.ApplyModifiedProperties();
                serializedObject.Update();
            }
        }


        void SavedGenerationSettings()
        {
            m_GenerationSettings.faceIndex = m_FontFaceIndex_prop.intValue;
            m_GenerationSettings.glyphRenderMode = (GlyphRenderMode)m_AtlasRenderMode_prop.intValue;
#if UNITY_2023_3_OR_NEWER
            m_GenerationSettings.pointSize       = (int)m_SamplingPointSize_prop.floatValue;
#else
            m_GenerationSettings.pointSize       = m_SamplingPointSize_prop.intValue;
#endif
            m_GenerationSettings.padding         = m_AtlasPadding_prop.intValue;
            m_GenerationSettings.atlasWidth      = m_AtlasWidth_prop.intValue;
            m_GenerationSettings.atlasHeight     = m_AtlasHeight_prop.intValue;
        }

        void RestoreGenerationSettings()
        {
            m_fontAsset.SourceFont_EditorRef = m_GenerationSettings.sourceFont;
            m_FontFaceIndex_prop.intValue = m_GenerationSettings.faceIndex;
#if UNITY_2023_3_OR_NEWER
            m_SamplingPointSize_prop.floatValue = m_GenerationSettings.pointSize;
#else
            m_SamplingPointSize_prop.intValue = m_GenerationSettings.pointSize;
#endif
            m_FontFaces = GetFontFaces();

            m_AtlasRenderMode_prop.intValue = (int)m_GenerationSettings.glyphRenderMode;
            m_AtlasPadding_prop.intValue = m_GenerationSettings.padding;
            m_AtlasWidth_prop.intValue = m_GenerationSettings.atlasWidth;
            m_AtlasHeight_prop.intValue = m_GenerationSettings.atlasHeight;
        }


        void UpdateFontAssetCreationSettings()
        {
            m_fontAsset.m_CreationSettings.faceIndex = m_FontFaceIndex_prop.intValue;
#if UNITY_2023_3_OR_NEWER
            m_fontAsset.m_CreationSettings.pointSize = (int)m_SamplingPointSize_prop.floatValue;
#else
            m_fontAsset.m_CreationSettings.pointSize = m_SamplingPointSize_prop.intValue;
#endif
            m_fontAsset.m_CreationSettings.renderMode = m_AtlasRenderMode_prop.intValue;
            m_fontAsset.m_CreationSettings.padding = m_AtlasPadding_prop.intValue;
            m_fontAsset.m_CreationSettings.atlasWidth = m_AtlasWidth_prop.intValue;
            m_fontAsset.m_CreationSettings.atlasHeight = m_AtlasHeight_prop.intValue;
        }


        void UpdateCharacterData(SerializedProperty property, int index)
        {
            TMP_Character character = m_fontAsset.characterTable[index];

            character.unicode = (uint)property.FindPropertyRelative("m_Unicode").intValue;
            character.scale = property.FindPropertyRelative("m_Scale").floatValue;

            SerializedProperty glyphProperty = property.FindPropertyRelative("m_Glyph");
            character.glyph.index = (uint)glyphProperty.FindPropertyRelative("m_Index").intValue;

            SerializedProperty glyphRectProperty = glyphProperty.FindPropertyRelative("m_GlyphRect");
            character.glyph.glyphRect = new GlyphRect(glyphRectProperty.FindPropertyRelative("m_X").intValue, glyphRectProperty.FindPropertyRelative("m_Y").intValue, glyphRectProperty.FindPropertyRelative("m_Width").intValue, glyphRectProperty.FindPropertyRelative("m_Height").intValue);

            SerializedProperty glyphMetricsProperty = glyphProperty.FindPropertyRelative("m_Metrics");
            character.glyph.metrics = new GlyphMetrics(glyphMetricsProperty.FindPropertyRelative("m_Width").floatValue, glyphMetricsProperty.FindPropertyRelative("m_Height").floatValue, glyphMetricsProperty.FindPropertyRelative("m_HorizontalBearingX").floatValue, glyphMetricsProperty.FindPropertyRelative("m_HorizontalBearingY").floatValue, glyphMetricsProperty.FindPropertyRelative("m_HorizontalAdvance").floatValue);

            character.glyph.scale = glyphProperty.FindPropertyRelative("m_Scale").floatValue;

            character.glyph.atlasIndex = glyphProperty.FindPropertyRelative("m_AtlasIndex").intValue;
        }


        void UpdateGlyphData(SerializedProperty property, int index)
        {
            Glyph glyph = m_fontAsset.glyphTable[index];

            glyph.index = (uint)property.FindPropertyRelative("m_Index").intValue;

            SerializedProperty glyphRect = property.FindPropertyRelative("m_GlyphRect");
            glyph.glyphRect = new GlyphRect(glyphRect.FindPropertyRelative("m_X").intValue, glyphRect.FindPropertyRelative("m_Y").intValue, glyphRect.FindPropertyRelative("m_Width").intValue, glyphRect.FindPropertyRelative("m_Height").intValue);

            SerializedProperty glyphMetrics = property.FindPropertyRelative("m_Metrics");
            glyph.metrics = new GlyphMetrics(glyphMetrics.FindPropertyRelative("m_Width").floatValue, glyphMetrics.FindPropertyRelative("m_Height").floatValue, glyphMetrics.FindPropertyRelative("m_HorizontalBearingX").floatValue, glyphMetrics.FindPropertyRelative("m_HorizontalBearingY").floatValue, glyphMetrics.FindPropertyRelative("m_HorizontalAdvance").floatValue);

            glyph.scale = property.FindPropertyRelative("m_Scale").floatValue;
        }


        void DisplayAddRemoveButtons(SerializedProperty property, int selectedRecord, int recordCount)
        {
            Rect rect = EditorGUILayout.GetControlRect(false, 20);

            rect.width /= 6;
            // Add Style
            rect.x = rect.width * 4 + 17;
            if (GUI.Button(rect, "+"))
            {
                int index = selectedRecord == -1 ? 0 : selectedRecord;

                if (index > recordCount)
                    index = recordCount;

                // Copy selected element
                property.InsertArrayElementAtIndex(index);

                // Select newly inserted element
                selectedRecord = index + 1;

                serializedObject.ApplyModifiedProperties();

                m_fontAsset.ReadFontAssetDefinition();

            }

            // Delete style
            rect.x += rect.width;
            if (selectedRecord == -1 || selectedRecord >= recordCount) GUI.enabled = false;
            if (GUI.Button(rect, "-"))
            {
                int index = selectedRecord == -1 ? 0 : selectedRecord;

                property.DeleteArrayElementAtIndex(index);

                selectedRecord = -1;
                serializedObject.ApplyModifiedProperties();

                m_fontAsset.ReadFontAssetDefinition();
                return;
            }

            GUI.enabled = true;
        }


        void DisplayPageNavigation(ref int currentPage, int arraySize, int itemsPerPage)
        {
            Rect pagePos = EditorGUILayout.GetControlRect(false, 20);
            pagePos.width /= 3;

            int shiftMultiplier = Event.current.shift ? 10 : 1; // Page + Shift goes 10 page forward

            // Previous Page
            GUI.enabled = currentPage > 0;

            if (GUI.Button(pagePos, "Previous Page"))
                currentPage -= 1 * shiftMultiplier;


            // Page Counter
            GUI.enabled = true;
            pagePos.x += pagePos.width;
            int totalPages = (int)(arraySize / (float)itemsPerPage + 0.999f);
            GUI.Label(pagePos, "Page " + (currentPage + 1) + " / " + totalPages, TMP_UIStyleManager.centeredLabel);

            // Next Page
            pagePos.x += pagePos.width;
            GUI.enabled = itemsPerPage * (currentPage + 1) < arraySize;

            if (GUI.Button(pagePos, "Next Page"))
                currentPage += 1 * shiftMultiplier;

            // Clamp page range
            currentPage = Mathf.Clamp(currentPage, 0, arraySize / itemsPerPage);

            GUI.enabled = true;
        }


        /// <summary>
        ///
        /// </summary>
        /// <param name="srcGlyphID"></param>
        /// <param name="dstGlyphID"></param>
        bool AddNewGlyph(int srcIndex, int dstGlyphID)
        {
            // Make sure Destination Glyph ID doesn't already contain a Glyph
            if (m_fontAsset.glyphLookupTable.ContainsKey((uint)dstGlyphID))
                return false;

            // Add new element to glyph list.
            m_GlyphTable_prop.arraySize += 1;

            // Get a reference to the source glyph.
            SerializedProperty sourceGlyph = m_GlyphTable_prop.GetArrayElementAtIndex(srcIndex);

            int dstIndex = m_GlyphTable_prop.arraySize - 1;

            // Get a reference to the target / destination glyph.
            SerializedProperty targetGlyph = m_GlyphTable_prop.GetArrayElementAtIndex(dstIndex);

            CopyGlyphSerializedProperty(sourceGlyph, ref targetGlyph);

            // Update the ID of the glyph
            targetGlyph.FindPropertyRelative("m_Index").intValue = dstGlyphID;

            serializedObject.ApplyModifiedProperties();

            m_fontAsset.SortGlyphTable();

            m_fontAsset.ReadFontAssetDefinition();

            return true;
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="glyphID"></param>
        void RemoveGlyphFromList(int index)
        {
            if (index > m_GlyphTable_prop.arraySize)
                return;

            int targetGlyphIndex = m_GlyphTable_prop.GetArrayElementAtIndex(index).FindPropertyRelative("m_Index").intValue;

            m_GlyphTable_prop.DeleteArrayElementAtIndex(index);

            // Remove all characters referencing this glyph.
            for (int i = 0; i < m_CharacterTable_prop.arraySize; i++)
            {
                int glyphIndex = m_CharacterTable_prop.GetArrayElementAtIndex(i).FindPropertyRelative("m_GlyphIndex").intValue;

                if (glyphIndex == targetGlyphIndex)
                {
                    // Remove character
                    m_CharacterTable_prop.DeleteArrayElementAtIndex(i);
                }
            }

            serializedObject.ApplyModifiedProperties();

            m_fontAsset.ReadFontAssetDefinition();
        }

        bool AddNewCharacter(int srcIndex, int dstGlyphID)
        {
            // Make sure Destination Glyph ID doesn't already contain a Glyph
            if (m_fontAsset.characterLookupTable.ContainsKey((uint)dstGlyphID))
                return false;

            // Add new element to glyph list.
            m_CharacterTable_prop.arraySize += 1;

            // Get a reference to the source glyph.
            SerializedProperty sourceCharacter = m_CharacterTable_prop.GetArrayElementAtIndex(srcIndex);

            int dstIndex = m_CharacterTable_prop.arraySize - 1;

            // Get a reference to the target / destination glyph.
            SerializedProperty targetCharacter = m_CharacterTable_prop.GetArrayElementAtIndex(dstIndex);

            CopyCharacterSerializedProperty(sourceCharacter, ref targetCharacter);

            // Update the ID of the glyph
            targetCharacter.FindPropertyRelative("m_Unicode").intValue = dstGlyphID;

            serializedObject.ApplyModifiedProperties();

            m_fontAsset.SortCharacterTable();

            m_fontAsset.ReadFontAssetDefinition();

            return true;
        }

        void RemoveCharacterFromList(int index)
        {
            if (index > m_CharacterTable_prop.arraySize)
                return;

            m_CharacterTable_prop.DeleteArrayElementAtIndex(index);

            serializedObject.ApplyModifiedProperties();

            m_fontAsset.ReadFontAssetDefinition();
        }

        void AddNewGlyphsFromProperty(SerializedProperty property)
        {
            if (m_GlyphsToAdd == null)
                m_GlyphsToAdd = new HashSet<uint>();
            else
                m_GlyphsToAdd.Clear();

            string propertyType = property.type;

            switch (propertyType)
            {
                case "LigatureSubstitutionRecord":
                    int componentCount = property.FindPropertyRelative("m_ComponentGlyphIDs").arraySize;
                    for (int i = 0; i < componentCount; i++)
                    {
                        uint glyphIndex = (uint)property.FindPropertyRelative("m_ComponentGlyphIDs").GetArrayElementAtIndex(i).intValue;
                        m_GlyphsToAdd.Add(glyphIndex);
                    }

                    m_GlyphsToAdd.Add((uint)property.FindPropertyRelative("m_LigatureGlyphID").intValue);

                    foreach (uint glyphIndex in m_GlyphsToAdd)
                    {
                        if (glyphIndex != 0)
                            m_fontAsset.AddGlyphInternal(glyphIndex);
                    }

                    break;
            }

        }


        // Check if any of the Style elements were clicked on.
        private bool DoSelectionCheck(Rect selectionArea)
        {
            Event currentEvent = Event.current;

            switch (currentEvent.type)
            {
                case EventType.MouseDown:
                    if (selectionArea.Contains(currentEvent.mousePosition) && currentEvent.button == 0)
                    {
                        currentEvent.Use();
                        return true;
                    }

                    break;
            }

            return false;
        }

        private void UpdateLigatureSubstitutionRecordLookup(SerializedProperty property)
        {
            serializedObject.ApplyModifiedProperties();
            AddNewGlyphsFromProperty(property);
            m_fontAsset.InitializeLigatureSubstitutionLookupDictionary();
            isAssetDirty = true;
        }

        void SetPropertyHolderGlyphIndexes()
        {
            uint firstCharacterUnicode = (uint)m_FirstCharacterUnicode_prop.intValue;
            if (firstCharacterUnicode != 0)
            {
                uint glyphIndex = m_fontAsset.GetGlyphIndex(firstCharacterUnicode);
                if (glyphIndex != 0)
                    m_EmptyGlyphPairAdjustmentRecord_prop.FindPropertyRelative("m_FirstAdjustmentRecord").FindPropertyRelative("m_GlyphIndex").intValue = (int)glyphIndex;
            }

            uint secondCharacterUnicode = (uint)m_SecondCharacterUnicode_prop.intValue;
            if (secondCharacterUnicode != 0)
            {
                uint glyphIndex = m_fontAsset.GetGlyphIndex(secondCharacterUnicode);
                if (glyphIndex != 0)
                    m_EmptyGlyphPairAdjustmentRecord_prop.FindPropertyRelative("m_SecondAdjustmentRecord").FindPropertyRelative("m_GlyphIndex").intValue = (int)glyphIndex;
            }
        }

        private void UpdatePairAdjustmentRecordLookup(SerializedProperty property)
        {
            GlyphPairAdjustmentRecord pairAdjustmentRecord = GetGlyphPairAdjustmentRecord(property);

            uint firstGlyphIndex = pairAdjustmentRecord.firstAdjustmentRecord.glyphIndex;
            uint secondGlyphIndex = pairAdjustmentRecord.secondAdjustmentRecord.glyphIndex;

            uint key = secondGlyphIndex << 16 | firstGlyphIndex;

            // Lookup dictionary entry and update it
            if (m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookup.ContainsKey(key))
                m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookup[key] = pairAdjustmentRecord;
        }

        GlyphPairAdjustmentRecord GetGlyphPairAdjustmentRecord(SerializedProperty property)
        {
            GlyphPairAdjustmentRecord pairAdjustmentRecord = new GlyphPairAdjustmentRecord();

            SerializedProperty firstAdjustmentRecordProperty = property.FindPropertyRelative("m_FirstAdjustmentRecord");
            uint firstGlyphIndex = (uint)firstAdjustmentRecordProperty.FindPropertyRelative("m_GlyphIndex").intValue;
            GlyphValueRecord firstValueRecord = GetValueRecord(firstAdjustmentRecordProperty.FindPropertyRelative("m_GlyphValueRecord"));

            pairAdjustmentRecord.firstAdjustmentRecord = new GlyphAdjustmentRecord(firstGlyphIndex, firstValueRecord);

            SerializedProperty secondAdjustmentRecordProperty = property.FindPropertyRelative("m_SecondAdjustmentRecord");
            uint secondGlyphIndex = (uint)secondAdjustmentRecordProperty.FindPropertyRelative("m_GlyphIndex").intValue;
            GlyphValueRecord secondValueRecord = GetValueRecord(secondAdjustmentRecordProperty.FindPropertyRelative("m_GlyphValueRecord"));

            pairAdjustmentRecord.secondAdjustmentRecord = new GlyphAdjustmentRecord(secondGlyphIndex, secondValueRecord);

            // TODO : Need to revise how Everything is handled in the event more enum values are added.
            int flagValue = property.FindPropertyRelative("m_FeatureLookupFlags").intValue;
            //pairAdjustmentRecord.featureLookupFlags = flagValue == -1 ? FontFeatureLookupFlags.IgnoreLigatures | FontFeatureLookupFlags.IgnoreSpacingAdjustments : (FontFeatureLookupFlags) flagValue;

            return pairAdjustmentRecord;
        }

        void SwapCharacterElements(SerializedProperty property, int selectedIndex, int newIndex)
        {
            property.MoveArrayElement(selectedIndex, newIndex);
        }

        void RemoveRecord(SerializedProperty property, int index)
        {
            if (index > property.arraySize)
                return;

            property.DeleteArrayElementAtIndex(index);

            serializedObject.ApplyModifiedProperties();

            m_fontAsset.ReadFontAssetDefinition();
        }

        GlyphValueRecord GetValueRecord(SerializedProperty property)
        {
            GlyphValueRecord record = new GlyphValueRecord();
            record.xPlacement = property.FindPropertyRelative("m_XPlacement").floatValue;
            record.yPlacement = property.FindPropertyRelative("m_YPlacement").floatValue;
            record.xAdvance = property.FindPropertyRelative("m_XAdvance").floatValue;
            record.yAdvance = property.FindPropertyRelative("m_YAdvance").floatValue;

            return record;
        }

        private void UpdateMarkToBaseAdjustmentRecordLookup(SerializedProperty property)
        {
            MarkToBaseAdjustmentRecord adjustmentRecord = GetMarkToBaseAdjustmentRecord(property);

            uint firstGlyphIndex = adjustmentRecord.baseGlyphID;
            uint secondGlyphIndex = adjustmentRecord.markGlyphID;

            uint key = secondGlyphIndex << 16 | firstGlyphIndex;

            // Lookup dictionary entry and update it
            if (m_FontFeatureTable.m_MarkToBaseAdjustmentRecordLookup.ContainsKey(key))
                m_FontFeatureTable.m_MarkToBaseAdjustmentRecordLookup[key] = adjustmentRecord;
        }

        MarkToBaseAdjustmentRecord GetMarkToBaseAdjustmentRecord(SerializedProperty property)
        {
            MarkToBaseAdjustmentRecord adjustmentRecord = new MarkToBaseAdjustmentRecord();

            adjustmentRecord.baseGlyphID = (uint)property.FindPropertyRelative("m_BaseGlyphID").intValue;
            SerializedProperty baseAnchorPointProperty = property.FindPropertyRelative("m_BaseGlyphAnchorPoint");

            GlyphAnchorPoint baseAnchorPoint = new GlyphAnchorPoint();
            baseAnchorPoint.xCoordinate = baseAnchorPointProperty.FindPropertyRelative("m_XCoordinate").floatValue;
            baseAnchorPoint.yCoordinate = baseAnchorPointProperty.FindPropertyRelative("m_YCoordinate").floatValue;
            adjustmentRecord.baseGlyphAnchorPoint = baseAnchorPoint;

            adjustmentRecord.markGlyphID = (uint)property.FindPropertyRelative("m_MarkGlyphID").intValue;
            SerializedProperty markAdjustmentRecordProperty = property.FindPropertyRelative("m_MarkPositionAdjustment");

            MarkPositionAdjustment markAdjustmentRecord = new MarkPositionAdjustment();
            markAdjustmentRecord.xPositionAdjustment = markAdjustmentRecordProperty.FindPropertyRelative("m_XPositionAdjustment").floatValue;
            markAdjustmentRecord.yPositionAdjustment = markAdjustmentRecordProperty.FindPropertyRelative("m_YPositionAdjustment").floatValue;
            adjustmentRecord.markPositionAdjustment = markAdjustmentRecord;

            return adjustmentRecord;
        }

        private void UpdateMarkToMarkAdjustmentRecordLookup(SerializedProperty property)
        {
            MarkToMarkAdjustmentRecord adjustmentRecord = GetMarkToMarkAdjustmentRecord(property);

            uint firstGlyphIndex = adjustmentRecord.baseMarkGlyphID;
            uint secondGlyphIndex = adjustmentRecord.combiningMarkGlyphID;

            uint key = secondGlyphIndex << 16 | firstGlyphIndex;

            // Lookup dictionary entry and update it
            if (m_FontFeatureTable.m_MarkToMarkAdjustmentRecordLookup.ContainsKey(key))
                m_FontFeatureTable.m_MarkToMarkAdjustmentRecordLookup[key] = adjustmentRecord;
        }

        MarkToMarkAdjustmentRecord GetMarkToMarkAdjustmentRecord(SerializedProperty property)
        {
            MarkToMarkAdjustmentRecord adjustmentRecord = new MarkToMarkAdjustmentRecord();

            adjustmentRecord.baseMarkGlyphID = (uint)property.FindPropertyRelative("m_BaseMarkGlyphID").intValue;
            SerializedProperty baseAnchorPointProperty = property.FindPropertyRelative("m_BaseMarkGlyphAnchorPoint");

            GlyphAnchorPoint baseAnchorPoint = new GlyphAnchorPoint();
            baseAnchorPoint.xCoordinate = baseAnchorPointProperty.FindPropertyRelative("m_XCoordinate").floatValue;
            baseAnchorPoint.yCoordinate = baseAnchorPointProperty.FindPropertyRelative("m_YCoordinate").floatValue;
            adjustmentRecord.baseMarkGlyphAnchorPoint = baseAnchorPoint;

            adjustmentRecord.combiningMarkGlyphID = (uint)property.FindPropertyRelative("m_CombiningMarkGlyphID").intValue;
            SerializedProperty markAdjustmentRecordProperty = property.FindPropertyRelative("m_CombiningMarkPositionAdjustment");

            MarkPositionAdjustment markAdjustment = new MarkPositionAdjustment();
            markAdjustment.xPositionAdjustment = markAdjustmentRecordProperty.FindPropertyRelative("m_XPositionAdjustment").floatValue;
            markAdjustment.yPositionAdjustment = markAdjustmentRecordProperty.FindPropertyRelative("m_YPositionAdjustment").floatValue;
            adjustmentRecord.combiningMarkPositionAdjustment = markAdjustment;

            return adjustmentRecord;
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="srcGlyph"></param>
        /// <param name="dstGlyph"></param>
        void CopyGlyphSerializedProperty(SerializedProperty srcGlyph, ref SerializedProperty dstGlyph)
        {
            // TODO : Should make a generic function which copies each of the properties.
            dstGlyph.FindPropertyRelative("m_Index").intValue = srcGlyph.FindPropertyRelative("m_Index").intValue;

            // Glyph -> GlyphMetrics
            SerializedProperty srcGlyphMetrics = srcGlyph.FindPropertyRelative("m_Metrics");
            SerializedProperty dstGlyphMetrics = dstGlyph.FindPropertyRelative("m_Metrics");

            dstGlyphMetrics.FindPropertyRelative("m_Width").floatValue = srcGlyphMetrics.FindPropertyRelative("m_Width").floatValue;
            dstGlyphMetrics.FindPropertyRelative("m_Height").floatValue = srcGlyphMetrics.FindPropertyRelative("m_Height").floatValue;
            dstGlyphMetrics.FindPropertyRelative("m_HorizontalBearingX").floatValue = srcGlyphMetrics.FindPropertyRelative("m_HorizontalBearingX").floatValue;
            dstGlyphMetrics.FindPropertyRelative("m_HorizontalBearingY").floatValue = srcGlyphMetrics.FindPropertyRelative("m_HorizontalBearingY").floatValue;
            dstGlyphMetrics.FindPropertyRelative("m_HorizontalAdvance").floatValue = srcGlyphMetrics.FindPropertyRelative("m_HorizontalAdvance").floatValue;

            // Glyph -> GlyphRect
            SerializedProperty srcGlyphRect = srcGlyph.FindPropertyRelative("m_GlyphRect");
            SerializedProperty dstGlyphRect = dstGlyph.FindPropertyRelative("m_GlyphRect");

            dstGlyphRect.FindPropertyRelative("m_X").intValue = srcGlyphRect.FindPropertyRelative("m_X").intValue;
            dstGlyphRect.FindPropertyRelative("m_Y").intValue = srcGlyphRect.FindPropertyRelative("m_Y").intValue;
            dstGlyphRect.FindPropertyRelative("m_Width").intValue = srcGlyphRect.FindPropertyRelative("m_Width").intValue;
            dstGlyphRect.FindPropertyRelative("m_Height").intValue = srcGlyphRect.FindPropertyRelative("m_Height").intValue;

            dstGlyph.FindPropertyRelative("m_Scale").floatValue = srcGlyph.FindPropertyRelative("m_Scale").floatValue;
            dstGlyph.FindPropertyRelative("m_AtlasIndex").intValue = srcGlyph.FindPropertyRelative("m_AtlasIndex").intValue;
        }


        void CopyCharacterSerializedProperty(SerializedProperty source, ref SerializedProperty target)
        {
            // TODO : Should make a generic function which copies each of the properties.
            int unicode = source.FindPropertyRelative("m_Unicode").intValue;
            target.FindPropertyRelative("m_Unicode").intValue = unicode;

            int srcGlyphIndex = source.FindPropertyRelative("m_GlyphIndex").intValue;
            target.FindPropertyRelative("m_GlyphIndex").intValue = srcGlyphIndex;

            target.FindPropertyRelative("m_Scale").floatValue = source.FindPropertyRelative("m_Scale").floatValue;
        }


        /// <summary>
        ///
        /// </summary>
        /// <param name="searchPattern"></param>
        /// <returns></returns>
        void SearchGlyphTable(string searchPattern, ref List<int> searchResults)
        {
            if (searchResults == null) searchResults = new List<int>();

            searchResults.Clear();

            int arraySize = m_GlyphTable_prop.arraySize;

            for (int i = 0; i < arraySize; i++)
            {
                SerializedProperty sourceGlyph = m_GlyphTable_prop.GetArrayElementAtIndex(i);

                int id = sourceGlyph.FindPropertyRelative("m_Index").intValue;

                // Check for potential match against a character.
                //if (searchPattern.Length == 1 && id == searchPattern[0])
                //    searchResults.Add(i);

                // Check for potential match against decimal id
                if (id.ToString().Contains(searchPattern))
                    searchResults.Add(i);

                //if (id.ToString("x").Contains(searchPattern))
                //    searchResults.Add(i);

                //if (id.ToString("X").Contains(searchPattern))
                //    searchResults.Add(i);
            }
        }


        void SearchCharacterTable(string searchPattern, ref List<int> searchResults)
        {
            if (searchResults == null) searchResults = new List<int>();

            searchResults.Clear();

            int arraySize = m_CharacterTable_prop.arraySize;

            for (int i = 0; i < arraySize; i++)
            {
                SerializedProperty sourceCharacter = m_CharacterTable_prop.GetArrayElementAtIndex(i);

                int id = sourceCharacter.FindPropertyRelative("m_Unicode").intValue;

                // Check for potential match against a character.
                if (searchPattern.Length == 1 && id == searchPattern[0])
                    searchResults.Add(i);
                else if (id.ToString("x").Contains(searchPattern))
                    searchResults.Add(i);
                else if (id.ToString("X").Contains(searchPattern))
                    searchResults.Add(i);

                // Check for potential match against decimal id
                //if (id.ToString().Contains(searchPattern))
                //    searchResults.Add(i);
            }
        }

        void SearchLigatureTable(string searchPattern, ref List<int> searchResults)
        {
            if (searchResults == null) searchResults = new List<int>();

            searchResults.Clear();

            // Lookup glyph index of potential characters contained in the search pattern.
            uint firstGlyphIndex = 0;
            TMP_Character firstCharacterSearch;

            if (searchPattern.Length > 0 && m_fontAsset.characterLookupTable.TryGetValue(searchPattern[0], out firstCharacterSearch))
                firstGlyphIndex = firstCharacterSearch.glyphIndex;

            uint secondGlyphIndex = 0;
            TMP_Character secondCharacterSearch;

            if (searchPattern.Length > 1 && m_fontAsset.characterLookupTable.TryGetValue(searchPattern[1], out secondCharacterSearch))
                secondGlyphIndex = secondCharacterSearch.glyphIndex;

            int arraySize = m_MarkToBaseAdjustmentRecords_prop.arraySize;

            for (int i = 0; i < arraySize; i++)
            {
                SerializedProperty record = m_MarkToBaseAdjustmentRecords_prop.GetArrayElementAtIndex(i);

                int baseGlyphIndex = record.FindPropertyRelative("m_BaseGlyphID").intValue;
                int markGlyphIndex = record.FindPropertyRelative("m_MarkGlyphID").intValue;

                if (firstGlyphIndex == baseGlyphIndex && secondGlyphIndex == markGlyphIndex)
                    searchResults.Add(i);
                else if (searchPattern.Length == 1 && (firstGlyphIndex == baseGlyphIndex || firstGlyphIndex == markGlyphIndex))
                    searchResults.Add(i);
                else if (baseGlyphIndex.ToString().Contains(searchPattern))
                    searchResults.Add(i);
                else if (markGlyphIndex.ToString().Contains(searchPattern))
                    searchResults.Add(i);
            }
        }

        void SearchKerningTable(string searchPattern, ref List<int> searchResults)
        {
            if (searchResults == null) searchResults = new List<int>();

            searchResults.Clear();

            // Lookup glyph index of potential characters contained in the search pattern.
            uint firstGlyphIndex = 0;
            TMP_Character firstCharacterSearch;

            if (searchPattern.Length > 0 && m_fontAsset.characterLookupTable.TryGetValue(searchPattern[0], out firstCharacterSearch))
                firstGlyphIndex = firstCharacterSearch.glyphIndex;

            uint secondGlyphIndex = 0;
            TMP_Character secondCharacterSearch;

            if (searchPattern.Length > 1 && m_fontAsset.characterLookupTable.TryGetValue(searchPattern[1], out secondCharacterSearch))
                secondGlyphIndex = secondCharacterSearch.glyphIndex;

            int arraySize = m_GlyphPairAdjustmentRecords_prop.arraySize;

            for (int i = 0; i < arraySize; i++)
            {
                SerializedProperty record = m_GlyphPairAdjustmentRecords_prop.GetArrayElementAtIndex(i);

                SerializedProperty firstAdjustmentRecord = record.FindPropertyRelative("m_FirstAdjustmentRecord");
                SerializedProperty secondAdjustmentRecord = record.FindPropertyRelative("m_SecondAdjustmentRecord");

                int firstGlyph = firstAdjustmentRecord.FindPropertyRelative("m_GlyphIndex").intValue;
                int secondGlyph = secondAdjustmentRecord.FindPropertyRelative("m_GlyphIndex").intValue;

                if (firstGlyphIndex == firstGlyph && secondGlyphIndex == secondGlyph)
                    searchResults.Add(i);
                else if (searchPattern.Length == 1 && (firstGlyphIndex == firstGlyph || firstGlyphIndex == secondGlyph))
                    searchResults.Add(i);
                else if (firstGlyph.ToString().Contains(searchPattern))
                    searchResults.Add(i);
                else if (secondGlyph.ToString().Contains(searchPattern))
                    searchResults.Add(i);
            }
        }

        void SearchMarkToBaseTable(string searchPattern, ref List<int> searchResults)
        {
            if (searchResults == null) searchResults = new List<int>();

            searchResults.Clear();

            // Lookup glyph index of potential characters contained in the search pattern.
            uint firstGlyphIndex = 0;
            TMP_Character firstCharacterSearch;

            if (searchPattern.Length > 0 && m_fontAsset.characterLookupTable.TryGetValue(searchPattern[0], out firstCharacterSearch))
                firstGlyphIndex = firstCharacterSearch.glyphIndex;

            uint secondGlyphIndex = 0;
            TMP_Character secondCharacterSearch;

            if (searchPattern.Length > 1 && m_fontAsset.characterLookupTable.TryGetValue(searchPattern[1], out secondCharacterSearch))
                secondGlyphIndex = secondCharacterSearch.glyphIndex;

            int arraySize = m_MarkToBaseAdjustmentRecords_prop.arraySize;

            for (int i = 0; i < arraySize; i++)
            {
                SerializedProperty record = m_MarkToBaseAdjustmentRecords_prop.GetArrayElementAtIndex(i);

                int baseGlyphIndex = record.FindPropertyRelative("m_BaseGlyphID").intValue;
                int markGlyphIndex = record.FindPropertyRelative("m_MarkGlyphID").intValue;

                if (firstGlyphIndex == baseGlyphIndex && secondGlyphIndex == markGlyphIndex)
                    searchResults.Add(i);
                else if (searchPattern.Length == 1 && (firstGlyphIndex == baseGlyphIndex || firstGlyphIndex == markGlyphIndex))
                    searchResults.Add(i);
                else if (baseGlyphIndex.ToString().Contains(searchPattern))
                    searchResults.Add(i);
                else if (markGlyphIndex.ToString().Contains(searchPattern))
                    searchResults.Add(i);
            }
        }

        void SearchMarkToMarkTable(string searchPattern, ref List<int> searchResults)
        {
            if (searchResults == null) searchResults = new List<int>();

            searchResults.Clear();

            // Lookup glyph index of potential characters contained in the search pattern.
            uint firstGlyphIndex = 0;
            TMP_Character firstCharacterSearch;

            if (searchPattern.Length > 0 && m_fontAsset.characterLookupTable.TryGetValue(searchPattern[0], out firstCharacterSearch))
                firstGlyphIndex = firstCharacterSearch.glyphIndex;

            uint secondGlyphIndex = 0;
            TMP_Character secondCharacterSearch;

            if (searchPattern.Length > 1 && m_fontAsset.characterLookupTable.TryGetValue(searchPattern[1], out secondCharacterSearch))
                secondGlyphIndex = secondCharacterSearch.glyphIndex;

            int arraySize = m_MarkToMarkAdjustmentRecords_prop.arraySize;

            for (int i = 0; i < arraySize; i++)
            {
                SerializedProperty record = m_MarkToMarkAdjustmentRecords_prop.GetArrayElementAtIndex(i);

                int baseGlyphIndex = record.FindPropertyRelative("m_BaseMarkGlyphID").intValue;
                int markGlyphIndex = record.FindPropertyRelative("m_CombiningMarkGlyphID").intValue;

                if (firstGlyphIndex == baseGlyphIndex && secondGlyphIndex == markGlyphIndex)
                    searchResults.Add(i);
                else if (searchPattern.Length == 1 && (firstGlyphIndex == baseGlyphIndex || firstGlyphIndex == markGlyphIndex))
                    searchResults.Add(i);
                else if (baseGlyphIndex.ToString().Contains(searchPattern))
                    searchResults.Add(i);
                else if (markGlyphIndex.ToString().Contains(searchPattern))
                    searchResults.Add(i);
            }
        }

        void DrawMarkToBasePreview(int selectedRecord, Rect rect)
        {
            MarkToBaseAdjustmentRecord adjustmentRecord = m_fontAsset.fontFeatureTable.m_MarkToBaseAdjustmentRecords[selectedRecord];

            uint baseGlyphIndex = adjustmentRecord.baseGlyphID;
            uint markGlyphIndex = adjustmentRecord.markGlyphID;

            if (baseGlyphIndex == 0 || markGlyphIndex == 0)
                return;

            float lineHeight = m_fontAsset.faceInfo.ascentLine - m_fontAsset.faceInfo.descentLine;
            float scale = rect.width < rect.height ? rect.width / lineHeight : rect.height / lineHeight;
            scale *= 0.9f;

            Glyph baseGlyph;
            m_fontAsset.glyphLookupTable.TryGetValue(baseGlyphIndex, out baseGlyph);

            if (baseGlyph == null)
                return;

            Rect center = new Rect(rect.x + rect.width / 2, rect.y + rect.height / 2, rect.width, rect.height);

            Vector2 origin = new Vector2(center.x, center.y);
            origin.x = origin.x - (baseGlyph.metrics.horizontalBearingX + baseGlyph.metrics.width / 2) * scale;
            origin.y = origin.y + (baseGlyph.metrics.horizontalBearingY - baseGlyph.metrics.height / 2) * scale;

            // Draw Baseline
            DrawBaseline(origin, rect.width, Color.grey);

            // Draw Origin
            DrawAnchorPoint(origin, Color.yellow);

            Rect baseGlyphPosition = new Rect(origin.x + baseGlyph.metrics.horizontalBearingX * scale, origin.y - baseGlyph.metrics.horizontalBearingY * scale , rect.width, rect.height);

            DrawGlyph(baseGlyph, baseGlyphPosition, scale);

            Vector2 baseAnchorPosition = new Vector2(origin.x + adjustmentRecord.baseGlyphAnchorPoint.xCoordinate * scale, origin.y - adjustmentRecord.baseGlyphAnchorPoint.yCoordinate * scale);

            DrawAnchorPoint(baseAnchorPosition, Color.green);

            // Draw Mark
            if (m_fontAsset.glyphLookupTable.ContainsKey(markGlyphIndex))
            {
                Glyph markGlyph = m_fontAsset.glyphLookupTable[markGlyphIndex];

                Rect markGlyphPosition = new Rect(baseAnchorPosition.x + (markGlyph.metrics.horizontalBearingX - adjustmentRecord.markPositionAdjustment.xPositionAdjustment) * scale, baseAnchorPosition.y + (adjustmentRecord.markPositionAdjustment.yPositionAdjustment - markGlyph.metrics.horizontalBearingY) * scale, markGlyph.metrics.width, markGlyph.metrics.height);

                // Draw Mark Origin
                DrawGlyph(markGlyph, markGlyphPosition, scale);
            }
        }

        void DrawMarkToMarkPreview(int selectedRecord, Rect rect)
        {
            MarkToMarkAdjustmentRecord adjustmentRecord = m_fontAsset.fontFeatureTable.m_MarkToMarkAdjustmentRecords[selectedRecord];

            uint baseGlyphIndex = adjustmentRecord.baseMarkGlyphID;
            uint markGlyphIndex = adjustmentRecord.combiningMarkGlyphID;

            if (baseGlyphIndex == 0 || markGlyphIndex == 0)
                return;

            float lineHeight = m_fontAsset.faceInfo.ascentLine - m_fontAsset.faceInfo.descentLine;
            float scale = rect.width < rect.height ? rect.width / lineHeight : rect.height / lineHeight;
            scale *= 0.9f;

            Glyph baseGlyph;
            m_fontAsset.glyphLookupTable.TryGetValue(baseGlyphIndex, out baseGlyph);

            if (baseGlyph == null)
                return;

            Rect center = new Rect(rect.x + rect.width / 2, rect.y + rect.height / 2, rect.width, rect.height);

            Vector2 origin = new Vector2(center.x, center.y);
            origin.x = origin.x - (baseGlyph.metrics.horizontalBearingX + baseGlyph.metrics.width / 2) * scale;
            origin.y = origin.y + (baseGlyph.metrics.horizontalBearingY - baseGlyph.metrics.height / 2) * scale;

            // Draw Baseline
            DrawBaseline(origin, rect.width, Color.grey);

            // Draw Origin
            DrawAnchorPoint(origin, Color.yellow);

            Rect baseGlyphPosition = new Rect(origin.x + baseGlyph.metrics.horizontalBearingX * scale, origin.y - baseGlyph.metrics.horizontalBearingY * scale , rect.width, rect.height);

            DrawGlyph(baseGlyph, baseGlyphPosition, scale);

            Vector2 baseAnchorPosition = new Vector2(origin.x + adjustmentRecord.baseMarkGlyphAnchorPoint.xCoordinate * scale, origin.y - adjustmentRecord.baseMarkGlyphAnchorPoint.yCoordinate * scale);

            DrawAnchorPoint(baseAnchorPosition, Color.green);

            // Draw Mark Glyph
            if (m_fontAsset.glyphLookupTable.ContainsKey(markGlyphIndex))
            {
                Glyph markGlyph = m_fontAsset.glyphLookupTable[markGlyphIndex];

                Rect markGlyphPosition = new Rect(baseAnchorPosition.x + (markGlyph.metrics.horizontalBearingX - adjustmentRecord.combiningMarkPositionAdjustment.xPositionAdjustment) * scale, baseAnchorPosition.y + (adjustmentRecord.combiningMarkPositionAdjustment.yPositionAdjustment - markGlyph.metrics.horizontalBearingY) * scale, markGlyph.metrics.width, markGlyph.metrics.height);

                DrawGlyph(markGlyph, markGlyphPosition, scale);
            }
        }

        void DrawBaseline(Vector2 position, float width, Color color)
        {
            Handles.color = color;

            // Horizontal line
            Handles.DrawLine(new Vector2(0f, position.y), new Vector2(width, position.y));
        }

        void DrawAnchorPoint(Vector2 position, Color color)
        {
            Handles.color = color;

            // Horizontal line
            Handles.DrawLine(new Vector2(position.x - 25, position.y), new Vector2(position.x + 25, position.y));

            // Vertical line
            Handles.DrawLine(new Vector2(position.x, position.y - 25), new Vector2(position.x, position.y + 25));
        }

        void DrawGlyph(Glyph glyph, Rect position, float scale)
        {
            // Get the atlas index of the glyph and lookup its atlas texture
            int atlasIndex = glyph.atlasIndex;
            Texture2D atlasTexture = m_fontAsset.atlasTextures.Length > atlasIndex ? m_fontAsset.atlasTextures[atlasIndex] : null;

            if (atlasTexture == null)
                return;

            Material mat;
            if (((GlyphRasterModes)m_fontAsset.atlasRenderMode & GlyphRasterModes.RASTER_MODE_BITMAP) == GlyphRasterModes.RASTER_MODE_BITMAP)
            {
                #if TEXTCORE_FONT_ENGINE_1_5_OR_NEWER
                if (m_fontAsset.atlasRenderMode == GlyphRenderMode.COLOR || m_fontAsset.atlasRenderMode == GlyphRenderMode.COLOR_HINTED)
                    mat = internalRGBABitmapMaterial;
                else
                    mat = internalBitmapMaterial;
                #else
                mat = internalBitmapMaterial;
                #endif

                if (mat == null)
                    return;

                mat.mainTexture = atlasTexture;
            }
            else
            {
                mat = internalSDFMaterial;

                if (mat == null)
                    return;

                mat.mainTexture = atlasTexture;
                mat.SetFloat(ShaderUtilities.ID_GradientScale, m_fontAsset.atlasPadding + 1);
            }

            GlyphRect glyphRect = glyph.glyphRect;

            int padding = m_fontAsset.atlasPadding;

            int glyphOriginX = glyphRect.x - padding;
            int glyphOriginY = glyphRect.y - padding;
            int glyphWidth = glyphRect.width + padding * 2;
            int glyphHeight = glyphRect.height + padding * 2;

            // Compute the normalized texture coordinates
            Rect texCoords = new Rect((float)glyphOriginX / atlasTexture.width, (float)glyphOriginY / atlasTexture.height, (float)glyphWidth / atlasTexture.width, (float)glyphHeight / atlasTexture.height);

            if (Event.current.type == EventType.Repaint)
            {
                // Draw glyph from atlas texture.
                Rect glyphDrawPosition = new Rect(position.x - padding * scale, position.y - padding * scale, position.width, position.height);

                //glyphDrawPosition.x += (glyphDrawPosition.width - glyphWidth * scale); // / 2;
                //glyphDrawPosition.y += (glyphDrawPosition.height - glyphHeight * scale); // / 2;
                glyphDrawPosition.width = glyphWidth * scale;
                glyphDrawPosition.height = glyphHeight * scale;

                // Could switch to using the default material of the font asset which would require passing scale to the shader.
                Graphics.DrawTexture(glyphDrawPosition, atlasTexture, texCoords, 0, 0, 0, 0, new Color(0.8f, 0.8f, 0.8f), mat);
            }
        }
    }
}
