/*
 * FormTemplate.m
 *
 * Copyrigth 1997-2003 by vhf interservice GmbH
 * Author:   Georg Fleischmann
 *
 * created:  1997-04-09
 * modified: 02.10.98
 */

#import "FormTemplate.h"
#import "types.h"	/* Min() */
//#import "common.h"

extern NSDictionary *appDict;
#define AppString(a)	(([appDict objectForKey:(a)]) ? [appDict objectForKey:(a)] : (a))

/* replace all apearances of 'from' with 'to' in 'string'
 * if we get a mutable string we modify in place
 *
 * created:  18.02.97
 * modified: 31.05.97 14.07.97 05.01.98
 */
static id replaceSubString(id string, NSString *from, NSString *to)
{   NSRange		range, searchRange;
    NSMutableString	*mutString = [NSMutableString string];
    int			start = 0;

    searchRange = NSMakeRange(0, [string length]);
    while( searchRange.length )
    {
        range = [string rangeOfString:from options:0/*NSCaseInsensitiveSearch*/ range:searchRange];
        if( !range.length )
            break;
        [mutString appendString:[string substringWithRange:NSMakeRange(start, range.location-start)]];
        [mutString appendString:to];

        start = searchRange.location = range.location+[from length];
        searchRange.length = [string length] - searchRange.location;
    }
    [mutString appendString:[string substringFromIndex:start]];
    if( [string isKindOfClass:[NSMutableString class]] )
        [string setString:mutString];
    else
        string = mutString;

    return string;
}

#define PRINTSERVERNAME	@"printServer"			/* the server name */
@protocol PrintServerProtocol
- (oneway void)print:(NSString*)data ofType:(int)type on:(NSString*)printerName;
- (oneway void)printPages:(NSArray*)pages ofType:(int)type on:(NSString*)printerName;
@end

@interface FormTemplate(PrivateMethods)
+ (void)printServer;
- (NSString*)replace:(NSString*)from with:to for:(NSString*)string;
- (NSString*)string:(NSString*)string withLength:(int)length;
@end

@implementation FormTemplate(PrivateMethods)

static id	printServer = nil;
+ (void)printServer
{
    if( !printServer )
    {
        if( !(printServer = [[NSConnection rootProxyForConnectionWithRegisteredName:AppString(@"printServer") host:@"*"] retain]) )
            NSLog(@"Can't connect to print server %@", AppString(@"printServer"));
        [printServer setProtocolForProxy:@protocol(PrintServerProtocol)];
    }
}

/* replace all apearances of 'from' with 'to' in 'string'
 *
 * created:  18.02.97
 * modified: 23.02.97
 */
- (NSString*)replace:(NSString*)from with:to for:(NSString*)string
{   NSRange	range;
    int		i;

    for(i=0; i<1000; i++)
    {   range = [string rangeOfString:from];
        if( !range.length )
            break;
        string = [NSString stringWithFormat:@"%@%@%@", [string substringToIndex:range.location], to, [string substringFromIndex:range.location+range.length]];
    }
    if(i >= 1000)
        NSLog(@"endless loop for:'%@'", from);

    return string;
}

/* created: 09.04.97
 */
- (NSString*)string:(NSString*)string withLength:(int)length
{
    int			i, space;
    NSMutableString	*newString;
    BOOL		alignLeft = YES;

    if (length < 0)
    {
        alignLeft = NO;
        length = -length;
    }

    /* ok */
    if( [string length] == length )
        return string;
    /* too long */
    if( [string length] > length )
        return [string substringToIndex:length];
    /* too short */
    newString = [NSMutableString string];
    space = length - [string length];
    for( i=0; i<space; i++ )
        [newString appendString:@" "];
    if( alignLeft )
        return [string stringByAppendingString:newString];
    else
        [newString appendString:string];
    return newString;
}

@end

@implementation FormTemplate

/* created: 08.02.97
 */
+ (FormTemplate*)template
{
    return [[[FormTemplate alloc] init] autorelease];
}

/* created: 15.02.97
 */
+ (FormTemplate*)templateWithString:(NSString*)string
{   FormTemplate	*tmpl;

    tmpl = [[[FormTemplate alloc] init] autorelease];
    [tmpl setTemplate:string];
    return tmpl;
}

+ (void)printPages:(NSArray*)pages printer:(NSString*)printer
{   //NSString	*printFile = @"/tmp/print.rtf";

    [[self class] printServer];
NS_DURING
    [printServer printPages:pages ofType:0 on:printer];
NS_HANDLER
    printServer = nil;
    NSLog(@"%@ received exception: %@", NSStringFromSelector(_cmd), localException);
    [[self class] printServer];
    [printServer printPages:pages ofType:0 on:printer];
NS_ENDHANDLER
}

