/* 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.
 */
#import "MHEPGTVT.h"

#define DEBUG 0

@interface NSObject (epgDelegate)
- (void)guessWholeSeries:(id)sender;
@end


@interface tItem : NSObject {
    NSString *myTitle, *myAddress, *regulars;
    NSMutableArray *children;
    NSMutableDictionary *metadata;
    BOOL amIExpandable, haveIDownloaded;
    int myEpisode, mySeason;
}
- (int)numberOfChildren;
- (id)childAtIndex:(int)index;
- (BOOL)expandable;
- (void)setExpandable:(BOOL)exp;
- (BOOL)expand;
- (BOOL)processSeries:(NSDictionary *)dict;
- (NSString *)processRegulars:(NSDictionary *)dict;
- (void)setObject:(id)object forKey:(NSString *)key;
- (id)objectForKey:(NSString *)key;
- (NSString *)address;
- (NSString *)title;
- (NSString *)summary;
- (void)download;
- (void)processEpisode:(NSDictionary *)keys;
+ (NSString *)deHTML:(NSString *)input;
- (NSString *)fixPerfs:(NSString *)input;
- (NSDictionary *)metadata;
- (void)setTitle:(NSString *)title;
- (void)setAddress:(NSString *)address;
- (int)episode;
- (int)season;
- (void)setEpisode:(int)ep;
- (void)setSeason:(int)se;

@end


@interface seriesScanner : NSObject {
    NSScanner *myScanner;
    NSString *spots[4], *Month;
    int index, count, Se, Ep, Day, Year;
    tItem *myItem;
    NSArray *mySequence;
    unsigned startLocation;
}

- (tItem *)parseEntryWithArray:(NSArray *)sequence;
- (void)carryOn;
- (void)dontCarryOn;
- (void)putNumber:(int)x intoValue:(NSString *)value;
- (void)performAction:(NSString *)action;
- (void)setScanner:(NSScanner *)scanner;

@end

@implementation seriesScanner

- (id)init
{
    int i;
    for (i=0;i<4;i++)
        spots[i] = nil;
    index = count = 0;
    Se = Ep = Day = Year = -1;
    myItem = nil;
    mySequence = nil;
    Month = nil;
    return [super init];
}

- (void)setScanner:(NSScanner *)scanner
{   myScanner = scanner;    }

- (tItem *)parseEntryWithArray:(NSArray *)sequence
{
    startLocation = [myScanner scanLocation];
    count = [sequence count];
    mySequence = sequence;
    for (index=0;index<count;index++) {
        NSString *buffer = nil;
        NSString *item = [sequence objectAtIndex:index];
        if ([item isEqualToString:@"ifNotScanString"]) {
            if (![myScanner superScanString:[mySequence objectAtIndex:++index]])
                [self carryOn];
            else
                [self dontCarryOn];
        } else if ([item isEqualToString:@"ifScanString"]) {
            if ([myScanner superScanString:[mySequence objectAtIndex:++index]])
                [self carryOn];
            else
                [self dontCarryOn];
        } else if ([item isEqualToString:@"ifScanInt"]) {
            buffer = [mySequence objectAtIndex:++index];
            int x = -1;
            if ([myScanner scanInt:&x])
                [self carryOn];
            [self putNumber:x intoValue:buffer];
        } else if ([item isEqualToString:@"ifNotScanInt"]) {
            buffer = [mySequence objectAtIndex:++index];
            int x = -1;
            if (![myScanner scanInt:&x])
                [self carryOn];
            [self putNumber:x intoValue:buffer];
        } else
            [self performAction:item];
    }
    tItem *newItem = [[[tItem alloc] init] autorelease];
    NSString *name = spots[0];
    if (!name) name = @"";
    if (!spots[2]) // date
        if (Day != -1 && Year != -1 && Month)
            spots[2] = [NSString stringWithFormat:@"%.2d %@ %.2d", Day, Month, Year];
    if (spots[2])
        name = [NSString stringWithFormat:@"%@ %@", spots[2], spots[0]];
    if (!spots[1]) // address
        spots[1] = @"no data";
    [newItem setAddress:spots[1]];
    if (Se != -1 && Ep != -1) {
        [newItem setObject:[NSString stringWithFormat:@"Season %d, Episode %.2d", Se, Ep] forKey:@"Album"];
        name = [NSString stringWithFormat:@"%d-%.2d, %@", Se, Ep, name];
    }
    if (Se != -1)
        [newItem setSeason:Se];
    if (Ep != -1)
        [newItem setEpisode:Ep];
    [newItem setTitle:name];
    if (spots[0])
        [newItem setObject:spots[0] forKey:@"Full Name"];
    if (spots[3])
        [newItem setObject:spots[3] forKey:@"Product"];
    return newItem;
}

- (void)carryOn
{
    if (index+1 >= [mySequence count]) return;
    while (YES) {
        NSString *item = [mySequence objectAtIndex:++index];
        [self performAction:item];
        if (++index >= [mySequence count]) return;
        item = [mySequence objectAtIndex:index];
        if (![item isEqualToString:@"and"]) {
            index--;
            break;
        }
    }
}

- (void)dontCarryOn
{
    if (index+1 >= [mySequence count]) return;
    while (YES) {
        NSString *item = [mySequence objectAtIndex:++index];
        // find out how many to skip
        NSArray *actions = [NSArray arrayWithObjects:@"skip", @"scan", @"scanInt", @"scanTo:", nil];
        int i, n = [actions count];
        for (i=0;i<n;i++)
            if ([item hasPrefix:[actions objectAtIndex:i]]) {
                index++;
                break;
            }
        if (++index >= [mySequence count]) return;
        item = [mySequence objectAtIndex:index];
        if (![item isEqualToString:@"and"]) {
            index--;
            break;
        }
    }
}

- (void)putNumber:(int)x intoValue:(NSString *)value
{
    if ([value isEqualToString:@"Season"])
        Se = x;
    else if ([value isEqualToString:@"Episode"])
        Ep = x;
    else if ([value isEqualToString:@"Day"])
        Day = x;
    else if ([value isEqualToString:@"Year"])
        Year = x;
}

