/*----------------------------------------------------------------------------*/
/* async_forms.cpp                                                            */
/* copyright (c) innovaphone 2013                                             */
/*                                                                            */
/*----------------------------------------------------------------------------*/

extern volatile bool connectivity_lan;
extern void phone_external_call_idle(const char *number);
extern void phone_external_call_ringing(const char *number);
extern void phone_external_call_offhook(const char *number);
extern void phone_audio_focus_loss(bool loss);
extern void phone_headset_plugged(bool plugged);
extern void phone_headset_hook();
extern void phone_audio_devices_updated();
extern void phone_earpiece_proximity(bool nearby);
extern void phone_registration_check();
extern void phone_set_push_token(const char *token);
extern void phone_push_notification();
extern void phone_set_expect_touches(bool expectTouchesSupported, bool expectTouches);
extern void phone_set_shown_locked(bool shownLockedSupported, bool shownLocked);
extern void phone_set_proximity(bool proximitySupported, bool proximity);
extern void phone_wake_phone();
extern void phone_open_permalink(const char * link);
extern void phone_local_notification_clicked(const char *ref);
extern void phone_local_notification_accept(const char *ref);
extern void phone_local_notification_reject(const char *ref);
extern void phone_local_notification_reply(const char *ref, const char *replyText);
extern void phone_contacts_search(const char *ref, const char *info);
extern void phone_contacts_search_more();
extern void phone_contacts_lookup(const char *ref, const char *info);
extern void phone_webserver_get(const char *context, bool complete, int length, const byte *data);
extern void phone_dial_number(const char *number, const char *name, int video_mode);
extern void phone_dial_sip(const char *sip, const char *name, int video_mode);
extern void phone_chat(const char *sip);
extern void phone_webcam_added(const char * id, bool facing_front, int orientation, const char * context, const char * description);
extern void phone_webcam_removed(const char * id);
extern void phone_webcam_query_result(const char * context);
extern void phone_webcam_failed(const char * id, const char * context);
extern void phone_webcam_data(const char * id, dword timestamp, int length, const byte *data, int previewLength, const byte *previewData);
extern void phone_webcam_sample(const char * id, dword timestamp, int length, const byte *sample, int width, int height, int stride, int videoFormat);
extern void phone_video_transcode(int id, dword timestamp, dword duration, short sequenceDiff, int length, const byte *data, int width, int height, int stride, dword flags);
extern void phone_stop_feed_video_containers(bool stop_feed);
extern void phone_active_after_close(bool active);
extern void phone_active_after_close_asked(bool asked);
extern void phone_address_book_inaccessible_warned(bool warned);
extern void phone_set_phone_account_enabled(bool enabled);
extern void phone_cx_supported(bool supported);
extern void phone_cx_start_call_action(const char * uuid, const char * number, const char * name, bool video);
extern void phone_cx_answer_call_action(const char * uuid);
extern void phone_cx_end_call_action(const char * uuid, int call_error);
extern void phone_cx_set_held_call_action(const char * uuid, bool held);
extern void phone_cx_set_muted_call_action(const char * uuid, bool muted);
extern void phone_cx_audio_route_changed(const char * uuid, const char * deviceId);
extern void phone_cx_set_group_call_action(const char * uuid, const char * with);
extern void phone_cx_play_dtmf_call_action(const char * uuid, const char * digits);
extern void phone_cx_call_error(const char * uuid, int call_error);
extern void phone_cx_show_incoming_call_ui(const char * uuid);
extern void phone_cx_check_incoming_call(const char * uuid);
extern void myApps_set_offline(bool offline);
extern void myApps_set_hotkey_action(int type, const char * dialNumber);
extern void phone_screen_capture_sample(dword timestamp, int length, const byte *sample, int width, int height, int stride, int videoFormat);
extern void phone_screen_capture_stopped();
extern void phone_main_shutdown();

#undef  DBG_TRC
#define DBG_TRC(args)   { if (trace) debug->printf args ; }
//#define DBG_TRC(args)   { debug->printf args ; }

#define ARRAY_ITEMS(array) (sizeof(array)/sizeof(array[0]))

static bool trace = false;
static class async_forms *forms = NULL;

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

