/* Copyright (c) 2004, Noah Sorscher
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 	• 	Redistributions of source code must retain the above copyright
 * 	notice, this list of conditions and the following disclaimer.
 * 	• 	Redistributions in binary form must reproduce the above copyright
 * 	notice, this list of conditions and the following disclaimer in the
 * 	documentation and/or other materials provided with the distribution.
 * 	• 	Neither the name of the Noah Sorscher nor the names of its
 * 	contributors may be used to endorse or promote products derived from
 * 	this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */
//
//  MHUtil.m
//  Hootenanny
//
//  Created by Noah Sorscher on Tue Mar 25 2003.
//  Copyright (c) 2003 __MyCompanyName__. All rights reserved.
//

#import "MHUtil.h"


@implementation MHUtil

//NSArray *supportDirLocations = nil;
//BOOL supportGiven = NO;

+ (id)imageHelper
{
    id vo = [NSConnection rootProxyForConnectionWithRegisteredName:@"mhImageHelper" host:nil];
    if (nil == vo) {
        // relaunch the helper app
        NSString *resourcePath = [[NSBundle mainBundle] pathForResource:@"dvdxchap" ofType:nil];
        resourcePath = [resourcePath stringByDeletingLastPathComponent];
        resourcePath = [resourcePath stringByAppendingPathComponent:@"MHTimeline.app/Contents/MacOS/MHTimeline"];
        NSTask *task = [NSTask launchedTaskWithLaunchPath:resourcePath arguments:[NSArray array]];
        while (nil == vo)
            vo = [NSConnection rootProxyForConnectionWithRegisteredName:@"mhImageHelper" host:nil];
    }
    return vo;
}

+ (id)spriteHelper
{
    id vo = [NSConnection rootProxyForConnectionWithRegisteredName:@"mhSpriteHelper" host:nil];
    if (nil == vo) {
        // relaunch the helper app
        NSString *resourcePath = [[NSBundle mainBundle] pathForResource:@"dvdxchap" ofType:nil];
        resourcePath = [resourcePath stringByDeletingLastPathComponent];
        resourcePath = [resourcePath stringByAppendingPathComponent:@"mhSpriteHelper.app/Contents/MacOS/mhSpriteHelper"];
        NSTask *task;
        task = [NSTask launchedTaskWithLaunchPath:resourcePath arguments:[NSArray array]];
        while (nil == vo)
            vo = [NSConnection rootProxyForConnectionWithRegisteredName:@"mhSpriteHelper" host:nil];
    }
    return vo;
}

NSArray *theArgs = nil;

+ (void)saveArgs:(NSArray *)args
{
    theArgs = [args retain];
}

+ (NSArray *)args
{
    return theArgs;
}


+ (NSString *)supportDir
{
    NSArray *steps = [NSArray arrayWithObjects:@"Library", @"Application Support", @"Metadata Hootenanny", nil];
    NSFileManager *fm = [NSFileManager defaultManager];
    int i, n = [steps count];
    NSString *path = NSHomeDirectory();
    for (i=0;i<n;i++) {
        path = [path stringByAppendingPathComponent:[steps objectAtIndex:i]];
        if (![fm fileExistsAtPath:path])
            [fm createDirectoryAtPath:path attributes:nil];
    }
    if (![path hasSuffix:@"/"])
        path = [path stringByAppendingString:@"/"];
    return path;
}

+ (void)clearSupportDir
{
    NSFileManager *fm = [NSFileManager defaultManager];
    NSString *dir = [MHUtil supportDir];
    NSArray *contents = [fm directoryContentsAtPath:dir];
    int i;
    for (i=0;i<[contents count];i++) {
        NSString *aFile = [contents objectAtIndex:i];
        if ([aFile hasSuffix:@".jpg"] || [aFile hasSuffix:@".gif"] || [aFile hasSuffix:@".mov"]) {
            aFile = [dir stringByAppendingPathComponent:aFile];
            [fm removeFileAtPath:aFile handler:nil];
        }
    }
}