- (void)performAction:(NSString *)action
{
    NSArray *strings = [NSArray arrayWithObjects:@"Full Name", @"address", @"Date", @"Product", nil];
    NSString *buffer = nil;
    if ([action isEqualToString:@"skip"])
        [myScanner scanPastString:[mySequence objectAtIndex:++index]];
    else if ([action isEqualToString:@"scan"])
        [myScanner scanString:[mySequence objectAtIndex:++index] intoString:nil];
    else if ([action isEqualToString:@"scanInt"]) {
        int x = -1;
        [myScanner scanInt:&x];
        [self putNumber:x intoValue:[mySequence objectAtIndex:++index]];
    } else if ([action isEqualToString:@"Season"])
        [myScanner scanInt:&Se];
    else if ([action isEqualToString:@"Episode"])
        [myScanner scanInt:&Ep];
    else if ([action hasPrefix:@"scanTo:"]) {
        action = [action substringFromIndex:7];
        [myScanner scanUpToString:[mySequence objectAtIndex:++index] intoString:&buffer];
        int i = [strings indexOfObject:action];
        if (i == NSNotFound)
            if ([action isEqualToString:@"Month"])
                Month = buffer;
            else
                NSLog(@"Not Found!");
        else {
            if (i != 1) // address
                buffer = [tItem deHTML:buffer];
            spots[i] = buffer;
        }
    } else if ([action isEqualToString:@"return"])
        [myScanner setScanLocation:startLocation];
}

@end

@implementation tItem
- (id)init
{
    myTitle = myAddress = regulars = nil;
    children = [[NSMutableArray alloc] init];
    metadata = nil;
    amIExpandable = haveIDownloaded = NO;
    myEpisode = mySeason = -1;
    return [super init];
}

- (int)numberOfChildren
{   return [children count];   }
- (id)childAtIndex:(int)index
{   return [children objectAtIndex:index];     }
- (BOOL)expandable
{   return amIExpandable;      }
- (void)setExpandable:(BOOL)exp
{   amIExpandable = exp;    }
- (BOOL)expand
{
    if (!myAddress) return NO;
    if (haveIDownloaded) return YES;
    NSString *htmlString = [MHEPGTVT cacheString:myAddress];
    if (!htmlString) return NO;
    NSString *path = [[NSUserDefaults standardUserDefaults] objectForKey:@"EPGkeys"];
    if (!path)
        path = [[NSBundle mainBundle] pathForResource:@"EPGkeys" ofType:@"xml"];
    NSDictionary *keys = [NSDictionary dictionaryWithContentsOfFile:path];
    if (!keys) {
        NSLog(@"EPGkeys file mis-formed!");
        return NO;
    }
    keys = [[keys objectForKey:@"processSeries"] objectForKey:@"ifURLContains"];
    NSMutableArray *allKeys = [NSMutableArray arrayWithArray:[keys allKeys]];
    if ([allKeys containsObject:@"*"]) {
        [allKeys removeObject:@"*"];
        [allKeys addObject:@"*"];
    }
    int i;
    for (i=0;i<[allKeys count];i++) {
        NSString *aKey = [allKeys objectAtIndex:i];
        if ([myAddress rangeOfString:aKey options:NSCaseInsensitiveSearch].location != NSNotFound || [aKey isEqualToString:@"*"])
            return [self processSeries:[keys objectForKey:aKey]];
    }
    return NO;
}

- (BOOL)processSeries:(NSDictionary *)keys
{
    regulars = [[MHEPGTVT regulars] objectForKey:[self title]];
    if (!regulars)
        regulars = [self processRegulars:[keys objectForKey:@"regulars"]];
    NSString *htmlString = [MHEPGTVT cacheString:myAddress];
    //NSString *htmlString = [NSString stringWithContentsOfURL:[NSURL URLWithString:myAddress]];
    int strikes = 0, count = 0;
    //if (![htmlString hasPrefix:@"<"])
        //htmlString = [NSString stringWithContentsOfCURL:[NSURL URLWithString:myAddress]];
    if (!htmlString) return NO;
    NSScanner *scan = [NSScanner scannerWithString:htmlString];
    [scan setCaseSensitive:NO];
    NSArray *sequence = [keys objectForKey:@"sequence"];
    if (!sequence) return NO;
    NSString *epNumberFind = [keys objectForKey:@"epNumberFind"];
    NSString *epStartFind = [keys objectForKey:@"epStartFind"];
    NSString *seNumberFind = [keys objectForKey:@"seNumberFind"];
    NSMutableArray *seasonStarts = nil;
    if (seNumberFind) {
        seasonStarts = [NSMutableArray array];
        NSString *buffer = nil;
        count = 1;
        while (![scan isAtEnd]) {
            if ([seNumberFind rangeOfString:@"%@"].length) {
                NSString *ending;
                switch (count) {
                    case 1: ending = @"st"; break;
                    case 2: ending = @"nd"; break;
                    case 3: ending = @"rd"; break;
                    default: ending = @"th"; break;
                }
                buffer = [NSString stringWithFormat:seNumberFind, count, ending];
            } else
                buffer = [NSString stringWithFormat:seNumberFind, count];
            [scan scanPastString:buffer];
            [seasonStarts addObject:[NSNumber numberWithInt:[scan scanLocation]]];
            count++;
        }
        count = 0;
        [scan setScanLocation:0];
    }
    while (YES) {
        if (strikes > 5) break;
        if (epNumberFind) {
            [scan setScanLocation:0];
            NSString *target = [NSString stringWithFormat:epNumberFind, count];
            while (YES) {
                [scan scanPastString:target];
                char uch = [[scan string] characterAtIndex:[scan scanLocation]-[target length]-1];
                if (uch < '0' || uch > '9')
                    break;
            }
        } else if (epStartFind)
            [scan scanPastString:epStartFind];
        if ([scan isAtEnd])
            strikes++;
        else {
            strikes = 0;
            seriesScanner *ss = [[seriesScanner alloc] init];
            [ss setScanner:scan];
            tItem *newItem = [ss parseEntryWithArray:sequence];
            [ss release];
            NSString *addy = [newItem address];
            if (![addy isEqualToString:@"no data"])
                [newItem setAddress: [[NSURL URLWithString:addy relativeToURL:[NSURL URLWithString:myAddress]] absoluteString]];
            else
                addy = myAddress;
            [newItem setObject:[newItem address] forKey:@"Website"];
            if ([keys objectForKey:@"address"])
                [newItem setAddress:[NSString stringWithFormat:[keys objectForKey:@"address"], addy, [newItem episode]]];
            if (regulars)
                [newItem setObject:regulars forKey:@"Performers"];
            [newItem setObject:[self title] forKey:@"Author"];
            if (seasonStarts) {
                int i;
                for (i=0;i<[seasonStarts count];i++)
                    if ([[seasonStarts objectAtIndex:i] intValue] > [scan scanLocation])
                        [newItem setSeason:i];
            }
            [children addObject:newItem];
        }
        count++;
    }
    haveIDownloaded = YES;
    return YES;
}

