
'//
'//  Create the index disk file
'//
Function ctx_CreateIndex_Internal( ByVal xdb As CHEETAH4_TYPE Ptr ) As Long

   Local tHeader       As INDEX_HEADER_NODE_TYPE
   Local sData        As String
   Local nParseCount  As Long
   Local x            As Long
   Local hFile        As Long
   Local ElementName  As String
   Local ElementValue As String
   Local sFilename    As String
                      
   ' Do some tests to ensure that the parameters were correctly
   ' filled in when the call was made.
   If (@xdb.nData = 0) Or (@xdb.nLen = 0) Then 
      @xdb.errcode = %ERR_DATA: GoTo ExitOut
   End If
   
   sData = Peek$(@xdb.nData, @xdb.nLen)

   
   ' The data string contains the filename and a list of Field
   ' definitions all deliminated by $DELIMITER
   nParseCount = ParseCount(sData, $DELIMITER)
   
   ' There *must* be at least the filename in the data string.
   If nParseCount < 1 Then @xdb.errcode = %ERR_DATA: GoTo ExitOut
   
   ' Load the elements into an array so we can parse them
   ReDim Elements(1 To nParseCount) As String
   Parse sData, Elements(), $DELIMITER
     
     
   ' Analyze the elements that were passed to this routine.
   For x = 1 To nParseCount
       ElementName = Trim$( Parse$(Elements(x), "=", 1) )
       If Len(ElementName) = 0 Then Iterate For
       
       ElementValue = Trim$( Parse$(Elements(x), "=", 2) )
       
       
       Select Case Asc(ElementName)
       
          Case 68, 100  'Dd  Disk
             sFilename = ElementValue
          
       End Select
       
   Next
                                                  
                                                  
   If Len(sFilename) = 0 Then @xdb.errcode = %ERR_FILENAME: GoTo ExitOut

   '// CREATE THE ACTUAL INDEX ON DISK //
   tHeader.idBytes    =  $INDEX_IDBYTES
   tHeader.Version    =  %INDEX_VERSION
   tHeader.FreeList   =  0
   tHeader.NumTags    =  0
   tHeader.TotalNodes =  1
   
   '// Create the actual index on disk //
   If Dir$( sFilename ) > "" Then Kill sFilename
   
   If FOpen( sFilename, 2, 4, hFile ) Then
      ' error occurred during creation
      @xdb.errcode = %ERR_FILEACCESS: GoTo ExitOut
   Else
      If fWriteAt( hFile, 1, SizeOf(tHeader), VarPtr(tHeader) ) Then
         @xdb.errcode = %ERR_FILEACCESS: GoTo ExitOut
      End If
      fclose hFile
   End If
                             

ExitOut:
   Function = @xdb.errcode 
         
End Function



'//
'//  Create a Tag memory type based on the disk type.
'//
Function ctx_CreateTagType_Internal( ByVal pBTX As BTX_TYPE Ptr, _
                                     ByVal pTagDisk As INDEX_TAG_TYPE_DISK Ptr, _
                                     ByRef MemAddress As Dword _
                                     ) As Long
      
   Local pTAG    As INDEX_TAG_TYPE_MEMORY Ptr
   Local errcode As Long
    
   ' Determine if the memory structure already exists. If it does
   ' not exist, then create it.
   pTAG = @pBTX.pFirstTAG
   Do Until pTAG = 0
      If @pTAG.@pTagDisk.TagName = @pTagDisk.TagName Then
         MemAddress = pTAG : Exit Function
      End If               
      pTAG = @pTAG.pNextTAG
   Loop
   
   
   ' Create a Tag memory structure and pass its address back to
   ' the calling function.
   pTAG = MemAlloc( SizeOf(INDEX_TAG_TYPE_MEMORY) )
   If pTAG = 0 Then errcode = %ERR_MEMORYALLOC: GoTo ExitOut
   
   @pTAG.pTagDisk = pTagDisk
   @pTAG.pBTX     = pBTX
   
   ' Add the pTAG to the pBTX linked list so the allocated
   ' memory can be deallocated when the index is closed or
   ' the Tag is deleted.
   @pTAG.pNextTAG = @pBTX.pFirstTAG
   @pBTX.pFirstTAG = pTAG
   
   MemAddress = pTAG
   