+ (NSString *)uniquePath:(NSString *)path
{
    NSString *outpath = nil;
    NSFileManager *fm = [NSFileManager defaultManager];
    int count = 1;
    if ([path hasSuffix:@".mov"]) {
        outpath = path;
        path = [path substringToIndex:[path length]-4];
        while ([fm fileExistsAtPath:outpath])
            outpath = [NSString stringWithFormat:@"%@ %d.mov", path, count++];
    } else {
        outpath = path;
        while ([fm fileExistsAtPath:outpath])
            outpath = [NSString stringWithFormat:@"%@ %d", path, count++];
    }
    return outpath;
}


/*
    
    for (i=0;i<n;i++) {
    
    }
    //supportGiven = YES;
    supportDirLocations = [NSArray arrayWithObjects:[fm currentDirectoryPath], NSHomeDirectory(), nil];
    NSString *buffer, *supportDir;
    int i, n = [supportDirLocations count];
    for (i=0;i<n;i++) {
        buffer = [supportDirLocations objectAtIndex:i];
        if ([fm isWritableFileAtPath:[supportDirLocations objectAtIndex:i]])
            break;
    }
    if (i == n) i = 0;
    
    supportDir = [NSString stringWithFormat:@"%@/Metadata Hootenanny Support/", [supportDirLocations objectAtIndex:i]];
    BOOL isDir;
    if (![fm fileExistsAtPath:supportDir isDirectory:&isDir])
        [fm createDirectoryAtPath:supportDir attributes:nil];
    return supportDir;
}
*/
/*
+ (void)killSupportDir
{
    if (!supportGiven) return;
    NSFileManager *fm = [NSFileManager defaultManager];
    supportDirLocations = [NSArray arrayWithObjects:[fm currentDirectoryPath], NSHomeDirectory(), nil];
    int i, n = [supportDirLocations count];
    BOOL isDir;
    NSString *path;
    for (i=0;i<n;i++) {
        path = [NSString stringWithFormat:@"%@/Metadata Hootenanny Support/", [supportDirLocations objectAtIndex:i]];
        if ([fm fileExistsAtPath:path isDirectory:&isDir] && isDir)
            [fm removeFileAtPath:path handler:nil];
    }
}
*/

+ (NSArray *)showOpenPanel
{
    NSOpenPanel *theOpenPanel = [NSOpenPanel openPanel];
    NSArray *theSelection;
    int result;
    
    [theOpenPanel setCanChooseFiles:YES];
    [theOpenPanel setCanChooseDirectories:YES];
    [theOpenPanel setResolvesAliases:YES];
    [theOpenPanel setAllowsMultipleSelection:YES];
    result = [theOpenPanel runModalForDirectory:nil file:nil types:nil];
    if (result == NSOKButton) {
        theSelection = [theOpenPanel URLs];
    } else {
        theSelection = [NSArray array];
    }
    return theSelection;
}


+ (NSString *)handleToString:(Handle)h
{
    NSString *outputString;
    Size theSize;
    NSData *theData;
    char *buffer;
    
    if (h == NULL) return NULL;
    theSize = GetHandleSize( h );
    
    theData = [[[NSData dataWithBytes:*h length:theSize] copy] autorelease];
    buffer = (char *)[theData bytes];
    outputString = [NSString stringWithCString:buffer];
    if (theSize < [outputString length])
        outputString = [outputString substringToIndex:theSize];
    return outputString;
}

+ (NSImage *)picHandleToImage:(PicHandle)picHandle
{
    if (picHandle){
            int imageSize   = GetHandleSize((Handle)picHandle);
            void *picBytes  = (*picHandle);
            NSData *data    = [NSData dataWithBytes:picBytes length:imageSize];
            //[data writeToFile:@"/theData" atomically:NO];
            NSPICTImageRep *imageRep     = [NSPICTImageRep imageRepWithData:data];
            NSImage *_posterImage = [[NSImage alloc] initWithSize:[imageRep size]];

                /* Convert the PIC image rep to a tiff image rep */
            [_posterImage lockFocus];
            [imageRep drawAtPoint:NSMakePoint(0,0)];
            [_posterImage unlockFocus];

            KillPicture(picHandle);
            return _posterImage;
    }
    else return nil;
}