- (NSString *)processRegulars:(NSDictionary *)dict
{
    NSString *htmlString = nil;
    int i;
    NSString *buffer;
    if (!dict) return nil;
    NSString *theAddy = nil;
    NSMutableString *output = nil;
    NSString *trim = [dict objectForKey:@"trimAddressEnd"];
    if (trim && [myAddress hasSuffix:trim]) {
        NSString *regAddress = [myAddress substringToIndex:[myAddress length]-[trim length]];
        theAddy = regAddress;
    } else
        theAddy = myAddress;
    htmlString = [MHEPGTVT cacheString:theAddy];
    if (!htmlString) return nil;
    NSArray *range = [dict objectForKey:@"range"];
    if (range) {
        NSRange aRange = [htmlString rangeOfString:[range objectAtIndex:0]];
        if (aRange.length)
            htmlString = [htmlString substringFromIndex:aRange.location];
        aRange = [htmlString rangeOfString:[range objectAtIndex:1]];
        if (aRange.length)
            htmlString = [htmlString substringToIndex:aRange.location];
    }
    NSScanner *scan = [NSScanner scannerWithString:htmlString];
    [scan setCaseSensitive:NO];
    while (![scan isAtEnd]) {
        buffer = nil;
        NSArray *skip = [dict objectForKey:@"skip"];
        if (skip)
            for (i=0;i<[skip count];i++)
                [scan scanPastString:[skip objectAtIndex:i]];
        NSString *end = [dict objectForKey:@"end"];
        if (end)
            [scan scanUpToString:end intoString:&buffer];
        if (buffer) {
            if (!output)
                output = [[NSMutableString alloc] init];
            if ([output length])
                [output appendString:@", "];
            [output appendString:buffer];
        }
    }
    return output;
}

- (void)setObject:(id)object forKey:(NSString *)key
{
    if (!metadata)
        metadata = [[NSMutableDictionary alloc] init];
    [metadata setObject:object forKey:key];
}

- (id)objectForKey:(NSString *)key
{
    return [metadata objectForKey:key];
}

- (NSString *)address
{   return myAddress;    }
- (NSString *)title
{   return myTitle;    }
- (NSString *)summary
{
    if (!haveIDownloaded)
        [self download];
    NSMutableString *output = [NSMutableString string];
    NSMutableArray *keys = [NSMutableArray arrayWithArray:[metadata allKeys]];
    if ([keys containsObject:@"Full Name"]) {
        [keys removeObject:@"Full Name"];
        [keys insertObject:@"Full Name" atIndex:0];
    }
    if ([keys containsObject:@"Information"]) {
        [keys removeObject:@"Information"];
        [keys addObject:@"Information"];
    }
    if ([keys containsObject:@"Description"]) {
        [keys removeObject:@"Description"];
        [keys addObject:@"Description"];
    }
    NSString *key, *value;
    int i, n = [keys count];
    for (i=0;i<n;i++) {
        key = [keys objectAtIndex:i];
        value = [metadata objectForKey:key];
        if ([key isEqualToString:@"Guest Stars"]) {
            if (regulars != nil && [regulars length] > 0) {
                if ([value length] > 0)
                    value = [NSString stringWithFormat:@"%@, %@", regulars, value];
                else
                    value = [regulars copy];
            }
            [metadata removeObjectForKey:key];
            key = @"Performers";
            [metadata setObject:value forKey:key];
        }
        if (key && value && [key isKindOfClass:[NSString class]] && [value isKindOfClass:[NSString class]])
            [output appendString:[NSString stringWithFormat:@"%@:\n%@\n\n",key,value]];
    }
    return output;
}

- (void)download
{
    if (!myAddress || [myAddress isEqualToString:@"no data"]) return;
    NSString *path = [[NSUserDefaults standardUserDefaults] objectForKey:@"EPGkeys"];
    if (!path)
        path = [[NSBundle mainBundle] pathForResource:@"EPGkeys" ofType:@"xml"];
    NSDictionary *keys = [NSDictionary dictionaryWithContentsOfFile:path];
    if (!keys) {
        NSLog(@"EPGkeys file mis-formed!");
        return;
    }
    keys = [[keys objectForKey:@"processEpisode"] objectForKey:@"ifURLContains"];
    NSMutableArray *allKeys = [NSMutableArray arrayWithArray:[keys allKeys]];
    if ([allKeys containsObject:@"*"]) {
        [allKeys removeObject:@"*"];
        [allKeys addObject:@"*"];
    }
    int i;
    for (i=0;i<[allKeys count];i++) {
        NSString *aKey = [allKeys objectAtIndex:i];
        if ([myAddress rangeOfString:aKey].length || [aKey isEqualToString:@"*"]) {
            NSDictionary *dict = [keys objectForKey:aKey];
            NSDictionary *link = [dict objectForKey:@"link"];
            if (link) {
                NSString *htmlString = [MHEPGTVT cacheString:myAddress];
                NSScanner *scan = [NSScanner scannerWithString:htmlString];
                NSArray *skip = [link objectForKey:@"skip"];
                int j;
                for (j=0;j<[skip count];j++)
                    [scan scanPastString:[skip objectAtIndex:j]];
                NSString *address = nil;
                [scan scanUpToString:[link objectForKey:@"end"] intoString:&address];
                if (address)
                    address = [NSString stringWithFormat:[link objectForKey:@"address"], address];
                if (address)
                    myAddress = address;
                dict = [keys objectForKey:[link objectForKey:@"goto"]];
            }
            [self processEpisode:dict];
            haveIDownloaded = YES;
            break;
        }
    }
}

