/*
    -----------------------------------------------------------------------------------------------
    File Name:		AXStringProcessor.m
    Project:		Parakeet
    Author:		Greg Howell
    Revision Date:	21/09/2003 (0.2)
    
    Description:	AXStringProcessor object implementation.
    -----------------------------------------------------------------------------------------------
*/

#import "AXStringProcessor.h"
#import "Parakeet.h"

NSString * smartQuoteLeft = @"\322";	// Note that \322 is the code for the opening smart
NSString * smartQuoteRight = @"\323";	// quote and \323 the code for the closing smart quote

@implementation AXStringProcessor

/*
    -----------------------------------------------------------------------------------------------
    Method Name:	setAXString
    Parameters:		NSMutableString object
    Returns:		None
    
    - Instance method for setting the string to process.
    -----------------------------------------------------------------------------------------------
*/
- (void)setAXString:(NSMutableString *)newString
{
    [newString retain];			// keep the string we are passed
    [stringToProcess release];		// release the previous string
    stringToProcess = newString;	// store the passed string
    if(DEBUG_PARAKEET){printf("AXStringProcessor:setAXString\n");}
}

/*
    -----------------------------------------------------------------------------------------------
    Method Name:	processAXString
    Parameters:		None
    Returns:		None
    
    - Instance method that produces the string to be returned.
    -----------------------------------------------------------------------------------------------
*/
- (void)processAXString
{
    if(DEBUG_PARAKEET){printf("AXStringProcessor:processAXString\n");}
    [self processAXHierarchy:stringToProcess];
}

/*
    -----------------------------------------------------------------------------------------------
    Method Name:	returnAXString
    Parameters:		None
    Return Value:	NSString object
    
    - Instance method that returns the produced string.
    -----------------------------------------------------------------------------------------------
*/
- (NSString *)returnAXString
{
    if(DEBUG_PARAKEET){printf("AXStringProcessor:returnAXString\n");}
    return currentStringProduced;
}

/*
    -----------------------------------------------------------------------------------------------
    Method Name:	reset
    Parameters:		None
    Returns:		None
    
    - Instance method that resets the object.
    -----------------------------------------------------------------------------------------------
*/
- (void)reset
{
    [currentStringProduced release];			// reset the currentStringProduced object
    currentStringProduced = @"";
    
    [stringToProcess release];				// reset the stringToProcess object
    stringToProcess = (NSMutableString *)@"";
    
    [lastStringProduced release];			// reset the lastStringProduced object
    lastStringProduced = @"";
    
    if(DEBUG_PARAKEET){printf("AXStringProcessor:reset\n");}
}

/*
    -----------------------------------------------------------------------------------------------
    Method Name:	extractAXValue
    Parameters:		NSString object
    Returns:		NSString object
    
    - Instance method that extracts a key out of an AX string.
    - Passed an NSString object.
    - String between the smart quotes in the passed string is extracted and returned.
    
    - Coded by Greg Howell 22/09/2003
    -----------------------------------------------------------------------------------------------
*/
- (NSString *)extractAXValue:(NSString *)sourceString
{
    NSRange startRange;			// used to locate the starting quote
    NSRange endRange;			// used to locate the ending quote
    NSRange outputRange;		// used to select the AX key value
    NSString * stringToExtractFrom;	// stores the string that is passed to the method
    NSString * valueToReturn;		// stores the string that the method is going to return
    int lengthOfInput;			// length of the passed string
    
    stringToExtractFrom = sourceString;			// store passed string
    lengthOfInput = 0;					// initialize length
    lengthOfInput = [stringToExtractFrom length];	// store length of passed input
    
    if (lengthOfInput > 0) {				// check that the passed string is of valid length
        startRange = [stringToExtractFrom rangeOfString:smartQuoteLeft];	// get start quote location
        endRange = [stringToExtractFrom rangeOfString:smartQuoteRight];		// get end quote location

        if((startRange.length < 0) && (endRange.length < 0)){
            valueToReturn = @"";
        }
        else{
            outputRange.location = startRange.location + 1;			// compute value location
            outputRange.length = endRange.location - startRange.location - 1;	// compute value length
    
            if (outputRange.length > 0){	// ensure that we are not going to return garbage
                valueToReturn = [stringToExtractFrom substringWithRange:outputRange];
            }
            else{
                valueToReturn = @"";	// return empty string if result is nothing
            }
        }
    }
    else{
        valueToReturn = @"";		// return empty string if input is empty string
    }
    
    return valueToReturn;		// return string to calling method
}

