// Copyright 1997-2000 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/Source/CVS/OmniGroup/Frameworks/OmniFoundation/OFCharacterScanner.h,v 1.5 2000/11/11 07:11:05 bungi Exp $

#import <OmniFoundation/OFObject.h>
#import <OmniFoundation/FrameworkDefines.h>
#import <OmniFoundation/OFFastCharacterSet.h>
#import <OmniFoundation/FrameworkDefines.h>
#import <OmniFoundation/CFString-OFExtensions.h>

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

@class OFTrie, OFTrieBucket;

@interface OFCharacterScanner : OFObject
{
    BOOL haveRewindMark;
    unsigned int rewindMarkOffset;

@public
    unichar *inputBuffer;	// A buffer of unichars, in which we are scanning
    unichar *scanLocation;	// Pointer to next unichar
    unichar *scanEnd;		// Pointer to position after end of valid characters
    unsigned int inputStringPosition;	// This is the position (in a possibly notional string buffer) of the first character in inputBuffer
    BOOL freeInputBuffer;	// Whether we should deallocate inputBuffer when we're done with it
    OFCaseConversionBuffer caseBuffer;
}

- init;
    // Designated initializer

// - (NSString *)string;

/* Implemented by subclasses */
- (BOOL)fetchMoreData;
- (void)_rewindCharacterSource;

/* -fetchMoreData should make scanLocation point to a valid character, but should leave the value of (scanLocation -inputBuffer+inputStringPosition) unchanged. If the scan location is past EOF, it should return NO. OFCharacterScanner's implementation returns NO. */

/* -_rewindCharacterSource is called to indicate that the subsequent -fetchMoreData call will be requesting a buffer other than the one immediately following the previous call. It doesn't actually have to do anything. OFCharacterScanner's implementation raises an exception. */

/* Used by subclasses to implement the above */
- (BOOL)fetchMoreDataFromString:(NSString *)inputString;
- (BOOL)fetchMoreDataFromCharacters:(unichar *)characters length:(unsigned int)length offset:(unsigned int)offset freeWhenDone:(BOOL)doFreeWhenDone;
// #warning the following method is obsolete; remove all references and delete it
// - (BOOL)fetchMoreDataFromCharacters:(unichar *)characters length:(unsigned int)length freeWhenDone:(BOOL)doFreeWhenDone;

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

- (void)setRewindMark;
- (void)rewindToMark;
- (void)discardRewindMark;

- (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 *)readTokenFragmentWithDelimiterCharacter:(unichar)character;
- (NSString *)readTokenFragmentWithDelimiterCSBitmap:(CSBitmap)delimiterCSBitmap;
- (NSString *)readTokenFragmentWithDelimiters:(NSCharacterSet *)delimiterSet;
- (NSString *)readFullTokenWithDelimiterCSBitmap:(CSBitmap)delimiterCSBitmap
   forceLowercase:(BOOL)forceLowercase;
- (NSString *)readFullTokenWithDelimiterCSBitmap:(CSBitmap)delimiterCSBitmap;
- (NSString *)readFullTokenWithDelimiterCharacter:(unichar)delimiterCharacter forceLowercase:(BOOL)forceLowercase;
- (NSString *)readFullTokenWithDelimiterCharacter:(unichar)delimiterCharacter;
- (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(OFCharacterScanner *scanner);
//	unsigned int scannerScanLocation(OFCharacterScanner *scanner);
//	unichar scannerPeekCharacter(OFCharacterScanner *scanner);
//	void scannerSkipPeekedCharacter(OFCharacterScanner *scanner);
//	unichar scannerReadCharacter(OFCharacterScanner *scanner);
//	BOOL scannerScanUpToCharacter(OFCharacterScanner *scanner, unichar scanCharacter);
//	BOOL scannerScanUpToCharacterInCSBitmap(OFCharacterScanner *scanner, CSBitmap delimiterBitmapRep);
//      BOOL scannerScanUpToCharacterNotInCSBitmap(OFCharacterScanner *scanner, CSBitmap memberBitmapRep)
//	BOOL scannerScanUpToCharacterInSet(OFCharacterScanner *scanner, NSCharacterSet *delimiterCharacterSet);
//

OmniFoundation_EXTERN const unichar OFCharacterScannerEndOfDataCharacter;
    // 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 OFCharacterScanner, this is currently '\0'--but you shouldn't rely on that behavior.)

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

static inline unsigned int
scannerScanLocation(OFCharacterScanner *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(OFCharacterScanner *scanner)
{
    if (!scannerHasData(scanner))
	return OFCharacterScannerEndOfDataCharacter;
    return *scanner->scanLocation;
}

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

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

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

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

static inline BOOL
scannerScanUpToCharacterInCSBitmap(OFCharacterScanner *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(OFCharacterScanner *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;
}

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

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

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

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