static int forms_txt_table[] =
{
    TXT_REPAIR_LOST_PHONE_ACCOUNT,
    TXT_SMARTPHONE_RESTART_NEEDED,
    TXT_OVERLAYING_PERMISSION_NEEDED,
};

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

async_forms::async_forms(class forms_async *forms_async)
{
    int i;

    this->forms_async = forms_async;
    allocated_ids = 128;
    obj_table = (class forms_object **)(malloc(allocated_ids * sizeof(class forms_object *)));
    for(i = 0; i < allocated_ids - 1; i++) {
        obj_table[i] = (class forms_object *)(&(obj_table[i + 1]));
    }
    obj_table[allocated_ids - 1] = 0;
    id_free = obj_table;

    allocated_obj_ids = 128;
    id_obj_table = (class forms_object **)(malloc(allocated_obj_ids * sizeof(class forms_object *)));

    obj_id = new_id(this);
    forms = this;
    for(i = 0; i < (int)(sizeof(forms_txt_table) / sizeof(forms_txt_table[0])); i++) {
        forms_async->enqueue(FORMS_ASYNC_ADD_LOCALIZED_TEXT, 0, MYAPPS_LANG_STRING(forms_txt_table[i], MYAPPS_LANGUAGE_EN));
    }
    forms_async->enqueue(FORMS_ASYNC_CREATE, obj_id);
}

void async_forms::new_id_resize()
{
    class forms_object **resized_obj_table;
    int i;
    
    resized_obj_table = (class forms_object **)(malloc(2 * allocated_ids * sizeof(class forms_object *)));
    memcpy(resized_obj_table, obj_table, allocated_ids * sizeof(class forms_object *));
    for(i = allocated_ids; i < 2 * allocated_ids - 1; i++) {
        resized_obj_table[i] = (class forms_object *)(&(resized_obj_table[i + 1]));
    }
    resized_obj_table[2 * allocated_ids - 1] = 0;
    id_free = &(resized_obj_table[allocated_ids]);
    free(obj_table);
    obj_table = resized_obj_table;
    allocated_ids *= 2;
}

void async_forms::obj_id_resize(int size)
{
    class forms_object **resized_id_obj_table;
    int n;
    
    n = allocated_obj_ids;
    while (n < size) {
        n *= 2;
    }
    resized_id_obj_table = (class forms_object **)(malloc(n * sizeof(class forms_object *)));
    memcpy(resized_id_obj_table, id_obj_table, allocated_obj_ids * sizeof(class forms_object *));
    free(id_obj_table);
    id_obj_table = resized_id_obj_table;
    allocated_obj_ids = n;
}

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

void forms_set_language(class forms_async *forms_async, int language)
{
    unsigned int i;

    for(i = 0; i < sizeof(forms_txt_table) / sizeof(forms_txt_table[0]); i++) {
        forms_async->enqueue(FORMS_ASYNC_CHANGE_LOCALIZED_TEXT, 0, MYAPPS_LANG_STRING(forms_txt_table[i], language));
    }
}

bool set_forms_property(class forms_async *forms_async, const char *name, const char *value)
{
    forms_async->enqueue(FORMS_ASYNC_SET_FORMS_PROPERTY, 0, name, value);
    return true;
}

void forms_webserver_started(class forms_async *forms_async, const char *url_to_load, const char *user_agent, const char *webserver_url, const char *start_fix_cert_url)
{
    forms_async->enqueue(FORMS_ASYNC_WEBSERVER_STARTED, 0, url_to_load, user_agent, webserver_url, start_fix_cert_url);
}

void forms_add_local_notification(class forms_async *forms_async, const char * ref, int type, const char * title, const char * text, const char * smallIcon, const char * largeIcon,
    const char * soundUrl, const char * buttonTitle, const char * acceptTitle, const char * rejectTitle, const char * link)
{
    forms_async->enqueue(FORMS_ASYNC_ADD_LOCAL_NOTIFICATION, 0, ref, type,
        title ? title : "",
        text ? text : "",
        smallIcon ? smallIcon : "",
        largeIcon ? largeIcon : "",
        soundUrl ? soundUrl : "",
        buttonTitle ? buttonTitle : "",
        acceptTitle ? acceptTitle : "",
        rejectTitle ? rejectTitle : "",
        link ? link : "");
}

void forms_remove_local_notification(class forms_async *forms_async, const char * ref)
{
    forms_async->enqueue(FORMS_ASYNC_REMOVE_LOCAL_NOTIFICATION, 0, ref);
}

