' *
' * The project site is at: http://sourceforge.jp/projects/fishbornas/
' *
' * First author tiritomato 2012.
' *
' * Distributed under the FishbornArchiveShelf License (See
' *  file "Licenses/License.txt" contained in a project, or the following link.
' *  http://sourceforge.jp/projects/fishbornas/scm/svn/blobs/head/trunk/Licenses/License.txt)
' *
' * 2012.06.07 Initial Revision (tiritomato)
' *

Partial Public Class Logic
    Partial Public Class CommandlineParser

        Public Class Arguments

            Public Const DASHCODE As Char = "-"c

            Public Class NamedParameterInfo
                Private m_c As Char
                Private m_t As System.Collections.Generic.SortedSet(Of String)
                Private m_ParameterizeMax As Integer

                Public ReadOnly Property Code As Char
                    Get
                        Return m_c
                    End Get
                End Property
                Public ReadOnly Property ParameterizeMax As Integer
                    Get
                        Return m_ParameterizeMax
                    End Get
                End Property

                Public Sub New(ByVal src As NamedParameterInfo)
                    MyClass.New()
                    If src Is Nothing Then Return
                    m_c = src.m_c
                    m_t = Nothing
                    If src.m_t IsNot Nothing Then
                        m_t = New System.Collections.Generic.SortedSet(Of String)
                        For Each strText As String In src.m_t
                            Dim strAdd As String = strText.TrimStart(DASHCODE)
                            If Not String.IsNullOrWhiteSpace(strAdd) And Not m_t.Contains(strAdd) Then m_t.Add(strAdd)
                        Next
                        If m_t.Count <= 0 Then m_t = Nothing
                    End If
                    m_ParameterizeMax = src.m_ParameterizeMax
                End Sub

                Public Sub New()
                    Me.New(vbNullChar)
                End Sub

                ''' <summary>
                ''' 
                ''' </summary>
                ''' <param name="c">Parameter group's default Option-charcode</param>
                ''' <param name="ParamMaxCt">Parameter group can read ParamMaxCt of params. default value = -1(InfinityReadable)</param>
                ''' <remarks></remarks>
                Public Sub New(ByVal c As Char, Optional ByVal ParamMaxCt As Integer = -1)
                    m_c = c
                    m_t = Nothing
                    m_ParameterizeMax = ParamMaxCt
                End Sub

                Public Sub New(ByVal c As Char, ByVal SubNameList As System.Collections.Generic.IEnumerable(Of String), Optional ByVal ParamMax As Integer = -1)

                    MyClass.New(c, ParamMax)

                    If SubNameList Is Nothing Then Return

                    m_t = New System.Collections.Generic.SortedSet(Of String)
                    For Each strText As String In SubNameList
                        If strText IsNot Nothing Then
                            Dim strAdd As String = strText.TrimStart(DASHCODE)
                            If Not String.IsNullOrWhiteSpace(strAdd) Then m_t.Add(strAdd)
                        End If
                    Next
                    If m_t.Count <= 0 Then m_t = Nothing

                End Sub

                Public Sub New(ByVal c As Char, ByVal SubName As String, Optional ByVal ParamMax As Integer = -1)
                    MyClass.New(c, {SubName}, ParamMax)
                End Sub

                Public Overloads Function Equals(ByVal c As Char) As Boolean
                    Return m_c.Equals(c)
                End Function
                Public Function StartsWith(ByVal t As String) As String
                    If Not m_t Is Nothing Then
                        For Each eachTxt As String In m_t
                            If t.StartsWith(eachTxt) Then Return eachTxt
                        Next
                    End If
                    Return Nothing
                End Function

            End Class ' NamedParameterInfo

            Public Class SplitInfo
                Inherits System.Collections.ObjectModel.ReadOnlyCollection(Of NamedParameterInfo)
                Public Sub New(ByVal val As System.Collections.Generic.IEnumerable(Of NamedParameterInfo))
                    MyBase.New(New System.Collections.Generic.List(Of NamedParameterInfo)(val))
                End Sub
                Public Shared Widening Operator CType(ByVal val As System.Collections.Generic.List(Of NamedParameterInfo)) As SplitInfo
                    Return New SplitInfo(val)
                End Operator
                Public Shared Widening Operator CType(ByVal val() As NamedParameterInfo) As SplitInfo
                    Return New SplitInfo(val)
                End Operator
            End Class

            Public Class Parameter
                Private m_Info As NamedParameterInfo
                Private m_Params As System.Collections.Generic.List(Of String)
                Private m_Exists As Integer

                Public Sub New()
                    m_Info = New NamedParameterInfo
                    m_Params = New System.Collections.Generic.List(Of String)
                    m_Exists = 0
                End Sub
                Public Sub New(ByVal n As NamedParameterInfo)
                    m_Info = New NamedParameterInfo(n)
                    m_Params = New System.Collections.Generic.List(Of String)
                    m_Exists = 0
                End Sub
                Public Sub New(ByVal src As Parameter)
                    m_Params = New System.Collections.Generic.List(Of String)
                    m_Exists = 0
                    If src Is Nothing Then
                        m_Info = New NamedParameterInfo
                        Return
                    End If
                    m_Info = New NamedParameterInfo(src.m_Info)
                    If Not src.m_Params Is Nothing Then
                        For Each AddItem As String In src.m_Params
                            m_Params.Add(AddItem)
                        Next
                    End If
                End Sub
                Public ReadOnly Property Info As NamedParameterInfo
                    Get
                        Return m_Info
                    End Get
                End Property
                Public ReadOnly Property Params As System.Collections.ObjectModel.ReadOnlyCollection(Of String)
                    Get
                        If m_Params Is Nothing Then Return Nothing
                        Return m_Params.AsReadOnly
                    End Get
                End Property

                ''' <summary>
                ''' 
                ''' </summary>
                ''' <value></value>
                ''' <returns>return parameter exists count</returns>
                ''' <remarks></remarks>
                Public ReadOnly Property Exists() As Integer
                    Get
                        Return m_Exists
                    End Get
                End Property

                ''' <summary>
                ''' This class get parsed commandline-collection from "args() as string"
                ''' </summary>
                ''' <remarks><newpara>For example, "-X term1 term2" is parsed like this. Item(X) = { term1, term2 }</newpara><newpara>If you want to get "X" exists count, read Item(X).Exists</newpara></remarks>
                Public Class Collection
                    Implements System.Collections.IEnumerable
                    Private m_ary As System.Collections.Generic.SortedList(Of Char, Parameter)
                    Private ReadOnly Property FindStartsWithKey(ByVal t As String) As Parameter
                        Get
                            If String.IsNullOrEmpty(t) Then Return Nothing
                            For Each param As Parameter In m_ary.Values
                                If Not param Is Nothing AndAlso Not param.m_Info Is Nothing Then
                                    If Not param.m_Info.StartsWith(t) Is Nothing Then Return param
                                End If
                            Next
                            Return Nothing
                        End Get
                    End Property
                    Public Function GetEnumerator() As System.Collections.IEnumerator Implements System.Collections.IEnumerable.GetEnumerator
                        Return m_ary.GetEnumerator
                    End Function
                    Default Public ReadOnly Property Item(ByVal c As Char) As Parameter
                        Get
                            If Not m_ary Is Nothing AndAlso m_ary.ContainsKey(c) Then Return m_ary(c)
                            Return Nothing
                        End Get
                    End Property

                    Public Sub New()
                        m_ary = Nothing
                    End Sub

                    Public Sub New(ByVal src As Collection)
                        MyClass.New()
                        If src Is Nothing Then Return
                        m_ary = New System.Collections.Generic.SortedList(Of Char, Parameter)
                        For Each c As Char In src.m_ary.Keys
                            m_ary.Add(c, New Parameter(src.m_ary(c)))
                        Next
                    End Sub

                    Public Sub New(ByVal inf As SplitInfo, ByVal cmdargs() As String)

                        m_ary = New System.Collections.Generic.SortedList(Of Char, Parameter)

                        m_ary.Add(vbNullChar, New Parameter()) ' create nonamed parameter group

                        For Each eachInf As NamedParameterInfo In inf ' create sorted dictionary collection
                            If m_ary.ContainsKey(eachInf.Code) = False Then
                                m_ary.Add(eachInf.Code, New Parameter(eachInf))
                            End If
                        Next

                        Dim CurrentParam As Parameter = m_ary(vbNullChar) ' default parameter group is noname group
                        CurrentParam.m_Exists += 1

                        For Each cmdarg As String In cmdargs

                            If cmdarg.StartsWith(DASHCODE) Then

                                ' [] Read as command group start []
                                ' if (cmdarg.Chars(0) == DASHCODE), term is read as command group settings

                                ' Select Currentparam.
                                ' At first, select by charcode. Charcode selecting has highest priority than select by subname string list.

                                Dim RegistCurrentParam As Parameter = CurrentParam
                                Dim Key As String = Nothing
                                Dim AddArg As String = cmdarg.TrimStart(DASHCODE)

                                If cmdarg.Length <= 1 Then
                                    CurrentParam = m_ary(vbNullChar)

                                ElseIf cmdarg.Chars(1) <> DASHCODE AndAlso m_ary.ContainsKey(cmdarg.Chars(1)) Then
                                    Key = cmdarg.Chars(1)
                                    CurrentParam = m_ary(Key)

                                Else
                                    CurrentParam = FindStartsWithKey(AddArg) ' Subname lists search
                                    If CurrentParam Is Nothing Then
                                        CurrentParam = m_ary(vbNullChar) ' If notfound from subname lists, currentparam reset default group.
                                    Else
                                        Key = CurrentParam.m_Info.StartsWith(AddArg) ' Get match part in AddArg to Key
                                    End If
                                End If

                                If RegistCurrentParam IsNot CurrentParam Then CurrentParam.m_Exists += 1 ' currentparam is changed

                                ' In this case : -X"param", {AddArg - Key} has remain part. (Key:X RemainPart:"param")
                                ' Add AddArg to CurrentParam.m_Params
                                If Not String.IsNullOrWhiteSpace(Key) And Key.Length < AddArg.Length Then ' easy error detect
                                    AddArg = AddArg.Substring(Key.Length).Trim
                                    If Not String.IsNullOrWhiteSpace(AddArg) Then CurrentParam.m_Params.Add(AddArg)
                                End If

                            Else

                                Dim AddArg As String = cmdarg.Trim
                                If Not String.IsNullOrWhiteSpace(AddArg) Then CurrentParam.m_Params.Add(AddArg)

                            End If

                            ' If CurrentParam.Params.Count is over ParameterizeMax, reset to nonamed param group
                            If (CurrentParam IsNot m_ary(vbNullChar)) And (0 <= CurrentParam.m_Info.ParameterizeMax) And (CurrentParam.m_Info.ParameterizeMax <= CurrentParam.m_Params.Count) Then
                                CurrentParam = m_ary(vbNullChar)
                                CurrentParam.m_Exists += 1
                            End If
                        Next

                    End Sub

                End Class ' Collection

            End Class ' Parameter

        End Class ' CommandlineArguments

    End Class
End Class