+ (NSString *)trackFormatForMovie:(Movie)movie ofType:(OSType)theType
{
    Track aTrack, theTrack = nil;
    Media aMedia;
    SampleDescriptionHandle descH = (SampleDescriptionHandle)NewHandleClear(0);
    OSType aType;
    char *buffer;
    char string[5];
    int i;
    long oldsize, newsize;
    
    oldsize = newsize = 0;
    i = 1;
    aTrack = GetMovieIndTrackType(movie, i++, theType, movieTrackMediaType);
    while (aTrack != nil) {
        newsize = GetTrackDataSize(aTrack, 0, GetTrackDuration(aTrack));
        if (GetTrackEnabled(aTrack) && newsize > oldsize) {
            oldsize = newsize;
            theTrack = aTrack;
        }
        aTrack = GetMovieIndTrackType(movie, i++, theType, movieTrackMediaType);
    }
    
    if (theTrack == nil) {
        i = 1;
        aTrack = GetMovieIndTrackType(movie, i++, MPEGMediaType, movieTrackMediaType);
        while (aTrack != nil) {
            newsize = GetTrackDataSize(aTrack, 0, GetTrackDuration(aTrack));
            if (GetTrackEnabled(aTrack) && newsize > oldsize) {
                oldsize = newsize;
                theTrack = aTrack;
            }
            aTrack = GetMovieIndTrackType(movie, i++, MPEGMediaType, movieTrackMediaType);
        }
    }
    if (theTrack == nil) return @"none";
    aMedia = GetTrackMedia(theTrack);
    GetMediaSampleDescription(aMedia, 1, descH);
    aType = (*descH)->dataFormat;
    buffer = (char *)&aType;
    for (i=0; i<4; i++) {
        string[i] = *buffer;
        buffer++;
    }
    string[4] = '\0';
    DisposeHandle((Handle)descH);
    return [NSString stringWithCString:string];
}

//by Jan Devos, then condensed
+ (NSString *)displayTime:(double)seconds
{	/* Super Deluxe Seconds To Display Convertor */
    int rounded;
    if( seconds < 60 ) {
        rounded = floor( seconds * 10 + 0.5 );
        /* we don't want to print x.0 */
        if( rounded % 10 == 0 ) {
            if( rounded == 10 )
                return [NSString stringWithFormat:@"1 second"];
            return [NSString stringWithFormat:@"%.0f seconds", seconds];
        }
        return [NSString stringWithFormat:@"%.1f seconds", seconds];
    } else if ( seconds < 60 * 60 ) {
        rounded = floor( seconds/6 + 0.5 );
        /* we don't want to print x.0 */
        if( rounded % 10 == 0 ) {
            if( rounded == 10 )
                return [NSString stringWithFormat:@"1 minute"];
            return [NSString stringWithFormat:@"%.0f minutes", seconds/60];
        }
        return [NSString stringWithFormat:@"%.1f minutes", seconds/60];
    } else if ( seconds < 60 * 60 * 24 ) {
        rounded = floor( seconds/360 + 0.5 ); 
        /* we don't want to print x.0 */
        if( rounded % 10 == 0 ) {
            if( rounded == 10 )
                return [NSString stringWithFormat:@"1 hour"];
            return [NSString stringWithFormat:@"%.0f hours", seconds/3600];
        }
        return [NSString stringWithFormat:@"%.1f hours", seconds/3600];
        }
    rounded = floor( seconds/8640 + 0.5 );
    /* we don't want to print x.0 */
    if( rounded % 10 == 0 ) {
        if( rounded == 10 )
            return [NSString stringWithFormat:@"1 day"];
        return [NSString stringWithFormat:@"%.0f days", seconds/86400];
    }
    return [NSString stringWithFormat:@"%.1f days", seconds/86400];
}