- (void)processEpisode:(NSDictionary *)keys
{
    NSString *regs = [self processRegulars:[keys objectForKey:@"regulars"]];
    if (regs) {
        NSString *perfs = [metadata objectForKey:@"Performers"];
        if (perfs && ![perfs isEqualToString:regs])
            regs = [NSString stringWithFormat:@"%@, %@", perfs, regs];
        [metadata setObject:regs forKey:@"Performers"];
    }
    int i, n;
    NSString *buffer = nil;
    NSString *htmlString = [MHEPGTVT cacheString:myAddress];
    if (!htmlString) return;
    NSScanner *scan = [NSScanner scannerWithString:htmlString];
    [scan setCaseSensitive:NO];
    /*
    NSDictionary *link = [keys objectForKey:@"link"];
    if (link) {
        buffer = nil;
        NSArray *skip = [link objectForKey:@"skip"];
        NSString *end = [link objectForKey:@"end"];
        n = [skip count];
        for (i=0;i<n;i++)
            [scan scanPastString:[skip objectAtIndex:i]];
        if (end)
            [scan scanUpToString:end intoString:&buffer];
        if (buffer) {
            NSString *newAddress = [link objectForKey:@"address"];
            if (newAddress)
                buffer = [NSString stringWithFormat:newAddress, buffer];
            myAddress = buffer;
            htmlString = [MHEPGTVT cacheString:myAddress];
        }
    }
    */
    NSArray *pieces = [myAddress componentsSeparatedByString:@"#"];
    NSRange r;
    if ([pieces count] == 2) {
        scan = [NSScanner scannerWithString:htmlString];
        [scan setCaseSensitive:NO];
        while (![scan isAtEnd]) {
            [scan scanPastString:@"<a"];
            [scan scanUpToString:@">" intoString:&buffer];
            r = [buffer rangeOfString:@"name" options:NSCaseInsensitiveSearch];
            if (r.length) {
                r = [buffer rangeOfString:[pieces objectAtIndex:1]];
                if (r.length) {
                    htmlString = [htmlString substringFromIndex:[scan scanLocation]];
                    break;
                }
            }
        }
        if ([scan isAtEnd]) { // didn't find our # link!  might be a typo in the <a name=> link.  look for the name
            NSString *name = [metadata objectForKey:@"Full Name"];
            if (name) {
                [scan setScanLocation:0];
                [scan scanUpToString:name intoString:nil];
                if (![scan isAtEnd]) {
                    int location = [scan scanLocation];
                    if (location > 5)
                        location -= 4; // go back before the last >
                    htmlString = [htmlString substringFromIndex:location];
                }
            }
        }
        if (![scan isAtEnd] && [[keys objectForKey:@"name2name"] boolValue]) {
            scan = [NSScanner scannerWithString:htmlString];
            [scan setCaseSensitive:NO];
            while (![scan isAtEnd]) {
                [scan scanUpToString:@"<a" intoString:nil];
                unsigned loc = [scan scanLocation];
                [scan scanUpToString:@">" intoString:&buffer];
                r = [buffer rangeOfString:@"name" options:NSCaseInsensitiveSearch];
                if (r.length) {
                    int letter = r.location + r.length;
                    char ch = [buffer characterAtIndex:letter];
                    while ((' ' == ch || '\t' == ch) && letter < [buffer length] - 1)
                        ch = [buffer characterAtIndex:++letter];
                    if ('=' == ch) {
                        ch = [buffer characterAtIndex:r.location - 1];
                        if ([[NSCharacterSet whitespaceAndNewlineCharacterSet] characterIsMember:ch]) {
                            htmlString = [htmlString substringToIndex:loc];
                            break;
                        }
                    }
                }
            }
        }
    }
    if ([keys objectForKey:@"trimAfter"]) {
        r = [htmlString rangeOfString:[keys objectForKey:@"trimAfter"] options:NSBackwardsSearch];
        if (r.length)
            htmlString = [htmlString substringToIndex:r.location];
    }
    scan = [NSScanner scannerWithString:htmlString];
    [scan setCaseSensitive:NO];
    NSArray *sequence = [keys objectForKey:@"sequence"];
    n = [sequence count];
    for (i=0;i<n;i++) {
        NSString *test = [[scan string] substringFromIndex:[scan scanLocation]];
        NSString *item = [sequence objectAtIndex:i];
        if ([item isEqualToString:@"skip"])
            [scan scanPastString:[sequence objectAtIndex:++i]];
        else if ([item hasPrefix:@"scanTo:"]) {
            item = [item substringFromIndex:7];
            NSString *buffer = nil;
            test = [sequence objectAtIndex:i+1];
            [scan scanUpToString:[sequence objectAtIndex:++i] intoString:&buffer];
            buffer = [tItem deHTML:buffer];
            if (buffer && [buffer length]) {
                if ([item isEqualToString:@"Performers"])
                    buffer = [self fixPerfs:buffer];
                if ([item isEqualToString:@"Product"] && [buffer hasPrefix:@"____"])
                    buffer = nil;
                NSString *oldValue = [metadata objectForKey:item];
                if (oldValue && buffer && ![oldValue isEqualToString:buffer])
                    buffer = [NSString stringWithFormat:@"%@, %@", oldValue, buffer];
                if ([buffer length])
                    [metadata setObject:buffer forKey:item];
            }
        } else if ([item isEqualToString:@"return"])
            [scan setScanLocation:0];
        else if ([item isEqualToString:@"newCap"]) {
            NSString *string = [scan string];
            NSString *newCap = [sequence objectAtIndex:++i];
            NSRange r = [string rangeOfString:newCap];
            unsigned loc = [scan scanLocation];
            if (r.location != NSNotFound) {
                string = [string substringToIndex:r.location];
                if (loc >= [string length])
                    loc = [string length] - 1;
                scan = [NSScanner scannerWithString:string];
                [scan setCaseSensitive:NO];
                [scan setScanLocation:loc];
            }
        }
    }
}