/*
    -----------------------------------------------------------------------------------------------
    Method Name:	lineIsEmpty
    Parameters:		NSString object
    Returns:		BOOL object
    
    - Instance method tests if a string object (line in this application) is empty.
    - Passed an NSString object to test.
    
    - Coded by Greg Howell 23/09/2003
    -----------------------------------------------------------------------------------------------
*/
- (BOOL)lineIsEmpty:(NSString *)lineToTest
{
    NSString * stringPassed;			// string to store passed object
    stringPassed  = lineToTest;			// store what we are passed

    if ([stringPassed isEqualToString:@""]){	// test for equality to the empty string object
        return TRUE;				// return true if equality returns true
    }
    else if([stringPassed isEqualToString:@" "]){
        return TRUE;
    }
    else{
        return FALSE;				// return false otherwise
    }
}

/*
    -----------------------------------------------------------------------------------------------
    Method Name:	processAXHierarchy
    Parameters:		NSString object
    Returns:		None
    
    - Processes the "hierarchy" section of the data returned by the Accessibility API.
    - Breaks the data into an array.
    - Loops through the array until it reaches an empty row.
    - Checks for various tags and constructs the string to speak to the user.
    - Provides the "intelligence" (dim as it may be) for Parakeet.
    
    - Coded by Greg Howell 23/09/2003, revised 14/10/2003
    -----------------------------------------------------------------------------------------------
*/
- (void)processAXHierarchy:(NSString *)passedString
{
    int rowBeingProcessed;					// the line of the array we are processing
    NSString * lineToProcess;					// the contents of the line of the array we are processing
    BOOL keepGoing;						// flag used in the while loop
    NSRange searchRange;					// utility search range
    NSMutableString * stringToReturn = (NSMutableString *)@"";	// string that we are producing
    // (note the strange way of initialising an NSMutableString - looks very dodgy - GH 24/09/2003)
    
    NSString * stringAppName;					// application name
    BOOL bInDock;						// flag to indicate we are in the Dock application
    BOOL bInFinder;						// flag to indicate we are in the Finder application
    BOOL bInMenuBar;						// flag to indicate we are in the Menu Bar area
    
    NSMutableString * menuInfo = (NSMutableString *)@"";	// string to store info about the child menu object
    
    keepGoing = TRUE;						// initialisation section
    rowBeingProcessed = 0;
    NSArray * AXDataArray = [(NSString *)passedString componentsSeparatedByString:@"\n"];
    bInDock = FALSE;
    bInMenuBar = FALSE;
    
    if(DEBUG_PARAKEET){printf("AXStringProcessor:processAXHieracrhy (%i rows of text to process)\n", [AXDataArray count]);}
    
    while(keepGoing){
        lineToProcess = [AXDataArray objectAtIndex:rowBeingProcessed];
        
        if([self lineIsEmpty:lineToProcess]){
            keepGoing = FALSE;
        }
        else{
            // ----------------------------------------------------------------------------- AXApplication
            searchRange = [lineToProcess rangeOfString:@"AXApplication"];
            if(searchRange.length){
                stringAppName = (NSMutableString *)[self extractAXValue:lineToProcess];

                bInDock = [stringAppName isEqualToString:@"Dock"];
                bInFinder = [stringAppName isEqualToString:@"Finder"];

                stringToReturn = (NSMutableString *)[(NSString *)stringToReturn stringByAppendingString:stringAppName];
                stringToReturn = (NSMutableString *)[(NSString *)stringToReturn stringByAppendingString:@" "];
            }
            // ----------------------------------------------------------------------------- AXWindow    
            searchRange = [lineToProcess rangeOfString:@"AXWindow"];
            if(searchRange.length){
                stringToReturn = (NSMutableString *)[(NSString *)stringToReturn stringByAppendingString:@" "];
                if(!(bInFinder)){
                    stringToReturn = (NSMutableString *)[(NSString *)stringToReturn stringByAppendingString:(NSMutableString *)[self extractAXValue:lineToProcess]];
                    stringToReturn = (NSMutableString *)[(NSString *)stringToReturn stringByAppendingString:@" window "];
                }
            }
            // ----------------------------------------------------------------------------- AXMenuBar
            searchRange = [lineToProcess rangeOfString:@"AXMenuBar"];
            if(searchRange.length){
                bInMenuBar = TRUE;
                menuInfo = (NSMutableString *)@"";
                menuInfo = (NSMutableString *)[(NSString *)menuInfo stringByAppendingString:(NSMutableString *)[self extractAXValue:lineToProcess]];
                menuInfo = (NSMutableString *)[(NSString *)menuInfo stringByAppendingString:@" menu bar "];   
            }
            // ----------------------------------------------------------------------------- AXMenu
            searchRange = [lineToProcess rangeOfString:@"AXMenu"];
            if(searchRange.length){
                bInMenuBar = TRUE;
                menuInfo = (NSMutableString *)@"";
                menuInfo = (NSMutableString *)[(NSString *)menuInfo stringByAppendingString:(NSMutableString *)[self extractAXValue:lineToProcess]];
                menuInfo = (NSMutableString *)[(NSString *)menuInfo stringByAppendingString:@" menu "];
            }
            // ----------------------------------------------------------------------------- AXMenuItem
            searchRange = [lineToProcess rangeOfString:@"AXMenuItem"];
            if(searchRange.length){
                bInMenuBar = TRUE;
                menuInfo = (NSMutableString *)@"";
                menuInfo = (NSMutableString *)[(NSString *)menuInfo stringByAppendingString:(NSMutableString *)[self extractAXValue:lineToProcess]];
                menuInfo = (NSMutableString *)[(NSString *)menuInfo stringByAppendingString:@" menu item "];
            }
            // ----------------------------------------------------------------------------- AXButton
            searchRange = [lineToProcess rangeOfString:@"AXButton"];
            if(searchRange.length){
                if(bInDock){
                    stringToReturn = (NSMutableString *)[(NSString *)stringToReturn stringByAppendingString:@" launch "];
                    stringToReturn = (NSMutableString *)[(NSString *)stringToReturn stringByAppendingString:(NSMutableString *)[self extractAXValue:lineToProcess]];
                }
                else{
                    stringToReturn = (NSMutableString *)[(NSString *)stringToReturn stringByAppendingString:@" "];
                    stringToReturn = (NSMutableString *)[(NSString *)stringToReturn stringByAppendingString:(NSMutableString *)[self extractAXValue:lineToProcess]];
                    stringToReturn = (NSMutableString *)[(NSString *)stringToReturn stringByAppendingString:@" button "];
                }
            }
            // -----------------------------------------------------------------------------
            rowBeingProcessed = rowBeingProcessed + 1;
        }
    }
    
    // add the menu information if we are in the menu bar
    if(bInMenuBar){
        stringToReturn = (NSMutableString *)@"";
        stringToReturn = (NSMutableString *)[(NSString *)stringToReturn stringByAppendingString:menuInfo];
    }
    
    // return the string we have produced
    currentStringProduced = (NSString *)stringToReturn;
}

/*
    -----------------------------------------------------------------------------------------------
    Method Name:	processAXAttributes
    Parameters:		NSString object
    Returns:		None
    
    -
    -----------------------------------------------------------------------------------------------
*/
- (void)processAXAttributes:(NSString *)passedString
{
    // not coded yet
}

/*
    -----------------------------------------------------------------------------------------------
    Method Name:	processAXRemaining
    Parameters:		NSString
    Returns:		None
    
    - 
    -----------------------------------------------------------------------------------------------
*/
- (void)processAXRemaining:(NSString *)passedString
{
    // not coded yet
}

@end
