//
//  tSeriesObject.m
//  Hootenanny
//
//  Created by Noah Sorscher on Tue Oct 29 2002.
//  Copyright (c) 2002 __MyCompanyName__. All rights reserved.
//

#import "tSeriesObject.h"


@implementation tSeriesObject

NSTextField *theTextField = nil;

static tSeriesObject *rootNode = nil;
//static tWWW *theWWW;

BOOL images = NO;

+ (id)rootNode
{
    //NSLog(@"rootNode");
    if (rootNode == nil)
        rootNode = [[tSeriesObject alloc] initWithTitle:@"Search Results" address:@"tRootNode"];
    return rootNode;
}

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

+ (void)setTextField:(NSTextField *)field
{
    theTextField = field;
}

- (id)initWithTitle:(NSString *)title address:(NSString *)address
{
    //NSLog(@"tSeriesObject initWithTitle:%@ address: %@", title, address);
    NSRange aRange;
    if (title == nil || address == nil) return nil;
    if (self = [super init]) {
        aRange = [title rangeOfString:@"(" options:NSBackwardsSearch];
        if (aRange.location != NSNotFound)
            displayString = [[title substringToIndex:aRange.location] copy];
        else
            displayString = [title copy];
        displayString = [[displayString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] retain];
        seriesName = nil;
        webAddress = [address copy];
        children = nil;
        metadata = nil;
        isLeaf = dl = NO;
        regulars = nil;
        coverImage = nil;
        epg = [[MHEPGTVT alloc] init];
        [epg setTextField:theTextField];
    }
    return self;
}

- (id)childAtIndex:(int)index
{
    //NSLog(@"tSeriesObject childAtIndex:");
    if (children == nil || index < 0 || index >= [children count])
        return self;
    return [children objectAtIndex:index];
}

- (BOOL)isLeafNode
{
    //NSLog(@"isLeafNode");
    return isLeaf;
}

- (int)numberOfChildren
{
    //NSLog(@"numberOfChildren");
    if (children == nil)
        return 0;
    int num = [children count];
    return num;
}

- (BOOL)removeChildren
{
    //NSLog(@"removeChildren");
    children = nil;
    return YES;
}

- (NSString *)title
{
    //NSLog(@"title: %@", displayString);
    return displayString;
}

- (NSString *)address
{
    return webAddress;
}

- (BOOL)addSeries:(tSeriesObject *)item
{
    //NSLog(@"addSeries");
    if (self != rootNode) return NO;
    if (item == nil) return NO;
    if (children == nil) children = [[NSMutableArray alloc] init];
    [children addObject:item];
    return YES;
}

- (BOOL)expand
{
    //NSLog(@"tSeriesObject expand");
    if (webAddress == nil) return NO;
    if ([webAddress isEqualToString:@"tRootNode"]) return YES;
    //NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    NSString *htmlString = [epg cacheString:webAddress];
    if (htmlString == nil) return NO;
    if (dl) return YES;
    
    NSMutableDictionary *aDict;
    NSString *address = [[webAddress componentsSeparatedByString:@"#"] objectAtIndex:0];
    NSRange aRange = [webAddress rangeOfString:@"guide.shtml" options:NSCaseInsensitiveSearch];
    if ([address hasPrefix:@"http://www.tvtome.com"]) aDict = [self parseTVTEpisodes:htmlString baseAddress:address];
    else if (aRange.location != NSNotFound) aDict = [self parseEPGGuide:htmlString baseAddress:address];
    else aDict = [self parseEPGEpisodes:htmlString baseAddress:address];
    NSArray *episodes = [aDict objectForKey:@"episodes"];
    regulars = [aDict objectForKey:@"regulars"];
    NSString *title;
    NSDictionary *data = nil;
    BOOL ldl;
    if (episodes == nil) return YES;
    tSeriesObject *newTSO;
    int i, n = [episodes count];
    for (i=0;i<n;i++) {
        address = [[episodes objectAtIndex:i] objectForKey:@"address"];
        title = [[episodes objectAtIndex:i] objectForKey:@"title"];
        data = [[episodes objectAtIndex:i] objectForKey:@"data"];
        ldl = [[[episodes objectAtIndex:i] objectForKey:@"dl"] boolValue];
        newTSO = [[tSeriesObject alloc] initLeafWithTitle:title address:address parent:self];
        if (data != nil) {
            [newTSO addMetadata:data];
        }
        if (children == nil)
            children = [[NSMutableArray alloc] init];
        [children addObject:newTSO];
    }
    dl = YES;
    //[pool release];
    return YES;
}

- (NSString *)regulars
{
    //NSLog(@"regulars");
    return regulars;
}

// Leaf Node Stuff

- (id)initLeafWithTitle:(NSString *)title address:(NSString *)address parent:(tSeriesObject *)aParent
{
    //NSLog(@"tSeriesObject initLeafWithTitle:address:parent:");
    if (self = [super init]) {
        parent = aParent;
        displayString = title;
        seriesName = nil;
        webAddress = address;
        children = nil;
        metadata = [[NSMutableDictionary alloc] initWithCapacity:25];
        isLeaf = YES;
        dl = NO;
        regulars = [parent regulars];
        epg = [[MHEPGTVT alloc] init];
        [epg setTextField:theTextField];
        //[metadata writeToFile:@"metadata.plist" atomically:YES];
    }
    return self;
}

- (void)setObject:(id)object forKey:(id)key
{
    //NSLog(@"tSeriesObject setObject:forKey:");
    if (metadata != nil)
        [metadata setObject:object forKey:key];
}