- copy
{   FormTemplate	*tmpl;

    tmpl = [[FormTemplate alloc] init];
    [tmpl setTemplate:template];
    return tmpl;
}

- (BOOL)loadTemplate:(NSString*)fileName
{   NSString *path = [NSString stringWithFormat:@"%@/%@", [[NSBundle mainBundle] resourcePath], fileName];

    [template release];
    if(! (template = [[NSString alloc] initWithContentsOfFile:fileName]) )
        if(! (template = [[NSString alloc] initWithContentsOfFile:path]) )
        {   NSLog(@"loadTemplate, Can't load template \"%@\"", path);
            return NO;
        }
    return YES;
}

- (void)setTemplate:(NSString*)string
{
    [template release];
    template = [string copy];
}

- (NSString*)string
{
    return template;
}

/* modified: 
 *
 * split template at #...# Name=> #/...#
 */
- (NSArray*)splitAt:(NSString*)name lines:(int*)lines
{   NSString		*start, *end, *header, *body, *trailer, *tmpStr;
    NSRange		range;
    NSCharacterSet	*newLineSet = [NSCharacterSet characterSetWithCharactersInString:@"\r\n"];
    unichar		chr, chr1;
    NSScanner		*scanner = [NSScanner scannerWithString:template];

    /* search for "#...#" */
    start = [NSString stringWithFormat:@"#%@", name];
#if 0
    range = [template rangeOfString:start options:NSCaseInsensitiveSearch];
    if( !range.length )
    {	NSLog(@"splitAt, Can't split template at \"%@\"", start);
        return NO;
    }
    header = [template substringToIndex:range.location];
    tmpStr = [template substringFromIndex:range.location+range.length];
#endif

    [scanner scanUpToString:start intoString:NULL];
    header = [template substringToIndex:[scanner scanLocation]];
    if( ![scanner scanString:start intoString:NULL] )
        return NO;
    [scanner scanUpToCharactersFromSet:[NSCharacterSet characterSetWithCharactersInString:@":#"] intoString:NULL];
    if( [scanner scanString:@":" intoString:NULL] )
        [scanner scanInt:lines];
    else
        *lines = 0;
    [scanner scanUpToString:@"#" intoString:NULL];
    [scanner scanString:@"#" intoString:NULL];
    tmpStr = [template substringFromIndex:[scanner scanLocation]];

    /* search for "#/.../" */
    end = [NSString stringWithFormat:@"#/%@#", name];
    range = [tmpStr rangeOfString:end options:NSCaseInsensitiveSearch];
    if( !range.length )
    {	NSLog(@"splitAt, Can't split template at \"%@\"", end);
        return NO;
    }
    body = [tmpStr substringToIndex:range.location];
    trailer = [tmpStr substringFromIndex:range.location+range.length];

    /* jump behind \r\n */
    range = [trailer rangeOfCharacterFromSet:newLineSet];
    if( (chr = [trailer characterAtIndex:range.location])=='\r' || chr=='\n' )
        range.location++;
    if( range.location<[trailer length] && ((chr1 = [trailer characterAtIndex:range.location])=='\r' || chr1=='\n') && chr!=chr1 )
        range.location++;
#if 0
    while( (chr = [trailer characterAtIndex:range.location])=='\r' || chr=='\n' )
    {   range.location ++;
        if( range.location >= [trailer length] )
            break;
    }
#endif
    trailer = [trailer substringFromIndex:range.location];

    return [NSArray arrayWithObjects:header, body, trailer, nil];
}

/* modified: 29.01.97
 */
- (BOOL)joinHeader:(NSString*)header body:(NSString*)body trailer:(NSString*)trailer
{
    [template release];
    template = [header stringByAppendingString:body];
    template = [[template stringByAppendingString:trailer] retain];
    return YES;
}

- (BOOL)removeTagsWithName:(NSString*)name
{   NSArray	*array;
    int		lines;

    if( !(array = [self splitAt:name lines:&lines]) )
        return NO;
    [self joinHeader:[array objectAtIndex:0] body:[array objectAtIndex:1] trailer:[array objectAtIndex:2]];
    return YES;
}

/* created: 09.04.97
 *
 * #...#
 */
- (BOOL)hasName:(NSString*)name
{   NSString	*separator;
    NSRange	range;

    /* search for "#...#" */
    separator = [NSString stringWithFormat:@"#%@#", name];
    range = [template rangeOfString:separator options:NSCaseInsensitiveSearch];
    if( !range.length )
        return NO;
    return YES;
}

/* created: 29.04.97
 *
 * #SumNet:14# -> "   1.000,-- DM"
 */
- (BOOL)setStrings:(NSString*)string forName:(NSString*)name
{
    while( [self setString:string forName:name] )
        ;
    return YES;
}