// by Jan Devos
+ (NSString *)displaySize:(double)megaBytes
{
    int rounded;
	/* Super Deluxe megaBytes To Display Convertor */
    if( megaBytes < 1024 ) {
        rounded = floor( megaBytes * 10 + 0.5 );
        /* we don't want to print x.0 */
        if( rounded % 10 == 0 ) {
            if( rounded == 10 )
                return [NSString stringWithFormat:@"1 MB"];
            return [NSString stringWithFormat:@"%.0f MB", megaBytes];
        }
        return [NSString stringWithFormat:@"%.1f MB", megaBytes];
    }
    rounded = floor( megaBytes*100/1024 + 0.5 );
		/* we don't want to print x.0 */
    if( rounded % 100 == 0 ) {
        if( rounded == 100 )
            return [NSString stringWithFormat:@"1 GB"];
        return [NSString stringWithFormat:@"%.0f GB", megaBytes/1024];
    } else if ( rounded % 10 == 0 )
        return [NSString stringWithFormat:@"%.1f GB", megaBytes/1024];
    return [NSString stringWithFormat:@"%.2f GB", megaBytes/1024];
}

+ (double)stringToNSeconds:(NSString *)input
{
    return [MHUtil stringToNSeconds:input timebase:0];
}

+ (double)stringToNSeconds:(NSString *)input timebase:(int)base
{
    char ch;
    const char *TimeInput = [input cString];
    int dec = 0;
    int dig, i, n = [input length];
    int isDec = 0;
    int colTal = 0;
    int total = 0;
    int numAfterDec = 0;
    for (i=0;i<n;i++) {
            ch = TimeInput[i];
            if (ch == '.') {
                isDec = i;
            } else if (isDec && [MHUtil isNum:ch]) {
                numAfterDec++;
                dec *= 10;
                dec += (ch - '0'); // * expon(10, decTally++);
            }
    }
    double divisor = base;
    if (!divisor) {
        divisor = 30;
        if (numAfterDec == 3) divisor = 1000;
    }
    // if no decimal, set isDec to just after the last time-valid char
    if (!isDec) {
        for (i=n-1; i>=0; i--) {
            ch = TimeInput[i];
            if ([MHUtil isNum:ch] || ch == ':') {
                isDec = i+1;
                break;
            }
        }
    }
    // Backwards from the decimal
    dig = 0;  // dig is the digits, like 0 for ones, 1 for tens, 2 for hundreds
    for (i=isDec-1; i>=0; i--) {
        ch = TimeInput[i];
        switch (ch) {
            case ':': colTal++;
                    dig = 0;
                    break;
            case '[': case ']': case ' ': case '\t': break;
            default:
                if ([MHUtil isNum:ch]) {
                    total += (ch - '0') * [MHUtil expon:10 toThe:dig] * [MHUtil expon:60 toThe:colTal];
                    dig++;
                } else {
                    i = 0;
                }
        }
    }
    return (double)total + (double)dec/divisor;
}

+ (NSString *)nSecondsToString:(double)seconds
{
    int hours, minutes, secs, frames;

    hours = seconds/(60*60);
    seconds -= (double)hours*60.0*60.0;
    minutes = seconds/60;
    seconds -= (double)minutes*60.0;
    secs = seconds;
    frames = (seconds-(double)secs)*30.0 + 0.45;
    
    return [NSString stringWithFormat:@"%02d:%02d:%02d.%02d", hours, minutes, secs, frames];
}

+ (NSString *)nSecondsToShortString:(int)seconds
{
    int hours = seconds/(60*60);
    seconds -= hours*60*60;
    int minutes = seconds/60;
    seconds -= minutes*60;
    int secs = seconds;
    if (hours)
        return [NSString stringWithFormat:@"%d:%02d:%02d", hours, minutes, secs];
    if (minutes)
        return [NSString stringWithFormat:@"%d:%02d", minutes, secs];
    return [NSString stringWithFormat:@"0:%02d", secs];
}