- (BOOL)addMetadata:(NSDictionary *)input
{
    //NSLog(@"addMetadata");
    if (input == nil || ![input isKindOfClass:[NSDictionary class]]) return NO;
    if (metadata == nil)
        metadata = [[NSMutableDictionary alloc] initWithCapacity:25];
    [metadata addEntriesFromDictionary:input];
    return YES;
}

- (NSMutableDictionary *)readTVTURL:(NSString *)inAddress
{
    //NSLog(@"tSeriesObject readTVTURL:");
    //NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    NSMutableDictionary *theResult = [[NSMutableDictionary alloc] initWithCapacity:10];
    if (inAddress == nil || ![inAddress isKindOfClass:[NSString class]] || [inAddress isEqualToString:@"no data"]) {
        //[pool release];
        return theResult;
    }
    NSString *htmlString = [epg cacheString:inAddress];
    /*
    NSUserDefaults *defs = [NSUserDefaults standardUserDefaults];
    NSMutableDictionary *cache = [[NSMutableDictionary alloc] initWithCapacity:100];
    [cache addEntriesFromDictionary:[defs objectForKey:@"Cache"]];
    NSString *htmlString = nil;
    NSString *address = [[inAddress componentsSeparatedByString:@"#"] objectAtIndex:0];
    htmlString = [cache objectForKey:address];
    if (htmlString == nil) {
        [[theWWW getEPGText] setStringValue:[NSString stringWithFormat:@"Downloading from %@...", address]];
        htmlString = [NSString stringWithContentsOfURL:[NSURL URLWithString:address]];
        if (htmlString != nil) {
            [cache setObject:htmlString forKey:address];
            [defs setObject:cache forKey:@"Cache"];
        }
    }
    */
    if (htmlString == nil) {
        //[pool release];
        return nil;
    }
    // shortString: starts at beginning of this episode
    // substring: shorter
    NSString *shortString, *substring, *name, *buffer, *desc, *date, *pc, *tag;
    NSArray *theComponents = [inAddress componentsSeparatedByString:@"#"];
    if ([theComponents count] != 2) return theResult;
    NSRange aRange;
    
    aRange = [htmlString rangeOfString:[theComponents objectAtIndex:1]];
    if (aRange.location == NSNotFound) return theResult;
    shortString =[htmlString substringFromIndex:aRange.location];
    aRange = [shortString rangeOfString:@"name=" options:NSCaseInsensitiveSearch];
    if (aRange.location != NSNotFound)
        shortString = [shortString substringToIndex:aRange.location];
    NSScanner *scanSmall, *scan = [[NSScanner alloc] initWithString:shortString];
    [scan setCaseSensitive:NO];
    int location;
    
    // name
  //if ([optFName state]) {
    name = nil;
    while (name == nil) {
        [scan scanUpToString:@">" intoString:nil];
        [scan scanString:@">" intoString:nil];
        [scan scanUpToString:@"<" intoString:&name];
    }
    if (name != nil) {
        name = [name stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"\" "]];
        [theResult setObject:name forKey:@"Full Name"];
    }
    
    
    // guest stars
    substring = nil;
    location = [scan scanLocation];
    NSMutableString *gs = [[NSMutableString alloc] init];
    //aRange = [shortString rangeOfString:@">gs" options:NSCaseInsensitiveSearch];
    [scan scanUpToString:@">gs" intoString:nil];
    //if (aRange.location != NSNotFound) {
    if (![scan isAtEnd]) {
        //substring = [shortString substringFromIndex:aRange.location];
        //aRange = [substring rangeOfString:@"</tr>" options:NSCaseInsensitiveSearch];
        [scan scanUpToString:@"</tr>" intoString:&substring];
        //if (aRange.location != NSNotFound) {
        if (substring != nil) {
            //substring = [substring substringToIndex:aRange.location];
            scanSmall = [[NSScanner alloc] initWithString:substring];
            [scanSmall setCaseSensitive:NO];
            while (![scanSmall isAtEnd]) {
                buffer = nil;
                [scanSmall scanUpToString:@"/tvtome/servlet/PersonDetail/personid" intoString:nil];
                [scanSmall scanUpToString:@">" intoString:nil];
                [scanSmall scanString:@">" intoString:nil];
                [scanSmall scanUpToString:@"<" intoString:&buffer];
                if (buffer != nil) {
                    if ([gs length] > 0) [gs appendString:@", "];
                    [gs appendString:buffer];
                }
            }
        }
    } else {
        [scan setScanLocation:location];
        [scan scanUpToString:@"</tr>" intoString:nil];
        [scan scanString:@"</tr>" intoString:nil];
    }
    //if ([gs length] > 0)
        [theResult setObject:gs forKey:@"Guest Stars"];
  //}
    //if (substring == nil || ![substring isKindOfClass:[NSString class]])
        //substring = shortString;
    
    //scanSmall = [[NSScanner alloc] initWithString:substring];
    // Description
  //if ([optDesc state]) {
    //[scanSmall setScanLocation:0];
    desc = nil;
    location = [scan scanLocation];
    [scan scanUpToString:@"</tr>" intoString:nil];
    [scan scanString:@"</tr>" intoString:nil];
    //[scan scanUpToString:@"</tr>" intoString:nil];
    //[scan scanUpToString:@"<br>" intoString:nil];
    //[scan scanString:@"<br>" intoString:nil];
    [scan scanUpToString:@"</tr>" intoString:&desc];
    if (desc != nil)
        [theResult setObject:[MHUtil fixIMDB:[MHUtil cleanHTML:desc]] forKey:@"Description"];
    else
        [scan setScanLocation:location];
  //}
    
    // Air Date
  //if ([optCreDate state]) {
    location = [scan scanLocation];
    date = nil;
    [scan scanUpToString:@"<i>b</i>:" intoString:nil];
    [scan scanString:@"<i>b</i>:" intoString:nil];
    [scan scanUpToString:@"<" intoString:&date];
    if (date != nil)
        [theResult setObject:[MHUtil fixIMDB:date] forKey:@"Creation Date"];
  //}
    
    // Production Code
  //if ([optProd state]) {
    [scan setScanLocation:location];
    pc = nil;
    [scan scanUpToString:@"<i>pc</i>:" intoString:nil];
    [scan scanString:@"<i>pc</i>:" intoString:nil];
    [scan scanUpToString:@"<" intoString:&pc];
    if (pc != nil && ![pc hasPrefix:@"_____"])
        [theResult setObject:[MHUtil fixIMDB:pc] forKey:@"Product"];
  //}
    
    // Writer
  //if ([optWrit state]) {
    NSMutableString *writer = [[NSMutableString alloc] init];
    [scan setScanLocation:location];
    //location = [scan scanLocation];
    [scan scanUpToString:@"<i>w</i>:" intoString:nil];
    [scan scanString:@"<i>w</i>:" intoString:nil];
    while (![scan isAtEnd]) {
        buffer = tag = nil;
        [scan scanUpToString:@"<" intoString:&buffer];
        [scan scanString:@"<" intoString:nil];
        if (buffer != nil) {
            if ([writer length] > 0 && ![writer hasSuffix:@" "]) [writer appendString:@" "];
            [writer appendString:buffer];
        }
        [scan scanString:@"/" intoString:nil];
        [scan scanUpToString:@">" intoString:&tag];
        [scan scanString:@">" intoString:nil];
        if (tag != nil) {
            if (![tag hasPrefix:@"a"] && ![tag hasPrefix:@"A"])
                break;
        }
    }
    if ([writer length] > 0)
        [theResult setObject:[MHUtil fixIMDB:writer] forKey:@"Writer"];
  //}
    
    // Director
  //if ([optDir state]) {
    NSMutableString *director = [[NSMutableString alloc] init];
    [scan setScanLocation:location];
    [scan scanUpToString:@"<i>d</i>:" intoString:nil];
    [scan scanString:@"<i>d</i>:" intoString:nil];
    while (![scan isAtEnd]) {
        buffer = tag = nil;
        [scan scanUpToString:@"<" intoString:&buffer];
        [scan scanString:@"<" intoString:nil];
        if (buffer != nil) {
            if ([director length] > 0 && ![director hasSuffix:@" "]) [director appendString:@" "];
            [director appendString:buffer];
        }
        [scan scanString:@"/" intoString:nil];
        [scan scanUpToString:@">" intoString:&tag];
        [scan scanString:@">" intoString:nil];
        if (tag != nil) {
            if (![tag hasPrefix:@"a"] && ![tag hasPrefix:@"A"])
                break;
        }
    }
    if ([director length] > 0)
        [theResult setObject:[MHUtil fixIMDB:director] forKey:@"Director"];
  //}
    //[pool release];
    return theResult;
}

