/* d4skip.c   (c)Copyright Sequiter Software Inc., 1988-1996.  All rights reserved. */

#include "d4all.h"
#ifndef S4UNIX
   #ifdef __TURBOC__
      #pragma hdrstop
   #endif
#endif

#ifdef S4SERVER
/* -2 means eof, -3 means bof */
long d4skipRecno( DATA4 *data, long n )
{
   #ifndef S4OFF_INDEX
      TAG4 *tag ;
      TAG4FILE *tagFile ;
   #endif
   long startRec, newRec ;
   #ifndef S4OFF_INDEX
      unsigned char *keyValue ;
      int rc ;
      long recno, nSkipped ;
   #endif
   CODE4 *c4 ;

   #ifdef S4VBASIC
      if ( c4parm_check( data, 2, E94801 ) )
         return -1 ;
   #endif

   #ifdef E4PARM_LOW
      if ( data == 0 )
         return error4( 0, e4parm_null, E94801 ) ;
   #endif

   c4 = data->codeBase ;
   if ( c4 == 0 )
      return e4info ;
   if ( error4code( c4 ) < 0 )
      return error4code( c4 ) ;

   if ( data->recNum < 1L )
   {
      #ifndef S4SERVER
         if ( c4->errSkip )
            return error4( c4, e4info, E84801 ) ;
      #endif
      return e4info ;
   }

   #ifndef S4OFF_INDEX
      tag = data->tagSelected ;  /* avoid function call */
      if ( tag == 0 )
      {
   #endif
      if ( n != 0L )
         if ( d4recCountLessEq( data, 1L ) == 0 )  /* count == 0 */
            return -4L ;

      data->bofFlag = 0 ;
      startRec = data->recNum ;
      newRec = startRec + n ;

      if ( newRec > 0L )
      {
         if ( d4recCountLessEq( data, newRec ) == 0 )
            return -2L ;
         return newRec ;
      }
      else
         return -3L ;
   #ifndef S4OFF_INDEX
      }
      else
      {
         tagFile = tag->tagFile ;
         if ( data->eofFlag )
         {
            if ( n >= 0L )
               return -2L ;

            rc = d4bottom( data ) ;
            if ( rc && rc != r4eof )
               return rc ;
            if ( rc == r4eof )
               return -2L ;
            n++ ;
            data->recNum = tfile4recNo( tagFile ) ;
         }

         data->bofFlag = 0 ;

         #ifndef S4OFF_WRITE
            if ( data->recordChanged )
            {
               rc = d4updateRecord( data, 0 ) ; /* was 1 */
               if ( rc < 0 )
                  return rc ;
            }
         #endif

         t4versionCheck( tag, 1, 0 ) ;

         if ( n == 0 )
            return data->recNum ;

         if ( tfile4recNo( tagFile ) != data->recNum )
         {
            if ( d4lockTest( data, data->recNum ) == 0 )  /* ensure latest from disk */
            {
               rc = d4go( data, data->recNum ) ;
               if ( rc < 0 )
                  return rc ;
            }

            expr4context( tagFile->expr, data ) ;
            tfile4exprKey( tagFile, &keyValue ) ;

            rc = tfile4go( tagFile, keyValue, data->recNum, 0 ) ;
            if ( rc < 0 )
               return rc ;

            if ( tfile4empty( tagFile ) )
               return -4L ;

            if ( tfile4eof( tagFile ) && n >= 0L )
               return -2L ;

            #ifdef S4HAS_DESCENDING
               if ( tagFile->header.descending )
               {
                  if ( (rc > 0) && (n < 0) )
                     n-- ;
               }
               else
                  if ( (rc > 0) && (n > 0) )
                     n-- ;
            #else
               if ( (rc > 0) && (n > 0) )
                  n-- ;
            #endif
         }
         else
         {
            if ( tfile4eof( tagFile ) )
               return -2L ;
         }

         nSkipped = tfile4dskip( tagFile, n ) ;

         if ( n > 0 && nSkipped != n )
            return -2L ;
         if ( n < 0 && nSkipped != n )
            return -3L ;

         if ( tfile4eof( tagFile ) )
            return -2L ;

         recno = tfile4recNo( tagFile ) ;
         if ( recno < 0 )
            return recno ;
         return recno ;
      }
   #endif
}
#endif /* S4SERVER */

/* flush the current record to disk, and update the data pointer to point
   to a valid tag location (in case the current record has been filtered out */
