Option Strict On
Option Explicit On

Imports System
Imports System.Globalization

Namespace Nexamas.UI.Components

    ''' <summary>
    ''' Friend-owned culture clock-format policy for MASTimePicker. The public TimePicker
    ''' remains API-stable; 12/24-hour behavior is inferred from the active culture so the
    ''' product layer does not duplicate localization/time-format decisions in controls.
    ''' </summary>
    Friend Enum MASTimePickerClockFormatKind
        TwentyFourHour = 0
        TwelveHour = 1
    End Enum

    Friend NotInheritable Class MASTimePickerClockFormat
        Friend Sub New(kind As MASTimePickerClockFormatKind,
                       culture As CultureInfo,
                       amDesignator As String,
                       pmDesignator As String)
            Me.Kind = kind
            Me.Culture = If(culture, CultureInfo.CurrentCulture)
            Me.AMDesignator = NormalizeDesignator(amDesignator, "AM")
            Me.PMDesignator = NormalizeDesignator(pmDesignator, "PM")
        End Sub

        Friend ReadOnly Property Kind As MASTimePickerClockFormatKind
        Friend ReadOnly Property Culture As CultureInfo
        Friend ReadOnly Property AMDesignator As String
        Friend ReadOnly Property PMDesignator As String

        Friend ReadOnly Property UsesTwelveHourClock As Boolean
            Get
                Return Kind = MASTimePickerClockFormatKind.TwelveHour
            End Get
        End Property

        Private Shared Function NormalizeDesignator(value As String,
                                                    fallback As String) As String
            Dim normalized As String = If(value, String.Empty).Trim()
            If normalized.Length = 0 Then normalized = fallback
            Return normalized
        End Function
    End Class

    Friend NotInheritable Class MASTimePickerClockFormatPolicy
        Private Sub New()
        End Sub

        Friend Shared Function Resolve(culture As CultureInfo) As MASTimePickerClockFormat
            Dim effectiveCulture As CultureInfo = If(culture, CultureInfo.CurrentCulture)
            Dim format As DateTimeFormatInfo = effectiveCulture.DateTimeFormat
            Dim kind As MASTimePickerClockFormatKind = ResolveKind(format)

            Return New MASTimePickerClockFormat(
                kind,
                effectiveCulture,
                format.AMDesignator,
                format.PMDesignator)
        End Function

        Friend Shared Function FormatHourDigits(value As TimeSpan,
                                                clockFormat As MASTimePickerClockFormat) As String
            Dim normalized As TimeSpan = MASTimePicker.NormalizeTime(value, 1)
            Dim hour As Integer = normalized.Hours

            If clockFormat IsNot Nothing AndAlso clockFormat.UsesTwelveHourClock Then
                hour = hour Mod 12
                If hour = 0 Then hour = 12
            End If

            Return hour.ToString("00", CultureInfo.InvariantCulture)
        End Function

        Friend Shared Function FormatManualEditDigits(value As TimeSpan,
                                                      clockFormat As MASTimePickerClockFormat) As String
            Dim normalized As TimeSpan = MASTimePicker.NormalizeTime(value, 1)
            Return FormatHourDigits(normalized, clockFormat) & normalized.Minutes.ToString("00", CultureInfo.InvariantCulture)
        End Function

        Friend Shared Function FormatManualEditPlaceholder(clockFormat As MASTimePickerClockFormat) As String
            If clockFormat IsNot Nothing AndAlso clockFormat.UsesTwelveHourClock Then Return "hh:mm"
            Return "HH:mm"
        End Function

        Friend Shared Function FormatHourLabel(value As TimeSpan,
                                               clockFormat As MASTimePickerClockFormat) As String
            If clockFormat Is Nothing OrElse Not clockFormat.UsesTwelveHourClock Then Return "Hour"
            Return "Hour " & ResolveMeridiemDesignator(value, clockFormat)
        End Function

        Friend Shared Function IsManualEditDigitsCandidateAllowed(digits As String,
                                                                  clockFormat As MASTimePickerClockFormat) As Boolean
            If digits Is Nothing Then Return False
            If digits.Length = 0 Then Return True
            If digits.Length > 4 Then Return False

            For Each ch As Char In digits
                If ch < "0"c OrElse ch > "9"c Then Return False
            Next

            If clockFormat IsNot Nothing AndAlso clockFormat.UsesTwelveHourClock Then
                Return IsTwelveHourCandidateAllowed(digits)
            End If

            Return IsTwentyFourHourCandidateAllowed(digits)
        End Function

        Friend Shared Function TryBuildManualEditTime(digits As String,
                                                      clockFormat As MASTimePickerClockFormat,
                                                      periodReference As TimeSpan,
                                                      ByRef value As TimeSpan) As Boolean
            value = TimeSpan.Zero
            If String.IsNullOrEmpty(digits) OrElse digits.Length <> 4 Then Return False
            If Not IsManualEditDigitsCandidateAllowed(digits, clockFormat) Then Return False

            Dim hour As Integer = Integer.Parse(digits.Substring(0, 2), CultureInfo.InvariantCulture)
            Dim minute As Integer = Integer.Parse(digits.Substring(2, 2), CultureInfo.InvariantCulture)

            If clockFormat IsNot Nothing AndAlso clockFormat.UsesTwelveHourClock Then
                Dim keepPm As Boolean = MASTimePicker.NormalizeTime(periodReference, 1).Hours >= 12
                If hour = 12 Then
                    hour = If(keepPm, 12, 0)
                ElseIf keepPm Then
                    hour += 12
                End If
            End If

            value = New TimeSpan(hour, minute, 0)
            Return True
        End Function

        Friend Shared Function ResolveMeridiemDesignator(value As TimeSpan,
                                                         clockFormat As MASTimePickerClockFormat) As String
            If clockFormat Is Nothing Then Return String.Empty
            Dim normalized As TimeSpan = MASTimePicker.NormalizeTime(value, 1)
            If normalized.Hours >= 12 Then Return clockFormat.PMDesignator
            Return clockFormat.AMDesignator
        End Function

        Private Shared Function ResolveKind(format As DateTimeFormatInfo) As MASTimePickerClockFormatKind
            If format Is Nothing Then Return MASTimePickerClockFormatKind.TwentyFourHour

            Dim pattern As String = If(format.ShortTimePattern, String.Empty)
            Dim marker As Char = FirstUnescapedHourPatternMarker(pattern)
            If marker = "h"c Then Return MASTimePickerClockFormatKind.TwelveHour
            If marker = "H"c Then Return MASTimePickerClockFormatKind.TwentyFourHour

            If pattern.IndexOf("tt", StringComparison.OrdinalIgnoreCase) >= 0 Then
                Return MASTimePickerClockFormatKind.TwelveHour
            End If

            Return MASTimePickerClockFormatKind.TwentyFourHour
        End Function

        Private Shared Function FirstUnescapedHourPatternMarker(pattern As String) As Char
            If String.IsNullOrEmpty(pattern) Then Return ChrW(0)

            Dim inQuote As Boolean = False
            Dim quoteChar As Char = ChrW(0)
            Dim escaped As Boolean = False

            For Each ch As Char In pattern
                If escaped Then
                    escaped = False
                    Continue For
                End If

                If ch = "\"c Then
                    escaped = True
                    Continue For
                End If

                If ch = "'"c OrElse ch = """"c Then
                    If inQuote AndAlso quoteChar = ch Then
                        inQuote = False
                        quoteChar = ChrW(0)
                    ElseIf Not inQuote Then
                        inQuote = True
                        quoteChar = ch
                    End If
                    Continue For
                End If

                If inQuote Then Continue For
                If ch = "h"c OrElse ch = "H"c Then Return ch
            Next

            Return ChrW(0)
        End Function

        Private Shared Function IsTwentyFourHourCandidateAllowed(digits As String) As Boolean
            If String.IsNullOrEmpty(digits) Then Return True

            ' A single digit is only a partial manual edit, not a completed hour.
            ' Do not reject visible typing before the user has supplied enough
            ' digits to prove that the hour or minute portion is impossible.
            If digits.Length >= 2 Then
                Dim hour As Integer = (DigitValue(digits.Chars(0)) * 10) + DigitValue(digits.Chars(1))
                If hour > 23 Then Return False
            End If

            If digits.Length >= 3 Then
                If DigitValue(digits.Chars(2)) > 5 Then Return False
            End If

            Return True
        End Function

        Private Shared Function IsTwelveHourCandidateAllowed(digits As String) As Boolean
            If String.IsNullOrEmpty(digits) Then Return True

            ' A single digit is only a partial manual edit, not a completed hour.
            ' Full-hour validation starts once the second hour digit arrives so
            ' the popup visibly accepts typing while still rejecting 00, 13, etc.
            If digits.Length >= 2 Then
                Dim hour As Integer = (DigitValue(digits.Chars(0)) * 10) + DigitValue(digits.Chars(1))
                If hour < 1 OrElse hour > 12 Then Return False
            End If

            If digits.Length >= 3 Then
                If DigitValue(digits.Chars(2)) > 5 Then Return False
            End If

            Return True
        End Function

        Private Shared Function DigitValue(ch As Char) As Integer
            Return AscW(ch) - AscW("0"c)
        End Function
    End Class

End Namespace
