' btxMove


Function ctx_Move_Internal( ByVal xdb As CHEETAH4_TYPE Ptr ) As Long
   
   Local pDBF    As DBF_TYPE Ptr
   Local pBTX    As BTX_TYPE Ptr
   Local pTAG    As INDEX_TAG_TYPE_MEMORY Ptr
   
   
   ' Do some tests to ensure that the parameters were correctly
   ' filled in when the call was made.
   If @xdb.reserved Then
      pTAG = @xdb.reserved
      pBTX = @pTAG.pBTX      ' get the associated index for this TAG
   End If
   
   If @xdb.nData4byte = 0 Then
      ' We can have a null database handle only if the index was opened
      ' as a standalone index.
      If pBTX = 0 Then
         @xdb.errcode = %ERR_DATA: GoTo ExitOut
      Else
         If @pBTX.StandAlone = %FALSE Then
            @xdb.errcode = %ERR_DATA: GoTo ExitOut
         End If
      End If      
   Else
      pDBF = @xdb.nData4byte
   End If

   
   ' All move operations for the Database and/or Index Tags are handled in
   ' this module. If no TAG handle is specified then the operation is
   ' performed on the database only.
   
   If pTAG Then  
      ' move using the index TAG
      @xdb.errcode = ctx_Move_btx_Internal( xdb, pDBF, pBTX, pTAG )         
   Else   
      ' move only using the database
      @xdb.errcode = ctx_Move_dbf_Internal( xdb, pDBF, pBTX, pTAG )         
   End If
   

ExitOut:
   Function = @xdb.errcode 

End Function



'//
'//  Move operation on the database only
'//
Function ctx_Move_dbf_Internal( ByVal xdb  As CHEETAH4_TYPE Ptr, _
                                ByVal pDBF As DBF_TYPE Ptr, _
                                ByVal pBTX As BTX_TYPE Ptr, _
                                ByVal pTAG As INDEX_TAG_TYPE_MEMORY Ptr _
                                ) As Long
   Local nRecNum As Long

   @xdb.nData4byte = 0  ' default record to zero
   
   Select Case @xdb.nAction
      Case %XDB_MOVEFIRST           
         nRecNum = 1
         
      Case %XDB_MOVELAST            
         nRecNum = @pDBF.DBH.RecordCount

      Case %XDB_MOVENEXT, %XDB_MOVENEXT_ATOMIC            
         nRecNum = @pDBF.nLastMoveRecord + 1 
         
      Case %XDB_MOVEPREV, %XDB_MOVEPREV_ATOMIC                
         nRecNum = @pDBF.nLastMoveRecord - 1 
   End Select                
   

   ' If the new RecNum is invalid then return an error
   If (nRecNum < 0) Or (nRecNum > @pDBF.DBH.RecordCount) Then
      @pDBF.nLastMoveRecord = 0
      @xdb.nData4byte = nRecNum
      @xdb.errcode = %ERR_MOVEERROR: GoTo ExitOut
   End If
   
   ' Save the current position so the next operation can start at this record.
   @pDBF.nLastMoveRecord = nRecNum
   @xdb.nData4byte = nRecNum

ExitOut:
   Function = @xdb.errcode 

End Function



