Option Strict On
Option Explicit On

Imports System
Imports System.Collections.Generic
Imports SkiaSharp

Namespace Nexamas.UI.TextInput

    ''' <summary>
    ''' Describes the preferred font families used by MAS text input controls.
    '''
    ''' This object is intentionally UI-level and optional. It only chooses a preferred
    ''' typeface for the control. MASShapedText remains responsible for glyph fallback
    ''' when the selected family does not contain a requested character.
    ''' </summary>
    Friend NotInheritable Class MASTextInputFontProfile

        Private ReadOnly _fallbackFamilies As New List(Of String)()
        Private _fallbackFamiliesView As IReadOnlyList(Of String) = Nothing

        Public Property DefaultFamily As String
        Public Property LatinFamily As String
        Public Property ArabicFamily As String
        Public Property MonospaceFamily As String
        Public Property PreferMonospace As Boolean
        Public Property TextSizeScale As Single = 1.0F

        Public ReadOnly Property FallbackFamilies As IReadOnlyList(Of String)
            Get
                If _fallbackFamiliesView Is Nothing Then
                    _fallbackFamiliesView = _fallbackFamilies.AsReadOnly()
                End If

                Return _fallbackFamiliesView
            End Get
        End Property

        Public Shared Function CreateDefault() As MASTextInputFontProfile
            Dim p As New MASTextInputFontProfile()

            p.DefaultFamily = "Segoe UI"
            p.LatinFamily = "Segoe UI"
            p.ArabicFamily = "Segoe UI"
            p.MonospaceFamily = "Consolas"
            p.PreferMonospace = False
            p.TextSizeScale = 1.0F

            p.AddFallbacks("Segoe UI", "Arial", "Tahoma", "Noto Sans", "Noto Sans Arabic")

            Return p
        End Function

        Public Shared Function CreateArabicFriendly() As MASTextInputFontProfile
            Return CreateArabicModern()
        End Function

        ''' <summary>
        ''' Arabic profile optimized for modern UI text. Good default for forms and controls.
        ''' </summary>
        Public Shared Function CreateArabicModern() As MASTextInputFontProfile
            Dim p As MASTextInputFontProfile = CreateDefault()
            p.DefaultFamily = "Segoe UI"
            p.LatinFamily = "Segoe UI"
            p.ArabicFamily = "Segoe UI"
            p.MonospaceFamily = "Consolas"
            p.PreferMonospace = False
            p.ResetFallbacks("Segoe UI", "Tahoma", "Arial", "Noto Sans Arabic", "Noto Sans")
            Return p
        End Function

        ''' <summary>
        ''' Arabic profile optimized for comfortable reading and longer Arabic text.
        ''' Uses common Naskh-style fallbacks when installed.
        ''' </summary>
        Public Shared Function CreateArabicReading() As MASTextInputFontProfile
            Dim p As MASTextInputFontProfile = CreateDefault()
            p.DefaultFamily = "Noto Naskh Arabic"
            p.LatinFamily = "Segoe UI"
            p.ArabicFamily = "Noto Naskh Arabic"
            p.MonospaceFamily = "Consolas"
            p.PreferMonospace = False
            p.ResetFallbacks("Noto Naskh Arabic", "Amiri", "Segoe UI", "Tahoma", "Arial")
            Return p
        End Function

        ''' <summary>
        ''' Arabic profile optimized for classic Windows Arabic fonts.
        ''' Useful when Noto fonts are not installed.
        ''' </summary>
        Public Shared Function CreateArabicClassic() As MASTextInputFontProfile
            Dim p As MASTextInputFontProfile = CreateDefault()
            p.DefaultFamily = "Tahoma"
            p.LatinFamily = "Segoe UI"
            p.ArabicFamily = "Tahoma"
            p.MonospaceFamily = "Consolas"
            p.PreferMonospace = False
            p.ResetFallbacks("Tahoma", "Traditional Arabic", "Arial", "Segoe UI", "Noto Sans Arabic")
            Return p
        End Function

        ''' <summary>
        ''' Latin profile optimized for modern Windows UI text.
        ''' </summary>
        Public Shared Function CreateLatinModern() As MASTextInputFontProfile
            Dim p As MASTextInputFontProfile = CreateDefault()
            p.DefaultFamily = "Segoe UI"
            p.LatinFamily = "Segoe UI"
            p.ArabicFamily = "Segoe UI"
            p.MonospaceFamily = "Consolas"
            p.PreferMonospace = False
            p.ResetFallbacks("Segoe UI", "Arial", "Calibri", "Tahoma", "Noto Sans")
            Return p
        End Function

        ''' <summary>
        ''' Latin profile optimized for longer reading text.
        ''' </summary>
        Public Shared Function CreateLatinSerif() As MASTextInputFontProfile
            Dim p As MASTextInputFontProfile = CreateDefault()
            p.DefaultFamily = "Georgia"
            p.LatinFamily = "Georgia"
            p.ArabicFamily = "Segoe UI"
            p.MonospaceFamily = "Consolas"
            p.PreferMonospace = False
            p.ResetFallbacks("Georgia", "Times New Roman", "Segoe UI", "Arial", "Tahoma")
            Return p
        End Function

        Public Shared Function CreateCodeFriendly() As MASTextInputFontProfile
            Return CreateLatinCode()
        End Function

        ''' <summary>
        ''' Monospace profile suitable for code, logs, JSON, SQL, and syntax highlighting.
        ''' </summary>
        Public Shared Function CreateLatinCode() As MASTextInputFontProfile
            Dim p As MASTextInputFontProfile = CreateDefault()
            p.PreferMonospace = True
            p.MonospaceFamily = "Cascadia Mono"
            p.LatinFamily = "Cascadia Mono"
            p.DefaultFamily = "Cascadia Mono"
            p.ArabicFamily = "Segoe UI"
            p.ResetFallbacks("Cascadia Mono", "Consolas", "Courier New", "Segoe UI", "Tahoma")
            Return p
        End Function

        Public Shared Function CreateCustom(defaultFamily As String,
                                            Optional latinFamily As String = Nothing,
                                            Optional arabicFamily As String = Nothing,
                                            Optional monospaceFamily As String = Nothing,
                                            Optional preferMonospace As Boolean = False) As MASTextInputFontProfile
            Dim p As MASTextInputFontProfile = CreateDefault()
            p.DefaultFamily = If(defaultFamily, String.Empty)
            p.LatinFamily = If(latinFamily, p.DefaultFamily)
            p.ArabicFamily = If(arabicFamily, p.DefaultFamily)
            p.MonospaceFamily = If(monospaceFamily, "Consolas")
            p.PreferMonospace = preferMonospace
            p.ResetFallbacks(p.DefaultFamily, p.LatinFamily, p.ArabicFamily, p.MonospaceFamily, "Segoe UI", "Tahoma", "Arial")
            Return p
        End Function

        Public Function Clone() As MASTextInputFontProfile
            Dim p As New MASTextInputFontProfile()
            p.DefaultFamily = Me.DefaultFamily
            p.LatinFamily = Me.LatinFamily
            p.ArabicFamily = Me.ArabicFamily
            p.MonospaceFamily = Me.MonospaceFamily
            p.PreferMonospace = Me.PreferMonospace
            p.TextSizeScale = Me.TextSizeScale

            For Each f As String In _fallbackFamilies
                If Not String.IsNullOrWhiteSpace(f) Then
                    p.AddFallbacks(f)
                End If
            Next

            Return p
        End Function

        Public Sub ResetFallbacks(ParamArray families As String())
            _fallbackFamilies.Clear()
            AddFallbacks(families)
        End Sub

        Public Sub AddFallbacks(ParamArray families As String())
            If families Is Nothing Then Return

            For Each family As String In families
                If String.IsNullOrWhiteSpace(family) Then Continue For

                Dim alreadyExists As Boolean = False
                For Each existing As String In _fallbackFamilies
                    If String.Equals(existing, family, StringComparison.OrdinalIgnoreCase) Then
                        alreadyExists = True
                        Exit For
                    End If
                Next

                If Not alreadyExists Then
                    _fallbackFamilies.Add(family)
                End If
            Next
        End Sub

        Public Function ResolveFamilyForText(text As String) As String
            If PreferMonospace AndAlso Not String.IsNullOrWhiteSpace(MonospaceFamily) Then
                Return MonospaceFamily
            End If

            Dim s As String = If(text, String.Empty)

            If ContainsArabic(s) AndAlso Not String.IsNullOrWhiteSpace(ArabicFamily) Then
                Return ArabicFamily
            End If

            If ContainsLatin(s) AndAlso Not String.IsNullOrWhiteSpace(LatinFamily) Then
                Return LatinFamily
            End If

            If Not String.IsNullOrWhiteSpace(DefaultFamily) Then
                Return DefaultFamily
            End If

            If _fallbackFamilies.Count > 0 Then
                Return _fallbackFamilies(0)
            End If

            Return String.Empty
        End Function

        Public Sub ApplyToPaint(paint As SKPaint, sampleText As String)
            If paint Is Nothing Then Return

            Dim family As String = ResolveFamilyForText(sampleText)
            If Not String.IsNullOrWhiteSpace(family) Then
                Dim tf As SKTypeface = TryCreateTypeface(family)
                If tf IsNot Nothing Then
                    paint.Typeface = tf
                End If
            End If

            If TextSizeScale > 0.05F AndAlso Math.Abs(TextSizeScale - 1.0F) > 0.001F Then
                paint.TextSize = Math.Max(1.0F, paint.TextSize * TextSizeScale)
            End If
        End Sub

        Private Shared Function TryCreateTypeface(family As String) As SKTypeface
            If String.IsNullOrWhiteSpace(family) Then Return Nothing

            Try
                Dim tf As SKTypeface = SKTypeface.FromFamilyName(family)
                If tf IsNot Nothing Then Return tf
            Catch masCaughtException1 As Exception
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowTextShapingFallback(masCaughtException1)
            End Try

            Return Nothing
        End Function

        Private Shared Function ContainsLatin(value As String) As Boolean
            If String.IsNullOrEmpty(value) Then Return False

            For Each ch As Char In value
                Dim code As Integer = AscW(ch)
                If (code >= &H41 AndAlso code <= &H5A) OrElse
                   (code >= &H61 AndAlso code <= &H7A) OrElse
                   (code >= &HC0 AndAlso code <= &H24F) Then
                    Return True
                End If
            Next

            Return False
        End Function

        Private Shared Function ContainsArabic(value As String) As Boolean
            If String.IsNullOrEmpty(value) Then Return False

            For Each ch As Char In value
                Dim code As Integer = AscW(ch)
                If (code >= &H600 AndAlso code <= &H6FF) OrElse
                   (code >= &H750 AndAlso code <= &H77F) OrElse
                   (code >= &H8A0 AndAlso code <= &H8FF) OrElse
                   (code >= &HFB50 AndAlso code <= &HFDFF) OrElse
                   (code >= &HFE70 AndAlso code <= &HFEFF) Then
                    Return True
                End If
            Next

            Return False
        End Function

    End Class

End Namespace