void forms_set_focus(class forms_async *forms_async, const char *app, const char *title)
{
    forms_async->enqueue(FORMS_ASYNC_SET_FOCUS, 0, app, title);
}

void forms_set_resize(class forms_async *forms_async, const char *app, int width, int height)
{
    forms_async->enqueue(FORMS_ASYNC_SET_RESIZE, 0, app, width, height);
}

void forms_app_window_opened(class forms_async *forms_async, const char *app, const char *title)
{
    forms_async->enqueue(FORMS_ASYNC_APP_WINDOW_OPENED, 0, app, title);
}

void forms_set_autostart(class forms_async *forms_async, bool autostart)
{
    forms_async->enqueue(FORMS_ASYNC_SET_AUTOSTART, 0, autostart);
}

void forms_set_offline(class forms_async *forms_async, int offline)
{
    forms_async->enqueue(FORMS_ASYNC_SET_OFFLINE, 0, offline);
}

void forms_set_docking(class forms_async *forms_async, int docking)
{
    forms_async->enqueue(FORMS_ASYNC_SET_DOCKING, 0, docking);
}

void forms_set_hotkey(class forms_async *forms_async, int type, const char *hotkey)
{
    forms_async->enqueue(FORMS_ASYNC_SET_HOTKEY, 0, type, hotkey);
}

void forms_set_desktop_notifications(class forms_async *forms_async, bool show)
{
    forms_async->enqueue(FORMS_ASYNC_SET_DESKTOP_NOTIFICATIONS, 0, show);
}

void forms_start_external_app(class forms_async *forms_async, const char *app, const char *param)
{
    forms_async->enqueue(FORMS_ASYNC_START_EXTERNAL_APP, 0, app, param);
}

void forms_set_badge_count(class forms_async *forms_async, const char * ref, unsigned count, const char * text)
{
    forms_async->enqueue(FORMS_ASYNC_SET_BADGE_COUNT, 0, ref, count,
        text ? text : "");
}

void forms_detect_webcams(class forms_async *forms_async, const char *context)
{
    forms_async->enqueue(FORMS_ASYNC_DETECT_WEBCAMS, 0, context);
}

void forms_start_webcam(class forms_async *forms_async, const char *id, const char *context, int width, int height, bool hdVideo)
{
    forms_async->enqueue(FORMS_ASYNC_START_WEBCAM, 0, id, context, width, height, hdVideo);
}

void forms_stop_webcam(class forms_async *forms_async, const char *id)
{
    forms_async->enqueue(FORMS_ASYNC_STOP_WEBCAM, 0, id);
}

void forms_webcam_full_intra_request(class forms_async *forms_async, const char *id)
{
    forms_async->enqueue(FORMS_ASYNC_WEBCAM_FULL_INTRA_REQUEST, 0, id);
}

void forms_video_transcode(class forms_async *forms_async, int id, dword timestamp, dword duration, short sequenceDiff, const void * buf, int len, int width, int height, int stride, dword flags, int from, int to)
{
    forms_async->enqueue(FORMS_ASYNC_VIDEO_TRANSCODE, 0, id, timestamp, duration, sequenceDiff, buf, len, width, height, stride, flags, from, to);
}

void forms_keyboard_event(class forms_async *forms_async, int code, bool keyDown)
{
    forms_async->enqueue(FORMS_ASYNC_KEYBOARD_EVENT, 0, code, keyDown);
}

void forms_mouse_event(class forms_async *forms_async, int x, int y, bool right, bool keyDown)
{
    forms_async->enqueue(FORMS_ASYNC_MOUSE_EVENT, 0, x, y, right, keyDown);
}

void forms_mouse_wheel_event(class forms_async *forms_async, bool direction)
{
    forms_async->enqueue(FORMS_ASYNC_MOUSE_WHEEL_EVENT, 0, direction);
}

void forms_contacts_search(class forms_async *forms_async, const char *ref, const char *pattern)
{
    forms_async->enqueue(FORMS_ASYNC_CONTACTS_SEARCH, 0, ref, pattern);
}

void forms_contacts_search_more(class forms_async *forms_async)
{
    forms_async->enqueue(FORMS_ASYNC_CONTACTS_SEARCH_MORE, 0);
}

