/* 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.
 */
//
//  MHIMDB.m
//  Hootenanny
//
//  Created by Noah Sorscher on Wed Mar 26 2003.
//  Copyright (c) 2003 __MyCompanyName__. All rights reserved.
//

#import "MHIMDB.h"
#import "noah.h"

#define DEBUG 0


@implementation MHIMDB


BOOL isChoices(NSString *input);


- (int)numberOfRowsInTableView:(NSTableView *)aTableView
{
    return [theResults count];
}

- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex
{
    return [[theResults objectAtIndex:rowIndex] objectForKey:@"title"];
}

- (BOOL) tableView: (NSTableView *) tableView shouldEditTableColumn: (NSTableColumn *) tableColumn row: (int) rowIndex
{
    return NO;
}

- (BOOL) tableView: (NSTableView *) tableView shouldSelectRow: (int) rowIndex
{
    currentRequest = [theResults objectAtIndex:rowIndex];
    NSString *path = [[NSUserDefaults standardUserDefaults] objectForKey:@"IMDBkeys"];
    if (!path)
        path = [[NSBundle mainBundle] pathForResource:@"IMDBkeys" ofType:@"xml"];
    NSDictionary *keys = [NSDictionary dictionaryWithContentsOfFile:path];
    if (linkButton && keys && [keys objectForKey:@"titleBaseAddress"]) {
        [linkButton performSelectorOnMainThread:@selector(setTitle:) withObject:[NSString stringWithFormat:[keys objectForKey:@"titleBaseAddress"], [[theResults objectAtIndex:rowIndex] objectForKey:@"code"]] waitUntilDone:NO];
    }
    if (DEBUG)
        [self displayIMDBForIndex:[NSNumber numberWithInt:rowIndex]];
    else
        [NSThread detachNewThreadSelector:@selector(displayIMDBForIndex:) toTarget:self withObject:[NSNumber numberWithInt:rowIndex]];
    return YES;
}

- (void) typeAheadString:(NSString *)inString inTableView:(NSTableView *)inTableView
{
        //NSLog(inString);
}

- (void) willClearTypeAheadString:(NSString *)inString inTableView:(NSTableView *)inTableView
{
    if (!inString || ![inString length]) return;
    int i, n = [theResults count];
    for (i=0;i<n;i++) {
        NSString *title = [[theResults objectAtIndex:i] objectForKey:@"title"];
        if ([title rangeOfString:inString options:NSCaseInsensitiveSearch].location != NSNotFound) {
            [self tableView:inTableView shouldSelectRow:i];
            [inTableView selectRow:i byExtendingSelection:NO];
            [inTableView scrollRowToVisible:i];
            return;
        }
    }
}

- (MHIMDB *)init
{
    //NSLog(@"MHIMDB");
    theResults = [[NSMutableArray alloc] init];
    imdbField = nil;
    theTable = nil;
    linkButton = nil;
    currentRequest = nil;
    latestSearch = nil;
    images = NO;
    self = [super init];
    return self;
}

- (void)setTextField:(NSTextField *)textfield
{
    imdbField = textfield;
}

- (void)setTableView:(NSTableView *)tableView
{
    theTable = tableView;
}

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

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

- (void)search:(NSString *)searchString
{
    [latestSearch release];
    NSMutableString *aString = [NSMutableString stringWithString:searchString];
    [aString replaceOccurrencesOfString:@"/" withString:@"_" options:0 range:NSMakeRange(0, [aString length])];
    latestSearch = [aString retain];
    if (DEBUG)
        [self imdbSearch:searchString];
    else
        [NSThread detachNewThreadSelector:@selector(imdbSearch:) toTarget:self withObject:searchString];
}

- (NSDictionary *)result:(int)index
{
    NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithDictionary:[theResults objectAtIndex:index]];
    [dict removeObjectForKey:@"dl"];
    [dict removeObjectForKey:@"title"];
    [dict removeObjectForKey:@"code"];
    [dict removeObjectForKey:@"address"];
    return dict;
}

