/*---------------------------------------------------------------------------*/
/* IosAsync.m                                                                */
/* copyright (c) innovaphone 2015                                            */
/*                                                                           */
/*---------------------------------------------------------------------------*/

#import "platform/platform.h"
#import "ios_async.h"
#import "platform/IosNamespace.h"
#import "IosAsync.h"

IosAsync *ios_async_this;
id <IosAsyncSignal> ios_async_signal_this;
extern t_ios_async_handler ios_async_handler_table[];

@interface IosAsync()

@property (nonatomic,weak) id <IosAsyncSignal> signal;
@property (nonatomic) struct tag_ios_async_handler *handler_table;
@property (nonatomic) unsigned int handler_count;
@property (nonatomic) unsigned int last_handler;
@property (nonatomic) dword queue_pos;
@property (nonatomic) byte *packet_buffer;

- (bool) dequeue_;
- (bool) get_bool_;
- (int) get_int_;
- (NSString *) get_string_;
#if !defined(PLATFORM_NO_PACKET)
- (const byte *) get_packet_:(int *)plen;
#endif

@end
@implementation IosAsync

+ (id) iosAsyncWithHandlerTable:(struct tag_ios_async_handler *)handler_table handlerCount:(unsigned int)handler_count iosAsyncSignal:(__weak id <IosAsyncSignal>)signal
{
    return [[self alloc] initWithHandlerTable:handler_table handlerCount:handler_count iosAsyncSignal:signal];
}

- (id) initWithHandlerTable:(struct tag_ios_async_handler *)handler_table handlerCount:(unsigned int)handler_count iosAsyncSignal:(__weak id <IosAsyncSignal>)signal
{
    unsigned int i;

    self = [super init];
    if (self) {
        IosAsync *__weak weakSelf = self;
        _dequeue = ^ bool () { return [weakSelf dequeue_]; };
        _get_bool = ^ bool () { return [weakSelf get_bool_]; };
        _get_int = ^ int () { return [weakSelf get_int_]; };
        _get_string = ^ NSString *() { return [weakSelf get_string_]; };
        _get_byte_array = ^ byte *(int *plen, bool claim) { return [weakSelf get_byte_array_:plen claim:claim]; };
        _get_memory = ^ byte *(int *plen) { return [weakSelf get_memory_:plen]; };
#if !defined(PLATFORM_NO_PACKET)
        _get_packet = ^ const byte *(int *plen) { return [weakSelf get_packet_:plen]; };
        self.packet_buffer = (byte *)(malloc(IOS_ASYNC_MAX_PACKET_SIZE));
#endif

        self.handler_table = handler_table;
        self.handler_count = handler_count;
        self.last_handler = 0;
        self.signal = signal;
        ios_async_this = self;
        ios_async_signal_this = signal;

        for (i = 0; i < handler_count; i++) {
            handler_table[i].selector = (intp)(void *)(NSSelectorFromString([NSString stringWithFormat:@"%s:", handler_table[i].method]));
        }
    }
    return self;
}

- (bool) dequeue_
{
    if (((ios_async_in_doorbell ^ ios_async_in_doorbell_ack) & IOS_ASYNC_QUEUE_MASK) == 0) {
        return false;
    }
    self.queue_pos = ios_async_in_doorbell_ack;
    unsigned int h = self.get_int();
    if (!(h < self.handler_count)) {
        NSLog(@"FATAL %s,%i: %s", __FILE__, __LINE__, ios_async_handler_table[self.last_handler].method);
        assert(0);
    }
    self.last_handler = h;
    t_ios_async_handler *handler = &(self.handler_table[h]);
    int obj_id = self.get_int();
    id obj = [self.signal ios_async_get_id_obj:obj_id];

//    NSLog(@"dequeue %s(%d,%d)", handler->method, obj_id, ios_async_queue[self.queue_pos & IOS_ASYNC_QUEUE_MASK].i);

    [obj performSelector:(SEL)(handler->selector) withObject:self];
    ios_async_in_doorbell_ack = (ios_async_in_doorbell_ack & ~IOS_ASYNC_QUEUE_MASK) | (self.queue_pos & IOS_ASYNC_QUEUE_MASK);
    return true;
}

- (bool) get_bool_
{
    bool z = ios_async_queue[self.queue_pos & IOS_ASYNC_QUEUE_MASK].z;
    self.queue_pos++;
    return z;
}

- (int) get_int_
{
    int i = ios_async_queue[self.queue_pos & IOS_ASYNC_QUEUE_MASK].i;
    self.queue_pos++;
    return i;
}

- (NSString *) get_string_
{
    char *a = ios_async_queue[self.queue_pos & IOS_ASYNC_QUEUE_MASK].a;
#ifdef PLATFORM_MALLOC
    ios_async_queue[self.queue_pos & IOS_ASYNC_QUEUE_MASK].a = NULL;
    self.queue_pos++;
    return (a ? [[NSString alloc] initWithBytesNoCopy:a length:strlen(a) encoding:NSUTF8StringEncoding freeWhenDone:true] : nil);
#else
    self.queue_pos++;
    return (a ? [NSString stringWithCString:a encoding:NSUTF8StringEncoding] : nil);
#endif
}

- (byte *) get_byte_array_:(int *)plen claim:(bool)claim
{
    byte *p = ios_async_queue[self.queue_pos & IOS_ASYNC_QUEUE_MASK].b;
    if (claim) {
        ios_async_queue[self.queue_pos & IOS_ASYNC_QUEUE_MASK].b = nil;
    }
    self.queue_pos++;
    *plen = ios_async_queue[self.queue_pos & IOS_ASYNC_QUEUE_MASK].i;
    self.queue_pos++;
    return p;
}

- (byte *) get_memory_:(int *)plen
{
    byte *p = ios_async_queue[self.queue_pos & IOS_ASYNC_QUEUE_MASK].b;
    self.queue_pos++;
    *plen = ios_async_queue[self.queue_pos & IOS_ASYNC_QUEUE_MASK].i;
    self.queue_pos++;
    return p;
}

#if !defined(PLATFORM_NO_PACKET)

- (const byte *) get_packet_:(int *)plen
{
    void *p = ios_async_queue[self.queue_pos & IOS_ASYNC_QUEUE_MASK].p;
    self.queue_pos++;
    if (!p) {
        return nil;
    }
    *plen = ios_async_get_packet (p, self.packet_buffer, IOS_ASYNC_MAX_PACKET_SIZE);
    return self.packet_buffer;
}

#endif
@end

void phone_ios_async_signal_handler() {
    [(NSObject *)ios_async_signal_this performSelectorOnMainThread:@selector(ios_async_signal_handler:) withObject:ios_async_this waitUntilDone:NO];
}