/* direction is -1 if line up backwards, or 1 if line up forwards */
#ifdef P4ARGS_USED
   #pragma argsused
#endif
int S4FUNCTION d4tagSync( DATA4 *data, TAG4 * const tag )
{
   #ifdef S4OFF_INDEX
      return 0 ;
   #else
      CODE4 *c4 ;
      #ifdef S4CLIENT
         int rc, saveRc ;
         CONNECTION4 *connection ;
         CONNECTION4TAG_SYNCH_INFO_IN info ;
         CONNECTION4TAG_SYNCH_INFO_OUT *out ;
         #ifndef S4OFF_MEMO
            int i ;
         #endif
      #endif

      #ifdef S4VBASIC
         if ( c4parm_check( data, 2, E94803 ) )
            return -1 ;
      #endif

      #ifdef E4PARM_HIGH
         if ( data == 0 )
            return error4( 0, e4parm_null, E94803 ) ;
      #endif

      if ( tag == 0 )
         return 0 ;

      c4 = data->codeBase ;
      if ( error4code( c4 ) < 0 )
         return error4code( c4 ) ;

      #ifdef S4CLIENT
         if ( data->recNum <= 0 || d4eof( data ) )
         {
            #ifndef S4OFF_MEMO
               for ( i = 0; i < data->dataFile->nFieldsMemo; i++ )
                  f4memoReset( data->fieldsMemo[i].field ) ;
            #endif
            data->recordChanged = 0 ;
            return 0 ;
         }

         if ( data->recordChanged == 0 )
            return 0 ;

         connection = data->dataFile->connection ;
         if ( connection == 0 )
            return error4stack( data->codeBase, e4connection, E94803 ) ;

         connection4assign( connection, CON4TAG_SYNCH, data4clientId( data ), data4serverId( data ) ) ;
         memcpy( info.tagName, tag->tagFile->alias, LEN4TAG_ALIAS  ) ;
         info.tagName[LEN4TAG_ALIAS] = 0 ;
         info.recno = d4recNo( data ) ;
         connection4addData( connection, &info, sizeof( CONNECTION4TAG_SYNCH_INFO_IN ), 0 ) ;
         saveRc = connection4repeat( connection, -2, -1, -1, data ) ;

         if ( saveRc == r4eof || saveRc == r4noRecords )
         {
            data->bofFlag = 0 ;
            rc = d4goEof( data ) ;
            if ( rc == 0 )
               rc = r4eof ;
            if ( saveRc == r4noRecords )
               data->bofFlag = 1 ;
            return rc ;
         }

         if ( saveRc == r4bof )
         {
            data->eofFlag = 0 ;
            rc = d4goBof( data ) ;
            data->bofFlag = 1 ;
            if ( rc == 0 )
               rc = r4bof ;
            return rc ;
         }

         if ( saveRc != 0 )
         {
            if ( saveRc < 0 )
               return connection4errorDescribe( connection, c4, saveRc, E94803, tag->tagFile->alias, 0, 0 ) ;
            return saveRc ;
         }

         if ( connection4len( connection ) != sizeof( CONNECTION4TAG_SYNCH_INFO_OUT ) )
            return error4( c4, e4packetLen, E94803 ) ;
         out = (CONNECTION4TAG_SYNCH_INFO_OUT *)connection4data( connection ) ;

         if ( out->recNo != 0 && out->recNo != d4recNo( data ) )
         {
            rc = d4go( data, out->recNo ) ;
            if ( rc < 0 )
               return rc ;
         }

         return saveRc ;
      #else
         return d4tagSyncDo( data, tag, 1 ) ;
      #endif
   #endif /* S4OFF_INDEX */
}

