// -*- coding: utf-8-dos -*-

#import <Cocoa/Cocoa.h>

#define NUM_THREADS 2

// [NSThread sleepUntilDate:[[NSDate date] addTimeInterval:0.5]];
// [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:10]];
NSPort* port1 = nil;
NSPort* port2 = nil;

@interface SubTask : NSObject {
@private
    NSString* ownerThreadName;
    NSNumber* taskNumber;
    SubTask* proxies[NUM_THREADS];
}
- (oneway void)hello;
- (oneway void)foo;
- (oneway void)bye;
- (NSArray*)createConnectionData;
- (oneway void)addNewThread:(in NSArray*)data;
- (void)startThread:(NSNumber*)data;
@end

@interface MainTask : NSObject {
@private
    SubTask* proxies[NUM_THREADS];
}
- (void)start;
- (void)addNewThread:(in NSArray*)data;
@end

@implementation MainTask
- (void)addNewThread:(in NSArray*)data {
    NSString* name = [[[NSThread currentThread] threadDictionary] objectForKey:@"name"];
    NSNumber* taskNumber = [data objectAtIndex:0];
    NSPort* port1 = [data objectAtIndex:1];
    NSPort* port2 = [data objectAtIndex:2];

    int task_num = [taskNumber intValue];
    NSConnection* connection = [NSConnection connectionWithReceivePort:port2
                                                           sendPort:port1];
    [connection retain];

    NSLog(@"[%s] MainTask::addNewThread %d start", [name UTF8String], task_num);
    proxies[task_num] = [(SubTask*)[[connection rootProxy] retain] retain];
    [proxies[task_num] hello];

    int i = 0;
    for (i = 0; i < NUM_THREADS; i++) {
        if (i == task_num) {
            continue;
        }
        if (proxies[i] == nil) {
            continue;
        }

        NSArray* newData = [proxies[task_num] createConnectionData];
        NSNumber* newTaskNumber = [newData objectAtIndex:0];
        NSPort* newPort1 = [newData objectAtIndex:1];
        NSPort* newPort2 = [newData objectAtIndex:2];
               
        NSLog(@"[%s] MainTask::addNewThread %d notify %d",
              [name UTF8String], task_num, i);
        SubTask* proxy = proxies[i];
        NSArray* array = [NSArray arrayWithObjects:newTaskNumber, newPort1, newPort2, nil];

        [proxies[task_num] foo];

        NS_DURING
        {
            //[proxy addNewThread:a];
            //[proxy addNewThread:newData];
            [proxy addNewThread:nil];
        }
        NS_HANDLER
        {
            NSLog(@"[%s] MainTask::addNewThread %d notify %d Failed",
                  [name UTF8String], task_num, i);
        }
        NS_ENDHANDLER;
    }

    NSLog(@"[%s] MainTask::addNewThread %d ok", [name UTF8String], task_num);
}

- (void)start {
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
    NSLog(@"start [%s]", [[[[NSThread currentThread] threadDictionary] objectForKey:@"name"] UTF8String]);

    NS_DURING
    {
        int task_num = 0;

        for (task_num = 0; task_num < NUM_THREADS; task_num++) {
            NSNumber* taskNumber = [[NSNumber numberWithInt:task_num] retain];
            proxies[task_num] = nil;
            SubTask* task = [[[SubTask alloc] init] retain];
            [NSThread detachNewThreadSelector:@selector(startThread:)
                      toTarget:task
                      withObject:taskNumber];
        }

        NSString* name = [[[NSThread currentThread] threadDictionary]
                          objectForKey:@"name"];
        NSLog(@"[%s] thread created", [name UTF8String]);
/*
        NSWindow* window = [NSWindow alloc];
        [window initWithContentRect: NSMakeRect(100, 100, 200, 200)
                styleMask: NSTitledWindowMask | NSMiniaturizableWindowMask | NSClosableWindowMask
                backing: NSBackingStoreBuffered
                defer: NO];
        [window retain];
        [window setTitle: @"Hello, input"];
        [window makeKeyAndOrderFront: nil];
*/
        // [NSException raise:@"Application_will_terminate" format:@"Someone requested to terminate application"];
        [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:10]];        
        //[NSApp run];
    }
    NS_HANDLER
    {
        NSLog(@"Objective-C exception [%s] name=[%s] reason=[%s]\n",
              [[[[NSThread currentThread] threadDictionary] objectForKey:@"name"] UTF8String],
              [[localException name] UTF8String],
              [[localException reason] UTF8String]);
    }
    NS_ENDHANDLER;
     
    [pool release];
}
@end

MainTask* mainTask = nil;

@implementation SubTask

