Attribute VB_Name = "modDict"
'---------------------------------------------------------------------------------------
' Module    : modDict
' Author    : d¢(daveCarter)
' Date      : 10/June/2015
' Purpose   : Parse eBook Dictionary and act as datasource to it.
' Version   : 1.1 10/Sep/2015
'              added Dict_GetWord_Bytes
'              added Log writing stuff
'              new Text Reader class, faster read and search
'                 this is a standalone text reader, previously
'                 it required another class SBuilder, no longer
'                 necessary, different approach to search taken.
'              applied new code layout design, trying to
'                 improve readability.
'              updated documentation
'
' Please note, there will be some spelling mistakes in the eBook
' I'm editing its file as I find them.
' Also, the parser is fairly simple, sometimes a word header includes
' other versions/variants of the word but they are not picked up...
'  the program was nigh on written before this anomaly was identified and
'  it makes more sense to start again than botch a fix.
'---------------------------------------------------------------------------------------
' references frmSplash
' requires Class::clsTextReader
'          Module::modWinAPI, MsgBox
'          Module::modMain, Log_Write
' -------------------------------------------------------------------

' [ NOTE: THIS MODULE NEEDS ITS OWN ERROR HANDLER
'     e.g. like modMain.Error_Manager ]
' -------------------------------------------------------------------

' ==Sub 7
' - dict_Init
' + Dict_LoadAlphaRange ( <i/o> Long CharIndex , <i/o> Long FirstLine , <i/o> Long LastLine )
' + Dict_LocateWordEx ( <i/o> String Word , <i/o> Long AlphaIndex , <i/o> Long WordIndex , <i/o> Long WordInstanceCount )
' + Dict_LocateWord ( <i/o> String Word , <i/o> Long AlphaIndex , <i/o> Long WordIndex )
' - dict_Parse_First
' - dict_Parse_Second
' + Dict_Unload

' ==Function 14
' + Dict_Ack::String
' + Dict_Data::clsTextReader
' + Dict_FileName::String
' + Dict_Footer::String
' + Dict_GetAlphaFromLineNumber::Long ( <i/o> Long LineNumber )
' + Dict_GetCharData::String ( <i/o> Long CharIndex )
' + Dict_GetCharWords::Variant ( <i/o> Long CharIndex )
' + Dict_GetWord_Bytes::Variant ( <i/o> Long AlphaIndex , <i/o> Long WordIndex )
' + Dict_GetWordCount::Long
' + Dict_GetWordFromLineNumber::Long ( <i/o> Long LineNumber )
' + Dict_GetWordDefinition::String ( <i/o> Long AlphaIndex , <i/o> Long WordIndex )
' + Dict_Header::String
' + Dict_Load::Long
' - Is_WordBreakChar::Boolean ( <i> Integer pKeyCode )

Option Explicit

' Data filename in accordance with Project
' Gutenberg's wishes (see copyright/footer)
Private Const _
   DICT_FILE_NAME _
      As String = _
         "29765-8.txt"     '// name of data file specified
                           '// by Project Gutenberg.

Public Const _
   DICT_LOAD_FAIL _
      As Long = -1         '// describes failed dict. load.
   
Public Const _
   DICT_LOAD_SUCCESS _
      As Long = 0          '// describes successful dict. load.

Private moTxtRdr _
   As clsTextReader        '// Text-Reader class that does all
                           '// the underlying grunt

'/* store for a word name and the line
'     on which it exists
Private Type WORD_INFO
   Word As String
   LineNumber As Long
' v2 ----------------------------------------------------------------
' WordType is a good idea but I noticed some slow down when
'  it was defined and used, so, for now, just leaving it
'  commented... perhaps things are at a stage where we actually
'  create a Word Index from the eBook data... ?
'   WordType As String         '// e.g. Noun, Verb, Adjective etc
End Type '*/

'/* store for alpha indexes e.g. A, B, C, ...Z
'     noting first and last line numbers and
'     including a bare bones string array of
'     words within the index and array of
'     WORD_INFO items, 1 per word.
'   basically, if the CharStr is 'C' then the word
'     array would have all words beginning
'     with the letter 'C'
Private Type ROLODEX_INFO
   FirstLine As Long       '// first line where index is declared
   LastLine As Long        '// last line of index
   CharNo As Long          '// ASCII code
   CharStr As String       '// string code
   Words() As WORD_INFO    '// see above WORD_INFO
   WordCount As Long       '// number of words in index e.g. UBound(WordsArr) below
   WordsArr() As String    '// string array of words in index (sorted presumed)