- (NSMutableDictionary *)readTVTURLguide:(NSString *)inAddress
{
    //NSLog(@"tSeriesObject readTVTURLguide");
   // NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    NSMutableDictionary *theResult = [[NSMutableDictionary alloc] initWithCapacity:10];
    if (inAddress == nil || ![inAddress isKindOfClass:[NSString class]] || [inAddress isEqualToString:@"no data"]) {
        //[pool release];
        return theResult;
    }
    NSString *htmlString = [epg cacheString:inAddress];
    /*
    NSUserDefaults *defs = [NSUserDefaults standardUserDefaults];
    NSMutableDictionary *cache = [[NSMutableDictionary alloc] initWithCapacity:100];
    [cache addEntriesFromDictionary:[defs objectForKey:@"Cache"]];
    NSString *htmlString = nil;
    NSString *address = [[inAddress componentsSeparatedByString:@"#"] objectAtIndex:0];
    htmlString = [cache objectForKey:address];
    if (htmlString == nil) {
        htmlString = [NSString stringWithContentsOfURL:[NSURL URLWithString:address]];
        if (htmlString != nil) {
            [cache setObject:htmlString forKey:address];
            [defs setObject:cache forKey:@"Cache"];
        }
    }
    */
    if (htmlString == nil) {
        //[pool release];
        return nil;
    }
    
    // shortString: starts at beginning of this episode
    // substring: shorter
    NSString *shortString, *name, *buffer;
    NSArray *theComponents = [inAddress componentsSeparatedByString:@"#"];
    if ([theComponents count] != 2) return theResult;
    NSRange aRange;
    int i, n;
    
    aRange = [htmlString rangeOfString:[theComponents objectAtIndex:1]];
    if (aRange.location == NSNotFound) return theResult;
    shortString =[htmlString substringFromIndex:aRange.location];
    aRange = [shortString rangeOfString:@"name=" options:NSCaseInsensitiveSearch];
    if (aRange.location != NSNotFound)
        shortString = [shortString substringToIndex:aRange.location];
    NSScanner *scan = [[NSScanner alloc] initWithString:shortString];
    [scan setCaseSensitive:NO];
    
    // name
  //if ([optFName state]) {
    name = nil;
    while (name == nil && ![scan isAtEnd]) {
        [scan scanUpToString:@">" intoString:nil];
        [scan scanString:@">" intoString:nil];
        [scan scanUpToString:@"<" intoString:&name];
    }
    if (name != nil) {
        name = [name stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"\" "]];
        [theResult setObject:name forKey:@"Full Name"];
    }
    
    NSArray *starts, *ends, *keys;
    NSString *aStart, *anEnd, *aKey;
    int theLocation = 0;
    
    starts = [NSArray arrayWithObjects:@"gs:", @"", nil];
    ends = [NSArray arrayWithObjects:@"<p>", @"</p>", nil];
    keys = [NSArray arrayWithObjects:@"Guest Stars", @"Description", nil];
    n = [starts count];
    for (i=0;i<n;i++) {
        theLocation = [scan scanLocation];
        buffer = nil;
        aStart = [starts objectAtIndex:i];
        anEnd = [ends objectAtIndex:i];
        aKey = [keys objectAtIndex:i];
        if (![aStart isEqualToString:@""]) {
            [scan scanUpToString:aStart intoString:nil];
            [scan scanString:aStart intoString:nil];
        }
        [scan scanUpToString:anEnd intoString:&buffer];
        if (buffer != nil) {
            buffer = [MHUtil fixIMDB:[MHUtil cleanHTML:buffer]];
            [theResult setObject:buffer forKey:aKey];
        } else {
            [scan setScanLocation:theLocation];
        }
    }
    
    // Colon-cues
    NSMutableString *value;
    
    starts = [NSArray arrayWithObjects:@"b", @"pc", @"w", @"d", nil];
    keys = [NSArray arrayWithObjects:@"Creation Date", @"Product", @"Writer", @"Director", nil];
    n = [starts count];
    for (i=0;i<n;i++) {
        [scan setScanLocation:theLocation];
        aStart = [NSString stringWithFormat:@"%@:", [starts objectAtIndex:i]];
        aKey = [keys objectAtIndex:i];
        [scan scanUpToString:aStart intoString:nil];
        [scan scanString:aStart intoString:nil];
        
        value = [[NSMutableString alloc] init];
        while (![scan isAtEnd]) {
            buffer = nil;
            [scan scanUpToCharactersFromSet:[NSCharacterSet whitespaceAndNewlineCharacterSet] intoString:&buffer];
            if (buffer != nil && ![buffer hasSuffix:@":"]) {
                if ([value length] > 0 && ![value hasSuffix:@" "])
                    [value appendString:@" "];
                [value appendString:buffer];
            } else break;
        }
        if ([value length] > 0 && ![value hasPrefix:@"_____"]) {
            buffer = [MHUtil fixIMDB:[MHUtil cleanHTML:value]];
            [theResult setObject:buffer forKey:aKey];
        }
    }
    //[pool release];
    return theResult;
}