- (oneway void)addNewThread:(in NSArray*)data {
    NSString* name = [[[NSThread currentThread] threadDictionary] objectForKey:@"name"];
    NSLog(@"[%s] SubTask::addNewThread start", [name UTF8String]);

    NSConnection* connection = [NSConnection connectionWithReceivePort:port2
                                             sendPort:port1];
    [connection retain];

    SubTask* proxy = [(SubTask*)[[connection rootProxy] retain] retain];
    [proxy bye];
    return;
/*
    NSNumber* subTaskNumber = [data objectAtIndex:0];
    int sub_task_num = [subTaskNumber intValue];
    NSLog(@"[%s] SubTask::addNewThread %d start", [name UTF8String], sub_task_num);
    NSPort* port1 = [data objectAtIndex:1];
    NSLog(@"[%s] SubTask::addNewThread %d port1", [name UTF8String], sub_task_num);
    NSPort* port2 = [data objectAtIndex:2];
    NSLog(@"[%s] SubTask::addNewThread %d port2", [name UTF8String], sub_task_num);

    NSConnection* connection = [NSConnection connectionWithReceivePort:port2
                                                              sendPort:port1];
    NSLog(@"[%s] SubTask::addNewThread %d connected", [name UTF8String], sub_task_num);

    [connection retain];

    proxies[sub_task_num] = [(SubTask*)[[connection rootProxy] retain] retain];

    int i = 0;
    for (i = 0; i < NUM_THREADS; i++) {
        if (proxies[i] == nil) {
            NSLog(@"[%s] SubTask::addNewThread %d notify %d=(null)",
                  [name UTF8String], sub_task_num, i);
            continue;
        }
        NSLog(@"[%s] SubTask::addNewThread %d notify %d",
              [name UTF8String], sub_task_num, i);
        [proxies[i] hello];
    }

    NSLog(@"[%s] SubTask::addNewThread %d ok", [name UTF8String], sub_task_num);
*/
}

- (oneway void)hello {
    NSString* name = [[[NSThread currentThread] threadDictionary] objectForKey:@"name"];
    NSLog(@"[%s] SubTask::hello", [name UTF8String]);
}

- (oneway void)bye {
    NSString* name = [[[NSThread currentThread] threadDictionary] objectForKey:@"name"];
    NSLog(@"[%s] SubTask::bye", [name UTF8String]);
}

- (oneway void)foo {
    NSString* name = [[[NSThread currentThread] threadDictionary] objectForKey:@"name"];
    NSLog(@"[%s] SubTask::foo", [name UTF8String]);

    port1 = [NSPort port];
    port2 = [NSPort port];
    [[port1 retain] retain];
    [[port2 retain] retain];
    NSConnection *serverConnection =
        [[NSConnection alloc] initWithReceivePort:port1
                                         sendPort:port2];
    [[serverConnection retain] retain];
    [serverConnection setRootObject:self];
}

- (NSArray*)createConnectionData {
    [[taskNumber retain] retain];

    NSPort* port1 = [NSPort port];
    NSPort* port2 = [NSPort port];
    [[port1 retain] retain];
    [[port2 retain] retain];

    NSString* str = @"this is test dat";
    [[str retain] retain];

    NSConnection *serverConnection =
        [[NSConnection alloc] initWithReceivePort:port1
                                         sendPort:port2];
    [[serverConnection retain] retain];
    [serverConnection setRootObject:self];

    NSArray* data = [NSArray arrayWithObjects:taskNumber, port1, port2, str, nil];
    [[data retain] retain];
    return data;
}

- (void)startThread:(NSNumber*)taskNumber_ {
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];

    NS_DURING
    {
        int i = 0;
        for (i = 0; i < NUM_THREADS; i++) {
            proxies[i] = nil;
        }

        taskNumber = taskNumber_;
        ownerThreadName = [@"SubTask" stringByAppendingString:[taskNumber description]];
        [ownerThreadName retain];

        NSThread* currentThread = [NSThread currentThread];
        [[currentThread threadDictionary] setObject:ownerThreadName forKey:@"name"];
        
        NSLog(@"[%s] SubTask::startThread ready", [ownerThreadName UTF8String]);

        NSArray* data = [self createConnectionData];
        
        //@synchronized (mainTask) {
            [mainTask performSelectorOnMainThread:@selector(addNewThread:)
                      withObject:data
                      waitUntilDone:NO];
        //}

        [[NSRunLoop currentRunLoop] run];
    }  
    NS_HANDLER
    {
        NSLog(@"Objective-C exception [%s] name=[%s] reason=[%s]\n",
              [[[[NSThread currentThread] threadDictionary] objectForKey:@"name"] UTF8String],
              [[localException name] UTF8String],
              [[localException reason] UTF8String]);
    }
    NS_ENDHANDLER;
       

    [pool release];
}
@end

int main() {
    fputs("main()\n", stderr);
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];

    NS_DURING
    {
        [NSApplication sharedApplication];

        [[[NSThread currentThread] threadDictionary] setObject:@"MainTask" forKey:@"name"];
        
        mainTask = [[[MainTask alloc] init] retain];
        //[mainTask performSelectorOnMainThread:@selector(start) withObject:nil waitUntilDone:NO];
        [mainTask start];

        //[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:10]];
        //[[NSRunLoop currentRunLoop] run];
        //[NSApp run];
    }
    NS_HANDLER
    {
        NSLog(@"Objective-C exception [%s] name=[%s] reason=[%s]\n",
              [[[[NSThread currentThread] threadDictionary] objectForKey:@"name"] UTF8String],
              [[localException name] UTF8String],
              [[localException reason] UTF8String]);
    }
    NS_ENDHANDLER;

    [pool release];
    fputs("main() return\n", stderr);
    return 0;
}