void forms_contacts_lookup(class forms_async *forms_async, const char *ref, const char *number, const char *prefixIntl, const char *prefixNtl, const char *prefixSubs, const char *areaCode, const char *countryCode)
{
    forms_async->enqueue(FORMS_ASYNC_CONTACTS_LOOKUP, 0, ref, number, prefixIntl, prefixNtl, prefixSubs, areaCode, countryCode);
}

void forms_webserver_get(class forms_async *forms_async, const char *context, const char *resourceName)
{
    forms_async->enqueue(FORMS_ASYNC_WEBSERVER_GET, 0, context, resourceName);
}

void forms_webserver_get_sent(class forms_async *forms_async, const char *context)
{
    forms_async->enqueue(FORMS_ASYNC_WEBSERVER_GET_SENT, 0, context);
}

void forms_cx_use(class forms_async *forms_async, bool use, const char *ringtone, bool supportsVideo, int maximumCallGroups, int maximumCallsPerCallGroup)
{
    forms_async->enqueue(FORMS_ASYNC_CX_USE, 0, use, ringtone, supportsVideo, maximumCallGroups, maximumCallsPerCallGroup);
}

void forms_cx_report_new_incoming_call(class forms_async *forms_async, const char *uuid, const char *number, const char *name, const char *transfer, const char *called,
    bool hasVideo, bool supportsDTMF, bool supportsHolding, bool supportsGrouping, bool supportsUngrouping)
{
    forms_async->enqueue(FORMS_ASYNC_CX_REPORT_NEW_INCOMING_CALL, 0, uuid, number, name, transfer, called,
        (hasVideo ? FORMS_CX_CALL_UPDATE_HAS_VIDEO : 0) |
        (supportsDTMF ? FORMS_CX_CALL_UPDATE_SUPPORTS_DTMF : 0) |
        (supportsHolding ? FORMS_CX_CALL_UPDATE_SUPPORTS_HOLDING : 0) |
        (supportsGrouping ? FORMS_CX_CALL_UPDATE_SUPPORTS_GROUPING : 0) |
        (supportsUngrouping ? FORMS_CX_CALL_UPDATE_SUPPORTS_UNGROUPING : 0));
}

void forms_cx_report_outgoing_call_connecting(class forms_async *forms_async, const char *uuid)
{
    forms_async->enqueue(FORMS_ASYNC_CX_REPORT_OUTGOING_CALL_CONNECTING, 0, uuid);
}

void forms_cx_report_outgoing_call_connected(class forms_async *forms_async, const char *uuid)
{
    forms_async->enqueue(FORMS_ASYNC_CX_REPORT_OUTGOING_CALL_CONNECTED, 0, uuid);
}

void forms_cx_report_call_updated(class forms_async *forms_async, const char *uuid, const char *number, const char *name, const char *transfer, const char *called,
    bool hasVideo, bool supportsDTMF, bool supportsHolding, bool supportsGrouping, bool supportsUngrouping)
{
    forms_async->enqueue(FORMS_ASYNC_CX_REPORT_CALL_UPDATED, 0, uuid, number, name, transfer, called,
        (hasVideo ? FORMS_CX_CALL_UPDATE_HAS_VIDEO : 0) |
        (supportsDTMF ? FORMS_CX_CALL_UPDATE_SUPPORTS_DTMF : 0) |
        (supportsHolding ? FORMS_CX_CALL_UPDATE_SUPPORTS_HOLDING : 0) |
        (supportsGrouping ? FORMS_CX_CALL_UPDATE_SUPPORTS_GROUPING : 0) |
        (supportsUngrouping ? FORMS_CX_CALL_UPDATE_SUPPORTS_UNGROUPING : 0));
}

void forms_cx_report_call_ended(class forms_async *forms_async, const char *uuid, int reason)
{
    forms_async->enqueue(FORMS_ASYNC_CX_REPORT_CALL_ENDED, 0, uuid, reason);
}

void forms_cx_request_start_call(class forms_async *forms_async, const char *uuid, const char *number, const char *name, bool video)
{
    forms_async->enqueue(FORMS_ASYNC_CX_REQUEST_START_CALL, 0, uuid, number, name, video);
}

void forms_cx_request_end_call(class forms_async *forms_async, const char *uuid)
{
    forms_async->enqueue(FORMS_ASYNC_CX_REQUEST_END_CALL, 0, uuid);
}