#ifndef S4OFF_INDEX
#ifndef S4CLIENT
int d4tagSyncDo( DATA4 *data, TAG4 * const tag, int direction )
{
   int rc ;
   CODE4 *c4 ;
   unsigned char *keyValue ;
   #ifndef S4OFF_MULTI
      int done, verifyReccount ;
      #ifndef S4OFF_TRAN
         int nSkipped ;
      #endif
   #endif
   TAG4 *oldSelected ;
   TAG4FILE *tagFile ;
   long recno ;

   c4 = data->codeBase ;

   tagFile = tag->tagFile ;
   expr4context( tagFile->expr, data ) ;
   tfile4exprKey( tagFile, &keyValue ) ;
   recno = tfile4recNo( tagFile ) ;
   if ( recno != data->recNum )
   {
      rc = tfile4go( tagFile, keyValue, data->recNum, 0 ) ;
      if ( rc )
         return rc ;
   }

   oldSelected = data->tagSelected ;
   d4tagSelect( data, tag ) ;

   #ifndef S4OFF_WRITE
      if ( data->recordChanged )
      {
         rc = d4updateRecord( data, 0 ) ;
         if ( rc )
         {
            d4tagSelect( data, oldSelected ) ;
            return rc ;
         }
      }
   #endif

   if ( data->eofFlag )
   {
      d4tagSelect( data, oldSelected ) ;

      /* if forward skip not true, not eof, here is */
      return d4goEof( data ) ;
   }
   else
   {
      t4versionCheck( tag, 1, 0 ) ;

      if ( tfile4eof( tagFile ) )
         return d4goEof( data ) ;

      recno = tfile4recNo( tagFile ) ;
      if ( recno != data->recNum )
         rc = d4go( data, recno ) ;
      else
         rc = 0 ;
   }

   #ifndef S4OFF_MULTI
      for( verifyReccount = 1, done = 0 ; done == 0 ; )
      {
         #ifndef S4OFF_TRAN
            if ( code4transEnabled( c4 ) )
               if ( t4unique( tag ) != 0 )
                  if ( code4tranStatus( c4 ) == r4active )
                     if ( d4lockTest( data, recno ) )
                     {
                        /* if within a transaction, and record is locked, it may mean
                           that the record contents have changed.  If the current tag key
                           does not match, it must be an old entry (if a unique tag),
                           and thus skip over
                        */
                        for ( ;; )
                        {
                           if ( recno > data->dataFile->minCount )   /* ensure correct sequencing for multi-user */
                               break ;  /* just let the code below take care of this case */
                           verifyReccount = 1 ;
                           expr4context( tagFile->expr, data ) ;
                           if ( recno != d4recNo( data ) )  /* need to reposition data4 first */
                               d4go( data, recno ) ;
                           tfile4exprKey( tagFile, &keyValue ) ;
                           if ( memcmp( tfile4key( tagFile ), keyValue, tagFile->header.keyLen ) != 0 )  /* different, so skip */
                              nSkipped = (int)tfile4skip( tagFile, direction ) ;
                           else  /* done */
                              break ;

                           if ( direction > 0  &&  nSkipped != 1 )
                           {
                              rc = d4goEof( data ) ;
                              done = 1 ;
                              break ;
                           }

                           if ( tfile4eof( tagFile ) )
                           {
                              data->bofFlag = 1 ;
                              rc = d4goEof( data ) ;
                              done = 1 ;
                              break ;
                           }
                           if ( tfile4recNo( tagFile ) != recno ) /* ok, may continue */
                           {
                              recno = tfile4recNo( tagFile ) ;
                              d4go( data, recno ) ;
                              break ;
                           }
                        }
                     }
         #endif /* S4OFF_TRAN */

         if ( done == 1 )
            break ;

         if ( verifyReccount == 1 )
         {
            verifyReccount = 0 ;
            if ( recno > data->dataFile->minCount )   /* ensure correct sequencing for multi-user */
            {
               if ( d4recCountLessEq( data, recno ) == 0 )
               {
                  rc = (int)tfile4skip( tagFile, direction ) ;
                  if ( rc < 0 )
                     done = 1 ;
                  else
                  {
                     if ( rc == 0 )
                     {
                        data->bofFlag = 1 ;
                        done = 1 ;
                        rc = d4goEof( data ) ;
                     }
                     else
                     {
                        recno = tfile4recNo( tagFile ) ;
                        if ( recno <= 0 || recno > data->dataFile->minCount )
                        {
                           done = 1 ;
                           rc = -1 ;
                        }
                        if ( tfile4recNo( tagFile ) != recno ) /* ok, may continue */
                        {
                           recno = tfile4recNo( tagFile ) ;
                           d4go( data, recno ) ;
                           break ;
                        }
                     }
                  }
               }
            }
            else
               done = 1 ;
         }
         else
            done = 1 ;
      }
   #endif /* S4OFF_MULTI */

   d4tagSelect( data, oldSelected ) ;
   return rc ;
}
#endif  /* S4CLIENT */
#endif  /* S4OFF_INDEX */

