// Copyright 1997-1999 Omni Development, Inc.  All rights reserved.
//
// This software may only be used and reproduced according to the
// terms in the file OmniSourceLicense.html, which should be
// distributed with this project and can also be found at
// http://www.omnigroup.com/DeveloperResources/OmniSourceLicense.html.
//
// $Header: /Network/Developer/Source/CVS/OmniGroup/OmniFoundation/OFStringScanner.h,v 1.18 1999/03/30 21:25:32 kc Exp $

#import "OFObject.h"

@class OFTrie, OFTrieBucket;

typedef const byte *CSBitmap; // Bitmap representation of an NSCharacterSet

#import <Foundation/NSString.h> // For unichar

@interface OFStringScanner : OFObject
{
@public
    NSString *targetString;
    unichar *inputBuffer;
    unichar *scanLocation;
    unichar *scanEnd;
    unsigned int inputStringPosition;
    BOOL freeInputBuffer;
}

- init;
    // Designated initializer
- initWithString:(NSString *)aString;
    // Scan the specified string.  Retains string for efficiency, so don't change it.

- (NSString *)string;

- (BOOL)fetchMoreData;
- (BOOL)fetchMoreDataFromString:(NSString *)inputString;
- (BOOL)fetchMoreDataFromCharacters:(unichar *)characters length:(unsigned int)length freeWhenDone:(BOOL)shouldFreeWhenDone;

- (unichar)peekCharacter;
- (void)skipPeekedCharacter;
- (unichar)readCharacter;

- (unsigned int)scanLocation;
- (void)setScanLocation:(unsigned int)aLocation;
- (void)skipCharacters:(int)anOffset;

- (BOOL)scanUpToCharacter:(unichar)aCharacter;
- (BOOL)scanUpToCharacterInSet:(NSCharacterSet *)delimiterCharacterSet;
- (BOOL)scanUpToString:(NSString *)delimiterString;
- (BOOL)scanUpToStringCaseInsensitive:(NSString *)delimiterString;

- (NSString *)readTokenFragmentWithDelimiterCSBitmap:
    (CSBitmap)delimiterCSBitmap;
- (NSString *)readTokenFragmentWithDelimiters:(NSCharacterSet *)delimiterSet;
- (NSString *)readFullTokenWithDelimiterCSBitmap:(CSBitmap)delimiterCSBitmap
   forceLowercase:(BOOL)forceLowercase;
- (NSString *)readFullTokenWithDelimiters:(NSCharacterSet *)delimiterCharacterSet forceLowercase:(BOOL)forceLowercase;
- (NSString *)readFullTokenOfSet:(NSCharacterSet *)tokenSet;
    // Relatively slow!  Inverts tokenSet and calls -readFullTokenWithDelimiters:
- (NSString *)readLine;
- (NSString *)readCharacterCount:(unsigned int)count;
- (unsigned int)scanUnsignedIntegerMaximumDigits:(unsigned int)maximumDigits;
- (int)scanIntegerMaximumDigits:(unsigned int)maximumDigits;
- (BOOL)scanString:(NSString *)string peek:(BOOL)doPeek;

- (OFTrieBucket *)readLongestTrieElement:(OFTrie *)trie;
- (OFTrieBucket *)readShortestTrieElement:(OFTrie *)trie;

@end

// Here's a list of the inline functions:
//
//	BOOL scannerHasData(OFStringScanner *scanner);
//	unsigned int scannerScanLocation(OFStringScanner *scanner);
//	unichar scannerPeekCharacter(OFStringScanner *scanner);
//	void scannerSkipPeekedCharacter(OFStringScanner *scanner);
//	unichar scannerReadCharacter(OFStringScanner *scanner);
//	BOOL scannerScanUpToCharacter(OFStringScanner *scanner, unichar scanCharacter);
//	BOOL scannerScanUpToCharacterInCSBitmap(OFStringScanner *scanner, CSBitmap delimiterBitmapRep);
//      BOOL scannerScanUpToCharacterNotInCSBitmap(OFStringScanner *scanner, CSBitmap memberBitmapRep)
//	BOOL scannerScanUpToCharacterInSet(OFStringScanner *scanner, NSCharacterSet *delimiterCharacterSet);
//

#import "FrameworkDefines.h"