'//
'//  Move operation using the index Tag
'//
Function ctx_Move_btx_Internal( ByVal xdb  As CHEETAH4_TYPE Ptr, _
                                ByVal pDBF As DBF_TYPE Ptr, _
                                ByVal pBTX As BTX_TYPE Ptr, _
                                ByVal pTAG As INDEX_TAG_TYPE_MEMORY Ptr _
                                ) As Long
   
   Local nLookupNodeNum  As Long
   Local nLookupNodePos  As Long
   Local nRegularNodeNum As Long
   Local nRegularNodePos As Long
   Local nResult         As Long
   Local x               As Long
   
   @xdb.nData4byte = 0  ' default record to zero
   
   Select Case @xdb.nAction
      Case %XDB_MOVEFIRST  ' move to the first key in the Tag           
         nLookupNodeNum = @pTAG.@pTagDisk.FirstLookupNode
         @xdb.errcode = ctx_GetNode_Internal( pBTX, _
                                              nLookupNodeNum, _
                                              VarPtr( @pTAG.LookupNode ) )
         If @xdb.errcode Then GoTo ExitOut    
         
         nRegularNodeNum = @pTAG.LookupNode.Node(1)
         If nRegularNodeNum = 0 Then
            @xdb.errcode = %ERR_MOVEERROR: GoTo ExitOut
         End If
            
         @xdb.errcode = ctx_GetNode_Internal( pBTX, nRegularNodeNum, VarPtr(@pTAG.RegularNode) )
         If @xdb.errcode Then GoTo ExitOut    
         
         If @pTAG.RegularNode.NumKeys = 0 Then
            @xdb.errcode = %ERR_MOVEERROR: GoTo ExitOut
         End If
         
         ' Retrieve the Key and RecNum from the first Key on the node
         MemString @pTAG.zLastMoveKey, Peek$(VarPtr(@pTAG.RegularNode.Buffer),  @pTAG.@pTagDisk.KeySize)
         @pTAG.nLastMoveRecNum = Peek( Long, VarPtr(@pTAG.RegularNode.Buffer) + @pTAG.@pTagDisk.KeySize ) 
         
         @xdb.nData4byte = @pTAG.nLastMoveRecNum
           
         @pTAG.LookupNodeNum  = nLookupNodeNum
         @pTAG.LookupNodePos  = 1
         @pTAG.RegularNodeNum = nRegularNodeNum
         @pTAG.RegularNodePos = 1
         
         GoTo ExitOut
         
         
         
      Case %XDB_MOVENEXT, %XDB_MOVENEXT_ATOMIC            
         
         If @xdb.nAction = %XDB_MOVENEXT Then
            ' Seek to the most previously moved to key/recnum
            nResult = ctx_SeekEngine_Internal( xdb, pBTX, pTAG, _
                                               @pTAG.zLastMoveKey, @pTAG.@pTagDisk.KeySize, _
                                               @pTAG.nLastMoveRecNum, %XDB_SEEK_KEY_AND_RECNUM )
            If nResult = %FALSE Then
               @xdb.errcode = %ERR_MOVEERROR  ' could not find the key 
               GoTo ExitOut
            End If
         End If

         ' Do an immediate get of the next key based on the position that was last obtained. 
         nRegularNodePos = @pTAG.RegularNodePos + 1
         nRegularNodeNum = @pTAG.RegularNodeNum 
         
         ' If we are going past the last key then we need to load the next node
         If nRegularNodePos > @pTAG.RegularNode.NumKeys Then
            
            nRegularNodeNum = @pTAG.RegularNode.NextNode
            If nRegularNodeNum = 0 Then
               @xdb.errcode = %ERR_MOVEERROR: GoTo ExitOut
            End If
            
            @xdb.errcode = ctx_GetNode_Internal( pBTX, nRegularNodeNum, VarPtr(@pTAG.RegularNode) )
            If @xdb.errcode Then GoTo ExitOut    
            
            nRegularNodePos = 1
         
         End If

         ' Get the Key and RecNum from the current position on the node.
         MemString @pTAG.zLastMoveKey, Peek$(VarPtr(@pTAG.RegularNode.Buffer) + ((nRegularNodePos-1) * (@pTAG.@pTagDisk.KeySize+4)), @pTAG.@pTagDisk.KeySize)
         @pTAG.nLastMoveRecNum = Peek( Long, VarPtr(@pTAG.RegularNode.Buffer) + _
                                 ( (nRegularNodePos-1) * ( @pTAG.@pTagDisk.KeySize + 4 ) ) + @pTAG.@pTagDisk.KeySize ) 

         @xdb.nData4byte = @pTAG.nLastMoveRecNum
         
         @pTAG.RegularNodeNum = nRegularNodeNum
         @pTAG.RegularNodePos = nRegularNodePos

         GoTo ExitOut
         


      Case %XDB_MOVELAST            
         ' Load the Lookup nodes until we reach the last node
         nLookupNodeNum = @pTAG.@pTagDisk.FirstLookupNode
         Do Until nLookupNodeNum = 0
            @xdb.errcode = ctx_GetNode_Internal( pBTX, _
                                                 nLookupNodeNum, _
                                                 VarPtr( @pTAG.LookupNode ) )
            If @xdb.errcode Then GoTo ExitOut    
            nLookupNodeNum = @pTAG.LookupNode.NextNode
         Loop
         
         For x = %INDEX_MAX_LOOKUP_KEYS To 1 Step -1
            If @pTAG.LookupNode.Node(x) Then
               nLookupNodePos = x: Exit For
            End If
         Next      
         
         nRegularNodeNum = @pTAG.LookupNode.Node(nLookupNodePos)
         If nRegularNodeNum = 0 Then
            @xdb.errcode = %ERR_MOVEERROR: GoTo ExitOut
         End If
            
         @xdb.errcode = ctx_GetNode_Internal( pBTX, nRegularNodeNum, VarPtr(@pTAG.RegularNode) )
         If @xdb.errcode Then GoTo ExitOut    
         
         nRegularNodePos = @pTAG.RegularNode.NumKeys
         
         ' Retrieve the RecNum from the last Key on the node
         MemString @pTAG.zLastMoveKey, Peek$(VarPtr(@pTAG.RegularNode.Buffer) + ((nRegularNodePos-1) * (@pTAG.@pTagDisk.KeySize+4)), @pTAG.@pTagDisk.KeySize)
         @pTAG.nLastMoveRecNum = Peek( Long, VarPtr(@pTAG.RegularNode.Buffer) + _
                                 ( (nRegularNodePos-1) * ( @pTAG.@pTagDisk.KeySize + 4 ) ) + @pTAG.@pTagDisk.KeySize ) 
         
         @xdb.nData4byte = @pTAG.nLastMoveRecNum
           
         @pTAG.LookupNodeNum  = nLookupNodeNum
         @pTAG.LookupNodePos  = nLookupNodePos 
         @pTAG.RegularNodeNum = nRegularNodeNum
         @pTAG.RegularNodePos = nRegularNodePos

         GoTo ExitOut

      
      Case %XDB_MOVEPREV, %XDB_MOVEPREV_ATOMIC                
         If @xdb.nAction = %XDB_MOVEPREV Then
            ' Seek to the most previously moved to key/recnum
            nResult = ctx_SeekEngine_Internal( xdb, pBTX, pTAG, _
                                               @pTAG.zLastMoveKey, @pTAG.@pTagDisk.KeySize, _
                                               @pTAG.nLastMoveRecNum, %XDB_SEEK_KEY_AND_RECNUM )
            If nResult = %FALSE Then
               @xdb.errcode = %ERR_MOVEERROR  ' could not find the key 
               GoTo ExitOut
            End If
         End If

         ' Do an immediate get of the previous key based on the position that was last obtained. 
         nRegularNodePos = @pTAG.RegularNodePos - 1
         nRegularNodeNum = @pTAG.RegularNodeNum 
         
         ' If we are going past the last key then we need to load the next node
         If nRegularNodePos < 1 Then
            
            nRegularNodeNum = @pTAG.RegularNode.PrevNode
            If nRegularNodeNum = 0 Then
               @xdb.errcode = %ERR_MOVEERROR: GoTo ExitOut
            End If
            
            @xdb.errcode = ctx_GetNode_Internal( pBTX, nRegularNodeNum, VarPtr(@pTAG.RegularNode) )
            If @xdb.errcode Then GoTo ExitOut    
            
            nRegularNodePos = @pTAG.RegularNode.NumKeys 
         
         End If

         ' Get the RecNum from the current position on the node.
         ' Get the Key and RecNum from the current position on the node.
         MemString @pTAG.zLastMoveKey, Peek$(VarPtr(@pTAG.RegularNode.Buffer) + ((nRegularNodePos-1) * (@pTAG.@pTagDisk.KeySize+4)), @pTAG.@pTagDisk.KeySize)
         @pTAG.nLastMoveRecNum = Peek( Long, VarPtr(@pTAG.RegularNode.Buffer) + _
                                 ( (nRegularNodePos-1) * ( @pTAG.@pTagDisk.KeySize + 4 ) ) + @pTAG.@pTagDisk.KeySize ) 

         @xdb.nData4byte = @pTAG.nLastMoveRecNum
         
         @pTAG.RegularNodeNum = nRegularNodeNum
         @pTAG.RegularNodePos = nRegularNodePos

         GoTo ExitOut
         
      
   End Select                
   

ExitOut:
   Function = @xdb.errcode 

End Function
