Option Strict On
Option Explicit On

Imports System
Imports System.ComponentModel
Imports Nexamas.UI.Services
Imports Nexamas.UI.TextInput
Imports Nexamas.UI.Theming

Namespace Nexamas.UI.Components

    Partial Public MustInherit Class MASInputBoxSkiaBase

#Region "Public API"

        <Browsable(True)>
        Public Overridable Property Text As String
            Get
                Return _state.Text
            End Get
            Set(value As String)
                Dim v As String = NormalizeAssignedText(value)

                If String.Equals(_state.Text, v, StringComparison.Ordinal) Then Return

                If Not _controller.SetText(v) Then Return

                InvalidateTextLayout()
                EnsureCaretVisible(TryGetContext())
                ResetCaretBlink()
                InvalidateVisual()
                RaiseTextChanged()
            End Set
        End Property

        Friend Property ClipboardService As IMASClipboardService
            Get
                Return _clipboardController.Service
            End Get
            Set(value As IMASClipboardService)
                _clipboardController.Service = value
                _controller.Clipboard = _clipboardController.Service
            End Set
        End Property

        Public Overridable Property Placeholder As String
            Get
                Return _placeholder
            End Get
            Set(value As String)
                Dim v As String = If(value, "")
                If String.Equals(_placeholder, v, StringComparison.Ordinal) Then Return

                _placeholder = v
                InvalidateVisual()
            End Set
        End Property

        ''' <summary>
        ''' Optional validation state used by the renderer to draw a non-invasive validation indicator.
        ''' The default is None and keeps existing visuals unchanged.
        ''' </summary>
        <Browsable(True)>
        Public Overridable Property ValidationState As MASTextValidationState
            Get
                Return _validationVisuals.State
            End Get
            Set(value As MASTextValidationState)
                If _validationVisuals.State = value Then Return

                _validationVisuals.State = value
                InvalidateVisual()
            End Set
        End Property

        ''' <summary>
        ''' Optional validation message reserved for tooltips, forms, and future validation UI.
        ''' The base renderer does not draw message text to avoid layout surprises.
        ''' </summary>
        <Browsable(True)>
        Public Overridable Property ValidationMessage As String
            Get
                Return _validationVisuals.Message
            End Get
            Set(value As String)
                Dim v As String = If(value, String.Empty)
                If String.Equals(_validationVisuals.Message, v, StringComparison.Ordinal) Then Return

                _validationVisuals.Message = v
                InvalidateVisual()
            End Set
        End Property

        ''' <summary>
        ''' Enables or disables the lightweight validation underline/dot.
        ''' </summary>
        <Browsable(True)>
        Public Overridable Property ShowValidationIndicator As Boolean
            Get
                Return _validationVisuals.ShowIndicator
            End Get
            Set(value As Boolean)
                If _validationVisuals.ShowIndicator = value Then Return

                _validationVisuals.ShowIndicator = value
                InvalidateVisual()
            End Set
        End Property

        ''' <summary>
        ''' Clears validation visuals and message.
        ''' </summary>
        Public Sub ClearValidation()
            If _validationVisuals.Clear() Then InvalidateVisual()
        End Sub

        ''' <summary>
        ''' Sets validation state and optional validation message in one operation.
        ''' </summary>
        Public Sub SetValidation(state As MASTextValidationState,
                                 Optional message As String = Nothing)

            If _validationVisuals.SetValidation(state, message) Then InvalidateVisual()
        End Sub

        <Browsable(True)>
        Public Overridable Property Multiline As Boolean
            Get
                Return _multilineController.Enabled
            End Get
            Set(value As Boolean)
                If Not _multilineController.SetEnabled(value) Then Return

                InvalidateTextLayout()
                EnsureCaretVisible(TryGetContext())
                ResetCaretBlink()
                InvalidateVisual()
            End Set
        End Property

        <Browsable(True)>
        Public Overridable Property MultilineWordWrap As Boolean
            Get
                Return _multilineController.WordWrap
            End Get
            Set(value As Boolean)
                If Not _multilineController.SetWordWrap(value) Then Return

                InvalidateTextLayout()
                EnsureCaretVisible(TryGetContext())
                ResetCaretBlink()
                InvalidateVisual()
            End Set
        End Property


        ''' <summary>
        ''' Optional font profile used by text rendering.
        ''' Set to Nothing to restore the default profile.
        ''' </summary>
        <Browsable(False)>
        Friend Overridable Property FontProfile As MASTextInputFontProfile
            Get
                Return _fontProfile
            End Get
            Set(value As MASTextInputFontProfile)
                If Object.ReferenceEquals(_fontProfile, value) Then Return

                _fontProfile = If(value, MASTextInputFontProfile.CreateDefault())
                InvalidateTextTheme()
                EnsureCaretVisible(TryGetContext())
                ResetCaretBlink()
                InvalidateVisual()
            End Set
        End Property


        ''' <summary>
        ''' Optional absolute text font size in DIP. Set to 0 to use theme typography size.
        ''' </summary>
        <Browsable(True)>
        Public Overridable Property TextFontSizeDip As Single
            Get
                Return _textFontSizeDip
            End Get
            Set(value As Single)
                Dim v As Single = If(Single.IsNaN(value) OrElse Single.IsInfinity(value), 0.0F, value)
                If v < 0.0F Then v = 0.0F
                If Math.Abs(_textFontSizeDip - v) < 0.01F Then Return

                _textFontSizeDip = v
                InvalidateTextTheme()
                EnsureCaretVisible(TryGetContext())
                ResetCaretBlink()
                InvalidateVisual()
            End Set
        End Property

        ''' <summary>
        ''' Enables sharper text painting for this input.
        ''' True by default. Disable only if a specific font looks better without snapping/hinting.
        ''' </summary>
        <Browsable(True)>
        Public Overridable Property TextCrispRendering As Boolean
            Get
                Return _textCrispRendering
            End Get
            Set(value As Boolean)
                If _textCrispRendering = value Then Return

                _textCrispRendering = value
                InvalidateTextTheme()
                ResetCaretBlink()
                InvalidateVisual()
            End Set
        End Property

        ''' <summary>
        ''' Simple external API: preferred default font family for this input.
        ''' </summary>
        Public Overridable Property TextFontFamily As String
            Get
                Return If(_fontProfile Is Nothing, String.Empty, If(_fontProfile.DefaultFamily, String.Empty))
            End Get
            Set(value As String)
                EnsureFontProfile()
                Dim v As String = If(value, String.Empty).Trim()
                If String.Equals(_fontProfile.DefaultFamily, v, StringComparison.Ordinal) Then Return

                _fontProfile.DefaultFamily = v
                _fontProfile.LatinFamily = v
                _fontProfile.ArabicFamily = v
                _fontProfile.PreferMonospace = False
                _fontProfile.ResetFallbacks(v, "Segoe UI", "Tahoma", "Arial", "Noto Sans", "Noto Sans Arabic")

                InvalidateTextTheme()
                EnsureCaretVisible(TryGetContext())
                ResetCaretBlink()
                InvalidateVisual()
            End Set
        End Property

        <Browsable(True)>
        Public Overridable Property LatinFontFamily As String
            Get
                Return If(_fontProfile Is Nothing, String.Empty, If(_fontProfile.LatinFamily, String.Empty))
            End Get
            Set(value As String)
                EnsureFontProfile()
                Dim v As String = If(value, String.Empty).Trim()
                If String.Equals(_fontProfile.LatinFamily, v, StringComparison.Ordinal) Then Return

                _fontProfile.LatinFamily = v
                _fontProfile.AddFallbacks(v)
                InvalidateTextTheme()
                EnsureCaretVisible(TryGetContext())
                ResetCaretBlink()
                InvalidateVisual()
            End Set
        End Property

        <Browsable(True)>
        Public Overridable Property ArabicFontFamily As String
            Get
                Return If(_fontProfile Is Nothing, String.Empty, If(_fontProfile.ArabicFamily, String.Empty))
            End Get
            Set(value As String)
                EnsureFontProfile()
                Dim v As String = If(value, String.Empty).Trim()
                If String.Equals(_fontProfile.ArabicFamily, v, StringComparison.Ordinal) Then Return

                _fontProfile.ArabicFamily = v
                _fontProfile.AddFallbacks(v)
                InvalidateTextTheme()
                EnsureCaretVisible(TryGetContext())
                ResetCaretBlink()
                InvalidateVisual()
            End Set
        End Property

        <Browsable(True)>
        Public Overridable Property MonospaceFontFamily As String
            Get
                Return If(_fontProfile Is Nothing, String.Empty, If(_fontProfile.MonospaceFamily, String.Empty))
            End Get
            Set(value As String)
                EnsureFontProfile()
                Dim v As String = If(value, String.Empty).Trim()
                If String.Equals(_fontProfile.MonospaceFamily, v, StringComparison.Ordinal) Then Return

                _fontProfile.MonospaceFamily = v
                _fontProfile.AddFallbacks(v)
                InvalidateTextTheme()
                EnsureCaretVisible(TryGetContext())
                ResetCaretBlink()
                InvalidateVisual()
            End Set
        End Property

        <Browsable(True)>
        Public Overridable Property PreferMonospaceFont As Boolean
            Get
                Return _fontProfile IsNot Nothing AndAlso _fontProfile.PreferMonospace
            End Get
            Set(value As Boolean)
                EnsureFontProfile()
                If _fontProfile.PreferMonospace = value Then Return

                _fontProfile.PreferMonospace = value
                InvalidateTextTheme()
                EnsureCaretVisible(TryGetContext())
                ResetCaretBlink()
                InvalidateVisual()
            End Set
        End Property

        Public Sub SetTextFont(family As String, Optional sizeDip As Single = 0.0F)
            Me.TextFontFamily = family
            If sizeDip > 0.0F Then Me.TextFontSizeDip = sizeDip
        End Sub

        Public Sub SetTextFontFamilies(defaultFamily As String,
                                       Optional latinFamily As String = Nothing,
                                       Optional arabicFamily As String = Nothing,
                                       Optional monospaceFamily As String = Nothing,
                                       Optional sizeDip As Single = 0.0F)
            FontProfile = MASTextInputFontProfile.CreateCustom(
                defaultFamily:=defaultFamily,
                latinFamily:=latinFamily,
                arabicFamily:=arabicFamily,
                monospaceFamily:=monospaceFamily,
                preferMonospace:=False)

            If sizeDip > 0.0F Then TextFontSizeDip = sizeDip
        End Sub

        Public Sub UseArabicModernFontProfile(Optional sizeDip As Single = 0.0F)
            FontProfile = MASTextInputFontProfile.CreateArabicModern()
            If sizeDip > 0.0F Then TextFontSizeDip = sizeDip
        End Sub

        Public Sub UseArabicReadingFontProfile(Optional sizeDip As Single = 0.0F)
            FontProfile = MASTextInputFontProfile.CreateArabicReading()
            If sizeDip > 0.0F Then TextFontSizeDip = sizeDip
        End Sub

        Public Sub UseArabicClassicFontProfile(Optional sizeDip As Single = 0.0F)
            FontProfile = MASTextInputFontProfile.CreateArabicClassic()
            If sizeDip > 0.0F Then TextFontSizeDip = sizeDip
        End Sub

        Public Sub UseLatinModernFontProfile(Optional sizeDip As Single = 0.0F)
            FontProfile = MASTextInputFontProfile.CreateLatinModern()
            If sizeDip > 0.0F Then TextFontSizeDip = sizeDip
        End Sub

        Public Sub UseLatinSerifFontProfile(Optional sizeDip As Single = 0.0F)
            FontProfile = MASTextInputFontProfile.CreateLatinSerif()
            If sizeDip > 0.0F Then TextFontSizeDip = sizeDip
        End Sub

        Public Sub UseLatinCodeFontProfile(Optional sizeDip As Single = 0.0F)
            FontProfile = MASTextInputFontProfile.CreateLatinCode()
            If sizeDip > 0.0F Then TextFontSizeDip = sizeDip
        End Sub

        Private Sub EnsureFontProfile()
            If _fontProfile Is Nothing Then
                _fontProfile = MASTextInputFontProfile.CreateDefault()
            End If
        End Sub

        ''' <summary>
        ''' Optional syntax highlighter. Rendering uses it only when SyntaxHighlightingEnabled=True.
        ''' </summary>
        <Browsable(False)>
        Friend Overridable Property SyntaxHighlighter As IMASTextSyntaxHighlighter
            Get
                Return _syntaxHighlighter
            End Get
            Set(value As IMASTextSyntaxHighlighter)
                If Object.ReferenceEquals(_syntaxHighlighter, value) Then Return

                _syntaxHighlighter = value
                InvalidateVisual()
            End Set
        End Property

        ''' <summary>
        ''' Enables optional syntax-colored rendering. Default is False.
        ''' </summary>
        <Browsable(True)>
        Public Overridable Property SyntaxHighlightingEnabled As Boolean
            Get
                Return _syntaxHighlightingEnabled
            End Get
            Set(value As Boolean)
                If _syntaxHighlightingEnabled = value Then Return

                _syntaxHighlightingEnabled = value
                InvalidateVisual()
            End Set
        End Property

        Public Sub UseDefaultFontProfile()
            FontProfile = MASTextInputFontProfile.CreateDefault()
        End Sub

        Public Sub UseArabicFriendlyFontProfile()
            UseArabicModernFontProfile()
        End Sub

        Public Sub UseCodeFontProfile()
            UseLatinCodeFontProfile()
        End Sub

        Public Overridable Property [ReadOnly] As Boolean
            Get
                Return _state.ReadOnly
            End Get
            Set(value As Boolean)
                If Not _controller.SetReadOnly(value) Then Return
                InvalidateVisual()
            End Set
        End Property

        Public Overridable Property UseSystemPasswordChar As Boolean
            Get
                Return _state.UseSystemPasswordChar
            End Get
            Set(value As Boolean)
                If Not _controller.SetUseSystemPasswordChar(value) Then Return

                InvalidateTextLayout()
                ResetCaretBlink()
                EnsureCaretVisible(TryGetContext())
                InvalidateVisual()
            End Set
        End Property

        Public Overridable Property PasswordChar As Char
            Get
                Return _state.PasswordChar
            End Get
            Set(value As Char)
                If Not _controller.SetPasswordChar(value) Then Return

                InvalidateTextLayout()
                ResetCaretBlink()
                EnsureCaretVisible(TryGetContext())
                InvalidateVisual()
            End Set
        End Property

        Public Overridable Property MaxLength As Integer
            Get
                Return _state.MaxLength
            End Get
            Set(value As Integer)
                Dim oldText As String = _state.Text

                If Not _controller.SetMaxLength(value) Then Return

                InvalidateTextLayout()
                EnsureCaretVisible(TryGetContext())
                ResetCaretBlink()
                InvalidateVisual()

                If Not String.Equals(oldText, _state.Text, StringComparison.Ordinal) Then
                    RaiseTextChanged()
                End If
            End Set
        End Property

        Public ReadOnly Property CaretIndex As Integer
            Get
                Return _state.CaretIndex
            End Get
        End Property

        Public Property SelectionStart As Integer
            Get
                Return _state.SelectionStart
            End Get
            Set(value As Integer)
                SetSelection(value, _state.SelectionLength)
            End Set
        End Property

        Public Property SelectionLength As Integer
            Get
                Return _state.SelectionLength
            End Get
            Set(value As Integer)
                SetSelection(_state.SelectionStart, value)
            End Set
        End Property

        Public ReadOnly Property SelectedText As String
            Get
                Return GetSelectedText()
            End Get
        End Property

        Public Sub FocusText()
            If Not Me.Enabled Then Return

            If Not Me.HasKeyboardFocus Then
                RequestFocus()
            End If

            ResetCaretBlink()
            InvalidateVisual()
        End Sub

        Public Sub SelectAll()
            _controller.SelectAll()

            EnsureCaretVisible(TryGetContext())
            ResetCaretBlink()
            InvalidateTextLayout()
            InvalidateVisual()
        End Sub

        Public Sub Clear()
            If Not _controller.Clear() Then Return

            _controller.ResetScroll()

            ResetCaretBlink()
            InvalidateTextLayout()
            InvalidateVisual()
            RaiseTextChanged()
        End Sub

        Public Sub AppendText(value As String)
            _controller.MoveCaretTo(_state.Text.Length, False)
            _controller.ClearSelection()

            Dim s As String = NormalizeIncomingText(value, GetRemainingCapacity())
            If s.Length = 0 Then Return

            Dim ctx As MASThemeContext = TryGetContext()
            Dim changed As Boolean = _controller.InsertText(s)

            If changed Then
                EnsureCaretVisible(ctx)
                ResetCaretBlink()
                InvalidateTextLayout()
                InvalidateVisual()
                RaiseTextChanged()
            End If
        End Sub

        Public Sub SetSelection(start As Integer, length As Integer)
            _controller.SetSelection(start, length)

            EnsureCaretVisible(TryGetContext())
            ResetCaretBlink()
            InvalidateTextLayout()
            InvalidateVisual()
        End Sub

#End Region

    End Class

End Namespace