int S4FUNCTION d4skip( DATA4 *data, const long nSkip )
{
   int rc ;
   CODE4 *c4 ;
   #ifdef S4CLIENT
      int saveRc ;
      CONNECTION4 *connection ;
      CONNECTION4GO_INFO_OUT *out ;
      CONNECTION4SKIP_INFO_IN info ;
   #else
      #ifndef S4OFF_INDEX
         unsigned char *keyValue ;
         long nSkipped, recno ;
         TAG4FILE *tagFile ;
      #endif
      long n ;
      #ifndef S4SERVER
         int saveFlag ;
      #endif
      long startRec, newRec ;
      int oldEofFlag, c1 ;
   #endif
   #ifndef S4OFF_INDEX
      TAG4 *tag ;
   #endif

   #ifdef S4VBASIC
      if ( c4parm_check( data, 2, E94802 ) )
         return -1 ;
   #endif

   #ifdef E4PARM_HIGH
      if ( data == 0 )
         return error4( 0, e4parm_null, E94802 ) ;
   #endif

   c4 = data->codeBase ;
   if ( error4code( c4 ) < 0 )
      return error4code( c4 ) ;

   if ( data->recNum < 1L )
   {
      #ifndef S4SERVER
         if ( c4->errSkip )
            error4( c4, e4info, E84801 ) ;
      #endif
      return e4info ;
   }

   #ifdef S4CLIENT
      rc = d4updateRecord( data, 0 ) ;   /* returns -1 if error4code( codeBase ) < 0 */
      if ( rc )
         return rc ;

      connection = data->dataFile->connection ;
      if ( connection == 0 )
         return error4stack( data->codeBase, e4connection, E94802 ) ;

      connection4assign( connection, CON4SKIP, data4clientId( data ), data4serverId( data ) ) ;
      info.startPos = data->recNum ;
      info.numSkip = nSkip ;
      #ifdef S4OFF_INDEX
         info.usesTag = 0 ;
      #else
         tag = data->tagSelected ;
         if ( tag == 0 )
            info.usesTag = 0 ;
         else
         {
            info.usesTag = 1 ;
            memcpy( info.tagName, tag->tagFile->alias, LEN4TAG_ALIAS  ) ;
            info.tagName[LEN4TAG_ALIAS] = 0 ;
         }
      #endif
      connection4addData( connection, &info, sizeof( CONNECTION4SKIP_INFO_IN ), 0 ) ;
      rc = connection4repeat( connection, -2, -1, -1, data ) ;
      if ( rc == r4locked )
         return r4locked ;

      if ( (long)connection4len( connection ) < (long)sizeof( CONNECTION4GO_INFO_OUT ) )
         return error4( c4, e4packetLen, E94802 ) ;
      out = (CONNECTION4GO_INFO_OUT *)connection4data( connection ) ;

      saveRc = out->skipRc ;
      if ( saveRc < 0 )
      {
         if ( tag == 0 )
            return connection4error( connection, c4, saveRc, E94802 ) ;
         else
            return connection4errorDescribe( connection, c4, saveRc, E94802, tag->tagFile->alias, 0, 0 ) ;
      }

      if ( saveRc == r4eof || saveRc == r4noRecords )
      {
         data->bofFlag = 0 ;
         rc = d4goEof( data ) ;
         if ( rc == 0 )
            rc = r4eof ;
         if ( saveRc == r4noRecords )
            data->bofFlag = 1 ;
         return rc ;
      }

      if ( saveRc == r4bof )
      {
         data->eofFlag = 0 ;
         rc = d4goBof( data ) ;
         data->bofFlag = 1 ;
         if ( rc == 0 )
            rc = r4bof ;
         return rc ;
      }

      if ( saveRc != 0 )
      {
         if ( saveRc < 0 )
            return error4( c4, saveRc, E94802 ) ;
         return saveRc ;
      }

      rc = connection4status( connection ) ;
      if ( out->recNo != 0 )
      {
         if ( (long)connection4len( connection ) != (long)sizeof( CONNECTION4GO_INFO_OUT ) + (long)dfile4recWidth( data->dataFile ) )
            return error4( c4, e4packetLen, E94802 ) ;
         return d4goVirtual( data, out->recNo, rc, out, connection ) ;  /* maybe r4locked, or whatever */
      }

      return rc ;
   #else
      n = nSkip ;

      #ifndef S4OFF_INDEX
         tag = data->tagSelected ;
         if ( tag == 0 )
         {
      #endif
         data->bofFlag = 0 ;
         startRec = data->recNum ;
         newRec = startRec + n ;
         if ( newRec > 0L )
         {
            #ifndef S4SERVER
               saveFlag = c4->errGo ;
               c4->errGo = 0 ;
            #endif
            #ifndef S4OFF_OPTIMIZE
               data->dataFile->hiPrio = -1 ;  /* indicate d4skip - data level */
            #endif
            rc = d4go( data, newRec ) ;
            #ifndef S4OFF_OPTIMIZE
               data->dataFile->hiPrio = 0 ;
            #endif
            #ifndef S4SERVER
               c4->errGo = saveFlag ;
            #endif
            if ( rc >= 0 && rc != r4entry )
               return rc ;
         }

         c1 = d4recCountLessEq( data, 1L ) ;
         if ( c1 < 0 )
            return c1 ;
         if ( ( c1 == 0 ) || ( d4recCountLessEq( data, newRec ) == 0 ) )
         {
            if ( c1 == 0L )
            {
               rc = d4goEof( data ) ;
               if ( rc != r4eof )
                  return rc ;
               data->bofFlag = 1 ;
            }
            if ( n < 0 )
            {
               data->bofFlag = 1 ;
               return r4bof ;
            }
            else
               return d4goEof( data ) ;
         }

         if ( newRec < 1L )
         {
            oldEofFlag = data->eofFlag ;
            rc = d4go( data, 1L ) ;
            if ( rc )
               return rc ;
            data->bofFlag = 1 ;
            data->eofFlag = oldEofFlag ;
            return r4bof ;
         }

         return d4go( data, newRec ) ;
      #ifndef S4OFF_INDEX
         }
         else
         {
            tagFile = tag->tagFile ;
            if ( data->eofFlag )
            {
               if ( n >= 0 )
                  return d4goEof( data ) ;

               rc = d4bottom( data ) ;
               if ( rc && rc != r4eof )
                  return rc ;
               if ( rc == r4eof )
               {
                  rc = d4goEof( data ) ;
                  if ( rc != r4eof )
                     return rc ;
                  return r4bof ;
               }
               n++ ;
               data->recNum = tfile4recNo( tagFile ) ;
            }

            data->bofFlag = 0 ;

            #ifndef S4OFF_WRITE
               if ( data->recordChanged )
               {
                  rc = d4updateRecord( data, 0 ) ; /* was 1 */
                  if ( rc < 0 )
                     return rc ;
               }
            #endif

            t4versionCheck( tag, 1, 0 ) ;

            if ( n == 0 )
               return 0 ;

            if ( tfile4recNo( tagFile ) != data->recNum )
            {
               rc = d4go( data, data->recNum ) ;
               if ( rc )
                  return rc ;

               expr4context( tagFile->expr, data ) ;
               tfile4exprKey( tagFile, &keyValue ) ;

               rc = tfile4go( tagFile, keyValue, data->recNum, 0 ) ;
               if ( rc < 0 )
                  return rc ;

               #ifdef S4HAS_DESCENDING
                  if ( tagFile->header.descending )
                  {
                     if ( (rc > 0) && (n < 0) )
                        n-- ;
                  }
                  else
                     if ( (rc > 0) && (n > 0) )
                        n-- ;
               #else
                  if ( (rc > 0) && (n > 0) )
                     n-- ;
               #endif
            }

            nSkipped = tfile4dskip( tagFile, n ) ;
            if ( n > 0  &&  nSkipped != n )
               return d4goEof( data ) ;

            if ( tfile4eof( tagFile ) )
            {
               data->bofFlag = 1 ;
               return d4goEof( data ) ;
            }

            recno = tfile4recNo( tagFile ) ;
            if ( recno < 0 )
               return (int)recno ;
            rc = d4go( data, recno ) ;
            if ( rc )
               return rc ;
            #ifndef S4OFF_MULTI
               rc = d4tagSyncDo( data, tag, n > 0 ? 1 : -1 ) ;
               if ( rc != 0 )
                  return rc ;
            #endif
            if ( n == nSkipped )
               return 0 ;

            #ifdef S4HAS_DESCENDING
               if ( ( n < 0 && !tagFile->header.descending ) ||  ( n < 0 && tagFile->header.descending ) )
            #else
               if ( n < 0 )
            #endif
            {
               data->bofFlag = 1 ;
               return r4bof ;
            }
         }
         return 0 ;
      #endif
   #endif
}