- (NSString *)summary
{
    //NSLog(@"tSeriesObject summary");
    if (!isLeaf)
        return [self getCoverPic];
    if (metadata == nil)
        return @"";
    NSMutableString *output = [[NSMutableString alloc] initWithCapacity:1000];
    NSString *key, *value = nil;
    int season, episode;
    NSDictionary *aDict = nil;
    
    if (!dl) {
        if ([[[webAddress lastPathComponent] lowercaseString] hasPrefix:@"guide"])
            aDict = [self readTVTURLguide:webAddress];
        else {
            NSRange aRange = [webAddress rangeOfString:@"/tvtome/servlet/GuidePageServlet/showid"];
            if (aRange.location == NSNotFound)
                aDict = [self readTVTURL:webAddress];
            else {
                NSString *address, *tvtHTML = [epg cacheString:webAddress];
                if (tvtHTML != nil) {
                    address = nil;
                    NSScanner *scan2 = [NSScanner scannerWithString:tvtHTML];
                    [scan2 setCaseSensitive:NO];
                    [scan2 scanUpToString:@">Episode List<" intoString:nil];
                    [scan2 scanUpToString:@"<a href=\"" intoString:nil];
                    [scan2 scanString:@"<a href=\"" intoString:nil];
                    [scan2 scanUpToString:@"\"" intoString:&address];
                    if (address != nil)
                        address = [NSString stringWithFormat:@"http://www.tvtome.com%@", address];
                    //NSURL *link = [NSURL URLWithString:address];
                    aDict = [self readTVTURL:address];
                }
            }
        }
        if (metadata == nil) metadata = [[NSMutableDictionary alloc] initWithCapacity:25];
        if (aDict != nil) [metadata addEntriesFromDictionary:aDict];
        [metadata setObject:[parent title] forKey:@"Author"];
        value = [metadata objectForKey:@"Album"];
        if (value == nil) {
            NSScanner *scan = [[NSScanner alloc] initWithString:displayString];
            if ([scan scanInt:&season] && [scan scanString:@"-" intoString:nil] && [scan scanInt:&episode])
                [metadata setObject:[NSString stringWithFormat:@"Season %d, Episode %d",season,episode] forKey:@"Album"];
        }
        dl = YES;
    }
    
    NSArray *keys = [metadata allKeys];
    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 != nil && value != nil && [key isKindOfClass:[NSString class]] && [value isKindOfClass:[NSString class]])
            [output appendString:[NSString stringWithFormat:@"%@:\n%@\n\n",key,value]];
    }
    return output;
}