/* created:  14.06.98
 * modified:
 *
 * #SumNet:14# -> "   1.000,-- DM"
 */
- (void)removeMasks
{   NSScanner	*scanner = [NSScanner scannerWithString:template];
    int		location, loc1, loc2;
    NSString	*header, *trailer;

    while( ![scanner isAtEnd] )
    {
        if( ![scanner scanUpToString:@"#" intoString:NULL] )
            break;
        header = [template substringToIndex:[scanner scanLocation]];
        location = [scanner scanLocation]+1;
        if( location >= [template length] )
            break;
        [scanner setScanLocation:location];

        if( ![scanner scanUpToString:@"#" intoString:NULL] )
            break;
        loc1 = [scanner scanLocation];
        [scanner setScanLocation:location];
        [scanner scanUpToString:@"\n" intoString:NULL];
        loc2 = [scanner scanLocation];

        if( loc1 < loc2 )
        {
            trailer = ( loc1+1 < [template length] ) ? [template substringFromIndex:loc1+1] : @"";
            [self joinHeader:header body:@"" trailer:trailer];
            scanner = [NSScanner scannerWithString:template];
        }
    }
}

/* created:  09.04.97
 * modified: 02.07.97
 *
 * #SumNet:14# -> "   1.000,-- DM"
 */
- (int)setString:(NSString*)string forName:(NSString*)name
{
    return [self setString:string forName:name removeEmptyLine:NO];
}
- (int)setString:(NSString*)string forName:(NSString*)name removeEmptyLine:(BOOL)removeLine
{   NSString	*header = @"", *body, *trailer;
    int		length, lines=1;
    NSScanner	*scanner = [NSScanner scannerWithString:template];
    NSString	*suffix, *scanString;
    NSRange	range;

    if( !string || ![string respondsToSelector:@selector(length)] )
        string = @"";
    length = [string length];
    scanString = [NSString stringWithFormat:@"#%@", name];
    [scanner scanUpToString:scanString intoString:NULL];
    if( ![scanner scanString:scanString intoString:NULL] )
        return 0;
    header = [template substringToIndex:[scanner scanLocation]-[scanString length]];
    range.location = [scanner scanLocation];
    [scanner scanUpToCharactersFromSet:[NSCharacterSet characterSetWithCharactersInString:@":#"] intoString:NULL];
    if( [scanner scanString:@":" intoString:NULL] )
    {   [scanner scanInt:&length];
        range.location = [scanner scanLocation];
    }
    [scanner scanUpToString:@"#" intoString:NULL];
    [scanner scanString:@"#" intoString:NULL];
    range.length = [scanner scanLocation] - range.location;
    range.length --;
    trailer = [template substringFromIndex:[scanner scanLocation]];

    if( !length && removeLine )
    {	NSCharacterSet	*newLineSet = [NSCharacterSet characterSetWithCharactersInString:@"\r\n"];

        body = @"";
        [scanner setCharactersToBeSkipped:[NSCharacterSet whitespaceCharacterSet]];
        [scanner scanUpToCharactersFromSet:newLineSet intoString:NULL];
        if([scanner scanString:@"\n" intoString:NULL])
            [scanner scanString:@"\r" intoString:NULL];
        else if([scanner scanString:@"\r" intoString:NULL])
            [scanner scanString:@"\n" intoString:NULL];
        trailer = [template substringFromIndex:[scanner scanLocation]];
    }
    else if( !range.length )
        body = [self string:string withLength:length];
    /* convert \r\n to newline used in template (rtf) */
    else
    {
        suffix = [template substringWithRange:range];
        body = @"";
        string = replaceSubString(string, @"\r", @"");
        range = NSMakeRange(0, [string length]);
        while( 1 )
        {   NSRange	lineRange, endRange;

            endRange = [string rangeOfString:@"\n" options:0 range:range];
            if( !endRange.length )
            {   body = [body stringByAppendingString:[string substringWithRange:range]];
                break;
            }
            lineRange.location = range.location;
            lineRange.length = endRange.location - lineRange.location;
            range.location = endRange.location+endRange.length;
            range.length = [string length] - range.location;
            body = [body stringByAppendingString:[string substringWithRange:lineRange]];
            body = [body stringByAppendingString:suffix];
            lines++;
        }
    }

    [self joinHeader:header body:body trailer:trailer];

    return lines;
}

/* build list from replacement dictionary
 * 
 * created:  15.02.97
 * modified: 18.02.97
 *
 * #...#
 * ...
 * #/...#
 *
 * keys contains the strings which have to be replaced:
 *   ["REPLACE1", "REPLACE2"]
 * vals contains the arrays with the values for the keys:
 *   [["string1", "string2", "string3"], ["ref1", "ref2", "ref3"]]
 */