void forms_cx_request_set_held_call(class forms_async *forms_async, const char *uuid, bool held)
{
    forms_async->enqueue(FORMS_ASYNC_CX_REQUEST_SET_HELD_CALL, 0, uuid, held);
}

void forms_cx_request_set_muted_call(class forms_async *forms_async, const char *uuid, bool muted)
{
    forms_async->enqueue(FORMS_ASYNC_CX_REQUEST_SET_MUTED_CALL, 0, uuid, muted);
}

void forms_cx_request_set_group_call(class forms_async *forms_async, const char *uuid, const char *with)
{
    forms_async->enqueue(FORMS_ASYNC_CX_REQUEST_SET_GROUP_CALL, 0, uuid, with);
}

void forms_cx_request_play_dtmf(class forms_async *forms_async, const char *uuid, const char *digits)
{
    forms_async->enqueue(FORMS_ASYNC_CX_REQUEST_PLAY_DTMF, 0, uuid, digits);
}

void forms_start_screen_capture(class forms_async *forms_async)
{
    forms_async->enqueue(FORMS_ASYNC_START_SCREEN_CAPTURE, 0);
}

void forms_stop_screen_capture(class forms_async *forms_async)
{
    forms_async->enqueue(FORMS_ASYNC_STOP_SCREEN_CAPTURE, 0);
}

void forms_set_conn_info(class forms_async *forms_async, bool up, bool standby, const char * cur, const char * pri, const char * sec, const char * master)
{
    forms_async->enqueue(FORMS_ASYNC_SET_CONN_INFO, 0, up, standby, cur, pri, sec, master);
}


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

void forms_event_trace              (class async_event *async_event)
{
    const char *string = async_event->get_string();
    ERR(("%s", string));
}