End Type '*/

Private mtRolo() _
   As ROLODEX_INFO         '// saves all alpha indexes e.g. A->Z, see
                           '// ROLODEX_INFO above

Private miFirstLine _
   As Long                 '// first line where data/dictionary starts
Private miLastLine _
   As Long                 '// last line of data/dictionary
   
Private miTotalWordCount _
   As Long                 '// total number of word definitions
                           '// found in the dictionary
                           
Private miWordCounts() As Long
                           
'Function Dict_GetWordType( _
'   AlphaIndex As Long, WordIndex As Long) As String
'
'   Dim tRolo As ROLODEX_INFO
'   tRolo = mtRolo(AlphaIndex)
'
'   Dim tWord As WORD_INFO
'   tWord = tRolo.Words(WordIndex)
'
'   Dict_GetWordType = tWord.WordType
'
'End Function

Function Dict_GetWordCounts() ' As Long()
   Dict_GetWordCounts = miWordCounts
End Function

' ***********************************************
' Dict_FirstChar << Long
' Returns the first char pos of the dictionary
'  e.g. for searches.
'
' Parameters:
'   none
' ***********************************************
Property Get Dict_FirstChar() As Long
   
   On Error GoTo ErrHan:
   
   Dict_FirstChar = moTxtRdr.LineStartPos(mtRolo(1).FirstLine)
   
   Exit Property

ErrHan:
   Debug.Print "Error " & Err.Number & " (" & Err.Description & ") in procedure Dict_FirstChar of Module modDict"
   
End Property

' ***********************************************
' Dict_LastChar << Long
' Returns the last char pos of the dictionary
'
' Parameters:
'   none
' ***********************************************
Property Get Dict_LastChar() As Long
   
   On Error GoTo ErrHan:
   
   Dict_LastChar = moTxtRdr.LineStartPos(miLastLine) ' roughly, minus delimiter length
   
   Exit Property

ErrHan:
   Debug.Print "Error " & Err.Number & " (" & Err.Description & ") in procedure Dict_LastChar of Module modDict"
   
End Property

' ***********************************************
' Dict_GetWordFromLineNumber << Long
' Returns the index to a word in an alpha index
'  from a line number.
'
' Parameters:
'   LineNumber::Long,
' ***********************************************
Function Dict_GetWordFromLineNumber( _
   LineNumber As Long) As Long
   
   Dim iLineNumber As Long
   iLineNumber = LineNumber
   
   Dim iAlpha As Long
   iAlpha = Dict_GetAlphaFromLineNumber(iLineNumber)
   
   Select Case iAlpha
      Case 1 To 26
         Dim tWords() As WORD_INFO
         tWords = mtRolo(iAlpha).Words
         
         Dim iCurr As Long, iMax As Long
         iMax = mtRolo(iAlpha).WordCount
         iCurr = 1
         Do While iLineNumber > tWords(iCurr).LineNumber
            iCurr = iCurr + 1
            If iCurr >= iMax Then Exit Do
         Loop
         
         Select Case iCurr
            Case 1 To iMax
               Dict_GetWordFromLineNumber = iCurr - 1
               
         End Select
         
   End Select
      
End Function

' ***********************************************
' Dict_GetAlphaFromLineNumber << Long
' Returns the alpha index of a line number
'  e.g. is line in A's, B's, C's ... Z's etc.
'  Index returned is 1 to 26 e.g. A-Z.
'
' Parameters:
'   LineNumber::Long,
' ***********************************************
Function Dict_GetAlphaFromLineNumber( _
   LineNumber As Long) As Long
   
   Dim iCurr As Long, iMax As Long
   iMax = 26
   
   iCurr = 1
   Do While LineNumber > mtRolo(iCurr).LastLine
      iCurr = iCurr + 1
      If iCurr >= iMax Then Exit Do
   Loop
   
   Select Case iCurr
      Case 1 To 26
         Dict_GetAlphaFromLineNumber = iCurr
   End Select
   
End Function