+ (NSString *)spaceToPlus:(NSString *)input
{
    if (!input) return @"";
    NSMutableString *s = [NSMutableString stringWithString:input];
    [s replaceOccurrencesOfString:@" " withString:@"+" options:0 range:NSMakeRange(0, [s length])];
    return [NSString stringWithString:s];
}

+ (BOOL)isNum:(char)ch
{
    return ch >= '0' && ch <= '9';
}

+ (int)expon:(int)a toThe:(int)b
{
    int i, t;
	
	t = 1;
	for (i=0; i<b; i++) {
		t *= a;
	}
	return t;
}

+ (NSURL *)stringToURL:(NSString *)string
{
    if ([string hasPrefix:@"/"])
        return [NSURL fileURLWithPath:string];
    else
        return [NSURL URLWithString:string];
}

// cuts out html and imdb artifacts
+ (NSString *)fixIMDB:(NSString *)input
{
    //NSLog(@"tWWW fixIMDB:");
    if (input == nil || ![input isKindOfClass:[NSString class]] || [input length] == 0) return @"";
    NSScanner *scan = [[NSScanner alloc] initWithString:input];
    NSString *buffer;
    NSMutableString *result = [NSMutableString string];
    
    // remove html tags
    if ([scan scanString:@"<" intoString:nil])
        [scan scanPastString:@">"];
    while (![scan isAtEnd]) {
        buffer = nil;
        [scan scanUpToString:@"<" intoString:&buffer];
        if (buffer)
            [result appendString:buffer];
        buffer = nil;
        [scan scanUpToString:@">" intoString:&buffer];
        [scan scanString:@">" intoString:nil];
        buffer = [MHUtil interpretTag:buffer];
        if (buffer)
            [result appendString:buffer];
        else
            [result appendString:@" "];
    }
    
    // remove objectionable parens
    [scan release];
    scan = [[NSScanner alloc] initWithString:result];
    result = [NSMutableString string];
    while (![scan isAtEnd]) {
        buffer = nil;
        [scan scanUpToString:@"(" intoString:&buffer];
        if (buffer)
            [result appendString:buffer];
        buffer = nil;
        [scan scanUpToString:@")" intoString:&buffer];
        [scan scanString:@")" intoString:nil];
        if ([MHUtil parenGood:buffer]) {
            [result appendString:buffer];
            [result appendString:@") "];
        }
    }
    /*
    const char *str = [result cString];
    NSMutableString *temp = [NSMutableString stringWithUTF8String:str];
    if (temp) result = temp;
    */
    // do escape chars
    [scan release];
    scan = [[NSScanner alloc] initWithString:result];
    result = [NSMutableString string];
    while (![scan isAtEnd]) {
        buffer = nil;
        [scan scanUpToString:@"&" intoString:&buffer];
        if (buffer) {
            if ([buffer hasPrefix:@";"])
                buffer = [buffer substringFromIndex:1];
            [result appendString:buffer];
        }
        buffer = nil;
        [scan scanUpToString:@";" intoString:&buffer];
        //[scan scanString:@";" intoString:nil];
        buffer = [MHUtil getEscapeChar:buffer];
        if (buffer)
            [result appendString:buffer];
    }
    
    // remove redundant white space
    [scan release];
    scan = [[NSScanner alloc] initWithString:result];
    result = [NSMutableString string];
    NSCharacterSet *set = [NSCharacterSet whitespaceAndNewlineCharacterSet];
    while (![scan isAtEnd]) {
        buffer = nil;
        [scan scanUpToCharactersFromSet:set intoString:&buffer];
        if (buffer) {
            [result appendString:buffer];
            [result appendString:@" "];
        }
        if ([scan isAtEnd]) break;
        [scan setScanLocation:[scan scanLocation]+1];
    }
    if ([result length]) [result deleteCharactersInRange:NSMakeRange([result length]-1, 1)];
    
    if ([result length] > 0) {
        set = [NSCharacterSet characterSetWithCharactersInString:@" \t\r\n,"];
        while ([result length] && [set characterIsMember:[result characterAtIndex:0]])
            [result deleteCharactersInRange:NSMakeRange(0, 1)];
        while ([result length] && [set characterIsMember:[result characterAtIndex:[result length]-1]])
            [result deleteCharactersInRange:NSMakeRange([result length]-1, 1)];
    }
    
	if ([result hasSuffix:@" more"])
		[result deleteCharactersInRange:NSMakeRange([result length]-5, 5)];
	
    if ([result hasSuffix:@" ..."])
        [result deleteCharactersInRange:NSMakeRange([result length]-4, 4)];
    
    [result replaceOccurrencesOfString:@"&," withString:@"," options:NSLiteralSearch range:NSMakeRange(0, [result length])];
    [result replaceOccurrencesOfString:@" ," withString:@"," options:NSLiteralSearch range:NSMakeRange(0, [result length])];
    /*
    NSString *output = nil;
    if ([result rangeOfString:@"\xc3"].length) { // the square root sign seems to be the first char whenever 8-bit chars get crammed into 7-bit spots.  so far.
	const char *str = [result cString];
	output = [NSString stringWithUTF8String:str];
    }
    */
    NSString *output = [NSString stringWithString:result];
    return output;
}