OmniFoundation_EXTERN unichar OFStringScannerEndOfDataCharacter;
    // This character is returned when a scanner is asked for a character past the end of its input.  (For backwards compatibility with earlier versions of OFStringScanner, this is currently '\0'--but you shouldn't rely on that behavior.)

static inline BOOL
scannerHasData(OFStringScanner *scanner)
{
    if (scanner->scanLocation >= scanner->scanEnd && ![scanner fetchMoreData])
	return NO;
    return YES;
}

static inline unsigned int
scannerScanLocation(OFStringScanner *scanner)
{
    if (!scannerHasData(scanner)) {
        // Don't return an offset which is longer than our input.
        scanner->scanLocation = scanner->scanEnd;
    }
    return scanner->inputStringPosition + (scanner->scanLocation - scanner->inputBuffer);
}

static inline unichar
scannerPeekCharacter(OFStringScanner *scanner)
{
    if (!scannerHasData(scanner))
	return OFStringScannerEndOfDataCharacter;
    return *scanner->scanLocation;
}

static inline void
scannerSkipPeekedCharacter(OFStringScanner *scanner)
{
    scanner->scanLocation++;
}

static inline unichar
scannerReadCharacter(OFStringScanner *scanner)
{
    unichar character;

    if (!scannerHasData(scanner))
	return OFStringScannerEndOfDataCharacter;
    character = *scanner->scanLocation;
    scannerSkipPeekedCharacter(scanner);
    return character;
}

static inline BOOL
scannerScanUpToCharacter(OFStringScanner *scanner, unichar scanCharacter)
{
    while (scannerHasData(scanner)) {
        while (scanner->scanLocation < scanner->scanEnd) {
            if (*scanner->scanLocation == scanCharacter)
                return YES;
            scanner->scanLocation++;
        }
    }
    return NO;
}

static inline BOOL characterIsMemberOfCSBitmap(CSBitmap bitmapRep, unichar character)
{
    return (bitmapRep[character >> 3] & (((unsigned int)0x1) << (character & 7)));
}

static inline BOOL
scannerScanUpToCharacterInCSBitmap(OFStringScanner *scanner, CSBitmap delimiterBitmapRep)
{
    if (!scannerHasData(scanner))
        return NO;
    do {
        while (scanner->scanLocation < scanner->scanEnd) {
            if (characterIsMemberOfCSBitmap(delimiterBitmapRep, *scanner->scanLocation))
                return YES;
            scanner->scanLocation++;
        }
    } while (scannerHasData(scanner));
    return NO;
}

static inline BOOL
scannerScanUpToCharacterNotInCSBitmap(OFStringScanner *scanner, CSBitmap memberBitmapRep)
{
    if (!scannerHasData(scanner))
        return NO;
    do {
        while (scanner->scanLocation < scanner->scanEnd) {
            if (!characterIsMemberOfCSBitmap(memberBitmapRep, *scanner->scanLocation))
                return YES;
            scanner->scanLocation++;
        }
    } while (scannerHasData(scanner));
    return NO;
}

#import <Foundation/NSCharacterSet.h>
#import <Foundation/NSData.h>

static inline CSBitmap bitmapForCharacterSetDoRetain(NSCharacterSet *characterSet, BOOL shouldRetain)
{
    NSData *bitmapRepresentation;

    bitmapRepresentation = [characterSet bitmapRepresentation];
    if (shouldRetain)
        [bitmapRepresentation retain];
    return [bitmapRepresentation bytes];
}

static inline BOOL
scannerScanUpToCharacterInSet(OFStringScanner *scanner, NSCharacterSet *delimiterCharacterSet)
{
    CSBitmap delimiterBitmapRep;

    if (!scannerHasData(scanner))
        return NO;
    delimiterBitmapRep = bitmapForCharacterSetDoRetain(delimiterCharacterSet, NO);
    return scannerScanUpToCharacterInCSBitmap(scanner, delimiterBitmapRep);
}

static inline BOOL scannerPeekString(OFStringScanner *scanner, NSString *string)
{
    return [scanner scanString:string peek:YES];
}

static inline BOOL scannerReadString(OFStringScanner *scanner, NSString *string)
{
    return [scanner scanString:string peek:NO];
}