ExitOut:
   Function = errcode 
         
End Function



'//
'//  Open the index disk file
'//
Function ctx_OpenIndex_Internal( ByVal xdb As CHEETAH4_TYPE Ptr ) As Long 

   Local pDBF           As DBF_TYPE Ptr
   Local pBTX           As BTX_TYPE Ptr
   
   Local sData          As String   'the incoming data
   Local sFilename      As String
   Local ElementName    As String
   Local ElementValue   As String
                       
   Local x              As Long
   Local nParseCount    As Long
   Local hFile          As Long
   Local AccessMode     As Long
   Local ShareMode      As Long  
   Local NoDatabase     As Long   ' index does not require a database (standalone)
   Local IsMultiUser    As Long   
   
   ' Do some tests to ensure that the parameters were correctly
   ' filled in when the call was made.
   If (@xdb.nData = 0) Or (@xdb.nLen = 0) Then 
      @xdb.errcode = %ERR_DATA: GoTo ExitOut
   End If

   pDBF = @xdb.nData4byte
   
   sData = Peek$(@xdb.nData, @xdb.nLen)
   
   ' The data string contains the filename and a list of Field
   ' definitions all deliminated by $DELIMITER
   nParseCount = ParseCount(sData, $DELIMITER)
   
   ' There *must* be at least the filename 
   If nParseCount < 1 Then @xdb.errcode = %ERR_DATA: GoTo ExitOut
   
   ' Load the elements into an array so we can parse them
   ReDim Elements( 1 To nParseCount ) As String
   Parse sData, Elements(), $DELIMITER

   ' Set the default Access and Share modes in case they are
   ' not specified in the incoming string definition.
   AccessMode = 2             
   ShareMode  = 4

   ' Analyze the elements that were passed to this routine.
   For x = 1 To nParseCount
       ElementName = Trim$( Parse$(Elements(x), "=", 1) )
       If Len(ElementName) = 0 Then Iterate For
       
       ElementValue = Trim$( Parse$(Elements(x), "=", 2) )
       
       
       Select Case Asc(ElementName)

          Case 68, 100  'Dd  Disk
             sFilename = ElementValue

          Case 65, 97   'Aa  Access Mode
             AccessMode = Val( ElementValue )
             
          Case 83, 115  'Ss  Share Mode   
             ShareMode  = Val( ElementValue )
            
          Case 78, 110  'Nn  NoDatabase
             ' We are attempting to use the index as standalone. That is, we
             ' are not tying the database to the index. Keys and record numbers
             ' can be freely added to the index without having to add them
             ' to a database as well.
             NoDatabase = IIf&( UCase$(ElementValue) = "TRUE", %TRUE, %FALSE )
             
          Case 77, 109  'Mm  Multiuser
             IsMultiUser = IIf&( UCase$(ElementValue) = "TRUE", %TRUE, %FALSE )
             
       End Select
   Next    

   
   ' Do some filename checks
   If NoDatabase = %FALSE Then
      If Len(sFilename)  = 0  Then @xdb.errcode = %ERR_FILENAME: GoTo ExitOut
      If Dir$(sFilename) = "" Then @xdb.errcode = %ERR_FILENAME: GoTo ExitOut
      ' The associated DBF is in the nData4byte element
      If pDBF = 0 Then @xdb.errcode = %ERR_DATA: GoTo ExitOut
   End If
   
   ' Do some mode checks
   If (AccessMode < 0) Or (AccessMode > 2) Then @xdb.errcode = %ERR_ACCESSMODE: GoTo ExitOut
   If (ShareMode < 1)  Or (ShareMode > 4)  Then @xdb.errcode = %ERR_SHAREMODE: GoTo ExitOut
   
   ' Open the index. 
   If fOpen( sFilename, AccessMode, ShareMode, hFile ) Then
      @xdb.errcode = %ERR_FILEACCESS: GoTo ExitOut
   End If   

   
   ' A index has now been opened. Allocate a new BTX_TYPE struture to hold
   ' all of the information related to this index. This index structure is
   ' then linked to the database, DBF_TYPE, structure.
   @xdb.reserved = MemAlloc( SizeOf(BTX_TYPE) )
   If @xdb.reserved = 0 Then @xdb.errcode = %ERR_MEMORYALLOC: GoTo ExitOut
   
   pBTX = @xdb.reserved
   
   @pBTX.MultiUser  = IsMultiUser
   @pBTX.StandAlone = NoDatabase
   @pBTX.hFile      = hFile 

   ' Set the linked list of indexes that are open with the database.
   If @pBTX.StandAlone = %FALSE Then
      @pBTX.pNextBTX  = @pDBF.pFirstBTX
      @pDBF.pFirstBTX = pBTX
      @pBTX.pDBF      = @xdb.nData4byte
   End If
   
   ' Get the Index header from disk.
   @xdb.errcode = ctx_GetNode_Internal( pBTX, 1, VarPtr(@pBTX.BTH) ) 
   If @xdb.errcode Then GoTo ExitOut
   
   ' Determine is this is a valid file type for Cheetah to handle
   If @pBTX.BTH.idBytes <> $INDEX_IDBYTES Then
      @xdb.errcode = %ERR_FILETYPE: GoTo ExitOut
   End If
   
   ' Create the TAG memory types.
   For x = 1 To %INDEX_MAX_TAGS                  
      If Len( @pBTX.BTH.Tags(x).TagName ) Then  
         ' We do not need the returned memory address so simply pass a zero.
         @xdb.errcode = ctx_CreateTagType_Internal( pBTX, VarPtr(@pBTX.BTH.Tags(x)), 0 )
         If @xdb.errcode Then GoTo ExitOut
      End If   
   Next                                
   