+ (BOOL)parenGood:(NSString *)input
{
    if (!input) return NO;
    NSArray *bad = [NSArray arrayWithObjects:@"more", @"view trailer", @"WGA", nil];
    if ([input hasPrefix:@"("])
        input = [input substringFromIndex:1];
    input = [input stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
    int i, n = [bad count];
    for (i=0;i<n;i++)
        if ([input isEqualToString:[bad objectAtIndex:i]]) return NO;
    n = [input length];
    char ch;
    for (i=0;i<n;i++) {
        ch = [input characterAtIndex:i];
        if (ch != '(' && ch != 'I' && ch != 'V' && ch != 'X' && ch != ' ')
            return YES;
    }
    return NO;
}

+ (NSString *)getEscapeChar:(NSString *)input
{
    if (!input) return @"";
    if ([input rangeOfCharacterFromSet:[NSCharacterSet whitespaceCharacterSet]].location != NSNotFound) return input;
    if ([input isEqualToString:@"&nbsp"]) return @" ";
    NSFileManager *fm = [NSFileManager defaultManager];
    input = [input stringByAppendingString:@";"];
    NSString *path = [MHUtil uniquePath:[[MHUtil supportDir] stringByAppendingString:@"xml.xml"]];
    [[NSArray arrayWithObject:input] writeToFile:path atomically:NO];
    NSMutableString *string = [NSMutableString stringWithContentsOfFile:path];
    [fm removeFileAtPath:path handler:nil];
    if (!string) return @"?";
    NSRange aRange = [string rangeOfString:@"amp;"];
    if (aRange.location != NSNotFound)
        [string deleteCharactersInRange:aRange];
    [string writeToFile:path atomically:NO];
    input = [[NSArray arrayWithContentsOfFile:path] objectAtIndex:0];
    [fm removeFileAtPath:path handler:nil];
    if (!input) input = @"?";
    return input;
    
    /*
    if ([input isEqualToString:@"&nbsp"]) return @" ";
    input = [input stringByAppendingString:@";"];
    NSMutableData *myData = [[NSMutableData alloc] init];
    NSKeyedArchiver *arch = [[NSKeyedArchiver alloc] initForWritingWithMutableData:myData];
    [arch setOutputFormat:NSPropertyListXMLFormat_v1_0];
    [arch encodeObject:[NSArray arrayWithObject:input]];
    [arch finishEncoding];
    const char *bytes = [myData bytes];
    [arch release];
    
    NSMutableString *string = [NSMutableString stringWithCString:bytes length:[myData length]];
    [string writeToFile:@"/wml.xml" atomically:NO];
    NSRange range = [string rangeOfString:@"amp;"];
    if (range.location == NSNotFound) return @"?";
    [string deleteCharactersInRange:range];
    
    [string writeToFile:@"/xml.xml" atomically:NO];
    NSArray *array = [NSArray arrayWithContentsOfFile:@"/xml.xml"];
    //string = [array objectAtIndex:0];
    
    [myData release];
    myData = [NSMutableData dataWithBytes:[string cString] length:[string length]];
    NSKeyedUnarchiver *unarch = [[NSKeyedUnarchiver alloc] initForReadingWithData:myData];
    string = [[unarch decodeObject] objectAtIndex:0];
    if (!string) string = @"?";
    return string;
    */
}

+ (NSString *)interpretTag:(NSString *)input
{
    if (!input) return nil;
    input = [input lowercaseString];
    if ([input hasPrefix:@"<br"] || [input hasPrefix:@"<p"])
        return @", ";
    return nil;
}

+ (NSString *)cleanHTML:(NSString *)input
{
    //NSLog(@"tWWW cleanHTML:");
    if (input == nil) return @"";
    NSScanner *scan = [[NSScanner alloc] initWithString:input];
    [scan setCaseSensitive:NO];
    NSString *buffer, *tag;
    NSMutableString *output = [[NSMutableString alloc] initWithCapacity:[input length]];
    
    while (![scan isAtEnd]) {
        buffer = tag = nil;
        [scan scanUpToString:@"<" intoString:&buffer];
        if (buffer != nil)
            [output appendString:buffer];
        [scan scanString:@"<" intoString:nil];
        [scan scanUpToString:@">" intoString:&tag];
        [scan scanString:@">" intoString:nil];
        if (![output hasSuffix:@" "] && ![output hasSuffix:@"\n"] && ![output hasSuffix:@"\t"] && [output length] > 0)
            [output appendString:@" "];
        if ([output length] > 0 && tag != nil && ([tag isEqualToString:@"br"] ||
            [tag isEqualToString:@"tr"] ||
            [tag isEqualToString:@"p"]))
                [output appendString:@"\n"];
    }
    return output;
}

+ (NSString *)newLines:(NSString *)input
{
    if (!input) return nil;
    NSMutableString *ms = [NSMutableString stringWithString:input];
    [ms replaceOccurrencesOfString:@"\r\n" withString:@"\r" options:0 range:NSMakeRange(0, [ms length])];
    [ms replaceOccurrencesOfString:@"\r" withString:@"\n" options:0 range:NSMakeRange(0, [ms length])];
    return [NSString stringWithString:ms];
}

+ (int)framesBetweenStart:(TimeValue)start end:(TimeValue)end movie:(Movie)movie
{
    int 			numFrames = 0;
    short 			flags = nextTimeMediaSample + nextTimeEdgeOK;
    OSType 			theType = VideoMediaType;
	
    GetMovieNextInterestingTime(movie, flags, 1, &theType, start, 0, &start, nil);
	if(start == -1) return numFrames;

	flags = nextTimeMediaSample; // Don't include the  nudge after the first interesting time.
	
	while(start != -1 && start < end)  // When the returned time equals -1, then there were no more interesting times.
	{
		numFrames++;
		GetMovieNextInterestingTime(movie, flags, 1, &theType, start, 0, &start, nil);
	}
    return numFrames;
}

+ (NSMutableDictionary *)spritesFromPlist:(NSDictionary *)plist
{
    NSMutableDictionary *newDict = [[NSMutableDictionary alloc] init];
    NSArray *tracks = [plist objectForKey:@"tracks"];
    NSMutableArray *newTracks = [NSMutableArray array];
    [newDict setObject:newTracks forKey:@"tracks"];
    int i, j, k;
    for (i=0;i<[tracks count];i++) {
        NSDictionary *aTrack = [tracks objectAtIndex:i];
        NSMutableDictionary *aNewTrack = [NSMutableDictionary dictionary];
        [newTracks addObject:aNewTrack];
        NSArray *keys = [aTrack allKeys];
        for (j=0;j<[keys count];j++)
            [aNewTrack setObject:[aTrack objectForKey:[keys objectAtIndex:j]] forKey:[keys objectAtIndex:j]];
        [aNewTrack removeObjectForKey:@"sprites"];
        NSArray *sprites = [aTrack objectForKey:@"sprites"];
        NSMutableArray *newSprites = [NSMutableArray array];
        [aNewTrack setObject:newSprites forKey:@"sprites"];
        for (j=0;j<[sprites count];j++) {
            NSDictionary *aSprite = [sprites objectAtIndex:j];
            NSMutableDictionary *aNewSprite = [NSMutableDictionary dictionary];
            [newSprites addObject:aNewSprite];
            keys = [aSprite allKeys];
            for (k=0;k<[keys count];k++)
                [aNewSprite setObject:[aSprite objectForKey:[keys objectAtIndex:k]] forKey:[keys objectAtIndex:k]];
            [aNewSprite setObject:aNewTrack forKey:@"parent"];
        }
    }
    return newDict;
}

+ (NSDictionary *)plist:(NSDictionary *)input
{
    NSMutableDictionary *newDict = [NSMutableDictionary dictionary];
    NSArray *tracks = [input objectForKey:@"tracks"];
    NSMutableArray *newTracks = [NSMutableArray array];
    [newDict setObject:newTracks forKey:@"tracks"];
    NSArray *keys, *values;
    NSString *key, *value;
    int i, n = [tracks count];
    for (i=0;i<n;i++) {
        NSDictionary *track = [tracks objectAtIndex:i];
        NSMutableDictionary *newTrack = [NSMutableDictionary dictionary];
        [newTracks addObject:newTrack];
        keys = [track allKeys];
        values = [track allValues];
        int j;
        for (j=[keys count]-1;j>=0;j--) {
            key = [keys objectAtIndex:j];
            value = [values objectAtIndex:j];
            if (![[MHUtil stringClass:value] isEqualToString:@"Unknown!"] && ![key isEqualToString:@"sprites"])
                [newTrack setObject:value forKey:key];
        }
        NSArray *sprites = [track objectForKey:@"sprites"];
        NSMutableArray *newSprites = [NSMutableArray array];
        [(NSMutableDictionary *)[newTracks objectAtIndex:i] setObject:newSprites forKey:@"sprites"];
        for (j=[sprites count]-1;j>=0;j--) {
            NSDictionary *sprite = [sprites objectAtIndex:j];
            NSMutableDictionary *newSprite = [NSMutableDictionary dictionary];
            [newSprites addObject:newSprite];
            keys = [sprite allKeys];
            values = [sprite allValues];
            int k;
            for (k=[keys count]-1;k>=0;k--) {
                key = [keys objectAtIndex:k];
                value = [values objectAtIndex:k];
                if (![[MHUtil stringClass:value] isEqualToString:@"Unknown!"] && ![key isEqualToString:@"parent"])
                    [newSprite setObject:value forKey:key];
            }
        }
    }
    return newDict;
}

+ (NSString *)stringClass:(id)input
{
    if ([input isKindOfClass:[NSString class]])
        return @"String";
    if ([input isKindOfClass:[NSNumber class]])
        return @"Number";
    if ([input isKindOfClass:[NSData class]])
        return @"Data";
    if ([input isKindOfClass:[NSDate class]])
        return @"Date";
    if ([input isKindOfClass:[NSDictionary class]])
        return @"Dictionary";
    if ([input isKindOfClass:[NSArray class]])
        return @"Array";
    return @"Unknown!";
}




@end