+ (NSString *)deHTML:(NSString *)input
{
    if (!input || ![input length]) return @"";
    NSScanner *scan = [NSScanner scannerWithString:input];
    NSMutableString *result = [NSMutableString string];
    [result setString:@""];
    NSString *buffer = nil;
    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 = [buffer lowercaseString];
        if ([buffer isEqualToString:@"<br"] || [buffer isEqualToString:@"<p"])
            [result appendString:@"\n"];
        else
            [result appendString:@" "];
    }
    scan = [NSScanner scannerWithString: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];
        buffer = [MHUtil getEscapeChar:buffer];
        if (buffer)
            [result appendString:buffer];
    }
    scan = [NSScanner scannerWithString:result];
    result = [NSMutableString string];
    [result setString:@""];
    NSCharacterSet *set = [NSCharacterSet whitespaceCharacterSet];
    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];
    }
    [result deleteCharactersInRange:NSMakeRange([result length]-1, 1)];
    
    if ([result hasPrefix:@"\""] && [result hasSuffix:@"\""]) {
        [result deleteCharactersInRange:NSMakeRange(0, 1)];
        [result deleteCharactersInRange:NSMakeRange([result length]-1, 1)];
    }
    if ([result characterAtIndex:[result length]-2] == ' ')
        [result deleteCharactersInRange:NSMakeRange([result length]-2, 2)];
    if ([result hasSuffix:@" pc"])
        [result deleteCharactersInRange:NSMakeRange([result length]-3, 3)];
    int length = [result length];
    if (length <= 0)
        return @"";
    return [result whitespaceTrimmed];
}

