Module Utils_ListViewColumnSort


    ' -------------------------------------------------------------------- The column currently used for sorting.
    Private m_SortingColumn As ColumnHeader = Nothing

    Friend Sub Sort_ListViewColumn(ByVal lv As ListView, Optional ByVal column As Int32 = -1)
        '
        ' -------------------------------------------------------------------- not sortable columns
        'If e.Column <= 0 Or e.Column >= 19 Then Exit Sub
        '
        ' -------------------------------------------------------------------- Get the new sorting column.
        Dim new_sorting_column As ColumnHeader = lv.Columns(column)
        ' -------------------------------------------------------------------- Figure out the new sorting order.
        Dim sort_order As System.Windows.Forms.SortOrder
        If m_SortingColumn Is Nothing Then
            ' ---------------------------------------------------------------- New column. Sort ascending.
            sort_order = SortOrder.Ascending
        Else
            ' ---------------------------------------------------------------- See if this is the same column.
            If new_sorting_column.Equals(m_SortingColumn) Then
                ' Same column. Switch the sort order.
                If m_SortingColumn.Text.StartsWith("> ") Then
                    sort_order = SortOrder.Descending
                Else
                    sort_order = SortOrder.Ascending
                End If
            Else
                ' ------------------------------------------------------------ New column. Sort ascending.
                sort_order = SortOrder.Ascending
            End If

            ' ---------------------------------------------------------------- Remove the old sort indicator.
            m_SortingColumn.Text = Strings.Mid(m_SortingColumn.Text, 3)
        End If

        ' -------------------------------------------------------------------- Display the new sort order.
        If sort_order = SortOrder.Ascending Then
            new_sorting_column.Text = "> " & new_sorting_column.Text
        Else
            new_sorting_column.Text = "< " & new_sorting_column.Text
        End If

        ' -------------------------------------------------------------------- Create a comparer.
        lv.ListViewItemSorter = New ListViewComparer(column, sort_order)

        ' -------------------------------------------------------------------- Sort.
        lv.Sort()
        lv.ListViewItemSorter = Nothing

        ' -------------------------------------------------------------------- Resize
        'If m_SortingColumn IsNot Nothing Then
        '    If m_SortingColumn.Index >= 0 Then
        '        lv.Columns(m_SortingColumn.Index).AutoResize(ColumnHeaderAutoResizeStyle.HeaderSize)
        '    End If
        'End If
        'lv.Columns(new_sorting_column.Index).AutoResize(ColumnHeaderAutoResizeStyle.HeaderSize)

        ' -------------------------------------------------------------------- Update old
        m_SortingColumn = new_sorting_column
    End Sub

    Friend Sub Sort_ListView_Stop()
        If m_SortingColumn Is Nothing Then Exit Sub
        If m_SortingColumn.Text.StartsWith("> ") Or m_SortingColumn.Text.StartsWith("< ") Then
            m_SortingColumn.Text = Strings.Mid(m_SortingColumn.Text, 3)
            m_SortingColumn = Nothing
        End If
    End Sub

    ' Importing for the Windows native logical sort
    <System.Runtime.InteropServices.DllImport("shlwapi.dll", CharSet:=System.Runtime.InteropServices.CharSet.Unicode)> _
    Private Function StrCmpLogicalW(ByVal psz1 As String, ByVal psz2 As String) As Integer
    End Function

    Class ListViewComparer
        Implements IComparer

        Private m_ColumnNumber As Integer
        Private m_SortOrder As SortOrder

        Public Sub New(ByVal column_number As Integer, ByVal sort_order As SortOrder)
            m_ColumnNumber = column_number
            m_SortOrder = sort_order
        End Sub

        Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer Implements System.Collections.IComparer.Compare
            Dim item_x As ListViewItem = DirectCast(x, ListViewItem)
            Dim item_y As ListViewItem = DirectCast(y, ListViewItem)

            ' --------------------------------------------------------- Get sub-item values and type
            Dim type_x As String = item_x.SubItems(2).Text
            Dim type_y As String = item_y.SubItems(2).Text

            Dim val_x As String = If(item_x.SubItems.Count > m_ColumnNumber, item_x.SubItems(m_ColumnNumber).Text, "")
            Dim val_y As String = If(item_y.SubItems.Count > m_ColumnNumber, item_y.SubItems(m_ColumnNumber).Text, "")

            ' --------------------------------------------------------- Always put folders at the top (or bottom if descending)
            If type_x <> type_y Then
                If type_x = "dir" Then Return If(m_SortOrder = SortOrder.Ascending, -1, 1)
                If type_y = "dir" Then Return If(m_SortOrder = SortOrder.Ascending, 1, -1)
            End If

            ' --------------------------------------------------------- Compare based on column type
            Dim result As Integer = 0

            ' Specific handling for the "Length" column (index 1) to sort as integer
            If m_ColumnNumber = 1 Then
                ' ----------------------------------------------------- Clean non-numeric characters
                result = GetNumericValue(val_x).CompareTo(GetNumericValue(val_y))
            Else
                ' ----------------------------------------------------- Use Windows native logical sort
                result = StrCmpLogicalW(val_x, val_y)
            End If

            ' --------------------------------------------------------- Apply sort order
            Return If(m_SortOrder = SortOrder.Ascending, result, -result)
        End Function
    End Class

    ' --------------------------------------------------------- Helper to extract only digits from a string
    Private Function GetNumericValue(ByVal s As String) As Long
        Dim numericString As String = ""
        For Each c As Char In s
            If Char.IsDigit(c) Then numericString &= c
        Next
        Return If(numericString = "", 0, CLng(numericString))
    End Function

End Module
