Option Strict On
Option Explicit On

Imports System
Imports System.Collections.Generic
Imports Nexamas.UI.Architecture
Imports Nexamas.UI.Composition
Imports Nexamas.UI.General
Imports Nexamas.UI.Layout
Imports Nexamas.UI.TextRendering
Imports Nexamas.UI.Theming
Imports Nexamas.UI.Values
Imports SkiaSharp

Namespace Nexamas.UI.Controls

    Partial Public NotInheritable Class MASLabel
        Friend Function MeasurePreferredSize(ctx As MASThemeContext) As SKSize
            Return MeasurePreferredSize(ctx, 0.0F)
        End Function

        Friend Function MeasurePreferredSize(ctx As MASThemeContext, maxWidthDip As Single) As SKSize
            If ctx Is Nothing OrElse ctx.Typography Is Nothing Then Return SKSize.Empty

            Using paint As SKPaint = CreateResolvedPaint(ctx)
                If paint Is Nothing Then Return SKSize.Empty

                Dim dpi As Single = PixelSnap.SafeDpi(ctx.Dpi)
                Dim paddingWidth As Single = (_paddingLeft + _paddingRight) * dpi
                Dim paddingHeight As Single = (_paddingTop + _paddingBottom) * dpi
                Dim maxWidthPx As Single = If(maxWidthDip > 0.0F, maxWidthDip * dpi, 0.0F)
                Dim contentMaxWidth As Single = Math.Max(0.0F, maxWidthPx - paddingWidth)

                If _multiLine AndAlso contentMaxWidth > 1.0F Then
                    Dim lines As List(Of String) = BuildLines(_text, paint, contentMaxWidth)
                    If lines.Count = 0 Then Return New SKSize(paddingWidth, paddingHeight)

                    Dim maxLineWidth As Single = 0.0F

                    For Each line As String In lines
                        maxLineWidth = Math.Max(maxLineWidth, MASShapedText.MeasureWidth(line, paint))
                    Next

                    Return New SKSize(
                        maxLineWidth + paddingWidth,
                        ResolveTotalTextHeight(paint, lines.Count) + paddingHeight)
                End If

                Dim width As Single = MASShapedText.MeasureWidth(MASLabelTypographyPolicy.ResolveRenderableText(_text, allowMultiline:=False), paint)
                Dim fm As SKFontMetrics = paint.FontMetrics
                Dim height As Single = Math.Max(0.0F, fm.Descent - fm.Ascent)

                Return New SKSize(width + paddingWidth, height + paddingHeight)
            End Using
        End Function

        Friend Function GetPreferredLayoutSize(context As MASLayoutMeasureContext) As SKSize Implements IMASLayoutParticipant.GetPreferredLayoutSize
            Return MeasureIntrinsicSize(CreateSizeContext(context), SizeIntent).DesiredSize
        End Function

        Friend Function GetMinLayoutSize(context As MASLayoutMeasureContext) As SKSize Implements IMASLayoutParticipant.GetMinLayoutSize
            Return MeasureIntrinsicSize(CreateSizeContext(context), SizeIntent).MinSize
        End Function

        Friend Function MeasureIntrinsicSize(context As MASSizeContext, intent As MASSize) As MASSizeResult Implements IMASIntrinsicSizeContract.MeasureIntrinsicSize
            Dim safeContext As MASSizeContext = MASIntrinsicControlSizeMetrics.EnsureContext(context)
            Dim availableWidthDip As Single = safeContext.AvailableSize.Width

            Dim desired As SKSize = ResolveIntrinsicDesiredSize(safeContext, availableWidthDip)
            Dim minimum As SKSize = ResolveIntrinsicMinimumSize(safeContext)
            Dim contentInset As New MASLayoutInset(_paddingLeft, _paddingTop, _paddingRight, _paddingBottom)

            Return MASSizeResolver.FromMeasured(
                desiredSize:=desired,
                minSize:=minimum,
                maxSize:=New SKSize(Single.PositiveInfinity, Single.PositiveInfinity),
                intent:=intent,
                context:=safeContext,
                canGrowWidth:=_multiLine OrElse _wordWrap,
                canGrowHeight:=False,
                contentInset:=contentInset,
                visualOverflowInset:=MASLayoutInset.Empty,
                hitOverflowInset:=MASLayoutInset.Empty,
                isFallback:=False)
        End Function

        Private Function ResolveIntrinsicDesiredSize(context As MASSizeContext, availableWidthDip As Single) As SKSize
            Dim boundedWidthDip As Single = 0.0F
            If availableWidthDip > 0.0F AndAlso Not Single.IsInfinity(availableWidthDip) Then
                boundedWidthDip = availableWidthDip
            End If

            If context IsNot Nothing AndAlso context.ThemeContext IsNot Nothing Then
                Return PixelSizeToLogicalSize(MeasurePreferredSize(context.ThemeContext, boundedWidthDip), context.ThemeContext)
            End If

            Dim textMeasureWidthDip As Single =
                MASIntrinsicControlSizeMetrics.ResolveLabelTextMeasureWidth(
                    availableWidth:=availableWidthDip,
                    paddingLeft:=_paddingLeft,
                    paddingRight:=_paddingRight)

            Dim textMeasure As MASTextMeasureResult =
                MASLayoutTextMeasureGateway.Measure(
                    If(context Is Nothing, Nothing, context.IntegrationContext),
                    MASLabelTypographyPolicy.ResolveRenderableText(_text, allowMultiline:=_multiLine),
                    _textStyle,
                    textMeasureWidthDip,
                    allowWrap:=_multiLine AndAlso _wordWrap)

            Return MASIntrinsicControlSizeMetrics.ResolveLabelDesiredSize(
                textSize:=textMeasure.Size,
                paddingLeft:=_paddingLeft,
                paddingTop:=_paddingTop,
                paddingRight:=_paddingRight,
                paddingBottom:=_paddingBottom)
        End Function

        Private Function ResolveIntrinsicMinimumSize(context As MASSizeContext) As SKSize
            Dim desired As SKSize = ResolveIntrinsicDesiredSize(context, 0.0F)
            If _multiLine AndAlso _wordWrap Then
                Dim singleLine As MASTextMeasureResult =
                    MASLayoutTextMeasureGateway.Measure(
                        If(context Is Nothing, Nothing, context.IntegrationContext),
                        MASLabelTypographyPolicy.ResolveRenderableText(_text, allowMultiline:=False),
                        _textStyle,
                        Single.PositiveInfinity,
                        allowWrap:=False)
                Dim singleLineSize As SKSize = MASIntrinsicControlSizeMetrics.ResolveLabelDesiredSize(
                    textSize:=singleLine.Size,
                    paddingLeft:=_paddingLeft,
                    paddingTop:=_paddingTop,
                    paddingRight:=_paddingRight,
                    paddingBottom:=_paddingBottom)

                Return New SKSize(
                    Math.Max(0.0F, Math.Min(desired.Width, singleLineSize.Width)),
                    Math.Max(0.0F, singleLineSize.Height))
            End If
            Return desired
        End Function

        Private Shared Function CreateSizeContext(context As MASLayoutMeasureContext) As MASSizeContext
            If context Is Nothing Then Return MASIntrinsicControlSizeMetrics.EnsureContext(Nothing)
            Return context.ToSizeContext()
        End Function


        Private Shared Function PixelSizeToLogicalSize(sizePx As SKSize, ctx As MASThemeContext) As SKSize
            If ctx Is Nothing Then Return SKSize.Empty

            Dim dpi As Single = PixelSnap.SafeDpi(ctx.Dpi)
            If dpi <= 0.0F Then dpi = 1.0F

            Return New SKSize(
                Math.Max(0.0F, sizePx.Width / dpi),
                Math.Max(0.0F, sizePx.Height / dpi))
        End Function
    End Class

End Namespace