void forms_event_set_property       (class async_event *async_event)
{
    const char *prop = async_event->get_string();
    const char *value = async_event->get_string();
    DBG_TRC(("DEBUG forms_event_set_property(%s,%s)", prop, value));
    if(strcmp(prop, "CPU/SHUTDOWN") == 0) {
        phone_main_shutdown();
    }
    else if(strcmp(prop, "PHONE/DIAL-NUMBER") == 0) {
        const char * name = strchr(value, '\n') + 1;
        char * number_buffer = (char *)(malloc(name - value));
        memcpy(number_buffer, value, (name - value) - 1);
        number_buffer[(name - value) - 1] = '\0';
        const char * video = strchr(name, '\n') + 1;
        char * name_buffer = (char *)(malloc(video - name));
        memcpy(name_buffer, name, (video - name) - 1);
        name_buffer[(video - name) - 1] = '\0';
        phone_dial_number(number_buffer, (name_buffer[0] != '\0') ? name_buffer : NULL, atoi(video));
        free(name_buffer);
        free(number_buffer);
    }
    else if(strcmp(prop, "PHONE/DIAL-SIP") == 0) {
        const char * name = strchr(value, '\n') + 1;
        char * sip_buffer = (char *)(malloc(name - value));
        memcpy(sip_buffer, value, (name - value) - 1);
        sip_buffer[(name - value) - 1] = '\0';
        const char * video = strchr(name, '\n') + 1;
        char * name_buffer = (char *)(malloc(video - name));
        memcpy(name_buffer, name, (video - name) - 1);
        name_buffer[(video - name) - 1] = '\0';
        phone_dial_sip(sip_buffer, (name_buffer[0] != '\0') ? name_buffer : NULL, atoi(video));
        free(name_buffer);
        free(sip_buffer);
    }
    else if(strcmp(prop, "PHONE/START-CHAT") == 0) {
        phone_chat(value);
    }
    else if(strcmp(prop, "PHONE/EXTERNAL-CALL-IDLE") == 0) {
        ERR(("External call idle %s", &value[(strlen(value) <= 3) ? 0 : strlen(value) - 3]));
        phone_external_call_idle(value);
    }
    else if(strcmp(prop, "PHONE/EXTERNAL-CALL-RINGING") == 0) {
        ERR(("External call ringing %s", &value[(strlen(value) <= 3) ? 0 : strlen(value) - 3]));
        phone_external_call_ringing(value);
    }
    else if(strcmp(prop, "PHONE/EXTERNAL-CALL-OFFHOOK") == 0) {
        ERR(("External call offhook %s", &value[(strlen(value) <= 3) ? 0 : strlen(value) - 3]));
        phone_external_call_offhook(value);
    }
    else if(strcmp(prop, "PHONE/AUDIO-FOCUS-LOSS") == 0) {
        ERR(("Audio focus loss %i", (value[0] != '\0') && (value[0] != '0')));
        phone_audio_focus_loss((value[0] != '\0') && (value[0] != '0'));
    }
    else if(strcmp(prop, "DSP/HEADSET-PLUGGED") == 0) {
        phone_headset_plugged((value[0] != '\0') && (value[0] != '0'));
    }
    else if(strcmp(prop, "DSP/HEADSET-HOOK") == 0) {
        phone_headset_hook();
    }
    else if(strcmp(prop, "DSP/AUDIO-DEVICES-UPDATED") == 0) {
        phone_audio_devices_updated();
    }
    else if(strcmp(prop, "DSP/EARPIECE-PROXIMITY") == 0) {
        phone_earpiece_proximity((value[0] != '\0') && (value[0] != '0'));
        phone_set_proximity(true, (value[0] != '\0') && (value[0] != '0'));
    }
    else if(strcmp(prop, "PHONE/TRANSFER-TO-NUMBER") == 0) {
    }
    else if(strcmp(prop, "PHONE/RAS-REGISTRATION-CHECK") == 0) {
        phone_registration_check();
    }
    else if(strcmp(prop, "PHONE/PAUSE-SUBSCRIPTIONS") == 0) {
    }
    else if(strcmp(prop, "PHONE/PUSH-TOKEN") == 0) {
        phone_set_push_token(value);
    }
    else if(strcmp(prop, "PHONE/PUSH-NOTIFICATION") == 0) {
        phone_push_notification();
    }
    else if(strcmp(prop, "PHONE/EXPECT-TOUCHES") == 0) {
        phone_set_expect_touches(true, (value[0] != '\0') && (value[0] != '0'));
    }
    else if(strcmp(prop, "PHONE/SHOWN-LOCKED") == 0) {
        phone_set_shown_locked(true, (value[0] != '\0') && (value[0] != '0'));
    }
    else if(strcmp(prop, "PHONE/SCREEN-CAPTURE-STOPPED") == 0) {
        phone_screen_capture_stopped();
    }
#ifdef __ANDROID__
    else if(strcmp(prop, "PHONE/SET-PHONE-ACCOUNT-ENABLED") == 0) {
        phone_set_phone_account_enabled((value[0] != '\0') && (value[0] != '0'));
    }
#endif
#ifdef _IOS_
    else if(strcmp(prop, "PHONE/STOP_FEED_CONTAINERS") == 0) {
        phone_stop_feed_video_containers((value[0] != '\0') && (value[0] != '0'));
    }
    else if(strcmp(prop, "PHONE/ACTIVE-AFTER-CLOSE") == 0) {
        phone_active_after_close((value[0] != '\0') && (value[0] != '0'));
    }
    else if(strcmp(prop, "PHONE/ACTIVE-AFTER-CLOSE-ASKED") == 0) {
        phone_active_after_close_asked((value[0] != '\0') && (value[0] != '0'));
    }
    else if(strcmp(prop, "PHONE/ADDRESS-BOOK-INACCESSIBLE-WARNED") == 0) {
        phone_address_book_inaccessible_warned((value[0] != '\0') && (value[0] != '0'));
    }
#endif
}

void forms_event_local_notification_clicked(class async_event *async_event)
{
    const char *ref = async_event->get_string();
    phone_local_notification_clicked(ref);
}

void forms_event_local_notification_accept(class async_event *async_event)
{
    const char *ref = async_event->get_string();
    phone_local_notification_accept(ref);
}

void forms_event_local_notification_reject(class async_event *async_event)
{
    const char *ref = async_event->get_string();
    phone_local_notification_reject(ref);
}

void forms_event_local_notification_reply(class async_event *async_event)
{
    const char *ref = async_event->get_string();
    const char *replyText = async_event->get_string();
    phone_local_notification_reply(ref, replyText);
}

