
Namespace Primitives

    ''' <summary>
    ''' Collection class for a list of objects.
    ''' </summary>
    ''' <remarks></remarks>
    Public NotInheritable Class ColumnCollection
        Implements IEnumerable

        Private _columns As List(Of Column) = New List(Of Column)()
        Private _indexer As IDictionary(Of String, Integer) = New Dictionary(Of String, Integer)()
        Private _table As ObjectTable

        ' Events
        Public Event CollectionChanged(ByVal sender As Object, ByVal e As CollectionChangedEventArgs)

        ''' <summary>
        ''' Initializes a new instance of the <see cref="Mercury.Mobile.Forms.Primitives.ColumnCollection"/> 
        ''' class with the specified table.
        ''' </summary>
        ''' <param name="table">The table to associate with this collection.</param>
        ''' <remarks></remarks>
        Friend Sub New(ByVal table As ObjectTable)
            Me._table = table
        End Sub

#Region "Properties"

        ''' <summary>
        ''' Gets the number of elements in this collection.
        ''' </summary>
        ''' <returns>The number of elements in this collection.</returns>
        ''' <remarks></remarks>
        Public ReadOnly Property Count() As Integer
            Get
                Return Me._columns.Count
            End Get
        End Property

        ''' <summary>
        ''' Gets the column at the specified index.
        ''' </summary>
        ''' <param name="index">The index of the column to get.</param>
        ''' <returns>The column at the specified index.</returns>
        ''' <remarks></remarks>
        Default Public ReadOnly Property Item(ByVal index As Integer) As Column
            Get
                Return Me._columns.Item(index)
            End Get
        End Property

        ''' <summary>
        ''' Gets the column associated with the specified name.
        ''' </summary>
        ''' <param name="columnName">The name of the column to get.</param>
        ''' <returns>The column associated with the specified name.</returns>
        ''' <remarks></remarks>
        Default Public ReadOnly Property Item(ByVal columnName As String) As Column
            Get
                If Me._indexer.ContainsKey(columnName) Then _
                    Return Me._columns(Me._indexer(columnName))
                Return Nothing
            End Get
        End Property

#End Region

#Region "Methods"

        ''' <summary>
        ''' Removes all elements from this collection.
        ''' </summary>
        ''' <remarks></remarks>
        Public Sub Clear()
            Me._indexer.Clear()
            Me._columns.Clear()
            Me.OnCollectionChanged(New CollectionChangedEventArgs(-1, CollectionChangedAction.Reset))
        End Sub

        ''' <summary>
        ''' Adds the specified column to this collection.
        ''' </summary>
        ''' <param name="value">The column to add.</param>
        ''' <remarks></remarks>
        Public Sub Add(ByVal value As Column)
            Me.AddInternal(value, False)
        End Sub

        ''' <summary>
        ''' Adds an enumerable range of columns to the collection.
        ''' </summary>
        ''' <param name="values">The range of columns to add.</param>
        ''' <remarks></remarks>
        Public Sub AddRange(ByVal values As IEnumerable(Of Column))
            If values IsNot Nothing Then
                For Each col As Column In values
                    Me.AddInternal(col, True)
                Next
                Me._RebuildIndexedView()
                For Each col As Column In values
                    Dim args = New CollectionChangedEventArgs(col.Ordinal, CollectionChangedAction.Add, col.Name)
                    Me.OnCollectionChanged(args)
                Next
            End If
        End Sub

        ''' <summary>
        ''' Returns the index of the specified column in the collection.
        ''' </summary>
        ''' <param name="value">The column to search for.</param>
        ''' <returns>The index of the specified column in the collection.</returns>
        ''' <remarks></remarks>
        Public Function IndexOf(ByVal value As Column) As Integer
            If Me._indexer.ContainsKey(value.Name) Then _
                Return Me._indexer(value.Name)
            Return -1
        End Function

        ''' <summary>
        ''' Returns the index of the specified column name in the collection.
        ''' </summary>
        ''' <param name="columnName">The name of the column to search for.</param>
        ''' <returns>The index of the specified column name in the collection.</returns>
        ''' <remarks></remarks>
        Public Function IndexOf(ByVal columnName As String) As Integer
            If columnName IsNot Nothing AndAlso Me._indexer.ContainsKey(columnName) Then _
                Return Me._indexer(columnName)
            Return -1
        End Function

        ''' <summary>
        ''' Removes the specified column from the collection.
        ''' </summary>
        ''' <param name="value">The column to remove.</param>
        ''' <remarks></remarks>
        Public Sub Remove(ByVal value As Column)
            If Me._indexer.ContainsKey(value.Name) Then
                Dim index = Me._indexer(value.Name)
                Me._columns.RemoveAt(index)
                Me._RebuildIndexedView()
                Dim args = New CollectionChangedEventArgs(index, CollectionChangedAction.Remove, value.Name)
                Me.OnCollectionChanged(args)
            End If
        End Sub

        ''' <summary>
        ''' Removes the column at the specified index from the collection.
        ''' </summary>
        ''' <param name="index">The index of the column to remove.</param>
        ''' <remarks></remarks>
        Public Sub RemoveAt(ByVal index As Integer)
            If index > -1 AndAlso index < Me._columns.Count Then
                Dim value = Me._columns(index)
                Me._columns.RemoveAt(index)
                Me._RebuildIndexedView()
                Dim args = New CollectionChangedEventArgs(index, CollectionChangedAction.Remove, value.Name)
                Me.OnCollectionChanged(args)
            End If
        End Sub

        Private Sub AddInternal(ByVal value As Column, ByVal delayIndexer As Boolean)
            If Me._indexer.ContainsKey(value.Name) Then _
                Throw New ArgumentException("Column name must be unique.")
            value.SetTable(Me._table)
            Me._columns.Add(value)
            If Not (delayIndexer) Then
                Me._RebuildIndexedView()
                Dim args = New CollectionChangedEventArgs(Me._columns.Count - 1, CollectionChangedAction.Add, value.Name)
                OnCollectionChanged(args)
            End If
        End Sub

        Private Sub _RebuildIndexedView()
            Me._indexer.Clear()
            For i As Integer = 0 To Me._columns.Count - 1
                Me._indexer.Add(Me._columns(i).Name, i)
            Next
        End Sub

#End Region

#Region "IEnumerable Interface Members"

        Public Function GetEnumerator() As System.Collections.IEnumerator Implements System.Collections.IEnumerable.GetEnumerator
            Return New ColumnEnumerator(Me)
        End Function

#Region "ColumnEnumerator Class"

        Private Class ColumnEnumerator
            Implements IEnumerator

            ' Declared variables
            Private m_iPos As Integer = -1
            Private _columns As ColumnCollection

            Public Sub New(ByVal column As ColumnCollection)
                Me._columns = column
            End Sub

            Public ReadOnly Property Current() As Object Implements System.Collections.IEnumerator.Current
                Get
                    Return Me._columns.Item(Me.m_iPos)
                End Get
            End Property

            Public Function MoveNext() As Boolean Implements System.Collections.IEnumerator.MoveNext
                If Me.m_iPos < (Me._columns.Count - 1) Then
                    Me.m_iPos += 1
                    Return True
                Else
                    Return False
                End If
            End Function

            Public Sub Reset() Implements System.Collections.IEnumerator.Reset
                Me.m_iPos = -1
            End Sub

        End Class

#End Region

#End Region

        Protected Sub OnCollectionChanged(ByVal e As CollectionChangedEventArgs)
            RaiseEvent CollectionChanged(Me, e)
        End Sub

        Friend Function GetIndexer() As IDictionary(Of String, Integer)
            Dim idxr = New Dictionary(Of String, Integer)()
            For Each entry As KeyValuePair(Of String, Integer) In Me._indexer
                idxr.Add(entry.Key, entry.Value)
            Next
            Return idxr
        End Function

    End Class

End Namespace