' ***********************************************
' Dict_LocateWordEx <<
' Takes a word and returns its alpha index and its
'  index within its alpha index e.g. HAPPY -> Alpha=8,
'  WordIndex=???  WordInstanceCount returns the number
'  of times the word is defined e.g. JACK has several
'  individual entries.
'
' Parameters:
'   Word::String,
'   AlphaIndex::Long,
'   WordInstanceCount::Long,
' ***********************************************
Sub Dict_LocateWordEx(Word As String, _
   AlphaIndex As Long, WordIndex As Long, _
   Optional WordInstanceCount As Long = 0)
   
   AlphaIndex = 0: WordIndex = 0: WordInstanceCount = 0
   If Len(Word) = 0 Then Exit Sub
   
   Dim sFind As String
   sFind = UCase$(Word)
   
   Dim iTmpAlpha As Long
   iTmpAlpha = AscW(Mid$(sFind, 1, 1))
   
   Select Case iTmpAlpha
      Case 65 To 90
         AlphaIndex = iTmpAlpha - 64
         Dim sWords() As String
         sWords = mtRolo(AlphaIndex).WordsArr
         Dim iCurr As Long, iMax As Long
         iMax = UBound(sWords)
         Do While iCurr < iMax
            iCurr = iCurr + 1
            If sWords(iCurr) = sFind Then
               WordIndex = iCurr
               WordInstanceCount = 1
               Dim iNext As Long
               For iNext = iCurr + 1 To iMax
                  If sWords(iNext) = sFind Then
                     WordInstanceCount = _
                        WordInstanceCount + 1
                  Else
                     Exit For
                  End If
               Next iNext
               Exit Do
            End If
         Loop
   End Select
      
End Sub

' ***********************************************
' Dict_LocateWord <<
' [deprecated v2. see Dict_LocateWordEx]
'
' Parameters:
'   Word::String,
'   AlphaIndex::Long,
'   WordIndex::Long,
' ***********************************************
Sub Dict_LocateWord(Word As String, _
   AlphaIndex As Long, WordIndex As Long)
   
   AlphaIndex = 0: WordIndex = 0
   If Len(Word) = 0 Then Exit Sub
   
   Dim sFind As String
   sFind = UCase$(Word)
   
   Dim iTmpAlpha As Long
   iTmpAlpha = AscW(Mid$(sFind, 1, 1))
   
   Select Case iTmpAlpha
      Case 65 To 90
         AlphaIndex = iTmpAlpha - 64
         Dim sWords() As String
         sWords = mtRolo(AlphaIndex).WordsArr
         Dim iCurr As Long, iMax As Long
         iMax = UBound(sWords)
         Do While iCurr < iMax
            iCurr = iCurr + 1
            If sWords(iCurr) = sFind Then
               WordIndex = iCurr
               Exit Do
            End If
         Loop
   End Select
      
End Sub

' ***********************************************
' Dict_GetCharWords << String()
' Returns a string array bearing all the words
'  in an alpha index e.g. all words beginning
'  with A, B or C etc.
'
' Parameters:
'   CharIndex::Long,
' ***********************************************
Function Dict_GetCharWords( _
   CharIndex As Long) As String()
   
   Select Case CharIndex
      Case 1 To 26
         Dict_GetCharWords = mtRolo(CharIndex).WordsArr
   End Select
   
End Function

' ***********************************************
' Dict_Unload <<
' Releases main data array and Yext Reader
'
' Parameters:
'   none
' ***********************************************
Sub Dict_Unload()
   Erase mtRolo
   Set moTxtRdr = Nothing
End Sub

' ***********************************************
' Dict_GetWordDefinition << String
' Returns a word and its full definition by
'  virtue of its alpha index and word index
'  inside the alpha index word array
'
' Parameters:
'   AlphaIndex::Long,
'   WordIndex::Long,
' ***********************************************
Function Dict_GetWordDefinition( _
   AlphaIndex As Long, WordIndex As Long) As String
   
   Dim tRolo As ROLODEX_INFO
   tRolo = mtRolo(AlphaIndex)
   
   Dim tWord As WORD_INFO
   tWord = tRolo.Words(WordIndex)
   
   Dim iFirst As Long, iLast As Long
   iFirst = tWord.LineNumber
   
   If WordIndex < UBound(tRolo.Words) Then
      ' v2 update, permit multiple entry word -----------------------------
      '  e.g. AARONIC; AARONICAL
      
