'========================================================
' File: MASTileBox.BitmapOwnership.vb
'========================================================
Option Strict On
Option Explicit On

Imports System
Imports SkiaSharp

Namespace Nexamas.UI.Components

    Partial Public NotInheritable Class MASTileBox

        Public NotInheritable Class MASTileItem
            Implements IDisposable

            Public Property Title As String = ""
            Public Property Subtitle As String = ""
            Public Property Tag As Object = Nothing
            Public Property IconId As Guid = Guid.Empty

            Private ReadOnly _bitmapGate As New Object()
            Private _bitmap As SKBitmap = Nothing
            Private _bitmapDisposer As Action(Of SKBitmap) = Nothing
            Private _bitmapProvider As Func(Of SKBitmap) = Nothing
            Private _providerBitmapDisposer As Action(Of SKBitmap) = Nothing
            Private _ownedProviderSnapshotBitmap As SKBitmap = Nothing
            Private _ownedProviderSnapshotDisposer As Action(Of SKBitmap) = Nothing
            Private _bitmapSnapshot As MASTileBitmapRenderSnapshot = MASTileBitmapRenderSnapshot.Empty
            Private _bitmapSnapshotRevision As Integer = 0
            Private _disposed As Boolean

            Public Sub New()
            End Sub

            Public Sub New(title As String, subtitle As String, bmp As SKBitmap)
                Me.New(title, subtitle, bmp, DirectCast(Nothing, Action(Of SKBitmap)))
            End Sub

            ''' <summary>
            ''' Creates a tile item from a bitmap and optionally transfers native bitmap
            ''' ownership to the tile item.
            ''' </summary>
            ''' <remarks>
            ''' The default MASTileBox bitmap contract remains borrowed. Pass
            ''' <paramref name="takeOwnership"/> only when the tile item should dispose the
            ''' bitmap when the item/control is disposed or when owned item resources are
            ''' released during replacement/clearing.
            ''' </remarks>
            Public Sub New(title As String,
                           subtitle As String,
                           bmp As SKBitmap,
                           takeOwnership As Boolean)
                Me.New(title, subtitle, bmp, ResolveDefaultBitmapDisposer(takeOwnership))
            End Sub

            ''' <summary>
            ''' Creates a tile item from a bitmap and an explicit ownership disposer.
            ''' </summary>
            ''' <remarks>
            ''' Passing Nothing keeps the bitmap borrowed. Passing a disposer makes the tile
            ''' item responsible for releasing the bitmap through that disposer.
            ''' </remarks>
            Public Sub New(title As String,
                           subtitle As String,
                           bmp As SKBitmap,
                           bitmapDisposer As Action(Of SKBitmap))
                Me.Title = If(title, "")
                Me.Subtitle = If(subtitle, "")
                Me.IconId = Guid.Empty
                _bitmap = bmp
                _bitmapDisposer = bitmapDisposer
                _bitmapProvider = Nothing
                _providerBitmapDisposer = Nothing
                RefreshBitmapRenderSnapshot()
            End Sub

            Public Sub New(title As String, subtitle As String, iconId As Guid, provider As Func(Of SKBitmap))
                Me.New(title, subtitle, iconId, provider, DirectCast(Nothing, Action(Of SKBitmap)))
            End Sub

            ''' <summary>
            ''' Creates a provider-backed tile item and optionally transfers ownership of
            ''' resolved provider snapshots to the tile item.
            ''' </summary>
            ''' <remarks>
            ''' The default provider contract remains borrowed. Use ownership only when the
            ''' provider returns a fresh bitmap that no external cache will dispose.
            ''' </remarks>
            Public Sub New(title As String,
                           subtitle As String,
                           iconId As Guid,
                           provider As Func(Of SKBitmap),
                           takeOwnership As Boolean)
                Me.New(title, subtitle, iconId, provider, ResolveDefaultBitmapDisposer(takeOwnership))
            End Sub

            ''' <summary>
            ''' Creates a provider-backed tile item with an explicit snapshot disposer.
            ''' </summary>
            ''' <remarks>
            ''' Passing Nothing keeps resolved provider bitmaps borrowed. Passing a disposer
            ''' makes the tile item release the previous owned provider snapshot before a new
            ''' one is installed and when the item/control is disposed.
            ''' </remarks>
            Public Sub New(title As String,
                           subtitle As String,
                           iconId As Guid,
                           provider As Func(Of SKBitmap),
                           bitmapDisposer As Action(Of SKBitmap))
                If provider Is Nothing Then
                    Throw New ArgumentNullException(NameOf(provider))
                End If

                Me.Title = If(title, "")
                Me.Subtitle = If(subtitle, "")
                Me.IconId = iconId
                _bitmapProvider = provider
                _providerBitmapDisposer = bitmapDisposer
                _bitmap = Nothing
                _bitmapDisposer = Nothing
            End Sub

            ''' <summary>
            ''' Returns the currently resolved drawable bitmap for external compatibility.
            ''' </summary>
            ''' <remarks>
            ''' This method never transfers bitmap ownership to the caller. MASTileBox
            ''' rendering does not call this method. Provider-backed items are resolved into
            ''' a render snapshot when the item enters or refreshes the tile collection, so
            ''' paint can read already prepared facts without invoking provider code.
            ''' </remarks>
            Public Function GetBitmap() As SKBitmap
                If _disposed Then Return Nothing

                If _bitmapProvider Is Nothing Then
                    Return _bitmap
                End If

                Dim snapshot As MASTileBitmapRenderSnapshot = GetBitmapRenderSnapshot()
                Dim prepared As SKBitmap = snapshot.TryGetDrawableBitmap()
                If prepared IsNot Nothing Then Return prepared

                Return ResolveProviderBitmapForExternalDemand()
            End Function

            Friend Sub RefreshBitmapRenderSnapshot()
                If _disposed Then
                    SyncLock _bitmapGate
                        _bitmapSnapshot = MASTileBitmapRenderSnapshot.Empty
                    End SyncLock
                    Return
                End If

                Dim nextBitmap As SKBitmap = Nothing
                Dim nextOwnership As MASTileBitmapOwnershipKind = MASTileBitmapOwnershipKind.None
                Dim providerResolved As Boolean = False

                If _bitmapProvider Is Nothing Then
                    ReleaseOwnedProviderSnapshot()
                    nextBitmap = _bitmap
                    nextOwnership = ResolveDirectBitmapOwnership(nextBitmap)
                Else
                    nextBitmap = ResolveProviderBitmap("MASTileItem.RefreshBitmapRenderSnapshot")
                    nextOwnership = ResolveProviderBitmapOwnership(nextBitmap)
                    providerResolved = True
                End If

                SyncLock _bitmapGate
                    _bitmapSnapshotRevision = NextBitmapSnapshotRevision(_bitmapSnapshotRevision)
                    _bitmapSnapshot = MASTileBitmapRenderSnapshot.Create(
                        bitmap:=nextBitmap,
                        ownershipKind:=nextOwnership,
                        iconId:=IconId,
                        revision:=_bitmapSnapshotRevision,
                        providerResolved:=providerResolved)
                End SyncLock
            End Sub

            Friend Function GetBitmapRenderSnapshot() As MASTileBitmapRenderSnapshot
                If _disposed Then Return MASTileBitmapRenderSnapshot.Empty

                SyncLock _bitmapGate
                    If _bitmapSnapshot Is Nothing Then Return MASTileBitmapRenderSnapshot.Empty
                    Return _bitmapSnapshot
                End SyncLock
            End Function

            Friend Sub ReleaseOwnedBitmapResources()
                ReleaseOwnedProviderSnapshot()
                ReleaseOwnedDirectBitmap()
            End Sub

            Private Function ResolveProviderBitmap(context As String) As SKBitmap
                Dim provider As Func(Of SKBitmap) = _bitmapProvider
                If provider Is Nothing Then Return Nothing

                Try
                    Return provider.Invoke()
                Catch masCaughtException As Exception
                    Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowRenderFallback(masCaughtException, context)
                    Return Nothing
                End Try
            End Function

            Private Function ResolveProviderBitmapForExternalDemand() As SKBitmap
                Dim resolved As SKBitmap = ResolveProviderBitmap("MASTileItem.GetBitmap.ExternalDemand")
                If resolved Is Nothing Then Return Nothing

                Dim ownershipKind As MASTileBitmapOwnershipKind = ResolveProviderBitmapOwnership(resolved)

                SyncLock _bitmapGate
                    _bitmapSnapshotRevision = NextBitmapSnapshotRevision(_bitmapSnapshotRevision)
                    _bitmapSnapshot = MASTileBitmapRenderSnapshot.Create(
                        bitmap:=resolved,
                        ownershipKind:=ownershipKind,
                        iconId:=IconId,
                        revision:=_bitmapSnapshotRevision,
                        providerResolved:=True)
                End SyncLock

                Return resolved
            End Function

            Private Function ResolveDirectBitmapOwnership(bitmap As SKBitmap) As MASTileBitmapOwnershipKind
                If bitmap Is Nothing Then Return MASTileBitmapOwnershipKind.None
                If _bitmapDisposer Is Nothing Then Return MASTileBitmapOwnershipKind.BorrowedDirect
                Return MASTileBitmapOwnershipKind.OwnedDirect
            End Function

            Private Function ResolveProviderBitmapOwnership(bitmap As SKBitmap) As MASTileBitmapOwnershipKind
                If bitmap Is Nothing Then
                    ReleaseOwnedProviderSnapshot()
                    Return MASTileBitmapOwnershipKind.None
                End If

                If _providerBitmapDisposer Is Nothing Then
                    ReleaseOwnedProviderSnapshot()
                    Return MASTileBitmapOwnershipKind.BorrowedProviderSnapshot
                End If

                ReplaceOwnedProviderSnapshot(bitmap, _providerBitmapDisposer)
                Return MASTileBitmapOwnershipKind.OwnedProviderSnapshot
            End Function

            Private Sub ReplaceOwnedProviderSnapshot(bitmap As SKBitmap,
                                                     disposer As Action(Of SKBitmap))
                Dim oldBitmap As SKBitmap = Nothing
                Dim oldDisposer As Action(Of SKBitmap) = Nothing

                SyncLock _bitmapGate
                    If Object.ReferenceEquals(_ownedProviderSnapshotBitmap, bitmap) Then
                        _ownedProviderSnapshotDisposer = disposer
                        Return
                    End If

                    oldBitmap = _ownedProviderSnapshotBitmap
                    oldDisposer = _ownedProviderSnapshotDisposer
                    _ownedProviderSnapshotBitmap = bitmap
                    _ownedProviderSnapshotDisposer = disposer
                End SyncLock

                DisposeBitmapWithDisposer(oldBitmap, oldDisposer, "MASTileItem.ProviderSnapshot.Replace")
            End Sub

            Private Sub ReleaseOwnedProviderSnapshot()
                Dim oldBitmap As SKBitmap = Nothing
                Dim oldDisposer As Action(Of SKBitmap) = Nothing

                SyncLock _bitmapGate
                    oldBitmap = _ownedProviderSnapshotBitmap
                    oldDisposer = _ownedProviderSnapshotDisposer
                    _ownedProviderSnapshotBitmap = Nothing
                    _ownedProviderSnapshotDisposer = Nothing
                    _bitmapSnapshot = MASTileBitmapRenderSnapshot.Empty
                End SyncLock

                DisposeBitmapWithDisposer(oldBitmap, oldDisposer, "MASTileItem.ProviderSnapshot.Release")
            End Sub

            Private Sub ReleaseOwnedDirectBitmap()
                Dim oldBitmap As SKBitmap = Nothing
                Dim oldDisposer As Action(Of SKBitmap) = Nothing

                SyncLock _bitmapGate
                    If _bitmap Is Nothing OrElse _bitmapDisposer Is Nothing Then Return

                    oldBitmap = _bitmap
                    oldDisposer = _bitmapDisposer
                    _bitmap = Nothing
                    _bitmapDisposer = Nothing
                    _bitmapSnapshot = MASTileBitmapRenderSnapshot.Empty
                End SyncLock

                DisposeBitmapWithDisposer(oldBitmap, oldDisposer, "MASTileItem.DirectBitmap.Release")
            End Sub

            Private Shared Function ResolveDefaultBitmapDisposer(takeOwnership As Boolean) As Action(Of SKBitmap)
                If Not takeOwnership Then Return Nothing
                Return AddressOf DisposeBitmapDirectly
            End Function

            Private Shared Sub DisposeBitmapDirectly(bitmap As SKBitmap)
                If bitmap IsNot Nothing Then bitmap.Dispose()
            End Sub

            Private Shared Sub DisposeBitmapWithDisposer(bitmap As SKBitmap,
                                                         disposer As Action(Of SKBitmap),
                                                         context As String)
                If bitmap Is Nothing OrElse disposer Is Nothing Then Return

                Try
                    disposer.Invoke(bitmap)
                Catch masCaughtException As Exception
                    Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowDisposeCleanup(masCaughtException)
                End Try
            End Sub

            Private Shared Function NextBitmapSnapshotRevision(current As Integer) As Integer
                If current = Integer.MaxValue Then Return 1
                Return current + 1
            End Function

            Public Overrides Function ToString() As String
                Return Title
            End Function

            Public Sub Dispose() Implements IDisposable.Dispose
                If _disposed Then Return
                _disposed = True

                ' Borrowed bitmaps are never disposed by MASTileItem. Owned bitmaps are
                ' released through their configured disposer before references are cleared.
                ReleaseOwnedBitmapResources()

                SyncLock _bitmapGate
                    _bitmap = Nothing
                    _bitmapDisposer = Nothing
                    _bitmapProvider = Nothing
                    _providerBitmapDisposer = Nothing
                    _bitmapSnapshot = MASTileBitmapRenderSnapshot.Empty
                End SyncLock

                Tag = Nothing
            End Sub

        End Class

    End Class

End Namespace
