Option Strict On
Option Explicit On

Imports System
Imports Nexamas.UI.Controls
Imports Nexamas.UI.FloatRuntime
Imports Nexamas.UI.Host

Namespace Nexamas.UI.Application

    ''' <summary>
    ''' Public handle returned by MASApplicationWindow.Services.Progress.
    ''' It updates one root-owned MASOperationProgressBox float and closes it through FloatRuntime ownership.
    ''' </summary>
    Public NotInheritable Class MASApplicationProgressHandle
        Implements IDisposable

        Private ReadOnly _rootHost As MASSkiaRootHost
        Private ReadOnly _floatHandle As MASFloatHandle
        Private ReadOnly _content As MASFloatControlsContent
        Private ReadOnly _progressBox As MASOperationProgressBox
        Private ReadOnly _gate As New Object()
        Private _closed As Boolean

        Friend Sub New(rootHost As MASSkiaRootHost,
                       floatHandle As MASFloatHandle,
                       content As MASFloatControlsContent,
                       progressBox As MASOperationProgressBox)
            If rootHost Is Nothing Then Throw New ArgumentNullException(NameOf(rootHost))
            If floatHandle Is Nothing Then Throw New ArgumentNullException(NameOf(floatHandle))
            If content Is Nothing Then Throw New ArgumentNullException(NameOf(content))
            If progressBox Is Nothing Then Throw New ArgumentNullException(NameOf(progressBox))

            _rootHost = rootHost
            _floatHandle = floatHandle
            _content = content
            _progressBox = progressBox
        End Sub

        Public ReadOnly Property IsOpen As Boolean
            Get
                If IsClosed() Then Return False

                Return _rootHost.InvokeOnUiThreadCore(
                    Function()
                        Return Not IsClosed() AndAlso _rootHost.FloatRuntimeCore.IsOpen(_floatHandle)
                    End Function,
                    fallback:=False,
                    diagnosticName:="MASApplicationProgressHandle.IsOpen")
            End Get
        End Property

        Public Sub Update(statusText As String, percent As Double)
            If IsClosed() Then Return

            Dim safeStatus As String = If(statusText, String.Empty)
            Dim safePercent As Double = percent

            _rootHost.PostToUiThreadCore(
                Sub()
                    UpdateOnUiThread(Nothing, safeStatus, safePercent, updateTitle:=False)
                End Sub,
                "MASApplicationProgressHandle.Update")
        End Sub

        Public Sub Update(titleText As String, statusText As String, percent As Double)
            If IsClosed() Then Return

            Dim safeTitle As String = titleText
            Dim safeStatus As String = If(statusText, String.Empty)
            Dim safePercent As Double = percent

            _rootHost.PostToUiThreadCore(
                Sub()
                    UpdateOnUiThread(safeTitle, safeStatus, safePercent, updateTitle:=True)
                End Sub,
                "MASApplicationProgressHandle.UpdateWithTitle")
        End Sub

        Public Sub Close()
            If Not TryMarkClosed() Then Return

            If Not _rootHost.InvokeOnUiThreadCore(
                Sub()
                    CloseOnUiThread()
                End Sub,
                "MASApplicationProgressHandle.Close") Then

                DisposeContentBoundary("MASApplicationProgressHandle.Close.DispatchFailed")
            End If
        End Sub

        Public Sub Dispose() Implements IDisposable.Dispose
            Close()
        End Sub

        Private Function IsClosed() As Boolean
            SyncLock _gate
                Return _closed
            End SyncLock
        End Function

        Private Function TryMarkClosed() As Boolean
            SyncLock _gate
                If _closed Then Return False
                _closed = True
                Return True
            End SyncLock
        End Function

        Private Sub UpdateOnUiThread(titleText As String,
                                     statusText As String,
                                     percent As Double,
                                     updateTitle As Boolean)
            If IsClosed() Then Return
            If _rootHost.IsDisposed Then Return

            If updateTitle AndAlso Not String.IsNullOrWhiteSpace(titleText) Then
                _progressBox.TitleText = titleText
            End If

            _progressBox.StatusText = If(statusText, String.Empty)
            _progressBox.WithPercent(percent)
            _rootHost.PresentFrameNow()
        End Sub

        Private Sub CloseOnUiThread()
            Try
                If Not _rootHost.IsDisposed Then
                    _rootHost.FloatRuntimeCore.Close(_floatHandle, MASFloatCloseReason.Programmatic)
                End If
            Catch masCaughtExceptionClose As Exception
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowBoundary(
                    masCaughtExceptionClose,
                    "MASApplicationProgressHandle.Close.FloatRuntime")
            End Try

            DisposeContentBoundary("MASApplicationProgressHandle.Close.Content")

            Try
                If Not _rootHost.IsDisposed Then _rootHost.PresentFrameNow()
            Catch masCaughtExceptionPresent As Exception
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowBoundary(
                    masCaughtExceptionPresent,
                    "MASApplicationProgressHandle.Close.Present")
            End Try
        End Sub

        Private Sub DisposeContentBoundary(boundaryName As String)
            Try
                _content.Dispose()
            Catch masCaughtExceptionContentDispose As Exception
                Nexamas.UI.Diagnostics.MASExceptionSilencer.SwallowDisposeCleanup(
                    masCaughtExceptionContentDispose,
                    If(boundaryName, "MASApplicationProgressHandle.DisposeContent"))
            End Try
        End Sub
    End Class

End Namespace