'      iLast = _
         mtRolo(AlphaIndex).Words(WordIndex + 1).LineNumber - 1
      
      Dim iOffset As Long
      Do
         
         iOffset = iOffset + 1
         
         iLast = _
            mtRolo(AlphaIndex).Words(WordIndex + iOffset).LineNumber - 1
            
      Loop While iLast < iFirst
      ' basically we want the first and last line of a definition but
      ' when two or more words share it the next word was the same
      ' word which caused iLast to be < iFirst
      ' -------------------------------------------------------------------
         
   Else
      Select Case AlphaIndex
         Case 1 To 25
            iLast = _
               mtRolo(AlphaIndex + 1).Words(1).LineNumber - 1
         Case 26
            iLast = mtRolo(26).LastLine
         Case Else
            Exit Function
      End Select
   End If
   
   Dict_GetWordDefinition = moTxtRdr.Lines(iFirst, iLast)
      
End Function

' ***********************************************
' Dict_GetWordName << String
'
'
' Parameters:
'   AlphaIndex::Long,
'   WordIndex::Long,
' ***********************************************
Function Dict_GetWordName( _
   AlphaIndex As Long, WordIndex As Long) As String

   Dim tRolo As ROLODEX_INFO
   tRolo = mtRolo(AlphaIndex)
   
   Dim tWord As WORD_INFO
   tWord = tRolo.Words(WordIndex)
   
   Dict_GetWordName = tWord.Word

End Function

' ***********************************************
' Dict_GetWord_Bytes << Byte()
' As Dict_GetWordDefinition but returns the word's
'  definition as a byte array
'
' Parameters:
'   AlphaIndex::Long,
'   WordIndex::Long,
' ***********************************************
Function Dict_GetWord_Bytes( _
   AlphaIndex As Long, WordIndex As Long, _
   Optional MeaningOnly As Boolean = False) As Byte()
   
   Dim tRolo As ROLODEX_INFO
   tRolo = mtRolo(AlphaIndex)
   
   Dim tWord As WORD_INFO
   tWord = tRolo.Words(WordIndex)
   
   Dim iFirst As Long, iLast As Long
   iFirst = tWord.LineNumber
   
   If WordIndex < UBound(tRolo.Words) Then
      
      ' v2 update, permit multiple entry word -----------------------------
      '  e.g. AARONIC; AARONICAL
      
'      iLast = _
         mtRolo(AlphaIndex).Words(WordIndex + 1).LineNumber - 1
      
      Dim iOffset As Long
      Do
         
         iOffset = iOffset + 1
         
         iLast = _
            mtRolo(AlphaIndex).Words(WordIndex + iOffset).LineNumber - 1
            
      Loop While iLast < iFirst
      ' basically we want the first and last line of a definition but
      ' when two or more words share it the next word was the same
      ' word which caused iLast to be < iFirst
      ' -------------------------------------------------------------------
      
   Else
      Select Case AlphaIndex
         Case 1 To 25
            iLast = _
               mtRolo(AlphaIndex + 1).Words(1).LineNumber - 1
         Case 26
            iLast = mtRolo(26).LastLine
         Case Else
            Exit Function
      End Select
   End If
   
   If MeaningOnly Then
      ' v2 ----------------------------------------------------------------
      '/* e.g. skip the word name, its pronunciation
      '   and extended attributes, just read on from
      '   the Definition bit.
      Do
         iFirst = iFirst + 1
      Loop While moTxtRdr.LineLength(iFirst) > 0
      iFirst = iFirst + 1
      If iFirst > iLast Then
         iFirst = iLast ' e.g. "First was bigger than last :o"
      End If
   End If
   
   Dict_GetWord_Bytes = moTxtRdr.LinesB(iFirst, iLast)
      
End Function

' ***********************************************
' Dict_Data << clsTextReader
' This returns the dictionary's Text-Reader
'
' Parameters:
'   none
' ***********************************************
Function Dict_Data() As clsTextReader
   Set Dict_Data = moTxtRdr
End Function