ExitOut:
   If @xdb.errcode Then 
      If pBTX Then
         fClose @pBTX.hFile
         ctx_DeallocateBTXmemory pBTX
      End If
      @xdb.reserved = 0
   End If
   Function = @xdb.errcode
    
End Function



'//
'//  Close an index file
'//
Function ctx_CloseIndex_Internal( ByVal xdb As CHEETAH4_TYPE Ptr ) As Long 
   
   Local pDBF       As DBF_TYPE Ptr
   Local pBTX       As BTX_TYPE Ptr 
   Local pSearchBTX As BTX_TYPE Ptr
   Local pNextBTX   As BTX_TYPE Ptr
   
   ' It is possible that CloseIndex can/will be called when no
   ' index has ever been opened. In that scenario then no pointer
   ' exists in .reserved. Simply exit without throwing an error.
   If @xdb.reserved = 0 Then GoTo ExitOut
   pBTX = @xdb.reserved
   
   ' Close the index file
   fClose @pBTX.hFile

   ' Remove the index from the database's list of open indexes only
   ' if this index was not opened in standalone mode.
   If @pBTX.StandAlone = %FALSE Then

      pDBF = @pBTX.pDBF          ' get the pointer to the database for this index
      
      pNextBTX = @pBTX.pNextBTX  ' next index in the chain
      
      ' Adjust the linked list to remove this index.
      ' Because this is a single linked list, we need to find the node
      ' that is immediately before the index that we are interested in. Start
      ' at the Workspace variable.
      If @pDBF.pFirstBTX = pBTX Then
         @pDBF.pFirstBTX = pNextBTX
      Else   
         pSearchBTX = @pDBF.pFirstBTX
         Do Until pSearchBTX = 0
            If @pSearchBTX.pNextBTX = pBTX Then
               ' Make the most previous index point to the index immediately after
               ' the index that we are deleting.
               @pSearchBTX.pNextBTX = pNextBTX
               Exit Do
            End If                          
            pSearchBTX = @pSearchBTX.pNextBTX
         Loop
      End If
   
   End If
   
   ' DeAllocate any manually allocated memory (this will also 
   ' deallocate any TAG memory as well).
   ctx_DeallocateBTXmemory pBTX              
   

ExitOut:
   Function = @xdb.errcode 
    
End Function



