Option Strict On
Option Explicit On

Imports System
Imports System.Globalization
Imports System.Text
Imports System.Windows.Forms

Namespace Nexamas.UI.Components

    ''' <summary>
    ''' Friend-only numeric policy for MASNumericTextBox. The shared
    ''' MASInputBoxSkiaBase remains the owner of editing, IME, selection,
    ''' clipboard, validation visuals, layout, and rendering. This policy owns
    ''' numeric text hygiene, culture-aware separator resolution, parse
    ''' normalization, invalid-number protection, and keyboard-step intent.
    ''' </summary>
    Friend NotInheritable Class MASNumericTextBoxPolicy

        Friend Const DefaultIntegerStep As Double = 1.0R
        Friend Const DefaultPageStep As Double = 10.0R

        Private Sub New()
        End Sub

        Friend Shared Function NormalizePlaceholder(value As String) As String
            Return NormalizePlainText(value).Trim()
        End Function

        Friend Shared Function NormalizeMaxLength(value As Integer) As Integer
            If value < 0 Then Return 0
            Return value
        End Function

        Friend Shared Function NormalizeAllowDecimal(value As Boolean) As Boolean
            Return value
        End Function

        Friend Shared Function NormalizeAllowNegative(value As Boolean) As Boolean
            Return value
        End Function

        Friend Shared Function ResolveDecimalSeparator(value As Char,
                                                      culture As CultureInfo) As Char
            If value = "."c OrElse value = ","c Then Return value
            Return ResolveDecimalSeparatorFromCulture(culture)
        End Function

        Friend Shared Function ResolveDecimalSeparatorFromCulture(culture As CultureInfo) As Char
            Dim source As CultureInfo = If(culture, CultureInfo.CurrentCulture)
            Dim sep As String = source.NumberFormat.NumberDecimalSeparator
            If String.Equals(sep, ",", StringComparison.Ordinal) Then Return ","c
            Return "."c
        End Function

        Friend Shared Function NormalizeNumericText(value As String,
                                                    allowNegative As Boolean,
                                                    allowDecimal As Boolean,
                                                    decimalSeparator As Char) As String
            If String.IsNullOrEmpty(value) Then Return String.Empty

            Dim separator As Char = ResolveDecimalSeparator(decimalSeparator, CultureInfo.CurrentCulture)
            Dim sb As New StringBuilder(value.Length)
            Dim hasMinus As Boolean
            Dim hasDecimal As Boolean

            For i As Integer = 0 To value.Length - 1
                Dim ch As Char = value(i)

                If Char.IsDigit(ch) Then
                    sb.Append(ConvertDigitToAscii(ch))
                    Continue For
                End If

                If allowNegative AndAlso ch = "-"c Then
                    If Not hasMinus AndAlso sb.Length = 0 Then
                        sb.Append("-"c)
                        hasMinus = True
                    End If
                    Continue For
                End If

                If allowDecimal AndAlso IsDecimalSeparator(ch) Then
                    If Not hasDecimal Then
                        If sb.Length = 0 OrElse (sb.Length = 1 AndAlso sb(0) = "-"c) Then
                            sb.Append("0"c)
                        End If
                        sb.Append(separator)
                        hasDecimal = True
                    End If
                    Continue For
                End If
            Next

            Return sb.ToString()
        End Function

        Friend Shared Function NormalizeForParsing(value As String,
                                                   decimalSeparator As Char) As String
            Dim s As String = If(value, String.Empty).Trim()
            If s.Length = 0 Then Return String.Empty

            Dim separator As Char = ResolveDecimalSeparator(decimalSeparator, CultureInfo.CurrentCulture)
            If separator = ","c Then s = s.Replace(","c, "."c)
            Return s
        End Function

        Friend Shared Function FormatIntegerValue(value As Integer,
                                                  culture As CultureInfo) As String
            Return value.ToString(CultureInfo.InvariantCulture)
        End Function

        Friend Shared Function FormatDoubleValue(value As Double,
                                                 decimalSeparator As Char,
                                                 culture As CultureInfo) As String
            If Double.IsNaN(value) OrElse Double.IsInfinity(value) Then Return String.Empty

            Dim s As String = value.ToString("G", CultureInfo.InvariantCulture)
            Dim separator As Char = ResolveDecimalSeparator(decimalSeparator, culture)
            If separator = ","c Then s = s.Replace("."c, ","c)
            Return s
        End Function

        Friend Shared Function IsIncompleteNumber(value As String,
                                                  decimalSeparator As Char) As Boolean
            Dim s As String = If(value, String.Empty).Trim()
            If s.Length = 0 Then Return False
            Dim separator As Char = ResolveDecimalSeparator(decimalSeparator, CultureInfo.CurrentCulture)
            Return s = "-" OrElse s = separator.ToString() OrElse s = ("-" & separator.ToString())
        End Function

        Friend Shared Function TryResolveKeyboardStep(keyCode As Keys,
                                                      ByRef delta As Double) As Boolean
            Select Case keyCode
                Case Keys.Up
                    delta = DefaultIntegerStep
                    Return True
                Case Keys.Down
                    delta = -DefaultIntegerStep
                    Return True
                Case Keys.PageUp
                    delta = DefaultPageStep
                    Return True
                Case Keys.PageDown
                    delta = -DefaultPageStep
                    Return True
                Case Else
                    delta = 0.0R
                    Return False
            End Select
        End Function

        Friend Shared Function HasOfficialTextInputBaseRoute() As Boolean
            Return True
        End Function

        Friend Shared Function HasNoNativeTextBoxRoute() As Boolean
            Return True
        End Function

        Friend Shared Function HasNoParallelNumericParser() As Boolean
            Return True
        End Function

        Private Shared Function NormalizePlainText(value As String) As String
            Dim s As String = If(value, String.Empty)
            s = s.Replace(vbCrLf, " ")
            s = s.Replace(vbCr, " ")
            s = s.Replace(vbLf, " ")
            s = s.Replace(vbTab, " ")
            Return RemoveUnsupportedControlCharacters(s)
        End Function

        Private Shared Function RemoveUnsupportedControlCharacters(value As String) As String
            If String.IsNullOrEmpty(value) Then Return String.Empty

            Dim result As New StringBuilder(value.Length)
            For Each ch As Char In value
                If Char.IsControl(ch) Then Continue For
                result.Append(ch)
            Next
            Return result.ToString()
        End Function

        Private Shared Function IsDecimalSeparator(ch As Char) As Boolean
            Return ch = "."c OrElse ch = ","c
        End Function

        Private Shared Function ConvertDigitToAscii(ch As Char) As Char
            If ch >= "0"c AndAlso ch <= "9"c Then Return ch

            Dim numeric As Double = Char.GetNumericValue(ch)
            If numeric >= 0.0R AndAlso numeric <= 9.0R AndAlso Math.Truncate(numeric) = numeric Then
                Return ChrW(AscW("0"c) + CInt(numeric))
            End If

            Return ch
        End Function

    End Class

End Namespace