' ***********************************************
' Dict_Load << Long
' This tries to load the dictionary and if
'  successful returns 0 else -1
'  basically this has to be called in order
'  to have a dictionary to work with!!!
'
' Parameters:
'   none
' ***********************************************
Function Dict_Load() As Long
   
   Log_Write "Begin Load"
   
   frmSplash.Splash_UpdateStatus "begin"
      
   If Not File_Exists(Dict_FileName) Then
      Log_Write "Load Dictionary, no file: " & Dict_FileName
      Dict_Load = DICT_LOAD_FAIL
   Else
      dict_Init
      dict_Parse_First
      dict_Parse_Second
      Dict_Load = DICT_LOAD_SUCCESS
   End If
   
   Log_Write "End Load"
   
End Function

' ***********************************************
' dict_Init <<
' extended instructions to carry out during a
'  load dictionary operation
'
' Parameters:
'   none
' ***********************************************
Private Sub dict_Init()
   
   Log_Write "Begin Dictionary Initialisation"
   
   miTotalWordCount = 0
   
   frmSplash.Splash_UpdateStatus _
      "read data file " & DICT_FILE_NAME
   
   Set moTxtRdr = New clsTextReader
   moTxtRdr.ReadFile Dict_FileName
   
   Log_Write "   reading Project Gutenberg info"
   
   frmSplash.Splash_UpdateStatus _
      "acquiring data start and end lines"
      
   miFirstLine = Gutenberg_Data_FirstLine(moTxtRdr)
   miLastLine = Gutenberg_Data_LastLine(moTxtRdr)
   
   ReDim mtRolo(26)
   ReDim dictWords(200000)
   
   Log_Write "End Dictionary Initialisation"
      
End Sub

' ***********************************************
' Dict_GetWordCount << Long
' Returns the number of word definitions found
'  in our dictionary
'
' Parameters:
'   none
' ***********************************************
Function Dict_GetWordCount() As Long
   Dict_GetWordCount = miTotalWordCount
End Function

' ***********************************************
' Dict_GetCharData << String
' Returns the complete text of an alpha index
'  e.g. all text in A section etc
'
' Parameters:
'   none
' ***********************************************
Function Dict_GetCharData( _
   CharIndex As Long) As String
   
   Dim iFirst As Long, iLast As Long
   Dict_LoadAlphaRange _
      CharIndex, iFirst, iLast
   
   Dict_GetCharData = _
      moTxtRdr.Lines(iFirst, iLast)
   
End Function

' ***********************************************
' Dict_LoadAlphaRange <<
' This loads the first and last line of an
'  alpha index section by virtue of an
'  index between 1 and 26 e.g. A->Z
'
' Parameters:
'   CharIndex::Long,
'   FirstLine::Long, line where alpha section starts//returned!!!
'   LastLine::Long, line where alpha section ends//returned!!!
' ***********************************************
Sub Dict_LoadAlphaRange(CharIndex As Long, _
   FirstLine As Long, LastLine As Long)
   
   Select Case CharIndex
      Case 1 To 26
         FirstLine = mtRolo(CharIndex).FirstLine
         LastLine = mtRolo(CharIndex).LastLine
   End Select
   
End Sub

' ***********************************************
' dict_Parse_First <<
' Tries to find a start line number for each
'  alpha section
'
' Parameters:
'   none
' ***********************************************
Private Sub dict_Parse_First()
   ' Description: finds where each alpha
   ' character starts and ends.
   Log_Write "Begin First Parse"
   Log_Write "   find start of each alpha index e.g. a, b, c...z"
   
   Dim iChar As Long
   iChar = 65
   
   Dim iIndex As Long
   
   Dim iCurr As Long
   iCurr = miFirstLine
   
   Do While iCurr < miLastLine
   
      iCurr = iCurr + 1
      
      If moTxtRdr.LineLength(iCurr) = 1 Then
         If moTxtRdr.Line(iCurr) = Chr$(iChar) Then
         
            frmSplash.Splash_UpdateStatus _
               Chr$(iChar) & " found to start on line " & iCurr
            
            Log_Write "    " & Chr$(iChar) & " found to start on line " & iCurr
            
            iIndex = iIndex + 1
            
            mtRolo(iIndex).CharNo = iChar
            mtRolo(iIndex).CharStr = Chr$(iChar)
            mtRolo(iIndex).FirstLine = iCurr
            
            If iIndex > 1 Then
               
               If iIndex = 26 Then
                  mtRolo(iIndex).LastLine = miLastLine - 1
               End If
               mtRolo(iIndex - 1).LastLine = iCurr - 1
               
            End If
            
            iChar = iChar + 1
            
         End If
      End If
   Loop
   
   Dim sLogMsg As String
   Select Case iIndex
      Case 26
         sLogMsg = _
            "    Z section found to end on line " _
            & miLastLine & vbCrLf & _
            "First Dictionary Parse Successful"
            
      Case Else
         sLogMsg = "First Parse Unsuccessful" & vbCrLf & "read up to char " & Chr$(iChar - 1)
   End Select
   
   Log_Write sLogMsg
   
   
   If Not iIndex = 26 Then ' // sanity check
      
      MsgBox _
         "bad data?" & vbCrLf & "read upto to char " & Chr$(iChar - 1), MB_ICONEXCLAMATION, "bad data?"
         
   End If
   
