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

#import "platform/platform.h"
#import "ios_event.h"
#import "platform/IosNamespace.h"
#import "IosEvent.h"

void main_signal();

@interface IosEvent()

@property (nonatomic) struct tag_ios_event_handler *handler_table;
@property (nonatomic) unsigned int handler_count;
@property (nonatomic) dword queue_free;

- (void) enqueue_:(int)event_id args:(va_list)args;

@end
@implementation IosEvent

+ (id) iosEventWithHandlerTable:(struct tag_ios_event_handler *)handler_table handlerCount:(unsigned int)handler_count
{
    return [[self alloc] initWithHandlerTable:handler_table handlerCount:handler_count];
}

- (id) initWithHandlerTable:(struct tag_ios_event_handler *)handler_table handlerCount:(unsigned int)handler_count
{
    self = [super init];
    if (self) {
        IosEvent *__weak weakSelf = self;
        _enqueue = ^ void (int event_id, ...) {
            va_list args;
            va_start(args, event_id);
            [weakSelf enqueue_:event_id args:args];
            va_end(args);
        };

        self.handler_table = handler_table;
        self.handler_count = handler_count;
        self.queue_free = 0;
        ios_event_in_doorbell_req = 0;
        ios_event_in_doorbell_ack = 0;
        ios_event_out_doorbell_req = 0;
        ios_event_out_doorbell_ack = 0;

        ios_event_queue = (t_ios_event_queue_arg *)(malloc(IOS_EVENT_QUEUE_SIZE * sizeof(t_ios_event_queue_arg)));
    }
    return self;
}

- (void) enqueue_:(int)event_id args:(va_list)args
{
    t_ios_event_handler *desc;
    NSString *str;
    NSData *data;
    int i, j;

    while(self.queue_free != (ios_event_in_doorbell_ack & IOS_EVENT_QUEUE_MASK)) {
        desc = &self.handler_table[ios_event_queue[self.queue_free].i];
        self.queue_free = (self.queue_free + 1) & IOS_EVENT_QUEUE_MASK;
        for(i = 0; desc->format[i] != '\0'; i++) {
            switch(desc->format[i]) {
            case 'A':
                if(ios_event_queue[self.queue_free].l) {
                    CFRelease(ios_event_queue[self.queue_free].l);
                }
                self.queue_free = (self.queue_free + 1) & IOS_EVENT_QUEUE_MASK;
                break;
            case 'B':
                if(ios_event_queue[self.queue_free].l) {
                    CFRelease(ios_event_queue[self.queue_free].l);
                }
            case 'M':
                self.queue_free = (self.queue_free + 2) & IOS_EVENT_QUEUE_MASK;
                break;
            }
            self.queue_free = (self.queue_free + 1) & IOS_EVENT_QUEUE_MASK;
        }
    }

    desc = &self.handler_table[event_id];
    j = ios_event_in_doorbell_req & IOS_EVENT_QUEUE_MASK;
    ios_event_queue[j].i = event_id;
    j = (j + 1) & IOS_EVENT_QUEUE_MASK;
    for(i = 0; desc->format[i] != '\0'; i++) {
        switch(desc->format[i]) {
        case 'A':
            str = va_arg(args, NSString *);
            if(str) {
                data = [NSData dataWithBytes:[str cStringUsingEncoding:NSUTF8StringEncoding] length:[str lengthOfBytesUsingEncoding:NSUTF8StringEncoding]+1];
                ios_event_queue[j].l = (void *)(CFBridgingRetain(data));
                j = (j + 1) & IOS_EVENT_QUEUE_MASK;
                ios_event_queue[j].a = (const char *)(data.bytes);
            }
            else {
                ios_event_queue[j].l = 0;
                j = (j + 1) & IOS_EVENT_QUEUE_MASK;
                ios_event_queue[j].a = 0;
            }
            break;
        case 'B':
        case 'M':
            data = va_arg(args, NSData *);
            if(data) {
                ios_event_queue[j].l = (void *)(CFBridgingRetain(data));
                j = (j + 1) & IOS_EVENT_QUEUE_MASK;
                ios_event_queue[j].i = data.length;
                j = (j + 1) & IOS_EVENT_QUEUE_MASK;
                ios_event_queue[j].b = data.bytes;
            }
            else {
                ios_event_queue[j].l = 0;
                j = (j + 1) & IOS_EVENT_QUEUE_MASK;
                ios_event_queue[j].i = 0;
                j = (j + 1) & IOS_EVENT_QUEUE_MASK;
                ios_event_queue[j].b = 0;
            }
            break;
        case 'Z':
            ios_event_queue[j].z = (bool)(va_arg(args, int));
            break;
        default:
            ios_event_queue[j].i = va_arg(args, int);
            break;
        }
        j = (j + 1) & IOS_EVENT_QUEUE_MASK;
    }
    ios_event_in_doorbell_req = (ios_event_in_doorbell_req & ~IOS_EVENT_QUEUE_MASK) | j;
    
    main_signal();
}

void async_event_release_memory(void * ref, byte * b, bool modified)
{
    CFRelease(ref);
}

@end