void forms_event_webcam_added       (class async_event *async_event)
{
    const char *id = async_event->get_string();
    bool facing_front = async_event->get_bool();
    int orientation = async_event->get_int();
    const char *context = async_event->get_string();
    const char *description = async_event->get_string();
    phone_webcam_added(id, facing_front, orientation, context, description);
}

void forms_event_webcam_removed     (class async_event *async_event)
{
    const char *id = async_event->get_string();
    phone_webcam_removed(id);
}

void forms_event_webcam_query_result(class async_event *async_event)
{
    const char *ref = async_event->get_string();
    phone_webcam_query_result(ref);
}

void forms_event_webcam_failed      (class async_event *async_event)
{
    const char *id = async_event->get_string();
    const char *context = async_event->get_string();
    phone_webcam_failed(id, context);
}

void forms_event_webcam_data        (class async_event *async_event)
{
    const char *id = async_event->get_string();
    dword timestamp = async_event->get_int();
    int offset = async_event->get_int();
    int length = async_event->get_int();
    int l;
    const byte *data = async_event->get_byte_array(&l);
    int previewOffset = async_event->get_int();
    int previewLength = async_event->get_int();
    const byte *previewData = async_event->get_byte_array(&l);
    phone_webcam_data(id, timestamp, length, &data[offset], previewLength, &previewData[previewOffset]);
}

void forms_event_webcam_sample      (class async_event *async_event)
{
    void *sample_ref;
    bool is_copy;

    const char *id = async_event->get_string();
    dword timestamp = async_event->get_int();
    int offset = async_event->get_int();
    int length = async_event->get_int();
    int l;
    byte *sample = async_event->get_memory(&l, &sample_ref, &is_copy);
    int width = async_event->get_int();
    int height = async_event->get_int();
    int stride = async_event->get_int();
    int videoFormat = async_event->get_int();
    phone_webcam_sample(id, timestamp, length, &sample[offset], width, height, stride, videoFormat);
    async_event_release_memory(sample_ref, sample, false);
}

void forms_event_video_transcode    (class async_event *async_event)
{
    int id = async_event->get_int();
    dword timestamp = async_event->get_int();
    dword duration = async_event->get_int();
    short sequenceDiff = (short)(async_event->get_int());
    int offset = async_event->get_int();
    int length = async_event->get_int();
    int l;
    const byte *data = async_event->get_byte_array(&l);
    int width = async_event->get_int();
    int height = async_event->get_int();
    int stride = async_event->get_int();
    dword flags = async_event->get_int();
    phone_video_transcode(id, timestamp, duration, sequenceDiff, length, &data[offset], width, height, stride, flags);
}

void forms_event_contacts_search    (class async_event *async_event)
{
    const char *ref = async_event->get_string();
    const char *info = async_event->get_string();
    phone_contacts_search(ref, info);
}

void forms_event_contacts_search_more(class async_event *async_event)
{
    phone_contacts_search_more();
}

void forms_event_contacts_lookup    (class async_event *async_event)
{
    const char *ref = async_event->get_string();
    const char *info = async_event->get_string();
    phone_contacts_lookup(ref, info);
}

void forms_event_webserver_get      (class async_event *async_event)
{
    const char *context = async_event->get_string();
    bool complete = async_event->get_bool();
    int offset = async_event->get_int();
    int length = async_event->get_int();
    int l;
    const byte *data = async_event->get_byte_array(&l);
    phone_webserver_get(context, complete, length, &data[offset]);
}

void forms_event_cx_supported       (class async_event *async_event)
{
    bool supported = async_event->get_bool();
    phone_cx_supported(supported);
}

void forms_event_cx_start_call_action(class async_event *async_event)
{
    const char *uuid = async_event->get_string();
    const char *handle = async_event->get_string();
    const char *name = async_event->get_string();
    bool video = async_event->get_bool();
    phone_cx_start_call_action(uuid, handle, name, video);
}

void forms_event_cx_answer_call_action(class async_event *async_event)
{
    const char *uuid = async_event->get_string();
    phone_cx_answer_call_action(uuid);
}

void forms_event_cx_end_call_action (class async_event *async_event)
{
    const char *uuid = async_event->get_string();
    int error = async_event->get_int();
    phone_cx_end_call_action(uuid, error);
}

void forms_event_cx_set_held_call_action(class async_event *async_event)
{
    const char *uuid = async_event->get_string();
    bool held = async_event->get_bool();
    phone_cx_set_held_call_action(uuid, held);
}