'//
'//  Create an index tag
'//
Function ctx_CreateTag_Internal( ByVal xdb As CHEETAH4_TYPE Ptr ) As Long

   Local pDBF         As DBF_TYPE Ptr
   Local pBTX         As BTX_TYPE Ptr
   Local LookupNode   As INDEX_LOOKUP_NODE_TYPE
   Local sData        As String
   Local nParseCount  As Long
   Local foundat      As Long
   Local NodeNum      As Long
   Local x            As Long
   Local NumFields    As Long
   Local ElementName  As String
   Local ElementValue As String
   
   Local sTagName     As String
   Local sExpression  As String
   Local sFieldName   As String
   Local nUppercase   As Long  
   Local nKeySize     As Long
   Local xdbtemp      As CHEETAH4_TYPE 
                      
   ' Do some tests to ensure that the parameters were correctly
   ' filled in when the call was made.
   If (@xdb.nData = 0) Or (@xdb.nLen = 0) Then 
      @xdb.errcode = %ERR_DATA: GoTo ExitOut
   End If
   
   ' The associated index is in the nData4byte element
   If @xdb.nData4byte = 0 Then @xdb.errcode = %ERR_DATA: GoTo ExitOut
   pBTX = @xdb.nData4byte
   pDBF = @pBTX.pDBF
   
   sData = Peek$(@xdb.nData, @xdb.nLen)

   
   ' The data string contains the filename and a list of Field
   ' definitions all deliminated by $DELIMITER
   nParseCount = ParseCount(sData, $DELIMITER)
   
   ' There *must* be at least the tagname in the data string.
   If nParseCount < 1 Then @xdb.errcode = %ERR_DATA: GoTo ExitOut
   
   ' Load the elements into an array so we can parse them
   ReDim Elements(1 To nParseCount) As String
   Parse sData, Elements(), $DELIMITER
     
   ' Analyze the elements that were passed to this routine.
   For x = 1 To nParseCount
       ElementName = Trim$( Parse$(Elements(x), "=", 1) )
       If Len(ElementName) = 0 Then Iterate For
       
       ElementValue = Trim$( Parse$(Elements(x), "=", 2) )
       
       
       Select Case Asc(ElementName)
       
          Case 84, 116  'Tt  Tag name
             sTagName = ElementValue
          
          Case 69, 101  'Ee  Expression
             sExpression = ElementValue
             
          Case 83, 115  'Ss  Size of a key (needed for standalone indexes)
             nKeySize   = Val( ElementValue )

          Case 85, 117  'Uu Uppercase
             nUppercase = IIf&( UCase$(ElementValue) = "TRUE", %TRUE, %FALSE )
             
       End Select
       
   Next
                                                  
                                                  
   ' If no valid tagname was specified then abort.
   If Len(sTagName) = 0 Then @xdb.errcode = %ERR_TAGNAME: GoTo ExitOut
   
   ' Ensure that the Expression is valid
   If @pBTX.StandAlone = %FALSE Then
      NumFields = ParseCount( sExpression, "+" )
      If ( NumFields > %INDEX_MAX_TAG_FIELDS ) Or ( Len(sExpression) = 0 ) Then 
         @xdb.errcode = %ERR_TAGEXPRESSION: GoTo ExitOut
      End If
   End If
   
   
   '// CREATE THE ACTUAL TAG //
   
   ' Find an empty slot to put the new Tag in.
   foundat = 0
   If @pBTX.BTH.NumTags + 1 <= %INDEX_MAX_TAGS Then
      For x = 1 To %INDEX_MAX_TAGS 
         If Len( @pBTX.BTH.Tags( x ).TagName ) = 0 Then
            foundat = x: Exit For
         End If   
      Next
   End If
   
   ' No more room, throw and error
   If foundat = 0 Then @xdb.errcode = %ERR_INDEXTAGFULL: GoTo ExitOut
      
   If @pBTX.StandAlone Then
      @pBTX.BTH.Tags( foundat ).KeySize = nKeySize
   Else   
#If Not %Def(%CHEETAH_INDEX_ONLY)
      For x = 1 To NumFields
         sFieldName    = Parse$( sExpression, "+", x ) 
         
         xdbtemp.nData = StrPtr( sFieldName )
         xdbtemp.nLen  = Min( 10, Len(sFieldName) )
         xdbtemp.reserved = @pBTX.pDBF
         
         If ctx_FieldNumber_Internal( VarPtr(xdbtemp) ) Then
            @xdb.errcode = %ERR_TAGEXPRESSION: GoTo ExitOut
         End If
         
         @pBTX.BTH.Tags( foundat ).fields(x) = xdbtemp.nData4byte
   
         @pBTX.BTH.Tags( foundat ).KeySize = @pBTX.BTH.Tags( foundat ).KeySize + _
               @pDBF.@pFields[xdbtemp.nData4byte-1].FieldLength
      Next