- (NSString *)fixPerfs:(NSString *)input
{
    if (!input || ![input length]) return @"";
	NSArray *bad = [NSArray arrayWithObjects:@"Recurring Role:\n", @"Guest Star:\n", nil];
    NSScanner *scan = [NSScanner scannerWithString:input];
    NSMutableString *output = [NSMutableString string];
    int nestCount = 0;
    while (![scan isAtEnd]) {
        NSString *buffer = [[scan string] substringFromIndex:[scan scanLocation]];
        [scan scanUpToCharactersFromSet:[NSCharacterSet characterSetWithCharactersInString:@"()"] intoString:&buffer];
        if (buffer && 0 == nestCount) {
            if ([output length] && ![buffer hasPrefix:@","])
                [output appendString:@", "];
            buffer = [buffer stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
            [output appendString:buffer];
        }
        if ([scan scanString:@"(" intoString:nil])
            nestCount++;
        else if ([scan scanString:@")" intoString:nil])
            nestCount--;
    }
	int i, n = [bad count];
	for (i=0;i<n;i++) {
		[output replaceOccurrencesOfString:[bad objectAtIndex:i] withString:@"" options:0 range:NSMakeRange(0,[output length]-1)];
	}
    return output;
}

- (NSDictionary *)metadata
{
    if (!haveIDownloaded)
        [self download];
    return metadata;
}
- (void)setTitle:(NSString *)title
{
    [myTitle release];
    myTitle = [title retain];
}
- (void)setAddress:(NSString *)address
{
    [myAddress release];
    myAddress = [address retain];
}

- (int)episode  {   return myEpisode;   }
- (int)season   {   return mySeason;    }
- (void)setEpisode:(int)ep
{
    BOOL toChange = (-1 == myEpisode && -1 != mySeason);
    myEpisode = ep;
    if (toChange) {
        myTitle = [[NSString stringWithFormat:@"%d-%.2d, %@", mySeason, myEpisode, myTitle] retain];
        [metadata setObject:[NSString stringWithFormat:@"Season %d, Episode %.2d", mySeason, myEpisode] forKey:@"Album"];
    }
}
- (void)setSeason:(int)se
{
    BOOL toChange = (-1 == mySeason && -1 != myEpisode);
    mySeason = se;
    if (toChange) {
        myTitle = [[NSString stringWithFormat:@"%d-%.2d, %@", mySeason, myEpisode, myTitle] retain];
        [metadata setObject:[NSString stringWithFormat:@"Season %d, Episode %.2d", mySeason, myEpisode] forKey:@"Album"];
    }
}

@end

@implementation MHEPGTVT

NSDictionary *definedRegulars = nil;

+ (void)setRegulars:(NSDictionary *)regs
{
    definedRegulars = regs;
}

+ (NSDictionary *)regulars
{
    return definedRegulars;
}

- (void)setDelegate:(id)del
{
    myDelegate = del;
}

- (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item
{
    //NSLog(@"outlineView:child:ofItem:");
    if (item == nil)
        return [seriesArray objectAtIndex:index];
    return [item childAtIndex:index];
}

- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
{
    return [item expandable];
}

- (int)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
{
    if (item == nil)
        return [seriesArray count];
    return [item numberOfChildren];
}

- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
{
    if (item == nil)
        return @"/";
    return [item title];
}

- (BOOL)outlineView:(NSOutlineView *)outlineView shouldEditTableColumn:(NSTableColumn *)tableColumn item:(id)item
{
    return NO;
}


- (BOOL)outlineView:(NSOutlineView *)outlineView shouldSelectItem:(id)item
{
    //NSLog(@"outlineView:shouldSelectItem:");
    if (linkButton)
        [linkButton setTitle:[(tItem *)item address]];
    if (epgText)
        [epgText setStringValue:@""];
    if (DEBUG)
        [self selectItem:item];
    else
        [NSThread detachNewThreadSelector:@selector(selectItem:) toTarget:self withObject:item];
    /*
    NSString *summary = [item summary];
    if (summary != nil)
        [epgText setStringValue:summary];
    else
        [epgText setStringValue:@"no data"];
        */
    return YES;
}

- (void)selectItem:(id)item
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    NSString *summary = [item summary];
    if (!summary)
        summary = @"no data";
    [epgText performSelectorOnMainThread:@selector(setStringValue:) withObject:summary waitUntilDone:NO];
    [pool release];
}

- (BOOL)outlineView:(NSOutlineView *)outlineView shouldExpandItem:(id)item
{
    //NSLog(@"outlineView:shouldExpandItem:");
    // expandingLock will be locked if and only if the expandItem method is the one calling for this expand.  so if it's locked, return YES.  but since the only way to check if it's locked involves locking it, unlock afterwards.
    if (![expandingLock tryLock]) return YES;
    [expandingLock unlock];
    if (DEBUG)
        [self expandItem:item];
    else
        [NSThread detachNewThreadSelector:@selector(expandItem:) toTarget:self withObject:item];
    /*
    if (item != nil)
        if (![item expand])
            [epgText setStringValue:@"error.  check internet connection"];
    */
    return NO;
}

- (void)expandItem:(id)item
{
    if (!item) return;
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    NSString *address = [(tItem *)item address];
    if (epgText && address)
        [epgText performSelectorOnMainThread:@selector(setStringValue:) withObject:[NSString stringWithFormat:@"downloading from %@...", address] waitUntilDone:NO];
    //if (linkButton && address)
        //[linkButton performSelectorOnMainThread:@selector(setTitle:) withObject:address waitUntilDone:NO];
    if (![item expand])
        //[epgText setStringValue:@"error.  check internet connection"];
        [epgText performSelectorOnMainThread:@selector(setStringValue:) withObject:[NSString stringWithFormat:@"error: no data found at %@ -- check internet connection", address] waitUntilDone:NO];
    else
        [epgText performSelectorOnMainThread:@selector(setStringValue:) withObject:@"" waitUntilDone:NO];
    [expandingLock tryLock];
    [theOutline expandItem:item];
    [expandingLock unlock];
    [pool release];
}

- (void)outlineView:(NSOutlineView *)view rightClickWithEvent:(NSEvent *)anEvent
{
    NSPoint point = [anEvent locationInWindow];
    point = [view convertPoint:point fromView:nil];
    id item = [view itemAtRow:[view rowAtPoint:point]];
    if (!item) return;
    if (![item expandable] || ![view isItemExpanded:item]) return;
    NSMenu *menu = [[[NSMenu alloc] init] autorelease];
    NSMenuItem *mItem = [[[NSMenuItem alloc] init] autorelease];
    [mItem setTitle:@"Auto-Complete Series"];
    [mItem setTarget:self];
    [mItem setAction:@selector(wholeSeries:)];
    [menu addItem:mItem];
    [view selectRow:[view rowAtPoint:point] byExtendingSelection:NO];
    [NSMenu popUpContextMenu:menu withEvent:anEvent forView:nil];
}

- (void)wholeSeries:(id)sender
{
    if ([myDelegate respondsToSelector:@selector(guessWholeSeries:)])
        [myDelegate guessWholeSeries:self];
}

- (NSDictionary *)guessOneMovie:(NSString *)name
{
    id item = [theOutline itemAtRow:[theOutline selectedRow]];
    int i, n = [item numberOfChildren];
    for (i=0;i<n;i++) {
        id child = [item childAtIndex:i];
        NSString *title = [child title];
        NSScanner *scan = [NSScanner scannerWithString:title];
        [scan setCharactersToBeSkipped:[[[NSCharacterSet alloc] init] autorelease]];
        int anInt;
        [scan scanInt:&anInt];
        [scan scanString:@"-" intoString:nil];
        [scan scanInt:&anInt];
        [scan scanString:@", " intoString:nil];
        
        [scan scanInt:&anInt];
        NSString *buffer = nil;
        if ([scan scanString:@" " intoString:nil])
            buffer = @" ";
        else if ([scan scanString:@"-" intoString:nil])
            buffer = @"-";
        if (buffer) {
            [scan scanUpToString:buffer intoString:nil];
            [scan scanString:buffer intoString:nil];
            [scan scanInt:&anInt];
        }
        [scan scanString:@" " intoString:nil];
        title = [title substringFromIndex:[scan scanLocation]];
        if ([name rangeOfString:title options:NSCaseInsensitiveSearch].location != NSNotFound) {
            buffer = [child summary];
            return [child metadata];
        }
    }
    return nil;
}

- (void) typeAheadString:(NSString *)inString inOutlineView:(NSOutlineView *)inOutlineView
{
    //NSLog(@"typeAhead");
}

- (void) willClearTypeAheadString:(NSString *)inString inOutlineView:(NSOutlineView *)inOutlineView
{
    if (!inString || ![inString length]) return;
    int i, n = [inOutlineView numberOfRows];
    for (i=0;i<n;i++) {
        NSString *title = [[inOutlineView itemAtRow:i] title];
        if ([title rangeOfString:inString options:NSCaseInsensitiveSearch].location != NSNotFound) {
            [self outlineView:inOutlineView shouldSelectItem:[inOutlineView itemAtRow:i]];
            [inOutlineView selectRow:i byExtendingSelection:NO];
            [inOutlineView scrollRowToVisible:i];
            return;
        }
    }
}

- (BOOL)search:(NSString *)searchString
{
    if (nil == searchString || [searchString isEqualToString:@""]) return NO;
    if (DEBUG)
        [self runSearch:searchString];
    else
        [NSThread detachNewThreadSelector:@selector(runSearch:) toTarget:self withObject:searchString];
    return YES;
}

- (void)runSearch:(NSString *)searchString
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    NSString *htmlString = nil;
    searchString = [MHUtil spaceToPlus:[searchString lowercaseString]];
    
    // cache stuff, get search results
    htmlString = [self cacheString:searchString];
    
    if (!htmlString || ![htmlString length]) {
        //[epgText setStringValue:@"error -- check internet connection"];
        [epgText performSelectorOnMainThread:@selector(setStringValue:) withObject:@"error: no data found at search URL -- check internet connection" waitUntilDone:NO];
        [pool release];
        return;
    }
    NSString *path = [[NSUserDefaults standardUserDefaults] objectForKey:@"EPGkeys"];
    if (!path)
        path = [[NSBundle mainBundle] pathForResource:@"EPGkeys" ofType:@"xml"];
    NSDictionary *keys = [NSDictionary dictionaryWithContentsOfFile:path];
    if (!keys) NSLog(@"EPGkeys file mis-formed!");
    if ([keys objectForKey:[searchString substringToIndex:3]])
        keys = [keys objectForKey:[searchString substringToIndex:3]];
    NSString *confirm = [keys objectForKey:@"confirmHit"];
    if (confirm && [htmlString rangeOfString:confirm options:NSCaseInsensitiveSearch].location != NSNotFound) {
        tItem *item = [self parseHit:htmlString withKeys:[keys objectForKey:@"processHit"]];
        if (item) {
            if (!seriesArray)
                seriesArray = [[NSMutableArray alloc] init];
            [seriesArray addObject:item];
        }
    } else {
        // display results
        keys = [keys objectForKey:@"processResults"];
        NSString *end = [keys objectForKey:@"end"];
        if (end) {
            NSRange r = [htmlString rangeOfString:end];
            if (r.location != NSNotFound)
                htmlString = [htmlString substringToIndex:r.location];
        }
        NSScanner *scan = [NSScanner scannerWithString:htmlString];
        [scan setCaseSensitive:NO];
        NSArray *skip = [keys objectForKey:@"skip"];
        NSString *address, *title;
        int i, n = [skip count];
        for (i=0;i<n;i++)
            [scan scanPastString:[skip objectAtIndex:i]];
        while (![scan isAtEnd]) {
            address = title = nil;
            // address
            NSDictionary *dict = [keys objectForKey:@"link"];
            skip = [dict objectForKey:@"skip"];
            n = [skip count];
            for (i=0;i<n;i++)
                [scan scanPastString:[skip objectAtIndex:i]];
            [scan scanUpToString:[dict objectForKey:@"end"] intoString:&address];
            if (address && [dict objectForKey:@"address"])
                address = [NSString stringWithFormat:[dict objectForKey:@"address"], address];
            // title
            dict = [keys objectForKey:@"title"];
            skip = [dict objectForKey:@"skip"];
            n = [skip count];
            for (i=0;i<n;i++)
                [scan scanPastString:[skip objectAtIndex:i]];
            [scan scanUpToString:[dict objectForKey:@"end"] intoString:&title];
            NSString *not = [dict objectForKey:@"notStart"];
            if (not && [title hasPrefix:not])
                title = nil;
            not = [dict objectForKey:@"notEnd"];
            if (not && [title hasSuffix:not])
                title = nil;
            NSString *trim = [dict objectForKey:@"trimEnd"];
            if (trim && [title hasSuffix:trim])
                title = [title substringToIndex:[title length]-[trim length]];
            if (title && [title rangeOfString:@"("].location != NSNotFound)
                title = [title substringToIndex:[title rangeOfString:@"("].location];
            if (title)
                title = [MHUtil fixIMDB:title];
            if (title && address) {
                tItem *item = [[[tItem alloc] init] autorelease];
                [item setTitle:title];
                [item setAddress:address];
                [item setExpandable:YES];
                if (!seriesArray)
                    seriesArray = [[NSMutableArray alloc] init];
                [seriesArray addObject:item];
            }
        }
    }
    //[epgText setStringValue:@""];
    [epgText performSelectorOnMainThread:@selector(setStringValue:) withObject:@"" waitUntilDone:NO];
    [theOutline reloadData];
    [pool release];
}

