#import <Security/Authorization.h>
#import <openssl/evp.h>
//#import <CoreServices/CoreServices.h>
#import "ApplicationDelegate.h"
#import "AppleScript.h"
#import "MyHTTPConnection.h"
#import "RecordingInfo.h"


// this code is from Jonathan Rentsch (BSD licensed)
static OSErr FindProcessBySignature( OSType type, OSType creator, ProcessSerialNumber *psn ) {
    ProcessSerialNumber tempPSN = { 0, kNoProcess };
    ProcessInfoRec procInfo;
    OSErr err = noErr;
    
    ///RequirePtr( psn );
    
    procInfo.processInfoLength = sizeof( ProcessInfoRec );
    procInfo.processName = nil;
    procInfo.processAppSpec = nil;
    
    while( !err ) {
        err = GetNextProcess( &tempPSN );
        if( !err )
            err = GetProcessInformation( &tempPSN, &procInfo );
        if( !err
            && procInfo.processType == type
            && procInfo.processSignature == creator ) {
            *psn = tempPSN;
            return noErr;
        }
    }
    
    return err;
}


@implementation ApplicationDelegate

- (void)dumpDescriptor:(NSAppleEventDescriptor *)descriptor withIndent:(int)indent {
    int descriptorType = [descriptor descriptorType];
    int numberOfItems = [descriptor numberOfItems];
    int i;
    for (i = 0; i < indent; ++i) {
        printf(" ");
    }
    printf("Descriptor type: %.4s\n", &descriptorType);
    for (i = 0; i < indent; ++i) {
        printf(" ");
    }
    printf("Number of items: %d\n", [descriptor numberOfItems]);
    for (i = 0; i < indent; ++i) {
        printf(" ");
    }
    printf("String value: %s\n", [[descriptor stringValue] cString]);
    /*int keyword = [descriptor keywordForDescriptorAtIndex:1];
    printf("Keyword for index 1: %.4s\n", &keyword);
    for (i = 0; i < indent; ++i) {
        printf(" ");
    }
    keyword = [descriptor keywordForDescriptorAtIndex:2];
    printf("Keyword for index 2: %.4s\n", &keyword);
    for (i = 0; i < indent; ++i) {
        printf(" ");
    }
    keyword = [descriptor keywordForDescriptorAtIndex:3];
    printf("Keyword for index 3: %.4s\n", &keyword);
    for (i = 0; i < indent; ++i) {
        printf(" ");
    }
    keyword = [descriptor keywordForDescriptorAtIndex:4];
    printf("Keyword for index 4: %.4s\n", &keyword);
    for (i = 0; i < indent; ++i) {
        printf(" ");
    }
    keyword = [descriptor keywordForDescriptorAtIndex:5];
    printf("Keyword for index 5: %.4s\n", &keyword);*/
    for (i = 1; i <= numberOfItems; ++i) {
        [self dumpDescriptor:[descriptor descriptorAtIndex:i] withIndent:indent + 2];
    }
}

// check whether EyeTV is running
- (BOOL)isEyeTVRunning {
    ProcessSerialNumber eyeTVPSN;
    if (FindProcessBySignature('APPL', 'EyTV', &eyeTVPSN) == noErr) {
        return TRUE;
    }
    return FALSE;
}

- (void)startEyeTV {
    @try {
        [AppleScript executeScript:@"tell application \"EyeTV\" to launch"];
        _tryToStartEyeTV = 0;
    } @catch (NSException *ex) {
        ++_tryToStartEyeTV;
        if (_tryToStartEyeTV == 5) {
            _tryToStartEyeTV = 0;
            NSRunAlertPanel(@"Cannot start EyeTV", @"Access to EyeTV recordings or live streams won't work.", nil, nil, nil);
        } else {
            [self performSelector:@selector(startEyeTV) withObject:nil afterDelay:2];
        }
    }
}

- (void)stopEyeTV {
    [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(startEyeTV) object:nil];
    _tryToStartEyeTV = 0;
    [AppleScript executeScript:@"tell application \"EyeTV\" to quit"];
}

// get EyeTV's bundle location
- (NSString *)getEyeTVPath {
    /*ProcessSerialNumber eyeTVPSN;
    if (FindProcessBySignature('APPL', 'EyTV', &eyeTVPSN) == noErr) {
        FSRef location;
        UInt8 path[1024];
        if (GetProcessBundleLocation(&eyeTVPSN, &location) == noErr) {
            if (FSRefMakePath(&location, path, 1024) == noErr) {
                return [[NSFileManager defaultManager] stringWithFileSystemRepresentation:(const char *)path length:strlen((const char *)path)];
            }
        }
    }*/     // we need to quit and relaunch EyeTV anyway when doing something with the path, so don't care for the actual running instance
    NSURL *temp;
    OSStatus err = LSGetApplicationForInfo(kLSUnknownType, 'EyTV', NULL, kLSRolesAll, NULL, (CFURLRef *)&temp);
    if (err == noErr) {
        [temp autorelease];
        return [temp path];
    }
    return nil;
}