- (void)imdbSearch:(NSString *)input
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    input = [MHUtil spaceToPlus:input];
    NSString *path = [[NSUserDefaults standardUserDefaults] objectForKey:@"IMDBkeys"];
    if (!path)
        path = [[NSBundle mainBundle] pathForResource:@"IMDBkeys" ofType:@"xml"];
    NSDictionary *keys = [NSDictionary dictionaryWithContentsOfFile:path];
    if (!keys) NSLog(@"IMDBkeys file mis-formed!");
    NSString *address = [keys objectForKey:@"searchAddress"];
    [theResults removeAllObjects];
    address = [NSString stringWithFormat:address, input];
    if (linkButton)
        [linkButton performSelectorOnMainThread:@selector(setTitle:) withObject:address waitUntilDone:NO];
    NSURL *resultURL = [NSURL URLWithString:address];
    NSString *resultString = nil;
    if (nil != imdbField)
        //[imdbField setStringValue:[NSString stringWithFormat:@"Downloading from %@...", address]];
        [imdbField performSelectorOnMainThread:@selector(setStringValue:) withObject:[NSString stringWithFormat:@"Downloading from %@...", address] waitUntilDone:NO];
    resultString = [NSString stringWithContentsOfURL:resultURL];
    if (!resultString)
        resultString = [NSString stringWithContentsOfCURL:resultURL];
    if (nil != imdbField)
        //[imdbField setStringValue:@""];
        [imdbField performSelectorOnMainThread:@selector(setStringValue:) withObject:@"" waitUntilDone:NO];
    if (resultString == nil && nil != imdbField) {
        //[imdbField setStringValue:[NSString stringWithFormat:@"error: no data in document at %@", address]];
        [imdbField performSelectorOnMainThread:@selector(setStringValue:) withObject:[NSString stringWithFormat:@"error: no data in document at %@", address] waitUntilDone:NO];
        [pool release];
        return;
    }
    else if (isChoices(resultString))
        [self displayChoices:resultString];
    else
        [self displayMovie:resultString];
    [[NSNotificationCenter defaultCenter] postNotificationName:@"mhWebPageDownloadedNotification" object:self userInfo:[NSDictionary dictionaryWithObjectsAndKeys:address, @"link", resultString, @"string", nil]];
    if (nil != theTable) {
        [theTable reloadData];
        if ([theTable selectedRow] != -1)
            [self tableView:theTable shouldSelectRow:[theTable selectedRow]];
    }
    [pool release];
}

- (void)displayChoices:(NSString *)input
{
    //NSLog(@"tWWW displayChoices:");
    NSString *path = [[NSUserDefaults standardUserDefaults] objectForKey:@"IMDBkeys"];
    if (!path)
        path = [[NSBundle mainBundle] pathForResource:@"IMDBkeys" ofType:@"xml"];
    NSDictionary *keys = [NSDictionary dictionaryWithContentsOfFile:path];
    if (!keys) NSLog(@"IMDBkeys file mis-formed!\n");
    keys = [keys objectForKey:@"results"];
    
    NSMutableDictionary *aResult;
    NSString *titleString, *codeString;
    NSScanner *scan = [[NSScanner alloc] initWithString:input];
    [scan setCaseSensitive:NO];
    int i, n;
    
    NSArray *skips = [keys objectForKey:@"skip0"];
    if (skips) {
        n = [skips count];
        for (i=0;i<n;i++) {
            [scan scanPastString:[skips objectAtIndex:i]];
        }
    }
    NSArray *skip1 = [keys objectForKey:@"skip1"], *skip2 = [keys objectForKey:@"skip2"];
    NSString *end1 = [keys objectForKey:@"end1"], *end2 = [keys objectForKey:@"end2"];
    
    while (![scan isAtEnd]) {
        titleString = codeString = nil;
        n = [skip1 count];
        for (i=0;i<n;i++)
            [scan scanPastString:[skip1 objectAtIndex:i]];
        [scan scanUpToString:end1 intoString:&codeString];
        if ([codeString hasSuffix:@"/"])
            codeString = [codeString substringToIndex:[codeString length]-1];
        n = [skip2 count];
        for (i=0;i<n;i++)
            [scan scanPastString:[skip2 objectAtIndex:i]];
        [scan scanUpToString:end2 intoString:&titleString];
        
        if (nil != titleString && nil != codeString) {
            titleString = [MHUtil fixIMDB:titleString];
            aResult = [[NSMutableDictionary alloc] init];
            [aResult setObject:titleString forKey:@"title"];
            [aResult setObject:codeString forKey:@"code"];
            [aResult setObject:[NSNumber numberWithBool:NO] forKey:@"dl"];
            [theResults addObject:aResult];
        }
    }
}

