Option Strict On
Option Explicit On

Imports System
Imports System.IO
Imports SkiaSharp

Namespace Nexamas.UI.Verification

    ''' <summary>
    ''' Writes the PNG difference artifact for a render-verification result.
    ''' The runner owns baseline/current persistence; this writer owns only the review diff asset used by CI and Showcase.
    ''' Diff PNGs are written only for real visual failures so seeded or matched scenarios do not spend time decoding,
    ''' looping over pixels, and encoding an artifact that the product UI intentionally does not show.
    ''' </summary>
    Friend NotInheritable Class MASRenderDifferenceImageWriter
        Private Sub New()
        End Sub

        Friend Shared Function RequiresDifferenceArtifact(diff As MASRenderDiffResult) As Boolean
            If diff Is Nothing OrElse diff.Scenario Is Nothing Then Return False
            Return diff.Outcome = MASRenderVerificationOutcome.Failed AndAlso diff.DifferentPixelCount > 0 AndAlso diff.PixelCount > 0
        End Function

        Friend Shared Sub WriteDifference(store As MASRenderBaselineStore, diff As MASRenderDiffResult)
            If store Is Nothing Then Throw New ArgumentNullException(NameOf(store))
            If diff Is Nothing OrElse diff.Scenario Is Nothing Then Return

            If Not RequiresDifferenceArtifact(diff) Then
                DeleteDifferenceIfExists(store, diff.Scenario)
                Return
            End If

            Dim baselinePath As String = store.GetBaselinePath(diff.Scenario)
            Dim currentPath As String = store.GetCurrentPath(diff.Scenario)
            Dim differencePath As String = store.GetDifferencePath(diff.Scenario)
            If Not File.Exists(baselinePath) OrElse Not File.Exists(currentPath) Then
                DeleteDifferenceIfExists(store, diff.Scenario)
                Return
            End If

            Using baseline As SKBitmap = SKBitmap.Decode(baselinePath)
                Using current As SKBitmap = SKBitmap.Decode(currentPath)
                    If baseline Is Nothing OrElse current Is Nothing Then
                        DeleteDifferenceIfExists(store, diff.Scenario)
                        Return
                    End If
                    If baseline.Width <> current.Width OrElse baseline.Height <> current.Height Then
                        DeleteDifferenceIfExists(store, diff.Scenario)
                        Return
                    End If

                    Using difference As New SKBitmap(current.Width, current.Height)
                        Dim pixelCount As Integer = current.Width * current.Height
                        Dim expectedPixels As SKColor() = baseline.Pixels
                        Dim actualPixels As SKColor() = current.Pixels
                        Dim differencePixels(pixelCount - 1) As SKColor
                        Dim safeCount As Integer = Math.Min(pixelCount, Math.Min(expectedPixels.Length, actualPixels.Length))

                        For index As Integer = 0 To safeCount - 1
                            Dim delta As Integer = MaxChannelDelta(expectedPixels(index), actualPixels(index))
                            If delta = 0 Then
                                differencePixels(index) = New SKColor(244, 248, 252)
                            Else
                                Dim intensity As Byte = CByte(Math.Max(96, Math.Min(255, delta)))
                                differencePixels(index) = New SKColor(255, CByte(Math.Max(0, 255 - (CInt(intensity) \ 3))), CByte(Math.Max(0, 255 - (CInt(intensity) \ 2))))
                            End If
                        Next

                        For index As Integer = safeCount To pixelCount - 1
                            differencePixels(index) = New SKColor(255, 96, 96)
                        Next

                        difference.Pixels = differencePixels

                        Dim parent As String = Path.GetDirectoryName(differencePath)
                        If Not String.IsNullOrWhiteSpace(parent) Then Directory.CreateDirectory(parent)
                        Using data As SKData = difference.Encode(SKEncodedImageFormat.Png, 92)
                            If data Is Nothing Then Return
                            Using stream As FileStream = File.Open(differencePath, FileMode.Create, FileAccess.Write, FileShare.None)
                                data.SaveTo(stream)
                            End Using
                        End Using
                    End Using
                End Using
            End Using
        End Sub

        Private Shared Sub DeleteDifferenceIfExists(store As MASRenderBaselineStore, scenario As MASRenderVerificationScenario)
            If store Is Nothing OrElse scenario Is Nothing Then Return

            Dim differencePath As String = store.GetDifferencePath(scenario)
            If String.IsNullOrWhiteSpace(differencePath) OrElse Not File.Exists(differencePath) Then Return

            Try
                File.Delete(differencePath)
            Catch masCaughtException1 As Exception
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowBoundary(masCaughtException1, "MASRenderDifferenceImageWriter.DeleteDifferenceIfExists")
            End Try
        End Sub

        Private Shared Function MaxChannelDelta(expected As SKColor, actual As SKColor) As Integer
            Dim a As Integer = Math.Abs(CInt(expected.Alpha) - CInt(actual.Alpha))
            Dim r As Integer = Math.Abs(CInt(expected.Red) - CInt(actual.Red))
            Dim g As Integer = Math.Abs(CInt(expected.Green) - CInt(actual.Green))
            Dim b As Integer = Math.Abs(CInt(expected.Blue) - CInt(actual.Blue))
            Return Math.Max(Math.Max(a, r), Math.Max(g, b))
        End Function
    End Class

End Namespace
