
#import "ABSubprocessWrapper.h"
#import "ABUtil.h"

@implementation ABSubprocessWrapper

- (id)initWithRouter:(ABRouter*)router name:(NSString*)name launchPath:(NSString*)launchPath arguments:(NSArray*)arguments;
{
    self = [super init];
    if (self) {
        
        _initialStdout = [[NSMutableString stringWithCapacity:AB_INITIAL_STDIO_LENGTH_APPROX + 500] retain];
        _initialStderr = [[NSMutableString stringWithCapacity:AB_INITIAL_STDIO_LENGTH_APPROX + 500] retain];
        
        _router = router;
        
        _name = [name retain];
        _launchPath = [launchPath retain];
        _arguments = [arguments retain];
        
        [router _registerComponent:self];
        
        _stdoutText = [NSMutableString stringWithCapacity:5000];
        [_stdoutText retain];
        
        [self _spawn];
    }
    return self;
}

- (void)_spawn
{
    _task = [[NSTask alloc] init];
    [_task setLaunchPath:_launchPath];
    [_task setArguments:_arguments];
    
    [_task setStandardInput: [NSPipe pipe]];
    [_task setStandardOutput: [NSPipe pipe]];
    [_task setStandardError: [NSPipe pipe]];
    
    [[NSNotificationCenter defaultCenter]
         addObserver:self 
         selector:@selector(stdoutData:) 
         name: NSFileHandleReadCompletionNotification 
         object: [[_task standardOutput] fileHandleForReading]];
    
    [[NSNotificationCenter defaultCenter]
         addObserver:self 
         selector:@selector(stderrData:) 
         name: NSFileHandleReadCompletionNotification 
         object: [[_task standardError] fileHandleForReading]];
    
    [[[_task standardOutput] fileHandleForReading] readInBackgroundAndNotify];
    [[[_task standardError] fileHandleForReading] readInBackgroundAndNotify];
    
    [_task launch];
    [_task retain];
}

- (NSTask*) task
{
    return _task;
}

- (void)killDashNine
{
    kill([_task processIdentifier], SIGKILL); // "Yeah it's kill dash nine / no more CPU time!"
}


- (NSString*)_getName
{
    return _name;
}

- (void) _sendEventJson:(NSString*)json
{
    if (_router.verbose) {
        NSLog(@"[ABSubprocessWrapper] _sendEventJson:%@", json);   
    }
    NSData *data = [json dataUsingEncoding:NSUTF8StringEncoding];
    NSString *base64 = [ABUtil b64encode:data];
    NSString *text = [NSString stringWithFormat:@"%@\n", base64];
    NSPipe* pipe = [_task standardInput];
    [[pipe fileHandleForWriting] writeData:[text dataUsingEncoding:NSUTF8StringEncoding]];
}

- (void)stdoutData:(NSNotification*)notification
{
	NSData *data = [[notification userInfo] objectForKey:NSFileHandleNotificationDataItem];
	if ([data length]) {
        NSString *s = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease];
        if ([_initialStdout length] < AB_INITIAL_STDIO_LENGTH_APPROX) {
            [_initialStdout appendString:s];
        }
        [_stdoutText appendString:s];
        [self checkForEvents];
	}
    else {
        [self stopProcess];
    }
	[[notification object] readInBackgroundAndNotify];  
}

- (void)stderrData:(NSNotification*)notification
{
	NSData *data = [[notification userInfo] objectForKey:NSFileHandleNotificationDataItem];
	if ([data length]) {
		NSString *s = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease];
        if ([_initialStderr length] < AB_INITIAL_STDIO_LENGTH_APPROX) {
            [_initialStderr appendString:s];
        }
        [_router _subprocess:self gotStderrText:s];
	}
    else {
        [self stopProcess];
    }
	[[notification object] readInBackgroundAndNotify];  
}

- (void)checkForEvents
{
    if (_router.verbose) {
        NSLog(@"[ABRouter] subproc checkForEvents");
    }
    NSRange range = [_stdoutText rangeOfString:@"\n"];
    while (range.location != NSNotFound) {
        NSString *base64 = [_stdoutText substringWithRange:NSMakeRange(0, range.location)];
        NSString *json = [[NSString alloc] initWithData:[ABUtil b64decode:base64] encoding:NSUTF8StringEncoding];
        if (_router.verbose) {
            NSLog(@"[ABRouter] subproc sent us json: %@", json);
        }
        [_router _processEventJson:json];
        [_stdoutText setString:[_stdoutText substringWithRange:NSMakeRange(
                                                               range.location + 1,
                                                               [_stdoutText length] - range.location - 1)]];
        range = [_stdoutText rangeOfString:@"\n"];
    }
}

- (void)stopProcess
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    
    [_task terminate];
    
    if (_router.delegate != nil) {
        [_router.delegate
             handleABExitOfTask:[self task]
             name:_name
             stdoutText:_initialStdout
             stderrText:_initialStderr];
    }
    [_router _subprocessTerminated:self];
}

- (void)respawn
{
    if (_router.verbose) {
        NSLog(@"[respawn] old PID: %d", (int)[_task processIdentifier]);
    }
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    [_task terminate];
    [_task waitUntilExit];
    [self _spawn];
    if (_router.verbose) {
        NSLog(@"[respawn] new PID: %d", (int)[_task processIdentifier]);
    }
}

@end