- (void) displayMovie:(NSString *)input
{
    //NSLog(@"tWWW displayMovie");
    if (input == nil) return;
    NSMutableDictionary *theResult = [self parseIMDB:input];
    if (!theResult) return;
    NSString *fname = [theResult objectForKey:@"Full Name"];
    [theResult setObject:fname forKey:@"title"];
    [theResult setObject:[NSNumber numberWithBool:YES] forKey:@"dl"];
    [theResults addObject:theResult];
}

- (void)displayIMDBForIndex:(NSNumber *)rowIndex
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    NSAttributedString *displayString;
    NSMutableDictionary *aResult, *entries;
    aResult = [theResults objectAtIndex:[rowIndex intValue]];
    if (![[aResult objectForKey:@"dl"] boolValue]) {
        entries = [self readIMDBCode:[aResult objectForKey:@"code"]];
        [aResult addEntriesFromDictionary:entries];
        [aResult setObject:[NSNumber numberWithBool:YES] forKey:@"dl"];
    }
    displayString = [self getDisplayString:aResult];
    if (nil != imdbField && aResult == currentRequest)
        //[imdbField setStringValue:(NSString *)displayString];
        [imdbField performSelectorOnMainThread:@selector(setStringValue:) withObject:displayString waitUntilDone:NO];
    //[[NSNotificationCenter defaultCenter] postNotificationName:@"mhWebPageDownloadedNotification" object:self userInfo:[NSDictionary dictionaryWithObjectsAndKeys:address, @"link", htmlString, @"string", nil]];
    [pool release];
}

- (NSAttributedString *)getDisplayString:(NSDictionary *)aDict
{
    //NSLog(@"tWWW getDisplayString:");
    NSMutableAttributedString *output = [[NSMutableAttributedString alloc] init];
    if (aDict == nil) return output;
    NSArray *keys = [aDict allKeys];
    int i, n = [keys count];
    NSString *key, *value;
    NSFileManager *fm = [NSFileManager defaultManager];
    NSString *supportDir = [MHUtil supportDir];
    
    for (i=0;i<n;i++) {
        key = [keys objectAtIndex:i];
        if ([self keyIsGood:key]) {
            value = [aDict objectForKey:key];
            if ([key isEqualToString:@"Cover Image"] && images) {
                // Icon shit
                NSString *path = [aDict objectForKey:@"code"];
                if (!path) path = latestSearch;
                path = [NSString stringWithFormat:@"%@%@.jpg",supportDir, path];
                if (![fm fileExistsAtPath:path]) {
                    NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:value]];
                    [data writeToFile:path atomically:NO];
                }
                [output appendAttributedString:[[NSAttributedString alloc] initWithString:@"Cover Image:\n"]];
                [output appendAttributedString:[NSAttributedString attributedStringWithAttachment:[[NSTextAttachment alloc] initWithFileWrapper:[[NSFileWrapper alloc] initWithPath:path]]]];
                [output appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n"]];
                
                [(NSMutableDictionary *)aDict setObject:path forKey:@"Cover Image"];
            } else
                [output appendAttributedString:[[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"%@:\n%@\n\n",key,value]]];
        }
    }
    return output;
}

- (BOOL)keyIsGood:(NSString *) key
{
    //NSLog(@"tWWW keyIsGood");
    NSArray *bad = [NSArray arrayWithObjects:@"title", @"code", @"dl", @"address", nil];
    //NSArray *good = [NSArray arrayWithObjects:@"Album", @"Author", @"Copyright", @"Creation Date", @"Full Name", @"Description", @"Director", @"Information", @"Performers", @"Product", @"Writer", @"Cover Image", nil];
    //NSArray *preferences = [NSArray arrayWithObjects:optAlbum, optAuthor, optCopy, optCreDate, optFName, optDesc, optDir, optInfo, optPerf, optProd, optWrit, optIcon, nil];
    int i, n = [bad count];
    if (key == nil) return NO;
    for (i=0;i<n;i++) {
        if ([key isEqualToString:[bad objectAtIndex:i]]) return NO;
    }
    /*
    n = [good count];
    for (i=0;i<n;i++) {
        if ([key isEqualToString:[good objectAtIndex:i]])
            return [[preferences objectAtIndex:i] state];
    }
    */
    return YES;
}