End Sub

' ***********************************************
' dict_Parse_Second <<
' Tries to discover and note all words inside
'  each alpha section
'
' Parameters:
'   none
' ***********************************************
Private Sub dict_Parse_Second()
   ' Description: find and record each dictionary word.
   ' -------------------------------------------------------------------
   ' Each dictionary word is written in upper case so
   ' I'm just recording words whose letters are all
   ' upper case and on which lines they start and end.
   ' Previously we parsed for where each letter starts
   ' and ends, now we use this information to read one
   ' letter at a time and record its words in its own array.
   ' -------------------------------------------------------------------
   
   On Error GoTo ErrHan:
   
   Log_Write "Begin Second Dictionary Parse"
   Log_Write "   e.g. find words inside each alpha index..."
   Log_Write "        all a words, b words, c words etc"
   
   Dim tRolo As ROLODEX_INFO
   Dim iCurr As Long, iMax As Long
   
   iMax = 26
   
   ReDim miWordCounts(iMax)
   
   Do While iCurr < iMax
   
      iCurr = iCurr + 1
      tRolo = mtRolo(iCurr)
      
      Dim iLine As Long, iCount As Long
      Dim bInAWord As Boolean, bLine() As Byte
      Dim sTmpWord As String, iChar As Long
      
      ReDim tRolo.Words(20000)
      ReDim tRolo.WordsArr(20000)
      
      Dim iFirstLine As Long
      iFirstLine = tRolo.FirstLine
      
      For iLine = tRolo.FirstLine To tRolo.LastLine
         '/* get the line to read
         bLine = moTxtRdr.LineB(iLine)
         
         '   loop thru' its byte data*/
         Dim iCharLoop As Long
         For iCharLoop = 0 To UBound(bLine) Step 2
            
            iChar = bLine(iCharLoop)
'            Debug.Print Chr$(iChar)
            
            If Is_WordBreakChar(iChar) Then
               If bInAWord Then
                  '/* sometimes there are muliple words
                  '   delimited by ';' these are other
                  '   versions/spellings of the word and
                  '   can not be presumed to start with
                  '   the same leading character/letter,
                  '   this is why this condition is being
                  '   tested!*/
                  GoTo IncludeChar:
               End If
            End If
            
            Select Case iChar
            
               Case 65 To 90
IncludeChar:
                  sTmpWord = sTmpWord & Chr$(iChar)
                  bInAWord = True
                  
               Case Else
                  
                  If iChar = 198 Then ' Æ
                     GoTo IncludeChar:
                  End If
                  
                  GoTo NextLine:
                  
            End Select
            
         Next iCharLoop
         
         If Len(sTmpWord) > 0 Then
            
            If Len(Trim$(sTmpWord)) = 0 Then GoTo NextLine:
            
            ' found a word in upper case!
            ' v2 added ----------------------------------------------------------
            ' NOTE: only if previous line is empty!!!
            ' e.g.
            '  LenB(moTxtRdr.LineB(iLine - 1)) = 0
            '     or something to that effect
            If iLine > iFirstLine Then
               If moTxtRdr.LineLength(iLine - 1) > 0 Then
                  GoTo NextLine:
               End If
            End If
            ' -------------------------------------------------------------------
            Dim iFind As Long
            iFind = InStr(1, sTmpWord, ";")
            If iFind > 0 Then
               '/* add each item e.g. AARONIC; AARONICAL
               Dim sItems() As String, sItem As String
               sItems = Split(sTmpWord, ";")
               Dim iItem As Long
               For iItem = LBound(sItems) To UBound(sItems)
                  sItem = Trim$(sItems(iItem))
                  If Len(sItem) > 0 Then
                     iCount = iCount + 1
                     tRolo.Words(iCount).Word = sItem
                     tRolo.Words(iCount).LineNumber = iLine
                     tRolo.WordsArr(iCount) = sItem
                  End If
               Next iItem
               
            Else
               '/* add single item
               iCount = iCount + 1
               tRolo.Words(iCount).Word = sTmpWord
               tRolo.Words(iCount).LineNumber = iLine