#EndIf
   End If
   
   
   If @pBTX.BTH.Tags( foundat ).KeySize = 0 Then @xdb.errcode = %ERR_KEYSIZE: GoTo ExitOut

   @pBTX.BTH.Tags( foundat ).TagName   = UCase$(sTagName)
   @pBTX.BTH.Tags( foundat ).MaxKeys   = Int( (%INDEX_NODE_SIZE - 10) / (@pBTX.BTH.Tags( foundat ).KeySize+4) )
   @pBTX.BTH.Tags( foundat ).HalfNode  = Fix( @pBTX.BTH.Tags( foundat ).MaxKeys / 2 )
   @pBTX.BTH.Tags( foundat ).MaxKeys   = @pBTX.BTH.Tags( foundat ).HalfNode * 2
   @pBTX.BTH.Tags( foundat ).Uppercase = nUppercase
   
   ' Create the TAG memory type
   @xdb.errcode = ctx_CreateTagType_Internal( pBTX, VarPtr(@pBTX.BTH.Tags(foundat)), 0 )
   If @xdb.errcode Then GoTo ExitOut

   ' Create a default LookupNode
   @xdb.errcode = ctx_GetFreeNode_Internal( pBTX, NodeNum )
   If @xdb.errcode Then GoTo ExitOut                  
   
   @pBTX.BTH.Tags( foundat ).FirstLookupNode = NodeNum

   ' Write the LookupNode to disk
   @xdb.errcode = ctx_PutNode_Internal( pBTX, NodeNum, VarPtr( LookupNode ) )
   If @xdb.errcode Then GoTo ExitOut


   Incr @pBTX.BTH.NumTags 

   ' Write the header back to disk
   @xdb.errcode = ctx_PutNode_Internal( pBTX, 1, VarPtr(@pBTX.BTH) ) 
   If @xdb.errcode Then GoTo ExitOut

   
ExitOut:
   Function = @xdb.errcode 
         
End Function



'//
'//  Get an index tag
'//
Function ctx_GetTag_Internal( ByVal xdb As CHEETAH4_TYPE Ptr ) As Long

   Local pBTX     As BTX_TYPE Ptr
   Local x        As Long
   Local sTagName As String
                      
   ' Do some tests to ensure that the parameters were correctly
   ' filled in when the call was made.
   If (@xdb.nData = 0) Or (@xdb.nLen = 0) Then 
      @xdb.errcode = %ERR_DATA: GoTo ExitOut
   End If
   
   ' The associated index is in the nData4byte element
   If @xdb.nData4byte = 0 Then @xdb.errcode = %ERR_DATA: GoTo ExitOut
   pBTX = @xdb.nData4byte
   
   sTagName = UCase$( Peek$(@xdb.nData, @xdb.nLen) )

                                                  
   ' If no valid tagname was specified then abort.
   If Len(sTagName) = 0 Then @xdb.errcode = %ERR_TAGNAME: GoTo ExitOut
   

   ' Find the Tag in the specified Tag memory list. If the Tag is not found then
   ' create a new Tag memory type. 
   @xdb.reserved = 0    

   For x = 1 To %INDEX_MAX_TAGS
      If @pBTX.BTH.Tags(x).TagName = sTagName Then
         
         ' If the Tag memory type already exists then the CreateTagType
         ' will return it, otherwise it will create a new memory type.
         @xdb.errcode = ctx_CreateTagType_Internal( pBTX, VarPtr(@pBTX.BTH.Tags(x)), @xdb.reserved )
         If @xdb.errcode Then GoTo ExitOut

         Exit For
      End If   
   Next
   
   If @xdb.reserved = 0 Then @xdb.errcode = %ERR_TAGNAME
   
   
ExitOut:
   Function = @xdb.errcode 
         
