Option Strict On
Option Explicit On

Imports System
Imports Nexamas.UI.Architecture
Imports Nexamas.UI.Controls
Imports Nexamas.UI.General
Imports Nexamas.UI.Rendering
Imports Nexamas.UI.Theming
Imports Nexamas.UI.Values
Imports SkiaSharp

Namespace Nexamas.UI.Components

    Friend NotInheritable Class MASTabItemStateRenderer

        Private Shared ReadOnly _tabContract As MASResolvedComponentContract =
    MASArchitectureRuntime.ResolveContractOrThrow(GetType(MASTabControl))

        Private Shared ReadOnly _tabItemContract As MASResolvedComponentContract =
    MASArchitectureRuntime.ResolveContractOrThrow(GetType(MASTabControl))

        Private ReadOnly _p As MASVisualPrimitivesPainter
        Private ReadOnly _shadow As MASShadowPainter

        Friend Sub New(primitives As MASVisualPrimitivesPainter,
                       shadow As MASShadowPainter)

            If primitives Is Nothing Then Throw New ArgumentNullException(NameOf(primitives))
            If shadow Is Nothing Then Throw New ArgumentNullException(NameOf(shadow))

            _p = primitives
            _shadow = shadow
        End Sub

        Friend Sub DrawStrip(canvas As SKCanvas,
                             lay As MASTabLayout.Metrics,
                             stripR As SKRect,
                             env As MASTabRenderEnv)

            If canvas Is Nothing Then Return
            If stripR.Width <= 1.0F OrElse stripR.Height <= 1.0F Then Return
            If env.Tabs Is Nothing OrElse env.Tabs.Strip Is Nothing Then Return

            Dim radiusPx As Single =
                MASRadiusResolver.ResolveSurfacePx(
                    contract:=_tabContract,
                    rPx:=stripR,
                    dpi:=env.Dpi)

            radiusPx = PixelSnap.SafeRadius(radiusPx)

            _shadow.DrawLayeredShadow(
                canvas:=canvas,
                r:=stripR,
                contract:=_tabContract,
                dpi:=env.Dpi,
                surfaceTheme:=env.Surface)

            Dim baseSpec As New MASVisualPrimitivesPainter.ChipBaseSpec With {
                .Top = env.Tabs.Strip.FillTop,
                .Bot = env.Tabs.Strip.FillBottom,
                .MidT = 0.56F,
                .TopAlpha = 255,
                .MidAlpha = 255,
                .BotAlpha = 255
            }

            Dim fxSpec As New MASVisualPrimitivesPainter.ChipFxSpec With {
                .WashColor = env.Tabs.Strip.WashColor,
                .WashAlpha = env.Tabs.Strip.WashAlpha,
                .InnerGlossColor = env.Tabs.Strip.InnerGlossColor,
                .InnerGlossAlpha = env.Tabs.Strip.InnerGlossAlpha,
                .InnerGlossFadeT = env.Tabs.Strip.InnerGlossFadeT,
                .InnerInsetMul = env.Tabs.Strip.InnerInsetMul,
                .InnerRadMul = env.Tabs.Strip.InnerRadMul
            }

            _p.DrawChipBaseNoShadow(canvas, env.ThemeContext, stripR, radiusPx, env.Dpi, baseSpec)

            _p.DrawChipFxSSOT(
                canvas:=canvas,
                r:=stripR,
                radius:=radiusPx,
                dpi:=env.Dpi,
                spec:=baseSpec,
                fxSpec:=fxSpec)

            MASSurfaceInteractionRing.DrawIdleRim(
                canvas:=canvas,
                baseR:=stripR,
                baseRadiusPx:=radiusPx,
                rimColor:=env.Tabs.Strip.RimColor,
                rimAlpha:=MASTabPremiumVisualPolicy.ResolveStripRimAlpha(),
                strokePx:=Math.Max(SurfaceTokens.FxRingMinStrokePx, lay.HairW),
                dpi:=env.Dpi,
                edgeInsetPx:=0.0F)
        End Sub

        Friend Sub DrawItem(canvas As SKCanvas,
                            lay As MASTabLayout.Metrics,
                            itemR As SKRect,
                            st As MASVisualInteractionState,
                            isEnabled As Boolean,
                            showFocusCue As Boolean,
                            env As MASTabRenderEnv)

            If canvas Is Nothing Then Return
            If itemR.Width <= 1.0F OrElse itemR.Height <= 1.0F Then Return
            If env.Tabs Is Nothing OrElse env.Tabs.Item Is Nothing Then Return

            If Not isEnabled Then
                st = MASVisualInteractionState.Disabled
                showFocusCue = False
            End If

            Dim visualR As SKRect = MASTabPremiumVisualPolicy.ResolveItemVisualRect(itemR, st, env.Dpi)
            If visualR.Width <= 1.0F OrElse visualR.Height <= 1.0F Then Return

            Dim radiusPx As Single =
                MASRadiusResolver.ResolveSurfacePx(
                    contract:=_tabItemContract,
                    rPx:=visualR,
                    dpi:=env.Dpi)

            radiusPx = PixelSnap.SafeRadius(radiusPx)

            Dim chipSpec As MASVisualPrimitivesPainter.ChipBaseSpec = BuildChipBaseSpec(env, st)
            Dim fxSpec As MASVisualPrimitivesPainter.ChipFxSpec = BuildChipFxSpec(env, st)

            If st = MASVisualInteractionState.Selected Then
                _shadow.DrawLayeredShadow(
                    canvas:=canvas,
                    r:=visualR,
                    contract:=_tabItemContract,
                    dpi:=env.Dpi,
                    surfaceTheme:=env.Surface)
            End If

            _p.DrawChipBaseNoShadow(canvas, env.ThemeContext, visualR, radiusPx, env.Dpi, chipSpec)

            _p.DrawChipFxSSOT(
                canvas:=canvas,
                r:=visualR,
                radius:=radiusPx,
                dpi:=env.Dpi,
                spec:=chipSpec,
                fxSpec:=fxSpec)

            DrawStateOverlay(canvas, visualR, st, env)

            DrawBorderHierarchy(canvas, lay, visualR, radiusPx, st, env)

            If showFocusCue Then
                DrawFocusCue(canvas, visualR, env)
            End If
        End Sub

        Private Shared Sub DrawStateOverlay(canvas As SKCanvas,
                                            itemR As SKRect,
                                            st As MASVisualInteractionState,
                                            env As MASTabRenderEnv)

            Dim masState As MASInteractionState = ToMASState(st)

            If masState = MASInteractionState.Enabled OrElse
               masState = MASInteractionState.None Then
                Return
            End If

            Dim spec As MASVisualInteractionOverlaySpec =
                MASInteractionResolver.Build(
                    contract:=_tabItemContract,
                    state:=masState,
                    theme:=env.AppTheme)

            If spec.FillEnabled OrElse spec.RingEnabled Then
                MASSurfaceInteractionRing.DrawOverlay(
                    canvas:=canvas,
                    baseRectPx:=itemR,
                    contract:=_tabItemContract,
                    spec:=spec,
                    dpi:=env.Dpi)
            End If
        End Sub

        Private Sub DrawFocusCue(canvas As SKCanvas,
                                 itemR As SKRect,
                                 env As MASTabRenderEnv)

            Dim spec As MASVisualInteractionOverlaySpec =
                MASInteractionResolver.Build(
                    contract:=_tabItemContract,
                    state:=MASInteractionState.Focused,
                    theme:=env.AppTheme)

            If spec.FillEnabled OrElse spec.RingEnabled Then
                MASSurfaceInteractionRing.DrawOverlay(
                    canvas:=canvas,
                    baseRectPx:=itemR,
                    contract:=_tabItemContract,
                    spec:=spec,
                    dpi:=env.Dpi)
            End If
        End Sub

        Friend Function ResolveTextColor(st As MASVisualInteractionState,
                                         isEnabled As Boolean,
                                         env As MASTabRenderEnv) As SKColor

            Dim baseCol As SKColor = env.Tabs.Text.Normal

            If Not isEnabled OrElse st = MASVisualInteractionState.Disabled Then
                Return env.Tabs.Text.Disabled
            End If

            Select Case st
                Case MASVisualInteractionState.Selected
                    Return ColorMath.Mix(baseCol, env.Foundation.Brand.BrandPrimaryStrong, 0.22F)
                Case MASVisualInteractionState.Hover
                    Return ColorMath.Mix(baseCol, env.Foundation.Brand.BrandHighlight, 0.12F)
                Case MASVisualInteractionState.Pressed
                    Return ColorMath.Mix(baseCol, env.Foundation.Brand.BrandPrimarySoft, 0.16F)
                Case Else
                    Return baseCol
            End Select
        End Function

        Private Function BuildChipBaseSpec(env As MASTabRenderEnv,
                                           st As MASVisualInteractionState) As MASVisualPrimitivesPainter.ChipBaseSpec

            Dim top As SKColor = env.Tabs.Item.BaseTop
            Dim bot As SKColor = env.Tabs.Item.BaseBottom
            Dim midT As Single = 0.56F

            Select Case st
                Case MASVisualInteractionState.Selected
                    top = ColorMath.Mix(top, env.Foundation.Brand.BrandSilverSoft, 0.18F)
                    bot = ColorMath.Mix(bot, env.Foundation.Brand.BrandPrimarySoft, 0.16F)
                    midT = 0.535F

                Case MASVisualInteractionState.Hover
                    top = ColorMath.Mix(top, env.Surface.SurfaceTop, 0.14F)
                    top = ColorMath.Mix(top, env.Foundation.Brand.BrandSilverSoft, 0.12F)
                    bot = ColorMath.Mix(bot, env.Foundation.Brand.BrandHighlight, 0.08F)
                    midT = 0.545F

                Case MASVisualInteractionState.Pressed
                    top = ColorMath.Mix(top, env.Surface.SurfaceMid, 0.12F)
                    bot = ColorMath.Mix(bot, env.Surface.SurfaceBot, 0.1F)
                    midT = 0.585F

                Case MASVisualInteractionState.Disabled
                    top = ColorMath.Mix(top, env.Foundation.Semantics.Disabled, 0.18F)
                    bot = ColorMath.Mix(bot, env.Foundation.Semantics.Disabled, 0.18F)
            End Select

            Return New MASVisualPrimitivesPainter.ChipBaseSpec With {
                .Top = top,
                .Bot = bot,
                .MidT = midT,
                .TopAlpha = MASTabPremiumVisualPolicy.ResolveChipBaseAlpha(st),
                .MidAlpha = MASTabPremiumVisualPolicy.ResolveChipBaseAlpha(st),
                .BotAlpha = MASTabPremiumVisualPolicy.ResolveChipBaseAlpha(st)
            }
        End Function

        Private Function BuildChipFxSpec(env As MASTabRenderEnv,
                                         st As MASVisualInteractionState) As MASVisualPrimitivesPainter.ChipFxSpec

            Dim washColor As SKColor = env.Tabs.Item.WashColor
            Dim glossColor As SKColor = env.Tabs.Item.InnerGlossColor

            Dim washAlpha As Byte = env.Tabs.Item.WashAlpha
            Dim glossAlpha As Byte = env.Tabs.Item.InnerGlossAlpha
            Dim glossFadeT As Single = env.Tabs.Item.InnerGlossFadeT
            Dim insetMul As Single = env.Tabs.Item.InnerInsetMul
            Dim radMul As Single = env.Tabs.Item.InnerRadMul

            Select Case st
                Case MASVisualInteractionState.Selected
                    washColor = ColorMath.Mix(washColor, env.Foundation.Brand.BrandHighlight, 0.18F)
                    glossColor = ColorMath.Mix(glossColor, SKColors.White, 0.14F)
                    washAlpha = ClampByte(env.Tabs.Item.WashAlpha + 14)
                    glossAlpha = ClampByte(env.Tabs.Item.InnerGlossAlpha + 12)
                    glossFadeT = 0.285F
                    insetMul = Math.Max(0.0F, insetMul - 0.004F)
                    radMul += 0.018F

                Case MASVisualInteractionState.Hover
                    washColor = ColorMath.Mix(washColor, env.Foundation.Brand.BrandHighlight, 0.1F)
                    glossColor = ColorMath.Mix(glossColor, SKColors.White, 0.08F)
                    washAlpha = ClampByte(env.Tabs.Item.WashAlpha + 8)
                    glossAlpha = ClampByte(env.Tabs.Item.InnerGlossAlpha + 7)
                    glossFadeT = Math.Max(0.22F, glossFadeT - 0.02F)

                Case MASVisualInteractionState.Pressed
                    washColor = ColorMath.Mix(washColor, env.Surface.SurfaceBot, 0.12F)
                    washAlpha = ClampByte(env.Tabs.Item.WashAlpha - 6)
                    glossAlpha = ClampByte(env.Tabs.Item.InnerGlossAlpha - 10)
                    glossFadeT = 0.36F
                    insetMul += 0.01F
                    radMul -= 0.016F

                Case MASVisualInteractionState.Disabled
                    washAlpha = ClampByte(CInt(Math.Round(washAlpha * 0.65F)))
                    glossAlpha = ClampByte(CInt(Math.Round(glossAlpha * 0.55F)))
            End Select

            washAlpha = MASTabPremiumVisualPolicy.ResolveWashAlpha(washAlpha, st)
            glossAlpha = MASTabPremiumVisualPolicy.ResolveGlossAlpha(glossAlpha, st)

            Return New MASVisualPrimitivesPainter.ChipFxSpec With {
                .WashColor = washColor,
                .WashAlpha = washAlpha,
                .InnerGlossColor = glossColor,
                .InnerGlossAlpha = glossAlpha,
                .InnerGlossFadeT = glossFadeT,
                .InnerInsetMul = insetMul,
                .InnerRadMul = radMul
            }
        End Function

        Private Sub DrawBorderHierarchy(canvas As SKCanvas,
                                        lay As MASTabLayout.Metrics,
                                        baseR As SKRect,
                                        radiusPx As Single,
                                        st As MASVisualInteractionState,
                                        env As MASTabRenderEnv)

            If canvas Is Nothing Then Return

            Dim outerCol As SKColor = env.Tabs.Item.BaseRim
            Dim innerCol As SKColor = ColorMath.Mix(env.Tabs.Item.BaseRim, env.Surface.BorderInner, 0.42F)

            Dim outerAlpha As Byte = MASTabPremiumVisualPolicy.ResolveOuterRimAlpha(st)
            Dim innerAlpha As Byte = MASTabPremiumVisualPolicy.ResolveInnerRimAlpha(st)

            Select Case st
                Case MASVisualInteractionState.Selected
                    outerCol = ColorMath.Mix(outerCol, env.Foundation.Brand.BrandHighlight, 0.26F)
                    innerCol = ColorMath.Mix(innerCol, env.Foundation.Brand.BrandSilverSoft, 0.18F)

                Case MASVisualInteractionState.Hover
                    outerCol = ColorMath.Mix(outerCol, env.Foundation.Brand.BrandHighlight, 0.14F)
            End Select

            MASSurfaceInteractionRing.DrawIdleRim(
                canvas:=canvas,
                baseR:=baseR,
                baseRadiusPx:=radiusPx,
                rimColor:=outerCol,
                rimAlpha:=outerAlpha,
                strokePx:=Math.Max(SurfaceTokens.FxRingMinStrokePx, lay.HairW),
                dpi:=env.Dpi,
                edgeInsetPx:=0.0F)

            MASSurfaceInteractionRing.DrawIdleRim(
                canvas:=canvas,
                baseR:=baseR,
                baseRadiusPx:=radiusPx,
                rimColor:=innerCol,
                rimAlpha:=innerAlpha,
                strokePx:=Math.Max(SurfaceTokens.FxRingMinStrokePx, lay.HairW * 0.9F),
                dpi:=env.Dpi,
                edgeInsetPx:=Math.Max(1.0F, 1.15F * env.Dpi))
        End Sub

        Private Shared Function ToMASState(st As MASVisualInteractionState) As MASInteractionState
            Select Case st
                Case MASVisualInteractionState.Hover
                    Return MASInteractionState.Hovered
                Case MASVisualInteractionState.Pressed
                    Return MASInteractionState.Pressed
                Case MASVisualInteractionState.Selected
                    Return MASInteractionState.Selected
                Case MASVisualInteractionState.Focused
                    Return MASInteractionState.Focused
                Case MASVisualInteractionState.Disabled
                    Return MASInteractionState.Disabled
                Case Else
                    Return MASInteractionState.Enabled
            End Select
        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 Class

End Namespace