Imports System.Threading
Imports Mercury.Mobile.Threading

Namespace Data

    ''' <summary>
    ''' A container collection that may be virtualized asynchronously.
    ''' </summary>
    ''' <typeparam name="T">The type of the items referenced by the collection.</typeparam>
    ''' <remarks></remarks>
    Public Class AsyncVirtualContainerCollection(Of T)
        Inherits VirtualContainerCollection(Of T)

#Region "Constructors"

        ''' <summary>
        ''' Initializes a new instance of the AsyncVirtualContainerCollection(Of T) 
        ''' class with the specified item provider.
        ''' </summary>
        ''' <param name="itemProvider">The provider interface for retrieving virtualized items.</param>
        Public Sub New(ByVal itemProvider As IVirtualizingItemProvider(Of T))
            MyBase.New(itemProvider)
            Me._synchronizationContext = ThreadSynchronizationContext.Current
        End Sub

        ''' <summary>
        ''' Initializes a new instance of the AsyncVirtualContainerCollection(Of T) 
        ''' class with the specified item provider and page size.
        ''' </summary>
        ''' <param name="itemProvider">The provider interface for retrieving virtualized items.</param>
        ''' <param name="pageSize">The number of elements in a page.</param>
        Public Sub New(ByVal itemProvider As IVirtualizingItemProvider(Of T), ByVal pageSize As Integer)
            MyBase.New(itemProvider, pageSize)
            Me._synchronizationContext = ThreadSynchronizationContext.Current
        End Sub

        ''' <summary>
        ''' Initializes a new instance of the AsyncVirtualContainerCollection(Of T) 
        ''' class with the specified item provider, page size, and timeout.
        ''' </summary>
        ''' <param name="itemProvider">The provider interface for retrieving virtualized items.</param>
        ''' <param name="pageSize">The number of elements in a page.</param>
        ''' <param name="pageTimeout">The number of milliseconds before the cached page invalidates.</param>
        ''' <remarks></remarks>
        Public Sub New(ByVal itemProvider As IVirtualizingItemProvider(Of T), ByVal pageSize As Integer, ByVal pageTimeout As Long)
            MyBase.New(itemProvider, pageSize, pageTimeout)
            Me._synchronizationContext = ThreadSynchronizationContext.Current
        End Sub

        ''' <summary>
        ''' Initializes a new instance of the AsyncVirtualContainerCollection(Of T) class with 
        ''' the specified item provider, page size, timeout, and synchronization context.
        ''' </summary>
        ''' <param name="itemProvider">The provider interface for retrieving virtualized items.</param>
        ''' <param name="pageSize">The number of elements in a page.</param>
        ''' <param name="pageTimeout">The number of milliseconds before the cached page invalidates.</param>
        ''' <param name="synchronizationContext">A Mercury.Mobile.Threading.ISynchronizationContext that 
        ''' can be used to synchronize access to the virtualizing collection.</param>
        ''' <remarks></remarks>
        Public Sub New(ByVal itemProvider As IVirtualizingItemProvider(Of T), ByVal pageSize As Integer, _
                       ByVal pageTimeout As Long, ByVal synchronizationContext As ISynchronizationContext)
            MyBase.New(itemProvider, pageSize, pageTimeout)
            Me._synchronizationContext = synchronizationContext
        End Sub

#End Region

#Region "Async Methods"

        Private ReadOnly _synchronizationContext As ISynchronizationContext

        ''' <summary>
        ''' Gets the synchronization context used for cross-thread operations.
        ''' </summary>
        ''' <returns>The Mercury.Mobile.Threading.ISynchronizationContext object used for cross-thread operations.</returns>
        ''' <remarks></remarks>
        Public ReadOnly Property SynchronizationContext() As ISynchronizationContext
            Get
                Return Me._synchronizationContext
            End Get
        End Property

        Private _isLoading As Boolean

        ''' <summary>
        ''' Gets or sets a value indicating whether the collection is loading.
        ''' </summary>
        ''' <returns>True if the collection is loading; otherwise, false.</returns>
        ''' <remarks></remarks>
        Public Property IsLoading() As Boolean
            Get
                Return Me._isLoading
            End Get
            Set(ByVal value As Boolean)
                If (value <> Me._isLoading) Then
                    Me._isLoading = value
                    OnPropertyChanged("IsLoading")
                End If
            End Set
        End Property

        Private _isInitializing As Boolean

        Public Property IsInitializing() As Boolean
            Get
                Return Me._isInitializing
            End Get
            Set(ByVal value As Boolean)
                If (value <> Me._isInitializing) Then
                    Me._isInitializing = value
                    OnPropertyChanged("IsInitializing")
                End If
            End Set
        End Property

        Protected Overrides Sub LoadCount()
            If Me.Count = 0 Then
                Me.IsInitializing = True
            End If
            ThreadPool.QueueUserWorkItem(AddressOf LoadCountWork)
        End Sub

        Private Sub LoadCountWork(ByVal args As Object)
            Dim count As Integer = Me.FetchCount()
            Me.SynchronizationContext.Send(AddressOf LoadCountCompleted, count)
        End Sub

        Private Sub LoadCountCompleted(ByVal args As Object)
            Dim newCount = CType(args, Integer)
            Me.UpdateCount(newCount)
            Me.IsInitializing = False
        End Sub

        Private Sub UpdateCount(ByVal count As Integer)
            If count <> Me.Count Then
                Me.SetCachedCount(count)
                Me.ResetPageCache()
                Me.FireCollectionReset()
            End If
        End Sub

        Protected Overrides Sub LoadPage(ByVal pageIndex As Integer, ByVal pageLength As Integer)
            Me.IsLoading = True
            ThreadPool.QueueUserWorkItem(AddressOf LoadPageWork, New Integer() {pageIndex, pageLength})
        End Sub

        Private Sub LoadPageWork(ByVal state As Object)
            Dim args = CType(state, Integer())
            Dim pageIndex = args(0)
            Dim pageLength = args(1)
            Dim totalCount As Integer = 0
            Dim dataItems = Me.FetchPage(pageIndex, pageLength, totalCount)
            Me.SynchronizationContext.Send(AddressOf LoadPageCompleted, New Object() {pageIndex, dataItems, totalCount})
        End Sub

        Private Sub LoadPageCompleted(ByVal state As Object)
            Dim args = TryCast(state, Object())
            Dim pageIndex = CType(args(0), Integer)
            Dim dataItems = CType(args(1), IList(Of T))
            Dim newCount = CType(args(2), Integer)
            Me.UpdateCount(newCount)

            Me.PopulatePage(pageIndex, dataItems)
            Me.IsLoading = False
        End Sub

#End Region

    End Class

End Namespace