End Function



'//
'//  Delete an index tag
'//
Function ctx_DeleteTag_Internal( ByVal xdb As CHEETAH4_TYPE Ptr ) As Long

   Local pBTX             As BTX_TYPE Ptr
   Local pTAG             As INDEX_TAG_TYPE_MEMORY Ptr
   Local LookupNode       As INDEX_LOOKUP_NODE_TYPE
   Local RegularNode      As INDEX_REGULAR_NODE_TYPE
   Local NextNode         As Long
   Local FirstRegularNode As Long
                      
   @xdb.errcode = ctx_GetTag_Internal( xdb )
   If @xdb.errcode Then GoTo ExitOut

   pTAG = @xdb.reserved   ' filled by the ctx_GetTag_Internal function   

   pBTX = @xdb.nData4byte
   

   ' Remove the Lookup nodes by sending them to the free chain 
   ' in the index header. 
   NextNode = @pTAG.@pTagDisk.FirstLookupNode
   If NextNode Then  ' a LookupNode list actually exists
      Do Until NextNode = 0
      
         If fGetAt( @pBTX.hFile, (NextNode * %INDEX_NODE_SIZE) + 1, _
                    SizeOf(LookupNode), VarPtr(LookupNode) ) Then
            Function = %ERR_FILEACCESS: Exit Function
         End If        
         
         ' If this is the first Lookup node then save the node number
         ' that defines where the linked list of Regular nodes begin.
         If NextNode = @pTAG.@pTagDisk.FirstLookupNode Then
            FirstRegularNode = LookupNode.Node(1)
         End If      
         
         If NextNode = 0 Then  ' we are at the end of the list
            ' Point the last node in the Lookup list to the start of the
            ' currently defined Free list and save it to disk.
            LookupNode.NextNode = @pBTX.BTH.FreeList 
            If fWriteAt( @pBTX.hFile, (NextNode * %INDEX_NODE_SIZE) + 1, _
                         SizeOf(LookupNode), VarPtr(LookupNode) ) Then
               Function = %ERR_FILEACCESS: Exit Function
            End If
         
            ' Point the Free list to the start of the Lookup list
            @pBTX.BTH.FreeList = @pTAG.@pTagDisk.FirstLookupNode
         
         Else
            NextNode = LookupNode.NextNode
         End If   
      Loop       

   End If
   

   ' Remove the Regular nodes by sending them to the free chain 
   ' in the index header. 
   NextNode = FirstRegularNode
   If NextNode Then  ' a list of Regular nodes actually exist
      Do Until NextNode = 0
      
         If fGetAt( @pBTX.hFile, (NextNode * %INDEX_NODE_SIZE) + 1, _
                    SizeOf(RegularNode), VarPtr(RegularNode) ) Then
            Function = %ERR_FILEACCESS: Exit Function
         End If              
         
         If NextNode = 0 Then  ' we are at the end of the list
            ' Point the last node in the Regular list to the start of the
            ' currently defined Free list and save it to disk.
            RegularNode.NextNode = @pBTX.BTH.FreeList 
            If fWriteAt( @pBTX.hFile, (NextNode * %INDEX_NODE_SIZE) + 1, _
                         SizeOf(RegularNode), VarPtr(RegularNode) ) Then
               Function = %ERR_FILEACCESS: Exit Function
            End If
         
            ' Point the Free list to the start of the RegularNode list
            @pBTX.BTH.FreeList = FirstRegularNode
         
         Else
            NextNode = RegularNode.NextNode
         End If   
      Loop       

   End If


   ' Reset the information for this Tag (memory for the TAG is deallocated
   ' when the index is closed).
   MemFree @pTAG.zLastMoveKey
   Type Set @pTAG = ""

   
   Decr @pBTX.BTH.NumTags
   
   
   ' Write the new information back to disk
   @xdb.errcode = ctx_PutNode_Internal( pBTX, 1, VarPtr(@pBTX.BTH) ) 
   

ExitOut:
   Function = @xdb.errcode 
         
End Function