- (NSMutableDictionary *)readIMDBCode:(NSString *)code
{
    if (DEBUG)
        NSLog(@"MHIMDB readIMDBCode");
    if (code == nil) return nil;
    NSString *path = [[NSUserDefaults standardUserDefaults] objectForKey:@"IMDBkeys"];
    if (!path)
        path = [[NSBundle mainBundle] pathForResource:@"IMDBkeys" ofType:@"xml"];
    NSString *baseAddress = [[NSDictionary dictionaryWithContentsOfFile:path] objectForKey:@"titleBaseAddress"];
    if (!baseAddress) NSLog(@"IMDBkeys file mis-formed!\n");
    NSString *address = [NSString stringWithFormat:baseAddress, code];
    NSURL *theURL = [NSURL URLWithString:address];
    if (imdbField != nil)
        //[imdbField setStringValue:[NSString stringWithFormat:@"Downloading from %@...", address]];
        [imdbField performSelectorOnMainThread:@selector(setStringValue:) withObject:[NSString stringWithFormat:@"Downloading from %@...", address] waitUntilDone:NO];
    NSString *htmlString = [NSString stringWithContentsOfURL:theURL];
    //if (!htmlString)
        //htmlString = [NSString stringWithContentsOfCURL:theURL];
    if (imdbField != nil)
        [imdbField setStringValue:@""];
    if (htmlString == nil) return nil;
    [[NSNotificationCenter defaultCenter] postNotificationName:@"mhWebPageDownloadedNotification" object:self userInfo:[NSDictionary dictionaryWithObjectsAndKeys:address, @"link", htmlString, @"string", nil]];
    NSMutableDictionary *dict = [self parseIMDB:htmlString];
    [dict setObject:[address truncate:@"?"] forKey:@"Website"];
    return dict;
}

- (NSMutableDictionary *)parseIMDB:(NSString *)input
{
    if (DEBUG)
        NSLog(@"MHIMDB parseIMDB");
    NSString *path = [[NSUserDefaults standardUserDefaults] objectForKey:@"IMDBkeys"];
    if (!path) path = [[NSBundle mainBundle] pathForResource:@"IMDBkeys" ofType:@"xml"];
    NSDictionary *keys = [NSDictionary dictionaryWithContentsOfFile:path];
    if (!keys) NSLog(@"IMDBkeys file mis-formed!\n");
    
    if (input == nil) return nil;
    NSString *confirm = [keys objectForKey:@"confirm"];
    if (confirm) {
        NSRange aRange = [input rangeOfString:confirm];
        if (aRange.location == NSNotFound) {
            NSLog(@"Confirmation String: \"%@\" Not Found", confirm);
            return nil;
        }
    }
    NSMutableDictionary *theResult = [[NSMutableDictionary alloc] init];
    NSScanner *scan = [[NSScanner alloc] initWithString:input];
    [scan setCaseSensitive:NO];
    NSString *buffer, *name, *skip, *end, *value;
    NSDictionary *cat;
    NSNumber *repeating;
    NSArray *cats = [keys objectForKey:@"categories"];
    int i, j, k, m, n = [cats count];
    if (DEBUG)
        NSLog(@"starting to read imdb page...");
    for (i=0;i<n;i++) {
        [scan setScanLocation:0];
        buffer = name = skip = end = value = nil;
        cat = [cats objectAtIndex:i];
        name = [cat objectForKey:@"name"];
        if (DEBUG && name)
            NSLog(name);
        end = [cat objectForKey:@"end"];
        repeating = [cat objectForKey:@"repeating"];
        m = [[cat objectForKey:@"skip"] count];
        for (j=0;j<m;j++)
            [scan scanPastString:[[cat objectForKey:@"skip"] objectAtIndex:j]];
        while (![scan isAtEnd]) {
            [scan scanUpToString:end intoString:&buffer];
            buffer = [MHUtil fixIMDB:buffer];
            if (value)
                value = [NSString stringWithFormat:@"%@, %@", value, buffer];
            else
                value = buffer;
            if (!repeating) break;
            k = [repeating intValue];
            for (j-=k; j<m; j++)
                [scan scanPastString:[[cat objectForKey:@"skip"] objectAtIndex:j]];
        }
        if (value)
            [theResult setObject:value forKey:name];
        if (DEBUG && value)
            NSLog(value);
    }
    
    
    [scan release];
    return theResult;
}


BOOL isChoices(NSString *input) {
    //NSLog(@"tWWW isChoices");
    NSRange aRange;
    aRange = [input rangeOfString:@"<TITLE>IMDb" options:NSCaseInsensitiveSearch];
    return aRange.location != NSNotFound;
}



@end