- (NSString *)getCoverPic
{
    //NSLog(@"tSeriesObject getCoverPic");
    if (!images) return @"";
    NSString *address = [webAddress copy];
    if ([address hasPrefix:@"http://www.tvtome.com"])
        address = [address substringToIndex:[address length]-11];
    NSRange aRange = [address rangeOfString:@"guide.shtml" options:NSCaseInsensitiveSearch];
    NSString *htmlString = [epg cacheString:address];
    if (htmlString == nil) return @"";
    NSScanner *scan = [NSScanner scannerWithString:htmlString];
    [scan setCaseSensitive:NO];
    NSString *where, *picAddy = nil, *buffer = nil;
    if ([webAddress hasPrefix:@"http://www.tvtome.com"]) {
        [scan scanUpToString:@" guide at TV Tome.<" intoString:nil];
        [scan scanUpToString:@"<img" intoString:nil];
        [scan scanUpToString:@"src=\"" intoString:nil];
        [scan scanString:@"src=\"" intoString:nil];
        [scan scanUpToString:@"\"" intoString:&picAddy];
        where = @"tvt";
    } else {
        [scan scanUpToString:@">Last Updated:<" intoString:nil];
        [scan scanUpToString:@"<img" intoString:nil];
        [scan scanUpToString:@"src=\"" intoString:nil];
        [scan scanString:@"src=\"" intoString:nil];
        [scan scanUpToString:@"\"" intoString:&buffer];
        if (![self isImage:buffer])
            buffer = @"cast.jpg";
        picAddy = [webAddress copy];
        NSRange bRange = [picAddy rangeOfString:@"/" options:NSBackwardsSearch];
        if (aRange.location == NSNotFound) { // Not a guide.shtml version
            if (![picAddy hasSuffix:@"/"])
                picAddy = [picAddy stringByAppendingString:@"/"];
            where = @"epg";
        } else { // a guide.shtml version
            picAddy = [picAddy substringToIndex:bRange.location+1];
            where = @"gui";
        }
        picAddy = [picAddy stringByAppendingString:buffer];
    }
    if (nil == picAddy)
        return @"no picture";
    NSString *path = [NSString stringWithFormat:@"%@%@%@%@", [MHUtil supportDir], where, displayString, [picAddy substringFromIndex:[picAddy length]-4]];
    if (![[NSFileManager defaultManager] fileExistsAtPath:path]) {
        NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:picAddy]];
        if (nil == data) {
            picAddy = [[picAddy substringToIndex:[picAddy length]-3] stringByAppendingString:@"gif"];
            path = [[path substringToIndex:[path length]-3] stringByAppendingString:@"gif"];
            data = [NSData dataWithContentsOfURL:[NSURL URLWithString:picAddy]];
            if (nil == data)
                return @"no picture";
        }
        [data writeToFile:path atomically:NO];
    }
    return (NSString *) [NSAttributedString attributedStringWithAttachment:[[NSTextAttachment alloc] initWithFileWrapper:[[NSFileWrapper alloc] initWithPath:path]]];
}

- (BOOL)isImage:(NSString *)input
{
    NSArray *good = [NSArray arrayWithObjects:@".jpg", @".jpeg", @".gif", @".png", nil];
    int i, n = [good count];
    for (i=0;i<n;i++) {
        if ([[input lowercaseString] hasSuffix:[good objectAtIndex:i]])
            return YES;
    }
    return NO;
}

- (NSDictionary *)metadata
{
    return metadata;
}