void forms_event_cx_set_muted_call_action(class async_event *async_event)
{
    const char *uuid = async_event->get_string();
    bool muted = async_event->get_bool();
    phone_cx_set_muted_call_action(uuid, muted);
}

void forms_event_cx_audio_route_changed(class async_event *async_event)
{
    const char *uuid = async_event->get_string();
    const char *deviceId = async_event->get_string();
    phone_cx_audio_route_changed(uuid, deviceId);
}

void forms_event_cx_set_group_call_action(class async_event *async_event)
{
    const char *uuid = async_event->get_string();
    const char *with = async_event->get_string();
    phone_cx_set_group_call_action(uuid, with);
}

void forms_event_cx_play_dtmf_call_action(class async_event *async_event)
{
    const char *uuid = async_event->get_string();
    const char *digits = async_event->get_string();
    phone_cx_play_dtmf_call_action(uuid, digits);
}

void forms_event_cx_call_error      (class async_event *async_event)
{
    const char *uuid = async_event->get_string();
    int call_error = async_event->get_int();
    phone_cx_call_error(uuid, call_error);
}

void forms_event_cx_show_incoming_call_ui(class async_event *async_event)
{
    const char *uuid = async_event->get_string();
    phone_cx_show_incoming_call_ui(uuid);
}

void forms_event_cx_check_incoming_call(class async_event *async_event)
{
    const char *uuid = async_event->get_string();
    phone_cx_check_incoming_call(uuid);
}

void forms_event_wake_phone         (class async_event *async_event)
{
    phone_wake_phone();
}

void forms_event_open_permalink     (class async_event *async_event)
{
    const char *link = async_event->get_string();
    phone_open_permalink(link);
}

void forms_event_phonelist_import   (class async_event *async_event)
{
    (void)(async_event->get_int());
    int type = async_event->get_int();
    int time = async_event->get_int();
    const char *number = async_event->get_string();
    int duration = async_event->get_int();
    const char *name = async_event->get_string();
    bool is_new = async_event->get_bool();
    DBG_TRC(("DEBUG forms_event_phonelist_import(%x,%u,%s,%u,%s,%i)",
        type, time, number, duration, name, is_new));
}

void forms_event_phonelist_more     (class async_event *async_event)
{
    (void)(async_event->get_int());
    int more = async_event->get_int();
    DBG_TRC(("DEBUG forms_event_phonelist_more(%i)", more));
}

void forms_event_phonelist_looped   (class async_event *async_event)
{
    (void)(async_event->get_int());
    DBG_TRC(("DEBUG forms_event_phonelist_looped()"));
}

void forms_event_phonelist_delete   (class async_event *async_event)
{
    (void)(async_event->get_int());
    int type = async_event->get_int();
    int time = async_event->get_int();
    const char *number = async_event->get_string();
    DBG_TRC(("DEBUG forms_event_phonelist_delete(%x,%u,%s)",
        type, time, number));
}

void forms_event_phonelist_change   (class async_event *async_event)
{
    (void)(async_event->get_int());
    DBG_TRC(("DEBUG forms_event_phonelist_change()"));
}

void forms_event_set_offline(class async_event *async_event)
{
    bool offline = async_event->get_bool();
    myApps_set_offline(offline);
}

void forms_event_set_hotkey_action(class async_event *async_event)
{
    int type = async_event->get_int();
    const char *number = async_event->get_string();
    myApps_set_hotkey_action(type, number);
}

void forms_event_screen_capture_sample(class async_event *async_event)
{
    void *sample_ref;
    bool is_copy;

    dword timestamp = async_event->get_int();
    int offset = async_event->get_int();
    int length = async_event->get_int();
    int l;
    byte *sample = async_event->get_memory(&l, &sample_ref, &is_copy);
    int width = async_event->get_int();
    int height = async_event->get_int();
    int stride = async_event->get_int();
    int videoFormat = async_event->get_int();
    phone_screen_capture_sample(timestamp, length, &sample[offset], width, height, stride, videoFormat);
    async_event_release_memory(sample_ref, sample, false);
}

void forms_event_delete_object(class async_event *async_event)
{
    int obj_id = async_event->get_int();
    delete forms->id_obj(obj_id);
}

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