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 "Viewport Rendering"

        Private Sub DrawViewport(canvas As SKCanvas,
                                 ctx As MASThemeContext,
                                 stage As SKRect,
                                 viewport As SKRect,
                                 dpi As Single,
                                 composition As MASComponentSurfaceMaterialComposition,
                                 stageTopCornerRadiusPx As Single)
            If stage.Width <= 1.0F OrElse stage.Height <= 1.0F Then Return
            If viewport.Width <= 1.0F OrElse viewport.Height <= 1.0F Then Return

            DrawMediaStageSurface(canvas, ctx, stage, dpi, composition, stageTopCornerRadiusPx)

            Dim content As SKRect = ResolveViewportContentRect(viewport, dpi)
            _lastContentRect = content
            If content.Width <= 1.0F OrElse content.Height <= 1.0F Then Return

            If Not HasImage Then
                DrawEmptyState(canvas, ctx, content, dpi)
                Return
            End If

            Dim zoomWindow As SKRect = ResolveFixedZoomWindow(content)
            _lastContentRect = zoomWindow
            If zoomWindow.Width <= 1.0F OrElse zoomWindow.Height <= 1.0F Then Return

            _lastFitZoom = ResolveFitZoomForRect(zoomWindow)
            If _fitMode Then
                _zoom = _lastFitZoom
                _panX = 0.0F
                _panY = 0.0F
            ElseIf _zoom <= _lastFitZoom + 0.0001F Then
                _zoom = _lastFitZoom
                _fitMode = True
                _panX = 0.0F
                _panY = 0.0F
            Else
                ClampPanToViewport()
            End If

            DrawImage(canvas, ctx, zoomWindow, dpi)
        End Sub

        Private Sub DrawImage(canvas As SKCanvas,
                              ctx As MASThemeContext,
                              zoomWindow As SKRect,
                              dpi As Single)
            If _bitmap Is Nothing Then Return

            Dim displayed As SKRect = ResolveDisplayedImageRect(zoomWindow)
            If displayed.Width <= 1.0F OrElse displayed.Height <= 1.0F Then Return

            Dim imageCarriesIndependentFrame As Boolean = Not _showFooter
            If imageCarriesIndependentFrame Then
                DrawImageAura(canvas, ctx, zoomWindow, dpi)
            End If

            canvas.Save()
            ClipToImageRoundRect(canvas, zoomWindow, dpi, imageCarriesIndependentFrame)

            Dim center As New SKPoint(zoomWindow.MidX + _panX, zoomWindow.MidY + _panY)
            canvas.Translate(center.X, center.Y)
            canvas.RotateDegrees(CSng(_rotationDegrees))
            canvas.Scale(If(_flippedHorizontal, -_zoom, _zoom), If(_flippedVertical, -_zoom, _zoom))

            Using paint As New SKPaint With {.IsAntialias = True, .FilterQuality = SKFilterQuality.High}
                Dim dest As New SKRect(-_bitmap.Width / 2.0F, -_bitmap.Height / 2.0F, _bitmap.Width / 2.0F, _bitmap.Height / 2.0F)
                canvas.DrawBitmap(_bitmap, dest, paint)
            End Using

            canvas.Restore()

            If imageCarriesIndependentFrame Then
                DrawImageBorder(canvas, ctx, zoomWindow, zoomWindow, dpi)
            End If
        End Sub

        Private Shared Sub ClipToImageRoundRect(canvas As SKCanvas,
                                               rect As SKRect,
                                               dpi As Single,
                                               bottomCornersRounded As Boolean)
            If canvas Is Nothing OrElse rect.Width <= 1.0F OrElse rect.Height <= 1.0F Then Return

            Dim radius As Single = ResolveImageCornerRadius(rect, dpi)
            If radius <= 0.0F Then
                canvas.ClipRect(rect, SKClipOperation.Intersect, True)
                Return
            End If

            Using clipPath As New SKPath()
                If bottomCornersRounded Then
                    clipPath.AddRoundRect(rect, radius, radius)
                Else
                    Dim rr As New SKRoundRect()
                    Dim zero As New SKPoint(0.0F, 0.0F)
                    Dim r As New SKPoint(radius, radius)
                    rr.SetRectRadii(rect, New SKPoint() {r, r, zero, zero})
                    clipPath.AddRoundRect(rr)
                End If
                canvas.ClipPath(clipPath, SKClipOperation.Intersect, True)
            End Using
        End Sub

        Private Shared Function ResolveImageCornerRadius(rect As SKRect,
                                                         dpi As Single) As Single
            If rect.Width <= 1.0F OrElse rect.Height <= 1.0F Then Return 0.0F
            dpi = PixelSnap.SafeDpi(dpi)

            ' Keep the displayed image visually aligned with the inner preview frame
            ' that groups the media stage and footer.  The image must not carry an
            ' independent radius, otherwise its corners drift away from the frame
            ' radius and the footer rail in themed Media compositions.
            Dim requested As Single = MediaContainerTokens.PreviewFrameRadiusDip * dpi
            Return PixelSnap.SafeRadius(Math.Min(requested, Math.Min(rect.Width, rect.Height) * 0.5F))
        End Function

        Private Sub DrawImageAura(canvas As SKCanvas,
                                  ctx As MASThemeContext,
                                  imageRect As SKRect,
                                  dpi As Single)
            If canvas Is Nothing OrElse imageRect.Width <= 1.0F OrElse imageRect.Height <= 1.0F Then Return
            If ImageViewerTokens.ImageShadowAlpha = 0 Then Return

            Dim radius As Single = ResolveImageCornerRadius(imageRect, dpi)
            Dim shadowRect As New SKRect(imageRect.Left,
                                         imageRect.Top + ImageViewerTokens.ImageShadowOffsetDip * dpi,
                                         imageRect.Right,
                                         imageRect.Bottom + ImageViewerTokens.ImageShadowOffsetDip * dpi)
            MASShadowPainter.DrawFlatRoundRectShadow(
                canvas:=canvas,
                rectPx:=shadowRect,
                radiusPx:=radius,
                color:=SKColors.Black.WithAlpha(ImageViewerTokens.ImageShadowAlpha))
        End Sub

        Private Sub DrawImageBorder(canvas As SKCanvas,
                                    ctx As MASThemeContext,
                                    stage As SKRect,
                                    imageRect As SKRect,
                                    dpi As Single)
            If canvas Is Nothing OrElse imageRect.Width <= 1.0F OrElse imageRect.Height <= 1.0F Then Return

            If ImageViewerTokens.ImageBorderAlpha = 0 Then Return

            canvas.Save()
            canvas.ClipRect(stage, SKClipOperation.Intersect, True)
            Using borderPaint As New SKPaint With {
                .IsAntialias = True,
                .Style = SKPaintStyle.Stroke,
                .StrokeWidth = Math.Max(1.0F, ImageViewerTokens.ImageBorderStrokeDip * dpi),
                .Color = ColorMath.WithAlpha(MASMediaVisualStyleResolver.Resolve(ctx, dpi).Stage.BorderOuter, ImageViewerTokens.ImageBorderAlpha)
            }
                Dim radius As Single = ResolveImageCornerRadius(imageRect, dpi)
                canvas.DrawRoundRect(imageRect, radius, radius, borderPaint)
            End Using
            canvas.Restore()
        End Sub

        Private Function ResolveDisplayedImageRect(content As SKRect) As SKRect
            If _bitmap Is Nothing Then Return SKRect.Empty
            Return MASImageViewerViewportGeometry.ResolveDisplayedRect(content,
                                                                       _bitmap.Width,
                                                                       _bitmap.Height,
                                                                       _rotationDegrees,
                                                                       _zoom,
                                                                       _panX,
                                                                       _panY)
        End Function

        Private Shared Function ResolveViewportContentRect(viewport As SKRect,
                                                          dpi As Single) As SKRect
            Return MASMediaViewportLayoutResolver.ResolveContentRect(viewport, dpi)
        End Function

        Private Function ResolveFixedZoomWindow(content As SKRect) As SKRect
            If _bitmap Is Nothing Then Return content
            If content.Width <= 1.0F OrElse content.Height <= 1.0F Then Return SKRect.Empty

            ' The safeguarded content window is the real media window.  Do not shrink
            ' it to the fitted bitmap and do not move residual letterbox space to the
            ' footer.  Fit zoom is resolved inside this reusable Media viewport window.
            Return MASMediaViewportLayoutResolver.ResolveFixedContentWindow(content)
        End Function

        Private Sub DrawEmptyState(canvas As SKCanvas,
                                   ctx As MASThemeContext,
                                   rect As SKRect,
                                   dpi As Single)
            Dim glyphSize As Single = ImageViewerTokens.EmptyGlyphSizeDip * dpi
            Dim glyph As New SKRect(rect.MidX - glyphSize / 2.0F, rect.MidY - glyphSize - 4.0F * dpi, rect.MidX + glyphSize / 2.0F, rect.MidY - 4.0F * dpi)
            Using paint As New SKPaint With {.IsAntialias = True, .Style = SKPaintStyle.Stroke, .StrokeWidth = Math.Max(1.0F, 2.0F * dpi), .Color = ResolveAccentColor(ctx).WithAlpha(ImageViewerTokens.MutedTextAlpha)}
                canvas.DrawRoundRect(glyph, 10.0F * dpi, 10.0F * dpi, paint)
                canvas.DrawLine(glyph.Left + 8.0F * dpi, glyph.Bottom - 10.0F * dpi, glyph.Left + 20.0F * dpi, glyph.Top + 24.0F * dpi, paint)
                canvas.DrawLine(glyph.Left + 20.0F * dpi, glyph.Top + 24.0F * dpi, glyph.Right - 8.0F * dpi, glyph.Bottom - 10.0F * dpi, paint)
            End Using

            Dim titleRect As New SKRect(rect.Left, rect.MidY + 4.0F * dpi, rect.Right, rect.MidY + 28.0F * dpi)
            TypographyTextPrimitives.DrawSingleLineText(canvas:=canvas, ctx:=ctx, text:=ImageViewerTokens.EmptyText, bounds:=titleRect, dpi:=dpi, style:=MASTypography.MASTextStyle.Body, color:=ResolvePrimaryTextColor(ctx).WithAlpha(ImageViewerTokens.TextAlpha), align:=TypographyTextPrimitives.TextAlign.Center, drawShadow:=False)
            Dim detailRect As New SKRect(rect.Left, titleRect.Bottom + 2.0F * dpi, rect.Right, titleRect.Bottom + 22.0F * dpi)
            TypographyTextPrimitives.DrawSingleLineText(canvas:=canvas, ctx:=ctx, text:=ImageViewerTokens.EmptyDetail, bounds:=detailRect, dpi:=dpi, style:=MASTypography.MASTextStyle.Small, color:=ResolveMutedTextColor(ctx).WithAlpha(ImageViewerTokens.MutedTextAlpha), align:=TypographyTextPrimitives.TextAlign.Center, drawShadow:=False)
        End Sub


        Private Sub DrawImageFileGlyph(canvas As SKCanvas,
                                       iconRect As SKRect,
                                       dpi As Single,
                                       color As SKColor)
            If canvas Is Nothing OrElse iconRect.Width <= 1.0F OrElse iconRect.Height <= 1.0F Then Return
            Using p As New SKPaint With {
                .IsAntialias = True,
                .Style = SKPaintStyle.Stroke,
                .StrokeWidth = Math.Max(1.0F, 1.4F * dpi),
                .Color = color,
                .StrokeCap = SKStrokeCap.Round,
                .StrokeJoin = SKStrokeJoin.Round
            }
                canvas.DrawRoundRect(iconRect, 4.0F * dpi, 4.0F * dpi, p)
                canvas.DrawCircle(iconRect.Left + iconRect.Width * 0.68F, iconRect.Top + iconRect.Height * 0.32F, 2.2F * dpi, p)
                canvas.DrawLine(iconRect.Left + iconRect.Width * 0.16F, iconRect.Bottom - iconRect.Height * 0.22F, iconRect.Left + iconRect.Width * 0.40F, iconRect.Top + iconRect.Height * 0.54F, p)
                canvas.DrawLine(iconRect.Left + iconRect.Width * 0.40F, iconRect.Top + iconRect.Height * 0.54F, iconRect.Left + iconRect.Width * 0.58F, iconRect.Bottom - iconRect.Height * 0.32F, p)
                canvas.DrawLine(iconRect.Left + iconRect.Width * 0.58F, iconRect.Bottom - iconRect.Height * 0.32F, iconRect.Right - iconRect.Width * 0.14F, iconRect.Bottom - iconRect.Height * 0.18F, p)
            End Using
        End Sub

        Private Sub DrawZoomBadge(canvas As SKCanvas,
                                  ctx As MASThemeContext,
                                  rect As SKRect,
                                  dpi As Single,
                                  accent As SKColor,
                                  textColor As SKColor)
            If rect.Width <= 1.0F OrElse rect.Height <= 1.0F Then Return
            dpi = PixelSnap.SafeDpi(dpi)

            Dim mediaStyle As MASMediaVisualStyle = MASMediaVisualStyleResolver.Resolve(ctx, dpi)
            MASMediaControlPainter.DrawChipSurface(canvas:=canvas,
                                                   rect:=rect,
                                                   style:=mediaStyle,
                                                   dpi:=dpi,
                                                   selected:=True)

            Dim percent As Integer = CInt(Math.Round(_zoom * 100.0F, MidpointRounding.AwayFromZero))
            Dim dotSize As Single = Math.Max(6.0F, MediaFooterTokens.StatusDotSizeDip * dpi)
            Dim dotRadius As Single = dotSize * 0.5F
            Dim dotCenterX As Single = rect.Left + 17.0F * dpi
            Dim dotCenterY As Single = rect.MidY
            Dim dotColor As SKColor = ResolveFooterStatusDotColor(ctx, accent)

            Using dot As New SKPaint With {
                .IsAntialias = True,
                .Style = SKPaintStyle.Fill,
                .Color = dotColor.WithAlpha(224)
            }
                canvas.DrawCircle(dotCenterX, dotCenterY, dotRadius, dot)
            End Using

            Dim textRect As New SKRect(rect.Left + 28.0F * dpi,
                                       rect.Top,
                                       rect.Right - 10.0F * dpi,
                                       rect.Bottom)
            TypographyTextPrimitives.DrawSingleLineText(canvas:=canvas,
                                                        ctx:=ctx,
                                                        text:=percent.ToString(CultureInfo.InvariantCulture) & "%",
                                                        bounds:=textRect,
                                                        dpi:=dpi,
                                                        style:=MASTypography.MASTextStyle.Body,
                                                        color:=textColor.WithAlpha(CByte(Math.Min(255, CInt(ImageViewerTokens.TextAlpha) + 4))),
                                                        align:=TypographyTextPrimitives.TextAlign.Center,
                                                        drawShadow:=False)
        End Sub

        Private Shared Function ResolveFooterStatusDotColor(ctx As MASThemeContext,
                                                            fallbackAccent As SKColor) As SKColor
            If ctx IsNot Nothing AndAlso ctx.SemanticTheme IsNot Nothing Then Return ctx.SemanticTheme.Success
            Return fallbackAccent
        End Function


#End Region

    End Class

End Namespace