'               If Len(s2ndLine) > 0 Then
'                  tRolo.Words(iCount).WordType = s2ndLine
''                  sTmpWord = sTmpWord & " [" & s2ndLine & "]"
'               End If
               
               tRolo.WordsArr(iCount) = sTmpWord
                              
            End If
            
         End If
         
NextLine:
         bInAWord = False
         sTmpWord = ""
      Next iLine
      
      ReDim Preserve tRolo.Words(iCount)
      ReDim Preserve tRolo.WordsArr(iCount)
      
      miWordCounts(iCurr) = iCount ' v1.1
      
      tRolo.WordCount = iCount
      mtRolo(iCurr) = tRolo
      miTotalWordCount = miTotalWordCount + iCount
      
      frmSplash.Splash_UpdateStatus _
         tRolo.CharStr & " words found " & iCount & ", total words so far = " & miTotalWordCount
      
      Log_Write "    " & tRolo.CharStr & " words found " & iCount
      
      iCount = 0
      Erase bLine
      bInAWord = False
      sTmpWord = ""
      
   Loop
   
   Log_Write "End Second Dictionary Parse"
   Log_Write "Total Word Definitions found = " & Dict_GetWordCount ' miTotalWordCount
   Log_Write "Dictionary Ready"
   
   Exit Sub

ErrHan:
   Debug.Print "Error " & Err.Number & " (" & Err.Description & ") in procedure dict_Parse_Second of Module modDict"
   
   Exit Sub
   Resume
End Sub

' ***********************************************
' Dict_FileName << String
' Returns the dictionary's file name
'
' Parameters:
'   none
' ***********************************************
Function Dict_FileName() As String
   
   Dict_FileName = _
      App.Path & "\" & DICT_FILE_NAME
      
End Function

' ***********************************************
' Dict_Ack << String
' Returns an acknowledgement of the data's
'  authors and project co-workers
'
' Parameters:
'   none
' ***********************************************
Function Dict_Ack() As String
   
   Dict_Ack = _
      "Data provided by Project Gutenberg in " & _
      "collaboration with Noah Webster" & vbCrLf & _
      "Spell Checker by Rde :)"
      
End Function

' ***********************************************
' Dict_Header << String
' Returns the dictionary's header section in which
'  the author introduces the dictionary
'
' Parameters:
'   none
' ***********************************************
Function Dict_Header() As String

   Dict_Header = _
      Gutenberg_Header(moTxtRdr, miFirstLine - 1)
      
End Function

' ***********************************************
' Dict_Footer << String
' Returns the sictionary's footer section in which
'  the author provides copyright information
'
' Parameters:
'   none
' ***********************************************
Function Dict_Footer() As String

   Dict_Footer = _
      Gutenberg_Copyright(moTxtRdr, miLastLine + 1)

End Function

' ***********************************************
' Is_WordBreakChar << Boolean
' Returns true/false in respect of a whether an
'  ASCII keycode can be described as a word
'  breaking character e.g. .,;:#' etc
' Note: not actually bothering with the underscore
'  character as being recognised as valid word char.
'
' Parameters:
'   pKeyCode::Integer, the key/char to test
' ***********************************************
Private Function Is_WordBreakChar( _
   ByVal pKeyCode As Integer) As Boolean
' tests if character is a word breaker

    On Error GoTo ErrHan:
    
    Is_WordBreakChar = pKeyCode < 48 Or _
                        pKeyCode > 57 And pKeyCode < 65 Or _
                        pKeyCode > 90 And pKeyCode < 96 Or _
                        pKeyCode > 122 And pKeyCode < 127

Exit Function
ErrHan:
    Debug.Print "modDict.Is_WordBreakChar.Error: " & Err.Number & "; " & Err.Description

End Function

