Imports SkiaSharp

Namespace Nexamas.UI.Verification

    ''' <summary>
    ''' Owns deterministic pixel comparison for render verification.
    ''' The comparer does not render controls and does not perform file IO; it only compares already-captured bitmaps.
    ''' </summary>
    Friend NotInheritable Class MASRenderImageComparer

        Private Sub New()
        End Sub

        Friend Shared Function Compare(scenario As MASRenderVerificationScenario,
                                       baseline As SKBitmap,
                                       current As SKBitmap,
                                       settings As MASRenderDiffSettings) As MASRenderDiffResult
            If scenario Is Nothing Then Throw New ArgumentNullException(NameOf(scenario))
            If settings Is Nothing Then settings = MASRenderDiffSettings.DefaultTolerance

            If baseline Is Nothing OrElse current Is Nothing Then
                Return New MASRenderDiffResult(scenario, MASRenderVerificationOutcome.Invalid, 0, 0, 0, settings, "Baseline or current bitmap is missing.")
            End If

            If baseline.Width <> current.Width OrElse baseline.Height <> current.Height Then
                Dim message = "Bitmap dimensions differ. Baseline=" & baseline.Width & "x" & baseline.Height & ", Current=" & current.Width & "x" & current.Height & "."
                Return New MASRenderDiffResult(scenario, MASRenderVerificationOutcome.Failed, 0, 0, 0, settings, message)
            End If

            Dim differentPixels As Integer = 0
            Dim maxDelta As Integer = 0
            Dim pixelCount As Integer = baseline.Width * baseline.Height
            Dim expectedPixels As SKColor() = baseline.Pixels
            Dim actualPixels As SKColor() = current.Pixels
            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 > maxDelta Then maxDelta = delta
                If delta > settings.ChannelTolerance Then differentPixels += 1
            Next

            If safeCount <> pixelCount Then
                differentPixels += Math.Max(0, pixelCount - safeCount)
                maxDelta = Math.Max(maxDelta, 255)
            End If

            Dim ratio As Double = If(pixelCount <= 0, 1.0R, CDbl(differentPixels) / CDbl(pixelCount))
            Dim passed As Boolean = ratio <= settings.MaxDifferentPixelRatio
            Dim outcome = If(passed, MASRenderVerificationOutcome.Passed, MASRenderVerificationOutcome.Failed)
            Dim resultMessage = If(passed, "Screenshot matches approved baseline.", "Screenshot differs from approved baseline.")
            Return New MASRenderDiffResult(scenario, outcome, pixelCount, differentPixels, maxDelta, settings, resultMessage)
        End Function

        Private Shared Function MaxChannelDelta(expected As SKColor, actual As SKColor) As Integer
            Dim a = Math.Abs(CInt(expected.Alpha) - CInt(actual.Alpha))
            Dim r = Math.Abs(CInt(expected.Red) - CInt(actual.Red))
            Dim g = Math.Abs(CInt(expected.Green) - CInt(actual.Green))
            Dim b = 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
