Option Strict On
Option Explicit On

Imports System
Imports System.Globalization
Imports System.IO
Imports System.Windows.Forms
Imports Nexamas.UI.Architecture
Imports Nexamas.UI.Composition
Imports Nexamas.UI.Controls
Imports Nexamas.UI.General
Imports Nexamas.UI.Layout
Imports Nexamas.UI.Rendering
Imports Nexamas.UI.TextRendering
Imports Nexamas.UI.Theming
Imports Nexamas.UI.Values
Imports SkiaSharp

Namespace Nexamas.UI.Components

    Partial Public NotInheritable Class MASImageViewer
#Region "Rendering Entry And Layout"

        Protected Overrides Sub Render(canvas As SKCanvas,
                                       ctx As MASThemeContext,
                                       pixelBounds As SKRect)
            If canvas Is Nothing OrElse ctx Is Nothing Then Return
            If pixelBounds.Width <= 1.0F OrElse pixelBounds.Height <= 1.0F Then Return

            _contract.AssertConsumable()

            Dim dpi As Single = PixelSnap.SafeDpi(ctx.Dpi)
            Dim bounds As SKRect = PixelSnap.SnapRect(pixelBounds, dpi)
            Dim responsiveProfile As MASResponsiveLayoutProfile = ResolveResponsiveLayoutProfile(ctx, bounds)
            Dim chromeScale As Single = responsiveProfile.ChromeScale
            Dim surfaceComposition As MASComponentSurfaceMaterialComposition = MASImageViewerSurfacePolicy.CreateComposition()
            Dim shellSpec As MASPanelShellSpec = CreatePanelShellSpec(bounds, dpi)
            Dim shellResult As MASPanelShellResult = MASPanelShellRenderer.Draw(
                canvas:=canvas,
                ctx:=ctx,
                dpi:=dpi,
                spec:=shellSpec)

            If shellResult Is Nothing OrElse shellResult.ContentRectPx.Width <= 1.0F OrElse shellResult.ContentRectPx.Height <= 1.0F Then Return

            Dim layout As MASMediaContainerLayoutPlan = ResolveLayout(shellResult.ContentRectPx, dpi, chromeScale)
            If layout.Surface.Width <= 1.0F OrElse layout.Surface.Height <= 1.0F Then Return

            Dim renderToolbar As Boolean = ShouldRenderToolbarChrome() AndAlso layout.Toolbar.Width > 1.0F AndAlso layout.Toolbar.Height > 1.0F

            _lastViewerFrameRect = layout.PreviewFrame
            _lastToolbarRect = If(ToolbarChromeAllowed(), layout.Toolbar, SKRect.Empty)
            _lastViewportRect = layout.Stage

            canvas.Save()
            ClipToPanelShell(canvas, shellResult)

            Try
                Dim railCornerRadiusPx As Single = ResolvePanelShellRailCornerRadius(shellResult, layout.Surface, dpi)
                Dim previewCornerRadiusPx As Single = ResolvePreviewFrameCornerRadius(layout.PreviewFrame, dpi)
                Dim previewRailCornerRadiusPx As Single = PixelSnap.RadiusMinus(previewCornerRadiusPx, MediaContainerTokens.PreviewFrameStrokeSafeInsetDip * dpi)

                If Not renderToolbar Then
                    _buttons = Array.Empty(Of MASMediaButtonModel)()
                End If

                DrawPreviewFrameSurface(canvas, ctx, layout.PreviewFrame, dpi, surfaceComposition, previewCornerRadiusPx)
                DrawViewport(canvas, ctx, layout.Stage, layout.Viewport, dpi, surfaceComposition, previewRailCornerRadiusPx)

                If renderToolbar AndAlso Not layout.ToolbarFloating Then
                    DrawToolbar(canvas, ctx, layout.Toolbar, dpi, MASImageViewerVisualPolicy.ResolveRightToLeft(), False, surfaceComposition, railCornerRadiusPx, chromeScale)
                End If

                If _showFooter AndAlso layout.Footer.Width > 1.0F AndAlso layout.Footer.Height > 1.0F Then
                    DrawFooter(canvas, ctx, layout.Footer, dpi, surfaceComposition, previewRailCornerRadiusPx, chromeScale)
                    DrawPreviewFrameFooterDivider(canvas, ctx, layout.Footer, dpi)
                End If

                DrawPreviewFrameOutline(canvas, ctx, layout.PreviewFrame, dpi, previewCornerRadiusPx)

                If renderToolbar AndAlso layout.ToolbarFloating Then
                    DrawToolbar(canvas, ctx, layout.Toolbar, dpi, MASImageViewerVisualPolicy.ResolveRightToLeft(), True, surfaceComposition, railCornerRadiusPx, chromeScale)
                End If
            Finally
                canvas.Restore()
            End Try
        End Sub

        Private Function ResolveResponsiveLayoutProfile(ctx As MASThemeContext,
                                                         pixelBounds As SKRect) As MASResponsiveLayoutProfile
            Return MASResponsiveLayoutResolver.FromTheme(ctx, pixelBounds, SizeIntent)
        End Function

        Private Function ResolveLayout(bounds As SKRect,
                                       dpi As Single,
                                       chromeScale As Single) As MASMediaContainerLayoutPlan
            Dim buttonSpecs As MASMediaButtonSpec() = ResolveToolbarButtonSpecs()
            Return MASMediaContainerLayoutResolver.Resolve(
                bounds:=bounds,
                dpi:=dpi,
                toolbarChromeAllowed:=ToolbarChromeAllowed(),
                reserveToolbarChrome:=ShouldReserveToolbarChrome(),
                showFooter:=_showFooter,
                toolbarActionCount:=buttonSpecs.Length,
                toolbarGroupBreakCount:=MASMediaButtonStripLayoutResolver.CountGroupBreaks(buttonSpecs),
                chromeScale:=chromeScale)
        End Function

        Private Shared Function CreatePanelShellSpec(bounds As SKRect,
                                                     dpi As Single) As MASPanelShellSpec
            Dim shellBounds As SKRect = ResolvePanelShellBounds(bounds, dpi)

            Return MASPanelShellSpec.Create(shellBounds).
                WithoutHeader().
                WithDensity(MASPanelShellDensity.Normal).
                WithPadding(MediaContainerTokens.PanelShellContentPaddingDip).
                WithStrengths(
                    border:=MASPanelShellStrength.Level5,
                    surface:=MASPanelShellStrength.Level5).
                WithSurfaceMaterialKey(MASImageViewerSurfacePolicy.ResolveRootMaterialKey()).
                Build()
        End Function

        Private Shared Function ResolvePanelShellBounds(bounds As SKRect,
                                                        dpi As Single) As SKRect
            dpi = PixelSnap.SafeDpi(dpi)
            Dim inset As Single = Math.Max(0.0F, MediaContainerTokens.PanelShellHostInsetDip * dpi)
            Dim shellBounds As SKRect = PixelSnap.SnapRectHalf(
                New SKRect(bounds.Left + inset,
                           bounds.Top + inset,
                           bounds.Right - inset,
                           bounds.Bottom - inset))

            If shellBounds.Width <= 1.0F OrElse shellBounds.Height <= 1.0F Then
                Return PixelSnap.SnapRectHalf(bounds)
            End If

            Return shellBounds
        End Function

        Private Shared Sub ClipToPanelShell(canvas As SKCanvas,
                                            shellResult As MASPanelShellResult)
            If canvas Is Nothing OrElse shellResult Is Nothing Then Return
            If shellResult.BaseRectPx.Width <= 1.0F OrElse shellResult.BaseRectPx.Height <= 1.0F Then Return

            Dim radius As Single = Math.Max(0.0F, shellResult.RadiusPx)
            If radius <= 0.0F Then
                canvas.ClipRect(shellResult.BaseRectPx, SKClipOperation.Intersect, True)
                Return
            End If

            Using clipPath As New SKPath()
                clipPath.AddRoundRect(shellResult.BaseRectPx, radius, radius)
                canvas.ClipPath(clipPath, SKClipOperation.Intersect, True)
            End Using
        End Sub

        Private Function ToolbarChromeAllowed() As Boolean
            Return _chromeMode <> MASImageViewerChromeMode.Hidden
        End Function

        Private Function ShouldReserveToolbarChrome() As Boolean
            Return ToolbarChromeAllowed() AndAlso _chromeMode = MASImageViewerChromeMode.Pinned
        End Function

        Private Function ShouldRenderToolbarChrome() As Boolean
            If Not ToolbarChromeAllowed() Then Return False
            If _chromeMode = MASImageViewerChromeMode.Pinned Then Return True
            Return _chromeHot OrElse _hoverAction <> ImageViewerAction.None OrElse _pressedAction <> ImageViewerAction.None
        End Function

        Private Shared Function ResolvePreviewFrameCornerRadius(previewFrame As SKRect,
                                                                 dpi As Single) As Single
            If previewFrame.Width <= 1.0F OrElse previewFrame.Height <= 1.0F Then Return 0.0F
            dpi = PixelSnap.SafeDpi(dpi)
            Dim requested As Single = Math.Max(0.0F, MediaContainerTokens.PreviewFrameRadiusDip * dpi)
            Return PixelSnap.SafeRadius(Math.Min(requested, Math.Min(previewFrame.Width, previewFrame.Height) * 0.5F))
        End Function

        Private Shared Sub DrawPreviewFrameSurface(canvas As SKCanvas,
                                                   ctx As MASThemeContext,
                                                   frameRect As SKRect,
                                                   dpi As Single,
                                                   composition As MASComponentSurfaceMaterialComposition,
                                                   cornerRadiusPx As Single)
            If canvas Is Nothing OrElse frameRect.Width <= 1.0F OrElse frameRect.Height <= 1.0F Then Return

            Dim surfaceStrength As Integer = MASImageViewerSurfacePolicy.ResolveSurfaceStrengthLevel(MASComponentSurfaceRegion.Viewport, 2)
            Dim borderStrength As Integer = MASImageViewerSurfacePolicy.ResolveBorderStrengthLevel(MASComponentSurfaceRegion.Viewport, 2)
            Dim frameStrength As MASSurfaceFrameStrength = MASImageViewerSurfacePolicy.ResolveFrameStrength(MASComponentSurfaceRegion.Viewport, MASSurfaceFrameStrength.Level1)

            MASSurfaceMaterialComponentGateway.TryDrawComposedMediaStageSurface(
                canvas:=canvas,
                ctx:=ctx,
                boundsPx:=frameRect,
                composition:=composition,
                region:=MASComponentSurfaceRegion.Viewport,
                fallbackMaterialKey:=MASImageViewerSurfacePolicy.ResolveFallbackMaterialKey(MASComponentSurfaceRegion.Viewport),
                dpi:=dpi,
                cornerRadiusPx:=cornerRadiusPx,
                opacity:=1.0F,
                surfaceStrengthLevel:=surfaceStrength,
                borderStrengthLevel:=borderStrength,
                frameStrength:=frameStrength)
        End Sub

        Private Shared Sub DrawPreviewFrameOutline(canvas As SKCanvas,
                                                   ctx As MASThemeContext,
                                                   frameRect As SKRect,
                                                   dpi As Single,
                                                   cornerRadiusPx As Single)
            If canvas Is Nothing OrElse ctx Is Nothing Then Return
            If frameRect.Width <= 1.0F OrElse frameRect.Height <= 1.0F Then Return
            dpi = PixelSnap.SafeDpi(dpi)

            Dim strokeWidth As Single = Math.Max(1.0F, MediaContainerTokens.PreviewFrameOutlineStrokeDip * dpi)
            Dim outlineRect As New SKRect(frameRect.Left + strokeWidth * 0.5F,
                                          frameRect.Top + strokeWidth * 0.5F,
                                          frameRect.Right - strokeWidth * 0.5F,
                                          frameRect.Bottom - strokeWidth * 0.5F)
            If outlineRect.Width <= 1.0F OrElse outlineRect.Height <= 1.0F Then Return

            Dim radius As Single = PixelSnap.SafeRadius(Math.Max(0.0F, cornerRadiusPx - strokeWidth * 0.5F))
            Dim style As MASMediaVisualStyle = MASMediaVisualStyleResolver.Resolve(ctx, dpi)

            Using stroke As New SKPaint With {
                .IsAntialias = True,
                .Style = SKPaintStyle.Stroke,
                .StrokeWidth = strokeWidth,
                .Color = ColorMath.WithAlpha(style.Stage.BorderOuter, MediaContainerTokens.PreviewFrameOutlineAlpha)
            }
                canvas.DrawRoundRect(outlineRect, radius, radius, stroke)
            End Using
        End Sub

        Private Shared Sub DrawPreviewFrameFooterDivider(canvas As SKCanvas,
                                                          ctx As MASThemeContext,
                                                          footerRect As SKRect,
                                                          dpi As Single)
            If canvas Is Nothing OrElse ctx Is Nothing Then Return
            If footerRect.Width <= 1.0F OrElse footerRect.Height <= 1.0F Then Return
            dpi = PixelSnap.SafeDpi(dpi)

            Dim strokeWidth As Single = Math.Max(1.0F, MediaContainerTokens.PreviewFrameFooterDividerStrokeDip * dpi)
            Dim y As Single = PixelSnap.SnapHalf(footerRect.Top + strokeWidth * 0.5F)
            Dim xInset As Single = Math.Max(1.0F, MediaContainerTokens.PreviewFrameStrokeSafeInsetDip * dpi)
            Dim lineLeft As Single = PixelSnap.SnapHalf(footerRect.Left + xInset)
            Dim lineRight As Single = PixelSnap.SnapHalf(footerRect.Right - xInset)
            If lineRight <= lineLeft Then Return

            Dim style As MASMediaVisualStyle = MASMediaVisualStyleResolver.Resolve(ctx, dpi)
            Using stroke As New SKPaint With {
                .IsAntialias = True,
                .Style = SKPaintStyle.Stroke,
                .StrokeWidth = strokeWidth,
                .Color = ColorMath.WithAlpha(style.Stage.BorderOuter, MediaContainerTokens.PreviewFrameFooterDividerAlpha)
            }
                canvas.DrawLine(lineLeft, y, lineRight, y, stroke)
            End Using
        End Sub


        Private Sub DrawMediaStageSurface(canvas As SKCanvas,
                                          ctx As MASThemeContext,
                                          bounds As SKRect,
                                          dpi As Single,
                                          composition As MASComponentSurfaceMaterialComposition,
                                          topCornerRadiusPx As Single)
            If bounds.Width <= 1.0F OrElse bounds.Height <= 1.0F Then Return

            Dim surfaceStrength As Integer = MASImageViewerSurfacePolicy.ResolveSurfaceStrengthLevel(MASComponentSurfaceRegion.Viewport, 2)
            Dim borderStrength As Integer = MASImageViewerSurfacePolicy.ResolveBorderStrengthLevel(MASComponentSurfaceRegion.Viewport, 1)
            Dim frameStrength As MASSurfaceFrameStrength = MASImageViewerSurfacePolicy.ResolveFrameStrength(MASComponentSurfaceRegion.Viewport, MASSurfaceFrameStrength.Level1)

            canvas.Save()
            ClipToPanelShellTopRail(canvas, bounds, topCornerRadiusPx)
            Try
                MASSurfaceMaterialComponentGateway.TryDrawComposedMediaStageSurface(
                    canvas:=canvas,
                    ctx:=ctx,
                    boundsPx:=bounds,
                    composition:=composition,
                    region:=MASComponentSurfaceRegion.Viewport,
                    fallbackMaterialKey:=MASImageViewerSurfacePolicy.ResolveFallbackMaterialKey(MASComponentSurfaceRegion.Viewport),
                    dpi:=dpi,
                    cornerRadiusPx:=0.0F,
                    opacity:=1.0F,
                    surfaceStrengthLevel:=surfaceStrength,
                    borderStrengthLevel:=borderStrength,
                    frameStrength:=frameStrength)
            Finally
                canvas.Restore()
            End Try
        End Sub



#End Region

    End Class

End Namespace