- (id)parseHit:(NSString *)htmlString withKeys:(NSDictionary *)keys
{
    if (!htmlString || ![htmlString length] || !keys) return nil;
    NSScanner *scan = [NSScanner scannerWithString:htmlString];
    [scan setCaseSensitive:NO];
    NSDictionary *dict = nil;
    // title
    NSString *title = nil;
    dict = [keys objectForKey:@"title"];
    NSArray *skip = [dict objectForKey:@"skip"];
    int i, n = [skip count];
    for (i=0;i<n;i++)
        [scan scanPastString:[skip objectAtIndex:i]];
    [scan scanUpToString:[dict objectForKey:@"end"] intoString:&title];
    NSString *trim = [dict objectForKey:@"trimAfter"];
    if (trim) {
        NSRange r = [title rangeOfString:trim options:NSCaseInsensitiveSearch];
        if (r.length && [trim length])
            title = [title substringToIndex:r.location];
    }
    // address
    NSString *address = nil;
    [scan setScanLocation:0];
    dict = [keys objectForKey:@"link"];
    skip = [dict objectForKey:@"skip"];
    n = [skip count];
    for (i=0;i<n;i++)
        [scan scanPastString:[skip objectAtIndex:i]];
    [scan scanUpToString:[dict objectForKey:@"end"] intoString:&address];
    if ([dict objectForKey:@"address"] && address)
        address = [NSString stringWithFormat:[dict objectForKey:@"address"], address];
    // item
    tItem *item = nil;
    if (title && address) {
        item = [[[tItem alloc] init] autorelease];
        [item setTitle:title];
        [item setAddress:address];
        [item setExpandable:YES];
    }
    return item;
}

- (void)removeSeries
{
    [seriesArray release];
    seriesArray = nil;
}

- (MHEPGTVT *)init
{
    epgText = nil;
    theOutline = nil;
    myDelegate = nil;
    seriesArray = nil;
    linkButton = nil;
    images = NO;
    expandingLock = [[NSLock alloc] init];
    self = [super init];
    return self;
}

- (void)setTextField:(NSTextField *)textField
{
    epgText = textField;
}

- (void)setOutlineView:(NSOutlineView *)outlineView
{
    theOutline = outlineView;
}

- (void)setDownloadsImages:(BOOL)flag
{
    images = flag;
}

- (void)setLinkButton:(NSButton *)button
{
    linkButton = button;
}

+ (NSString *)cacheString:(NSString *)input
{
    id epg = [[MHEPGTVT alloc] init];
    NSString *htmlString = [epg cacheString:input];
    [epg release];
    return htmlString;
}

