Option Strict On
Option Explicit On

Imports Nexamas.UI.Architecture
Imports Nexamas.UI.General
Imports Nexamas.UI.Theming
Imports Nexamas.UI.Values
Imports SkiaSharp

Namespace Nexamas.UI.Components

    ''' <summary>
    ''' Resolves tile item runtime state into render specs without owning the tile's artistic identity.
    ''' The central interaction system normalizes and validates state legality; this resolver may keep
    ''' tile-specific material tuning such as lift, surface adaptation, and icon motion.
    ''' </summary>
    Friend NotInheritable Class MASTileStateResolver

        Private Sub New()
        End Sub

        Friend Structure TileStateRenderSpec
            Friend MASVisualInteractionState As MASInteractionState
            Friend ShadowRect As SKRect
            Friend Overlay As MASVisualInteractionOverlaySpec
            Friend Surface As ResolvedTileSurface
            Friend IconOffsetY As Single
            Friend IconScaleMul As Single
        End Structure

        Friend Structure ResolvedTileSurface
            Friend BaseTop As SKColor
            Friend BaseMid As SKColor
            Friend BaseBottom As SKColor
            Friend InnerHighlight As SKColor
            Friend BorderHairline As SKColor
            Friend BorderInner As SKColor
        End Structure

        Private Structure TileInteractionColors
            Friend Fill As SKColor
            Friend Ring As SKColor
        End Structure

        Private Structure TileSurfaceAdaptTuning
            Friend FillStrength As Single
            Friend RingStrength As Single
        End Structure

        Friend Shared Function ResolveRenderSpec(ctx As MASThemeContext,
                                                 tileTheme As IMASTileTheme,
                                                 contract As MASResolvedComponentContract,
                                                 baseRect As SKRect,
                                                 dpi As Single,
                                                 enabled As Boolean,
                                                 isHover As Boolean,
                                                 isPressed As Boolean,
                                                 isSelected As Boolean) As TileStateRenderSpec

            Dim rawState As MASInteractionState = MASInteractionState.None

            If Not enabled Then
                rawState = MASInteractionState.Disabled
            Else
                If isSelected Then rawState = rawState Or MASInteractionState.Selected
                If isPressed Then rawState = rawState Or MASInteractionState.Pressed
                If isHover Then rawState = rawState Or MASInteractionState.Hovered
            End If

            Dim interactionState As MASInteractionState =
                MASInteractionResolver.NormalizeState(rawState)

            If contract IsNot Nothing Then
                contract.AssertConsumable()
            End If

            Dim resolvedSurface As ResolvedTileSurface =
                ResolveSurface(ctx, tileTheme, interactionState)

            Dim overlay As MASVisualInteractionOverlaySpec =
                ResolveOverlaySpec(ctx, contract, interactionState)

            Return New TileStateRenderSpec With {
                .MASVisualInteractionState = interactionState,
                .ShadowRect = ApplyShadowStateOffset(baseRect, interactionState, dpi),
                .Overlay = overlay,
                .Surface = resolvedSurface,
                .IconOffsetY = ResolveIconOffsetY(interactionState, dpi),
                .IconScaleMul = ResolveIconScaleMul(interactionState)
            }
        End Function

#Region "State"
#Region "Surface"

        Private Shared Function ResolveSurface(ctx As MASThemeContext,
                                               tileTheme As IMASTileTheme,
                                               state As MASInteractionState) As ResolvedTileSurface

            Dim emptySurface As New ResolvedTileSurface With {
                .BaseTop = SKColors.Transparent,
                .BaseMid = SKColors.Transparent,
                .BaseBottom = SKColors.Transparent,
                .InnerHighlight = SKColors.Transparent,
                .BorderHairline = SKColors.Transparent,
                .BorderInner = SKColors.Transparent
            }

            If ctx Is Nothing OrElse tileTheme Is Nothing OrElse tileTheme.Surface Is Nothing Then
                Return emptySurface
            End If

            Dim s As MASTileSurfaceTheme = tileTheme.Surface
            Dim app As IMASAppTheme = ctx.Theme

            Dim baseSurface As New ResolvedTileSurface With {
                .BaseTop = s.BaseTop,
                .BaseMid = s.BaseMid,
                .BaseBottom = s.BaseBottom,
                .InnerHighlight = s.InnerHighlight,
                .BorderHairline = s.BorderHairline,
                .BorderInner = s.BorderInner
            }

            If app Is Nothing Then Return baseSurface

            Dim colors As TileInteractionColors =
                ResolveTileInteractionColors(app, state)

            Dim tuning As TileSurfaceAdaptTuning =
                ResolveTileSurfaceAdaptTuning(state)

            Return ApplyInteractionSurface(
                baseSurface:=baseSurface,
                colors:=colors,
                tuning:=tuning,
                state:=state,
                disabledColor:=MASAppThemeContracts.Foundation(app).Semantics.Disabled)
        End Function

        Private Shared Function ResolveTileInteractionColors(app As IMASAppTheme,
                                                             state As MASInteractionState) As TileInteractionColors

            Dim c As New TileInteractionColors With {
                .Fill = SKColors.Transparent,
                .Ring = SKColors.Transparent
            }

            If app Is Nothing OrElse MASAppThemeContracts.Foundation(app).Interaction Is Nothing Then Return c

            Select Case state

                Case MASInteractionState.Hovered
                    If MASAppThemeContracts.Foundation(app).Interaction.Hover.Fill.Enabled Then c.Fill = MASAppThemeContracts.Foundation(app).Interaction.Hover.Fill.Color
                    If MASAppThemeContracts.Foundation(app).Interaction.Hover.Ring.Enabled Then c.Ring = MASAppThemeContracts.Foundation(app).Interaction.Hover.Ring.Color

                Case MASInteractionState.Pressed
                    If MASAppThemeContracts.Foundation(app).Interaction.Pressed.Fill.Enabled Then c.Fill = MASAppThemeContracts.Foundation(app).Interaction.Pressed.Fill.Color
                    If MASAppThemeContracts.Foundation(app).Interaction.Pressed.Ring.Enabled Then c.Ring = MASAppThemeContracts.Foundation(app).Interaction.Pressed.Ring.Color

                Case MASInteractionState.Selected
                    If MASAppThemeContracts.Foundation(app).Interaction.Selected.Fill.Enabled Then c.Fill = MASAppThemeContracts.Foundation(app).Interaction.Selected.Fill.Color
                    If MASAppThemeContracts.Foundation(app).Interaction.Selected.Ring.Enabled Then c.Ring = MASAppThemeContracts.Foundation(app).Interaction.Selected.Ring.Color

                Case MASInteractionState.Focused
                    If MASAppThemeContracts.Foundation(app).Interaction.Focused.Fill.Enabled Then c.Fill = MASAppThemeContracts.Foundation(app).Interaction.Focused.Fill.Color
                    If MASAppThemeContracts.Foundation(app).Interaction.Focused.Ring.Enabled Then c.Ring = MASAppThemeContracts.Foundation(app).Interaction.Focused.Ring.Color

                Case MASInteractionState.Disabled
                    If MASAppThemeContracts.Foundation(app).Interaction.DisabledFill.Enabled Then c.Fill = MASAppThemeContracts.Foundation(app).Interaction.DisabledFill.Color
                    c.Ring = MASAppThemeContracts.Foundation(app).Semantics.Disabled

            End Select

            Return c
        End Function

#End Region
        Private Shared Function ResolveTileSurfaceAdaptTuning(state As MASInteractionState) As TileSurfaceAdaptTuning
            Dim t As New TileSurfaceAdaptTuning With {
                .FillStrength = 0.0F,
                .RingStrength = 0.0F
            }

            Select Case state

                Case MASInteractionState.Hovered
                    t.FillStrength = 0.16F
                    t.RingStrength = 0.2F

                Case MASInteractionState.Pressed
                    t.FillStrength = 0.22F
                    t.RingStrength = 0.18F

                Case MASInteractionState.Selected
                    t.FillStrength = 0.18F
                    t.RingStrength = 0.3F

                Case MASInteractionState.Focused
                    t.FillStrength = 0.14F
                    t.RingStrength = 0.24F

                Case MASInteractionState.Disabled
                    t.FillStrength = 0.2F
                    t.RingStrength = 0.16F

            End Select

            t.FillStrength = Clamp01(t.FillStrength)
            t.RingStrength = Clamp01(t.RingStrength)

            Return t
        End Function

        Private Shared Function ApplyInteractionSurface(baseSurface As ResolvedTileSurface,
                                                        colors As TileInteractionColors,
                                                        tuning As TileSurfaceAdaptTuning,
                                                        state As MASInteractionState,
                                                        disabledColor As SKColor) As ResolvedTileSurface

            If state = MASInteractionState.None Then Return baseSurface

            Dim fillT As Single = Clamp01(tuning.FillStrength)
            Dim ringT As Single = Clamp01(tuning.RingStrength)

            Dim fillColor As SKColor = colors.Fill
            Dim ringColor As SKColor = colors.Ring

            If state = MASInteractionState.Disabled AndAlso fillColor.Alpha = 0 Then fillColor = disabledColor
            If state = MASInteractionState.Disabled AndAlso ringColor.Alpha = 0 Then ringColor = disabledColor

            If fillColor.Alpha = 0 AndAlso ringColor.Alpha = 0 Then Return baseSurface

            Dim baseTop As SKColor = baseSurface.BaseTop
            Dim baseMid As SKColor = baseSurface.BaseMid
            Dim baseBottom As SKColor = baseSurface.BaseBottom
            Dim innerHighlight As SKColor = baseSurface.InnerHighlight
            Dim borderHairline As SKColor = baseSurface.BorderHairline
            Dim borderInner As SKColor = baseSurface.BorderInner

            Select Case state

                Case MASInteractionState.Hovered
                    baseTop = ColorMath.Mix(baseTop, fillColor, fillT * 0.14F)
                    baseMid = ColorMath.Mix(baseMid, fillColor, fillT * 0.18F)
                    baseBottom = ColorMath.Mix(baseBottom, fillColor, fillT * 0.14F)

                    innerHighlight = ColorMath.WithAlpha(
                        ColorMath.Mix(innerHighlight, SKColors.White, 0.08F),
                        ClampByte(CInt(innerHighlight.Alpha) + 10))

                    borderHairline = ColorMath.WithAlpha(
                        ColorMath.Mix(borderHairline, ringColor, ringT * 0.42F),
                        ClampByte(CInt(borderHairline.Alpha) + 12))

                    borderInner = ColorMath.WithAlpha(
                        ColorMath.Mix(borderInner, ringColor, ringT * 0.26F),
                        ClampByte(CInt(borderInner.Alpha) + 10))

                Case MASInteractionState.Pressed
                    baseTop = ColorMath.Mix(baseTop, fillColor, fillT * 0.14F)
                    baseMid = ColorMath.Mix(baseMid, fillColor, fillT * 0.2F)
                    baseBottom = ColorMath.Mix(baseBottom, fillColor, fillT * 0.24F)

                    innerHighlight = ColorMath.WithAlpha(
                        ColorMath.Mix(innerHighlight, baseTop, 0.18F),
                        ClampByte(CInt(Math.Round(innerHighlight.Alpha * 0.72F))))

                    borderHairline = ColorMath.WithAlpha(
                        ColorMath.Mix(borderHairline, ringColor, ringT * 0.34F),
                        ClampByte(CInt(borderHairline.Alpha) + 8))

                    borderInner = ColorMath.WithAlpha(
                        ColorMath.Mix(borderInner, ringColor, ringT * 0.2F),
                        ClampByte(CInt(Math.Round(borderInner.Alpha * 0.84F))))

                Case MASInteractionState.Selected
                    baseTop = ColorMath.Mix(baseTop, fillColor, fillT * 0.12F)
                    baseMid = ColorMath.Mix(baseMid, fillColor, fillT * 0.22F)
                    baseBottom = ColorMath.Mix(baseBottom, fillColor, fillT * 0.2F)

                    innerHighlight = ColorMath.WithAlpha(
                        ColorMath.Mix(innerHighlight, fillColor, fillT * 0.12F),
                        ClampByte(CInt(innerHighlight.Alpha) + 6))

                    borderHairline = ColorMath.WithAlpha(
                        ColorMath.Mix(borderHairline, ringColor, ringT * 0.56F),
                        ClampByte(CInt(borderHairline.Alpha) + 20))

                    borderInner = ColorMath.WithAlpha(
                        ColorMath.Mix(borderInner, ringColor, ringT * 0.4F),
                        ClampByte(CInt(borderInner.Alpha) + 22))

                Case MASInteractionState.Focused
                    baseTop = ColorMath.Mix(baseTop, fillColor, fillT * 0.12F)
                    baseMid = ColorMath.Mix(baseMid, fillColor, fillT * 0.16F)
                    baseBottom = ColorMath.Mix(baseBottom, fillColor, fillT * 0.14F)

                    innerHighlight = ColorMath.WithAlpha(
                        ColorMath.Mix(innerHighlight, SKColors.White, 0.06F),
                        ClampByte(CInt(innerHighlight.Alpha) + 8))

                    borderHairline = ColorMath.WithAlpha(
                        ColorMath.Mix(borderHairline, ringColor, ringT * 0.48F),
                        ClampByte(CInt(borderHairline.Alpha) + 14))

                    borderInner = ColorMath.WithAlpha(
                        ColorMath.Mix(borderInner, ringColor, ringT * 0.3F),
                        ClampByte(CInt(borderInner.Alpha) + 12))

                Case MASInteractionState.Disabled
                    baseTop = ColorMath.Mix(baseTop, fillColor, fillT * 0.18F)
                    baseMid = ColorMath.Mix(baseMid, fillColor, fillT * 0.22F)
                    baseBottom = ColorMath.Mix(baseBottom, fillColor, fillT * 0.24F)

                    innerHighlight = ColorMath.WithAlpha(
                        innerHighlight,
                        ClampByte(CInt(Math.Round(innerHighlight.Alpha * 0.42F))))

                    borderHairline = ColorMath.WithAlpha(
                        ColorMath.Mix(borderHairline, ringColor, ringT * 0.3F),
                        ClampByte(CInt(Math.Round(borderHairline.Alpha * 0.65F))))

                    borderInner = ColorMath.WithAlpha(
                        ColorMath.Mix(borderInner, ringColor, ringT * 0.34F),
                        ClampByte(CInt(Math.Round(borderInner.Alpha * 0.58F))))

            End Select

            Return New ResolvedTileSurface With {
                .BaseTop = baseTop,
                .BaseMid = baseMid,
                .BaseBottom = baseBottom,
                .InnerHighlight = innerHighlight,
                .BorderHairline = borderHairline,
                .BorderInner = borderInner
            }
        End Function

#End Region

#Region "Overlay"

        Private Shared Function ResolveOverlaySpec(ctx As MASThemeContext,
                                                   contract As MASResolvedComponentContract,
                                                   state As MASInteractionState) As MASVisualInteractionOverlaySpec

            If state = MASInteractionState.None Then Return EmptyOverlaySpec()

            If ctx Is Nothing OrElse ctx.Theme Is Nothing OrElse contract Is Nothing Then
                Return EmptyOverlaySpec()
            End If

            contract.AssertConsumable()

            Return MASInteractionResolver.Build(
                contract:=contract,
                state:=state,
                theme:=ctx.Theme)
        End Function

        Private Shared Function EmptyOverlaySpec() As MASVisualInteractionOverlaySpec
            Return New MASVisualInteractionOverlaySpec With {
                .FillEnabled = False,
                .RingEnabled = False,
                .Fill = SKColors.Transparent,
                .Ring = SKColors.Transparent,
                .FillBlendMode = SKBlendMode.SrcOver,
                .ExtraFillInsetPx = 0.0F,
                .ExtraRingInsetPx = 0.0F,
                .Shape = MASVisualInteractionOverlayShape.Closed,
                .State = MASInteractionState.None
            }
        End Function

#End Region

#Region "Motion"

        Private Shared Function ApplyShadowStateOffset(baseRect As SKRect,
                                                       state As MASInteractionState,
                                                       dpi As Single) As SKRect

            Dim dy As Single = 0.0F

            Select Case state
                Case MASInteractionState.Hovered
                    dy = -0.55F * dpi

                Case MASInteractionState.Pressed
                    dy = 0.75F * dpi

                Case MASInteractionState.Selected
                    dy = -0.15F * dpi

                Case Else
                    dy = 0.0F
            End Select

            Return New SKRect(baseRect.Left, baseRect.Top + dy, baseRect.Right, baseRect.Bottom + dy)
        End Function

        Private Shared Function ResolveIconOffsetY(state As MASInteractionState,
                                                   dpi As Single) As Single
            Select Case state
                Case MASInteractionState.Hovered
                    Return -0.2F * dpi

                Case MASInteractionState.Pressed
                    Return 0.45F * dpi

                Case MASInteractionState.Selected
                    Return -0.1F * dpi

                Case Else
                    Return 0.0F
            End Select
        End Function

        Private Shared Function ResolveIconScaleMul(state As MASInteractionState) As Single
            Select Case state
                Case MASInteractionState.Hovered
                    Return 1.02F

                Case MASInteractionState.Pressed
                    Return 0.985F

                Case MASInteractionState.Selected
                    Return 1.01F

                Case Else
                    Return 1.0F
            End Select
        End Function

#End Region

#Region "Helpers"

        Private Shared Function Clamp01(v As Single) As Single
            If Single.IsNaN(v) OrElse Single.IsInfinity(v) Then Return 0.0F
            If v < 0.0F Then Return 0.0F
            If v > 1.0F Then Return 1.0F
            Return v
        End Function

        Private Shared Function ClampByte(v As Integer) As Byte
            If v < 0 Then Return 0
            If v > 255 Then Return 255
            Return CByte(v)
        End Function

#End Region

    End Class

End Namespace