- (NSMutableDictionary *)parseTVTEpisodes:(NSString *)htmlString baseAddress:(NSString *)baseAddress
{
    //NSLog(@"tSeriesObject parseTVTEpisodes");
    if (htmlString == nil || ![htmlString isKindOfClass:[NSString class]]) return nil;
    NSScanner *scan = [NSScanner scannerWithString:htmlString];
    [scan setCaseSensitive:NO];
    NSString *buffer, *name, *tag, *date, *date2, *address, *pcode;
    int season, ep, count, date1, date3, strikes, strikeout = 5;
    NSMutableDictionary *entry, *theResult = [[NSMutableDictionary alloc] initWithCapacity:10];
    //NSCharacterSet *end = [NSCharacterSet characterSetWithCharactersInString:@"\n\r<"];
    NSMutableArray *episodes = [[NSMutableArray alloc] init];
    [theResult setObject:episodes forKey:@"episodes"];
    //NSMutableString *theRegulars = [[NSMutableString alloc] initWithCapacity:128];
    //[theResult setObject:theRegulars forKey:@"regulars"];
    
    // Regulars
    buffer = nil;
    address = baseAddress;
    if ([address hasSuffix:@"eplist.html"]) address = [address substringToIndex:[address length]-11];
    NSString *baseString = [epg cacheString:address];
    NSScanner *scan2 = [NSScanner scannerWithString:baseString];
    [scan2 setCaseSensitive:NO];
    [scan2 scanUpToString:@"Show Stars" intoString:nil];
    [scan2 scanUpToString:@"</td></tr>\n" intoString:&buffer];
    [theResult setObject:[self parseTVTRegulars:buffer] forKey:@"regulars"];
    
    
    // Episodes
    count = 1;
    strikes = 0;
    //[htmlString writeToFile:@"htmlString.txt" atomically:YES];
    while (strikes < strikeout) {
        [scan setScanLocation:0];
        buffer = name = tag = date = date2 = address = pcode = nil;
        season = ep = date1 = date3 = -1;
        [scan scanUpToString:[NSString stringWithFormat:@">%d.<", count++] intoString:nil];
        if ([scan isAtEnd]) strikes++;
        else strikes = 0;
        //[scan scanString:[NSString stringWithFormat:@">%d.<", count++] intoString:nil];
        [scan scanUpToString:@"\"small\">" intoString:nil];
        [scan scanString:@"\"small\">" intoString:nil];
        //[scan scanUpToString:@"<" intoString:&buffer];
        [scan scanInt:&season];
        //if (-1 != season) break;
        [scan scanString:@"-" intoString:nil];
        [scan scanInt:&ep];
        [scan scanUpToString:@"\"small\">" intoString:nil];
        [scan scanString:@"\"small\">" intoString:nil];
        [scan scanUpToString:@"<" intoString:&pcode];
        [scan scanUpToString:@"nowrap>" intoString:nil];
        [scan scanString:@"nowrap>" intoString:nil];
        [scan scanUpToString:@"<" intoString:&date];
        [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:@"<" intoString:&name];
        
        if (![scan isAtEnd]) {
            if (name == nil) name = @"";
            if (date != nil)
                name = [NSString stringWithFormat:@"%@ %@", date, name];
            name = [NSString stringWithFormat:@"%d-%.2d, %@", season, ep, name];
            entry = [[NSMutableDictionary alloc] initWithCapacity:10];
            [entry setObject:name forKey:@"title"];
            if (address != nil) {
                NSURL *link, *base;
                if (baseAddress != nil) {
                    base = [NSURL URLWithString:baseAddress];
                    link = [NSURL URLWithString:address relativeToURL:base];
                    address = [link absoluteString];
                }
                [entry setObject:address forKey:@"address"];
                [entry setObject:[NSNumber numberWithBool:NO] forKey:@"dl"];
            } else {
                [entry setObject:@"no data" forKey:@"address"];
                [entry setObject:[NSNumber numberWithBool:YES] forKey:@"dl"];
            }
            [episodes addObject:entry];
        }
    }
    return theResult;
    
}

- (NSMutableString *)parseTVTRegulars:(NSString *)htmlString
{
    //NSLog(@"tSeriesObject parseTVTRegulars");
    NSScanner *scan = [NSScanner scannerWithString:htmlString];
    [scan setCaseSensitive:NO];
    NSMutableString *regs = [[NSMutableString alloc] init];
    NSString *buffer;
    
    while (![scan isAtEnd]) {
        buffer = nil;
        [scan scanUpToString:@"<a href=\"/tvtome/servlet/PersonDetail/personid" intoString:nil];
        [scan scanUpToString:@">" intoString:nil];
        [scan scanString:@">" intoString:nil];
        [scan scanUpToString:@"<" intoString:&buffer];
        if (nil != buffer) {
            if ([regs length] > 0 && ![regs hasSuffix:@", "])
                [regs appendString:@", "];
            [regs appendString:buffer];
        }
    }
    return regs;
}

- (NSMutableDictionary *)parseEPGEpisodes:(NSString *)htmlString baseAddress:(NSString *)baseAddress
{
    //NSLog(@"tSeriesObject parseEPGEpisodes:baseAddress:");
    if (htmlString == nil || ![htmlString isKindOfClass:[NSString class]]) return nil;
    NSScanner *shortScanner, *scan = [[NSScanner alloc] initWithString:htmlString];
    [scan setCaseSensitive:NO];
    NSString *shortString;
    NSString *buffer, *name, *tag, *date, *date2, *address, *pcode;
    int season, ep, count, date1, date3, strikes, strikeout = 5;
    NSMutableDictionary *entry, *theResult = [[NSMutableDictionary alloc] initWithCapacity:10];
    NSCharacterSet *end = [NSCharacterSet characterSetWithCharactersInString:@"\n\r<"];
    NSMutableArray *episodes = [[NSMutableArray alloc] init];
    [theResult setObject:episodes forKey:@"episodes"];
    NSMutableString *theRegulars = [[NSMutableString alloc] initWithCapacity:128];
    [theResult setObject:theRegulars forKey:@"regulars"];
    
    // Regulars
    shortString = nil;
    [scan scanUpToString:@"<ul>" intoString:nil];
    [scan scanUpToString:@"<p>" intoString:&shortString];
    if (shortString != nil) {
        shortScanner = [[NSScanner alloc] initWithString:shortString];
        [shortScanner setCaseSensitive:NO];
        [shortScanner scanUpToString:@"<li>" intoString:nil];
    	while (![shortScanner isAtEnd]) {
            buffer = nil;
            [shortScanner scanUpToString:@"<strong>" intoString:nil];
            [shortScanner scanString:@"<strong>" intoString:nil];
            [shortScanner scanUpToString:@"</strong>" intoString:&buffer];
            if (buffer != nil) {
                if ([theRegulars length] > 0)
                    [theRegulars appendString:@", "];
                [theRegulars appendString:buffer];
            }
        }
    }
    //[shortScanner release];
    
    // Episodes
    count = 1;
    strikes = 0;
    //[htmlString writeToFile:@"htmlString.txt" atomically:YES];
    while (strikes < strikeout) {
        [scan setScanLocation:0];
        buffer = name = tag = date = date2 = address = pcode = nil;
        season = ep = date1 = date3 = -1;
        [scan scanUpToString:[NSString stringWithFormat:@"%d. ", count] intoString:nil];
        if ([scan isAtEnd]) strikes++;
        else strikes = 0;
        [scan scanString:[NSString stringWithFormat:@"%d. ", count++] intoString:nil];
        //[scan scanUpToString:@"<" intoString:&buffer];
        [scan scanInt:&season];
        [scan scanString:@"-" intoString:nil];
        [scan scanInt:&ep];
        if (![scan scanString:@"           " intoString:nil])
            [scan scanUpToString:@" " intoString:&pcode];
        if ([scan scanInt:&date1]) {
            [scan scanUpToString:@" " intoString:&date2];
            [scan scanInt:&date3];
        }
        if (date1 != -1 && date2 != nil && date3 != -1)
            date = [NSString stringWithFormat:@"%d %@ %.2d", date1, date2, date3];
        if ([scan scanString:@"<" intoString:nil]) {
            [scan scanUpToString:@"href=\"" intoString:&buffer];
            [scan scanString:@"href=\"" intoString:nil];
            [scan scanUpToString:@"\"" intoString:&address];
            [scan scanUpToString:@">" intoString:&buffer];
            [scan scanString:@">" intoString:nil];
        }
        [scan scanUpToCharactersFromSet:end intoString:&name];
        if (name != nil) {
            if (date != nil)
                name = [NSString stringWithFormat:@"%@ %@", date, name];
            name = [NSString stringWithFormat:@"%d-%.2d, %@", season, ep, name];
            entry = [[NSMutableDictionary alloc] initWithCapacity:10];
            [entry setObject:name forKey:@"title"];
            if (address != nil) {
                NSURL *link, *base;
                if (baseAddress != nil) {
                    base = [NSURL URLWithString:baseAddress];
                    link = [NSURL URLWithString:address relativeToURL:base];
                    address = [link absoluteString];
                }
                [entry setObject:address forKey:@"address"];
                [entry setObject:[NSNumber numberWithBool:NO] forKey:@"dl"];
            } else {
                [entry setObject:@"no data" forKey:@"address"];
                [entry setObject:[NSNumber numberWithBool:YES] forKey:@"dl"];
            }
            [episodes addObject:entry];
        }
    }
    return theResult;
}