- (BOOL)setListWithKeys:(NSArray*)keys replaceWith:(NSArray*)vals forName:(NSString*)name
{   NSString	*body = @"";
    int		i, iCnt, j, lines;
    NSArray	*array;

    if( !(array = [self splitAt:name lines:&lines]) )
        return NO;

    if( [keys count] && [[vals objectAtIndex:0] count] )
    {
        for( j=0; (!lines || j<lines) && [[vals objectAtIndex:0] count]; j++ )
        {   FormTemplate	*bodyTemplate = [FormTemplate templateWithString:[array objectAtIndex:1]];

            for(i=0, iCnt=[keys count]; i<iCnt; i++)
            {   int	lcnt;
                id	val = [[vals objectAtIndex:i] objectAtIndex:0];

                if( [val isKindOfClass:[NSNumber class]] )
                    val = [val stringValue];
                lcnt = [bodyTemplate setString:val forName:[keys objectAtIndex:i]];
                j += (lcnt) ? lcnt-1 : 0;
                [[vals objectAtIndex:i] removeObjectAtIndex:0];
            }
            body = [body stringByAppendingString:[bodyTemplate string]];
        }
    }
    [self joinHeader:[array objectAtIndex:0] body:body trailer:[array objectAtIndex:2]];

    return YES;
}

/*
 * offset	start offset in vals array
 */
- (int)setListWithKeys:(NSArray*)keys replaceWith:(NSArray*)vals offset:(int)offset forName:(NSString*)name
{   NSString	*body = @"", *string;
    int		i, iCnt, j = 0, l, lines;
    NSArray	*array;
    NSRange	range;

    if( !(array = [self splitAt:name lines:&lines]) )
        return 0;

    if( [keys count] && [[vals objectAtIndex:0] count] )
    {
        for( j=offset, l=0; (!lines || l<lines) && j<[[vals objectAtIndex:0] count]; j++ )
        {   FormTemplate	*bodyTemplate = [FormTemplate templateWithString:[array objectAtIndex:1]];

            for(i=0, iCnt=[keys count]; i<iCnt; i++)
            {   id	val = [[vals objectAtIndex:i] objectAtIndex:j];

                if( [val isKindOfClass:[NSNumber class]] )
                    val = [val stringValue];
                [bodyTemplate setString:val forName:[keys objectAtIndex:i]];
                //l += (lcnt) ? lcnt : 1;
            }
            string = [bodyTemplate string];
            range = NSMakeRange(0, [string length]);
            while( range.length )
            {   range = [string rangeOfString:@"\n" options:0 range:range];
                if( !range.length )
                    break;
                l++;
                range.location += range.length;
                range.length = [string length] - range.location;
            }
            body = [body stringByAppendingString:string];
        }
    }
    [self joinHeader:[array objectAtIndex:0] body:body trailer:[array objectAtIndex:2]];

    return j;
}

- (BOOL)removePartWithName:(NSString*)name
{   NSArray	*array;
    int		lines;

    if( !(array = [self splitAt:name lines:&lines]) )
        return NO;
    [self joinHeader:[array objectAtIndex:0] body:@"" trailer:[array objectAtIndex:2]];
    return YES;
}

- (void)writeToFile:(NSString*)fileName
{
    [template writeToFile:fileName atomically:YES];
}

- (void)setPrinterName:(NSString*)printer
{
    [printerName release];
    printerName = [printer retain];
}

/* created: 09.04.97
 */
- (BOOL)printPage
{   //NSString	*printFile = @"/tmp/print.rtf";

    [[self class] printServer];
    [printServer print:template ofType:0 on:printerName];

#if 0
    //printf("%s", [template cString]);
    [template writeToFile:printFile atomically:NO];
    //[[NSWorkspace sharedWorkspace] launchApplication:AppString(@"/Net/www/LocalLibrary/WWWApps/Printer.app") showIcon:NO autolaunch:NO];
    if( printerName )
        system([[NSString stringWithFormat:@"/Net/www/LocalLibrary/WWWApps/Printer.app/Printer -p=%@ %@", printerName, printFile] cString]);
    else
        system([[NSString stringWithFormat:@"/Net/www/LocalLibrary/WWWApps/Printer.app/Printer %@", printFile] cString]);
#endif
#ifdef _CAN_PRINT
    [[[[Print alloc] init] autorelease] printRTF:template];
    //system([[NSString stringWithFormat:@"/usr/bin/enscript -p/tmp/test.ps -B -g -sA4 -T 4 %@", printFile] cString]);
#endif
    return YES;
}

- (void)dealloc
{
    [template release];
    [printerName release];
    [super dealloc];
}

@end
