/*---------------------------------------------------------------------------*/
/* ios_async.cpp                                                             */
/* copyright (c) innovaphone 2014                                            */
/*                                                                           */
/*---------------------------------------------------------------------------*/

#define IOS_ASYNC_CPP_

#include "platform/platform.h"

#include <sys/poll.h>
#include "platform/phone_ios.h"
#if !defined(PLATFORM_NO_PACKET)
#include "common/linux/linux_filedesc.h"
#include "common/lib/config_options.h"
#include "box/ios/ios_main.h"
#endif

#include "ios_async.h"

#define FILE_ "ios_async.cpp"

USING_NAMESPACE;

/*----------------------------------------------------------------------------*/

volatile t_ios_async_queue_arg *ios_async_queue;

volatile dword ios_async_in_doorbell;
dword ios_async_in_doorbell_ack;
volatile dword ios_async_out_doorbell;
dword ios_async_out_doorbell_ack;

/*----------------------------------------------------------------------------*/

bool ios_async::init(t_ios_async_handler *handler_table, unsigned int handler_count, const char *main_module)
{
    this->handler_table = handler_table;
    this->handler_count = handler_count;

    ios_async_queue = (tag_ios_async_queue_arg *)(malloc(IOS_ASYNC_QUEUE_SIZE * sizeof(tag_ios_async_queue_arg)));
    queue_free = 0;

    ios_async_in_doorbell = 0;
    ios_async_in_doorbell_ack = 0;
    ios_async_out_doorbell = 0;
    ios_async_out_doorbell_ack = 0;

#if !defined(PLATFORM_NO_PACKET)
    this->main_module = main_module;
    main = 0;
    packet_pending = false;
#endif
    return true;
}

#if !defined(PLATFORM_MALLOC)

void ios_async::set_checked()
{
    t_ios_async_handler *desc;
    dword d, i;

    d = queue_free;
    while(d != (ios_async_in_doorbell & IOS_ASYNC_QUEUE_MASK)) {
        desc = &(handler_table[ios_async_queue[d].i]);
        d = (d + 1) & IOS_ASYNC_QUEUE_MASK;
        for(i = 0; desc->format[i] != '\0'; i++) {
            switch(desc->format[i]) {
#if !defined(PLATFORM_NO_PACKET)
            case 'P':
                if(ios_async_queue[d].p) {
                    ((class packet *)(ios_async_queue[d].p))->leak_check();
                }
                break;
#endif
            case 'A':
                bufman->set_checked(ios_async_queue[d].a);
                break;
            case 'B':
                bufman->set_checked(ios_async_queue[d].b);
            case 'M':
                d = (d + 1) & IOS_ASYNC_QUEUE_MASK;
                break;
            }
            d = (d + 1) & IOS_ASYNC_QUEUE_MASK;
        }
    }
}

#endif
#if !defined(PLATFORM_NO_PACKET)

void ios_async::packet_processed()
{
    packet_pending = false;
}

#endif

void ios_async::enqueue(unsigned int method, ...)
{
    va_list args;
    t_ios_async_handler *desc;
    const byte *b;
    byte *q;
    int size;
    dword i, d;

    while(queue_free != (ios_async_in_doorbell_ack & IOS_ASYNC_QUEUE_MASK)) {
        desc = &(handler_table[ios_async_queue[queue_free].i]);
        queue_free = (queue_free + 1) & IOS_ASYNC_QUEUE_MASK;
        for(i = 0; desc->format[i] != '\0'; i++) {
            switch(desc->format[i]) {
#if !defined(PLATFORM_NO_PACKET)
            case 'P':
                if(ios_async_queue[queue_free].p) {
                    delete (class packet *)(ios_async_queue[queue_free].p);
                }
                break;
#endif
            case 'A':
                if (ios_async_queue[queue_free].a) {
#ifdef PLATFORM_MALLOC
                    free(ios_async_queue[queue_free].a);
#else
                    bufman->free(ios_async_queue[queue_free].a);
#endif
                }
                break;
            case 'B':
                if (ios_async_queue[queue_free].b) {
#ifdef PLATFORM_MALLOC
                    free(ios_async_queue[queue_free].b);
#else
                    bufman->free(ios_async_queue[queue_free].b);
#endif
                }
            case 'M':
                queue_free = (queue_free + 1) & IOS_ASYNC_QUEUE_MASK;
                break;
            }
            queue_free = (queue_free + 1) & IOS_ASYNC_QUEUE_MASK;
        }
    }

    va_start(args, method);
    d = ios_async_in_doorbell & IOS_ASYNC_QUEUE_MASK;
    ios_async_queue[d].i = method;
    d = (d + 1) & IOS_ASYNC_QUEUE_MASK;
    desc = &(handler_table[method]);
    for(i = 0; desc->format[i] != '\0'; i++) {
        switch(desc->format[i]) {
#if !defined(PLATFORM_NO_PACKET)
        case 'P':
            if(!packet_pending) {
                packet_pending = true;
                if(!main) {
                    main = modman->find(main_module);
                }
                main->queue_event(main, main_event_start_keep_active());
            }
            ios_async_queue[d].p = va_arg(args, void *);
            break;
#endif
        case 'A':
#ifdef PLATFORM_MALLOC
            ios_async_queue[d].a = _strdup((const char *)(va_arg(args, const char *)));
#else
            ios_async_queue[d].a = bufman->alloc_strcopy((const char *)(va_arg(args, const char *)));
#endif
            break;
        case 'B':
            b = (const byte *)(va_arg(args, const byte *));
            size = va_arg(args, int);
            if (b) {
#ifdef PLATFORM_MALLOC
                ios_async_queue[d].b = (byte *)(malloc(size));
#else
                ios_async_queue[d].b = (byte *)(bufman->alloc(size));
#endif
                memcpy(ios_async_queue[d].b, b, size);
            }
            else {
                ios_async_queue[d].b = NULL;
            }
            d = (d + 1) & IOS_ASYNC_QUEUE_MASK;
            ios_async_queue[d].i = size;
            break;
        case 'M':
            q = (byte *)(va_arg(args, byte *));
            size = va_arg(args, int);
            ios_async_queue[d].b = q;
            d = (d + 1) & IOS_ASYNC_QUEUE_MASK;
            ios_async_queue[d].i = size;
            break;
        case 'Z':
            ios_async_queue[d].z = (bool)va_arg(args, int);
            break;
        default:
            ios_async_queue[d].i = va_arg(args, int);
            break;
        }
        d = (d + 1) & IOS_ASYNC_QUEUE_MASK;
    }
    va_end(args);

    ios_async_in_doorbell = (ios_async_in_doorbell & ~IOS_ASYNC_QUEUE_MASK) | (d & IOS_ASYNC_QUEUE_MASK);
}

#if !defined(PLATFORM_NO_PACKET)

int ios_async_get_packet (void *packet, byte *buffer, int buffer_len)
{
    class packet_ptr pp;
    return ((class packet *) packet)->read (&pp, buffer, buffer_len);
}

#endif
/*----------------------------------------------------------------------------*/