- (NSMutableDictionary *)parseEPGGuide:(NSString *)input baseAddress:(NSString *)baseAddress
{
    //NSLog(@"tSeriesObject parseEPGGuide:baseAddress:");
    NSMutableDictionary *theResult = [[NSMutableDictionary alloc] initWithCapacity:10];
    NSMutableArray *episodes = [[NSMutableArray alloc] init];
    [theResult setObject:episodes forKey:@"episodes"];
    if (input == nil || ![input isKindOfClass:[NSString class]]) return theResult;
    NSScanner *scan = [[NSScanner alloc] initWithString:input];
    [scan setCaseSensitive:NO];
    
    NSString *buffer, *theRegulars, *newAddress, *title = @"";
    theRegulars = [self epgGuideRegulars:input];
    if (![theRegulars isEqualToString:@""])
        [theResult setObject:theRegulars forKey:@"regulars"];
    NSArray *seasonSpots = [self epgGuideSeasons:input];
    // Episodes
    int season, ep, i, n;
    NSMutableDictionary *anEpisode;
    
    //NSMutableDictionary *data;
    while (![scan isAtEnd]) {
        buffer = nil;
        season = ep = -1;
        [scan scanUpToString:@"name=\"ep" intoString:nil];
        [scan scanString:@"name=\"ep" intoString:nil];
        [scan scanInt:&ep];
        n = [seasonSpots count];
        for (i=0;i<n;i++) {
            if ([[seasonSpots objectAtIndex:i] intValue] > [scan scanLocation]) break;
            season = i + 1;
        }
        [scan scanUpToString:@"<strong>" intoString:nil];
        [scan scanString:@"<strong>" intoString:nil];
        [scan scanUpToString:@"</strong>" intoString:&buffer];
        //[scan scanUpToString:@"<a name" intoString:&buffer];
        //data = [self epgGuideEpisode:buffer];
        if (season != -1 && ep != -1) {
            //[data setObject:[NSString stringWithFormat:@"Season %d, Episode %d", season, ep] forKey:@"Album"];
            title = [NSString stringWithFormat:@"%d-%.2d ", season, ep];
        }
        if (buffer != nil)
            title = [title stringByAppendingString:buffer];
        //buffer = nil;
        //buffer = [data objectForKey:@"Creation Date"];
        //if (buffer != nil)
            //title = [NSString stringWithFormat:@"%@%@ ", title, buffer];
        //buffer = nil;
        //buffer = [data objectForKey:@"Full Name"];
        //if (buffer != nil)
            //title = [title stringByAppendingString:buffer];
        anEpisode = [[NSMutableDictionary alloc] initWithCapacity:10];
        [anEpisode setObject:title forKey:@"title"];
        //[anEpisode setObject:data forKey:@"data"];
        if (baseAddress == nil || ![baseAddress isKindOfClass:[NSString class]])
            newAddress = @"no data";
        else
            newAddress = [NSString stringWithFormat:@"%@#ep%.3d", baseAddress, ep];
        [anEpisode setObject:newAddress forKey:@"address"];
        [anEpisode setObject:[NSNumber numberWithBool:NO] forKey:@"dl"];
        [episodes addObject:anEpisode];
    }
    return theResult;
}