- (NSString *)cacheString:(NSString *)input
{
    NSUserDefaults *defs = [NSUserDefaults standardUserDefaults];
    NSMutableDictionary *cache = [[NSMutableDictionary alloc] initWithCapacity:100];
    NSString *path = [defs objectForKey:@"cacheLocation"];
    NSFileManager *fm = [NSFileManager defaultManager];
    BOOL isDir;
    if (![fm fileExistsAtPath:[path stringByDeletingLastPathComponent] isDirectory:&isDir] || !isDir) {
        path = [NSHomeDirectory() stringByAppendingPathComponent:@"Application Support/Metahoot Download Cache.xml"];
        if (![fm fileExistsAtPath:[path stringByDeletingLastPathComponent] isDirectory:&isDir] || !isDir) {
            path = [NSHomeDirectory() stringByAppendingPathComponent:@"Metahoot Download Cache.xml"];
        }
    }
    NSDictionary *fileCache = [NSDictionary dictionaryWithContentsOfFile:path];
    if (fileCache)
        [cache addEntriesFromDictionary:fileCache];
    
    NSString *htmlString = nil;
    NSString *address = [[input componentsSeparatedByString:@"#"] objectAtIndex:0];
    htmlString = [cache objectForKey:address];
    if (nil == htmlString) {
        NSString *keyspath = [[NSUserDefaults standardUserDefaults] objectForKey:@"EPGkeys"];
        if (!keyspath)
            keyspath = [[NSBundle mainBundle] pathForResource:@"EPGkeys" ofType:@"xml"];
        NSDictionary *keys = [NSDictionary dictionaryWithContentsOfFile:keyspath];
        if (!keys) {
            NSLog(@"EPGkeys file mis-formed!");
            return nil;
        }
        if ([keys objectForKey:[input substringToIndex:3]]) { // EPG or TVT
            keys = [keys objectForKey:[input substringToIndex:3]];
			address = [keys objectForKey:@"searchAddress"];
            address = [NSString stringWithFormat:address, [input substringFromIndex:4]];
        }
        if (epgText)
            [epgText performSelectorOnMainThread:@selector(setStringValue:) withObject:[NSString stringWithFormat:@"Downloading from %@...", address] waitUntilDone:NO];
        if (linkButton)
            [linkButton performSelectorOnMainThread:@selector(setTitle:) withObject:address waitUntilDone:NO];
        htmlString = [NSString stringWithContentsOfURL:[NSURL URLWithString:address]];
        if (!htmlString || ![[htmlString whitespaceTrimmed] hasPrefix:@"<"])
            htmlString = [NSString stringWithContentsOfCURL:[NSURL URLWithString:address]];
        if (epgText)
            [epgText performSelectorOnMainThread:@selector(setStringValue:) withObject:@"" waitUntilDone:NO];
        if (htmlString) {
            [[NSNotificationCenter defaultCenter] postNotificationName:@"mhWebPageDownloadedNotification" object:self userInfo:[NSDictionary dictionaryWithObjectsAndKeys:address, @"link", htmlString, @"string", nil]];
            if ([input hasPrefix:@"epg:"] || [input hasPrefix:@"tvt:"])
                address = input;
            [cache setObject:htmlString forKey:address];
            [cache writeToFile:path atomically:YES];
        }
    }
    if (epgText)
        [epgText performSelectorOnMainThread:@selector(setStringValue:) withObject:@"" waitUntilDone:NO];
    [cache release];
    return htmlString;
}
/*
- (void)displayEPGresults:(NSString *)input
{
    //NSLog(@"tWWW displayEPGresults");
    BOOL success;
    if (input == nil) {
        NSBeep();
        return;
    }
    NSString *buffer, *address;
    tSeriesObject *aResult;
    NSScanner *scan = [[NSScanner alloc] initWithString:input];
    [scan setCaseSensitive:NO];
    while (![scan isAtEnd]) {
        buffer = address = nil;
        [scan scanUpToString:@"/?u=" intoString:nil];
        [scan scanUpToString:@"http%3A//" intoString:nil];
        [scan scanString:@"http%3A//" intoString:nil];
        [scan scanUpToString:@"\"" intoString:&address];
        if (address != nil) {
            address = [NSString stringWithFormat:@"http://%@",address];
            [scan scanUpToString:@"STRONG>" intoString:nil];
            [scan scanString:@"STRONG>" intoString:nil];
            [scan scanUpToString:@"</STRONG>" intoString:&buffer];
            if (buffer != nil) {
                buffer = [MHUtil cleanHTML:buffer];
                if (![buffer hasPrefix:@"epguides"]) {
                    aResult = [[tSeriesObject alloc] initWithTitle:buffer address:address];
                    success = [[tSeriesObject rootNode] addSeries:aResult];
                }
            }
        }
    }
    if (nil != theOutline) {
        [theOutline expandItem:[tSeriesObject rootNode]];
        [theOutline reloadData];
    }
}

- (void)displayTVTresults:(NSString *)htmlString
{
    tSeriesObject *aResult;
    BOOL success;
    NSRange aRange = [htmlString rangeOfString:@"Didn't find what you were looking for?" options:NSCaseInsensitiveSearch];
    if (aRange.location != NSNotFound)
        htmlString = [htmlString substringToIndex:aRange.location];
    NSScanner *scan = [NSScanner scannerWithString:htmlString];
    NSString *buffer, *address;
    [scan scanUpToString:@"Show search for:" intoString:nil];
    [scan setCaseSensitive:NO];
    while (![scan isAtEnd]) {
        buffer = address = nil;
        [scan scanUpToString:@"<a href=\"" intoString:nil];
        [scan scanString:@"<a href=\"" intoString:nil];
        [scan scanUpToString:@"\"" intoString:&address];
        [scan scanUpToString:@">" intoString:nil];
        [scan scanString:@">" intoString:nil];
        [scan scanUpToString:@"</a>" intoString:&buffer];
        if (buffer != nil && address != nil) {
            address = [NSString stringWithFormat:@"http://www.tvtome.com%@eplist.html", address];
            aResult = [[tSeriesObject alloc] initWithTitle:buffer address:address];
            success = [[tSeriesObject rootNode] addSeries:aResult];
        }
    }
    [theOutline expandItem:[tSeriesObject rootNode]];
    [theOutline reloadData];
}
*/

@end