- (void)handleHTTPError:(NSError *)error {
    NSString *errorMessage = [error localizedDescription];
    if (errorMessage == nil) {
        errorMessage = @"";
    }
    NSRunAlertPanel(NSLocalizedString(@"CANNOT_START_HTTP_SERVER", @"Cannot start CyTV's HTTP server"), errorMessage, nil, nil, nil);
}

- (void)applicationWillFinishLaunching:(NSNotification *)aNotification {
    [self startEyeTV];
    [_preferencesController initPreferences];
    [_preferencesController addObserver:self forKeyPath:@"serverPort" options:NSKeyValueObservingOptionNew context:NULL];
}

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    // start the HTTP server
    _httpServer = [[HTTPServer alloc] init];
    [_httpServer setPort:[_preferencesController serverPort]];
    [_httpServer setType:@"_http._tcp"];
    [_httpServer setName:[NSString stringWithFormat:@"CyTV (%@)", [((NSString *)CSCopyMachineName()) autorelease]]];
    [_httpServer setConnectionClass:[MyHTTPConnection self]];
    [_httpServer setTXTRecordDictionary:[NSDictionary dictionary]];
    NSError *error;
    if (![_httpServer start:&error]) {
        [self handleHTTPError:error];
    }
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    if ([keyPath isEqualToString:@"serverPort"]) {
        printf("New server port: %d\n", [_preferencesController serverPort]);
        [_httpServer stop];
        [_httpServer setPort:[_preferencesController serverPort]];
        if ([_preferencesController serverPort] < 1024) {
            // TODO
        }
        NSError *error;
        if (![_httpServer start:&error]) {
            [self handleHTTPError:error];
        }
    }
}

- (void)installOrRemoveEyeTVPlugin:(BOOL)remove {
    NSString *eyeTVPath = [self getEyeTVPath];
    if (eyeTVPath == nil) {
        NSRunAlertPanel(@"Cannot install the plugin", @"The path to the EyeTV application cannot be determined.", nil, nil, nil);
        return;
    }
    BOOL wasRunning = FALSE;
    if ([self isEyeTVRunning]) {
        wasRunning = TRUE;
        @try {
            [self stopEyeTV];
        } @catch (NSException *ex) {
            if (remove) {
                NSRunAlertPanel(@"Cannot remove the plugin", @"Cannot quit EyeTV.", nil, nil, nil);
            } else {
                NSRunAlertPanel(@"Cannot install the plugin", @"Cannot quit EyeTV.", nil, nil, nil);
            }
            return;
        }
    }
    NSString *resourcePath = [[NSBundle mainBundle] resourcePath];
    NSString *sourcePath = [resourcePath stringByAppendingString:@"/EyeTVPluginCyTV.bundle"];
    NSString *destPath = [eyeTVPath stringByAppendingString:@"/Contents/Plugins"];
    NSString *completeDestPath = [destPath stringByAppendingString:@"/EyeTVPluginCyTV.bundle"];
    NSFileManager *fileManager = [NSFileManager defaultManager];
    if (![fileManager removeFileAtPath:completeDestPath handler:nil]) {
        if (remove) {
            if ([fileManager fileExistsAtPath:completeDestPath]) {
                NSRunAlertPanel(@"Cannot remove the plugin", @"Cannot remove the plugin files from the EyeTV plugin directory.\n\nIf you installed EyeTV as a different user, please start CyTV as this user and try again.", nil, nil, nil);
            } else {
                NSRunAlertPanel(@"Cannot remove the plugin", @"The plugin isn't installed.", nil, nil, nil);
            }
        }
    } else if (remove) {
        NSRunAlertPanel(@"Plugin removed", @"The plugin has been removed. EyeTV will now be restarted.", nil, nil, nil);
    }
    if (!remove) {
        [fileManager createDirectoryAtPath:destPath attributes:nil];
        if (![fileManager copyPath:sourcePath toPath:completeDestPath handler:nil]) {
            NSRunAlertPanel(@"Cannot install the plugin", @"Cannot write the plugin files to the EyeTV plugin directory.\n\nIf you installed EyeTV as a different user, please start CyTV as this user and try again.", nil, nil, nil);
        } else {
            NSRunAlertPanel(@"Plugin installed", @"The plugin has been installed. EyeTV will now be restarted.", nil, nil, nil);
        }
    }
    [self startEyeTV];
    if (wasRunning) {
        [self performSelector:@selector(startEyeTV) withObject:nil afterDelay:2];
    }
}

- (IBAction)installEyeTVPlugin:(id)sender {
    [self installOrRemoveEyeTVPlugin:FALSE];
}

- (IBAction)removeEyeTVPlugin:(id)sender {
    [self installOrRemoveEyeTVPlugin:TRUE];
}

@end