- (NSString *)epgGuideRegulars:(NSString *)input
{
    //NSLog(@"tSeriesObject epgGuideRegulars");
    if (input == nil || ![input isKindOfClass:[NSString class]]) return @"";
    NSString *buffer, *shortString = nil;
    NSRange aRange = [input rangeOfString:@"<bl>" options:NSCaseInsensitiveSearch];
    if (aRange.location == NSNotFound)
        aRange = [input rangeOfString:@"<ul>" options:NSCaseInsensitiveSearch];
    if (aRange.location != NSNotFound)
        shortString = [input substringFromIndex:aRange.location];
    else shortString = input;
    aRange = [shortString rangeOfString:@"<p>" options:NSCaseInsensitiveSearch];
    if (aRange.location != NSNotFound)
        shortString = [shortString substringToIndex:aRange.location];
    NSMutableString *theRegulars = [[NSMutableString alloc] initWithCapacity:1000];
    
    NSScanner *scan = [[NSScanner alloc] initWithString:shortString];
    [scan setCaseSensitive:NO];
    while (![scan isAtEnd]) {
        buffer = nil;
        [scan scanUpToString:@"<strong>" intoString:nil];
        [scan scanString:@"<strong>" intoString:nil];
        [scan scanUpToString:@"</strong>" intoString:&buffer];
        if (buffer != nil) {
            if ([theRegulars length] > 0)
                [theRegulars appendString:@", "];
            [theRegulars appendString:buffer];
        }
    }
    return theRegulars;
}

- (NSArray *)epgGuideSeasons:(NSString *)input
{
    //NSLog(@"tSeriesObject epgGuideSeasons");
    NSMutableArray *theSeasons = [[NSMutableArray alloc] init];
    if (input == nil || ![input isKindOfClass:[NSString class]]) return theSeasons;
    NSString *buffer;
    int seasonCount = 1;
    NSScanner *scan = [[NSScanner alloc] initWithString:input];
    [scan setCaseSensitive:NO];
    while (![scan isAtEnd]) {
        switch (seasonCount % 10) {
            case 1: buffer = @"st";  break;
            case 2: buffer = @"nd";  break;
            case 3: buffer = @"rd";  break;
            default: buffer = @"th";  break;
        }
        buffer = [NSString stringWithFormat:@"name=\"%d%@\"", seasonCount, buffer];
        [scan scanUpToString:buffer intoString:nil];
        if ([scan isAtEnd]) break;
        [theSeasons addObject:[NSNumber numberWithInt:[scan scanLocation]]];
        seasonCount++;
    }
    return theSeasons;
}

- (NSMutableDictionary *)epgGuideEpisode:(NSString *)input
{
    //NSLog(@"tSeriesObject epgGuideEpisode:");
    NSMutableDictionary *theResult = [[NSMutableDictionary alloc] initWithCapacity:10];
    if (input == nil || ![input isKindOfClass:[NSString class]]) return theResult;
    NSArray *starts, *ends, *keys, *startsC, *keysC;
    // round 1:
    starts = [NSArray arrayWithObjects:@"<strong>", @"gs:", @"", nil];
    ends = [NSArray arrayWithObjects:@"</strong>", @"<p>", @"</p>", nil];
    keys = [NSArray arrayWithObjects:@"Full Name", @"Guest Stars", @"Description", nil];
    // round 2:
    startsC = [NSArray arrayWithObjects:@"b:", @"pc:", @"w:", @"d:", nil];
    keysC = [NSArray arrayWithObjects:@"Creation Date", @"Product", @"Writer", @"Director", nil];
    
    NSScanner *scan = [[NSScanner alloc] initWithString:input];
    [scan setCaseSensitive:NO];
    NSString *buffer, *aStart, *anEnd, *aKey;
    NSRange aRange;
    // round 1
    int theLocation, i, n = [starts count];
    for (i=0;i<n;i++) {
        buffer = nil;
        aStart = [starts objectAtIndex:i];
        anEnd = [ends objectAtIndex:i];
        aKey = [keys objectAtIndex:i];
        theLocation = [scan scanLocation];
        if (![aStart isEqualToString:@""]) {
            [scan scanUpToString:aStart intoString:nil];
            [scan scanString:aStart intoString:nil];
        }
        [scan scanUpToString:anEnd intoString:&buffer];
        if (buffer != nil) {
            buffer = [MHUtil fixIMDB:[MHUtil cleanHTML:buffer]];
            [theResult setObject:buffer forKey:aKey];
            theLocation = [scan scanLocation];
        } else {
            [scan setScanLocation:theLocation];
        }
    }
    // round 2
    NSCharacterSet *cSet = [NSCharacterSet characterSetWithCharactersInString:@":<"];
    n = [startsC count];
    for (i=0;i<n;i++) {
        buffer = nil;
        aStart = [startsC objectAtIndex:i];
        aKey = [keysC objectAtIndex:i];
        [scan setScanLocation:theLocation];
        [scan scanUpToString:aStart intoString:nil];
        [scan scanString:aStart intoString:nil];
        [scan scanUpToCharactersFromSet:cSet intoString:&buffer];
        if (buffer != nil) {
            if ([scan scanString:@":" intoString:nil]) {
                aRange = [buffer rangeOfString:@" " options:NSBackwardsSearch];
                if (aRange.location != NSNotFound)
                    buffer = [buffer substringToIndex:aRange.location];
            }
            [theResult setObject:buffer forKey:aKey];
        }
    }
    return theResult;
}

@end