'//
'//  Get a free node from the index (Free list or end of file)
'//
Function ctx_GetFreeNode_Internal( ByVal pBTX As BTX_TYPE Ptr, _
                                   ByRef NodeNum As Long _
                                   ) As Long

   ' Can we get a new node from the Free list for this index? If not,
   ' then get it from the end of the index.
   
   Local errcode     As Long
   Local RegularNode As INDEX_REGULAR_NODE_TYPE
   
   If @pBTX.BTH.FreeList Then
   
      NodeNum = @pBTX.BTH.FreeList

      ' Update the FreeList to point to the next node in the list.
      errcode = ctx_GetNode_Internal( pBTX, NodeNum, VarPtr(RegularNode) )
      If errcode Then GoTo ExitOut
      @pBTX.BTH.FreeList = RegularNode.NextNode
      
      errcode = ctx_PutNode_Internal( pBTX, 1, VarPtr(@pBTX.BTH) )
      If errcode Then GoTo ExitOut
      
   Else
   
      ' Get the node number from the end of the disk file.
      Incr @pBTX.BTH.TotalNodes
      NodeNum = @pBTX.BTH.TotalNodes
      
   End If

ExitOut:
   Function = errcode 
         
End Function


'//
'//  Put a node into the free node list in the index 
'//
Function ctx_PutFreeNode_Internal( ByVal pBTX As BTX_TYPE Ptr, _
                                   ByVal NodeNum As Long _
                                   ) As Long

   Local errcode     As Long
   Local RegularNode As INDEX_REGULAR_NODE_TYPE
   
   ' Update the node being freed to point to the current start of the FreeList
   errcode = ctx_GetNode_Internal( pBTX, NodeNum, VarPtr(RegularNode) )
   If errcode Then GoTo ExitOut
   RegularNode.NextNode = @pBTX.BTH.FreeList 

   errcode = ctx_PutNode_Internal( pBTX, NodeNum, VarPtr(RegularNode) )
   If errcode Then GoTo ExitOut

   ' Update the FreeList to point to the incoming node.
   @pBTX.BTH.FreeList = NodeNum 
      
   errcode = ctx_PutNode_Internal( pBTX, 1, VarPtr(@pBTX.BTH) )
   If errcode Then GoTo ExitOut


ExitOut:
   Function = errcode 
         
End Function


'//
'//  Get a node from the index 
'//
Function ctx_GetNode_Internal( ByVal pBTX As BTX_TYPE Ptr, _
                               ByVal NodeNum As Long, _
                               ByVal MemAddress As Dword _
                               ) As Long

   Local errcode As Long
   
   ' Get the node
   If fGetAt( @pBTX.hFile, _
              ( (NodeNum-1) * %INDEX_NODE_SIZE ) + 1, _
              %INDEX_NODE_SIZE, _
              MemAddress ) Then
      errcode = %ERR_FILEACCESS: GoTo ExitOut
   End If

ExitOut:
   Function = errcode 

End Function
        
        

'//
'//  Put a node into the index 
'//
Function ctx_PutNode_Internal( ByVal pBTX As BTX_TYPE Ptr, _
                               ByVal NodeNum As Long, _
                               ByVal MemAddress As Dword _
                               ) As Long

   Local errcode As Long

   ' Put the node
   If fWriteAt( @pBTX.hFile, _
                ( (NodeNum-1) * %INDEX_NODE_SIZE ) + 1, _
                %INDEX_NODE_SIZE, _
                MemAddress ) Then
      errcode = %ERR_FILEACCESS: GoTo ExitOut
   End If

ExitOut:
   Function = errcode 

End Function


'//
'//  Free memory held by a specific index and its TAGs
'//
Function ctx_DeallocateBTXmemory( ByVal pBTX As BTX_TYPE Ptr ) As Long

   Local pTAG     As INDEX_TAG_TYPE_MEMORY Ptr
   Local pNextTAG As INDEX_TAG_TYPE_MEMORY Ptr
   
   If pBTX = 0 Then Exit Function

   ' Iterate all TAGS in the index and free their memory
   pTAG = @pBTX.pFirstTAG
   Do Until pTAG = 0  
      pNextTAG = @pTAG.pNextTAG
      
      MemFree @pTAG.zLastMoveKey
      MemFree pTAG
       
      pTAG = pNextTAG
   Loop
   
   MemFree pBTX
    
End Function

