#include <stdlib.h>
#include <string.h>
#include <glib.h>
#include "android.h"
#include <android/log.h>
#include "navit.h"
#include "config_.h"
#include "command.h"
#include "debug.h"
#include "event.h"
#include "callback.h"
#include "country.h"
#include "projection.h"
#include "coord.h"
#include "map.h"
#include "mapset.h"
#include "navit_nls.h"
#include "transform.h"
#include "color.h"
#include "types.h"
#include "search.h"
#include "start_real.h"
#include "track.h"

JNIEnv *jnienv;
jobject *android_activity = NULL;

struct android_search_priv {
    struct jni_object search_result_obj;
    struct event_idle *idle_ev;
    struct callback *idle_clb;
    struct search_list *search_list;
    struct attr search_attr;
    gchar **phrases;
    int current_phrase_per_level[4];
    int partial;
    int found;
};


JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *aVm, void *aReserved) {
    if ((*aVm)->GetEnv(aVm,(void**)&jnienv, JNI_VERSION_1_6) != JNI_OK) {
        dbg(lvl_error,"Failed to get the environment");
        return -1;
    }
    dbg(lvl_debug,"Found the environment");
    return JNI_VERSION_1_6;
}

int android_find_class_global(char *name, jclass *ret) {
    *ret=(*jnienv)->FindClass(jnienv, name);
    if (! *ret) {
        dbg(lvl_error,"Failed to get Class %s",name);
        return 0;
    }
    *ret = (*jnienv)->NewGlobalRef(jnienv, *ret);
    return 1;
}

int android_find_method(jclass class, char *name, char *args, jmethodID *ret) {
    *ret = (*jnienv)->GetMethodID(jnienv, class, name, args);
    if (*ret == NULL) {
        dbg(lvl_error,"Failed to get Method %s with signature %s",name,args);
        return 0;
    }
    return 1;
}


int android_find_static_method(jclass class, char *name, char *args, jmethodID *ret) {
    *ret = (*jnienv)->GetStaticMethodID(jnienv, class, name, args);
    if (*ret == NULL) {
        dbg(lvl_error,"Failed to get static Method %s with signature %s",name,args);
        return 0;
    }
    return 1;
}


/**
 * @brief Starts the Navitlib for Android
 *
 * @param env provided by JVM
 * @param thiz the calling Navit instance
 * @param lang a string describing the language
 * @param path relates to NAVIT_DATA_DIR on linux
 * @param map_path where the binfiles are stored
 */
JNIEXPORT void JNICALL Java_org_navitproject_navit_Navit_navitMain( JNIEnv* env, jobject thiz,
        jstring lang, jstring path, jstring map_path) {
    const char *langstr;
    const char *map_file_path;
    jnienv=env;

    android_activity = (*jnienv)->NewGlobalRef(jnienv, thiz);

    langstr=(*env)->GetStringUTFChars(env, lang, NULL);
    dbg(lvl_debug,"enter env=%p thiz=%p activity=%p lang=%s",env,thiz,android_activity,langstr);
    setenv("LANG",langstr,1);
    (*env)->ReleaseStringUTFChars(env, lang, langstr);

    map_file_path=(*env)->GetStringUTFChars(env, map_path, NULL);
    setenv("NAVIT_USER_DATADIR",map_file_path,1);
    (*env)->ReleaseStringUTFChars(env, map_path, map_file_path);

    const char *strings=(*env)->GetStringUTFChars(env, path, NULL);
    main_real(1, &strings);
    (*env)->ReleaseStringUTFChars(env, path, strings);
}

JNIEXPORT void JNICALL Java_org_navitproject_navit_Navit_navitDestroy( JNIEnv* env, jobject thiz) {
    dbg(lvl_debug, "shutdown navit");
    exit(0);
}

JNIEXPORT void JNICALL Java_org_navitproject_navit_NavitGraphics_sizeChangedCallback( JNIEnv* env, jobject thiz,
        jlong id, jint w, jint h) {
    dbg(lvl_debug,"enter %p %d %d",(struct callback *)(intptr_t)id,w,h);
    if (id) {
        callback_call_2((struct callback *)(intptr_t)id, w, h);
    }
}

JNIEXPORT void JNICALL Java_org_navitproject_navit_NavitGraphics_paddingChangedCallback(JNIEnv* env, jobject thiz,
        jlong id, jint left, jint top, jint right, jint bottom) {
    dbg(lvl_debug,"enter %p %d %d %d %d",(struct callback *)(intptr_t)id, left, top, right, bottom);
    if (id)
        callback_call_4((struct callback *)(intptr_t)id, left, top, right, bottom);
}

JNIEXPORT void JNICALL Java_org_navitproject_navit_NavitGraphics_buttonCallback( JNIEnv* env, jobject thiz,
        jlong id, jint pressed, jint button, jint x, jint y) {
    dbg(lvl_debug,"enter %p %d %d",(struct callback *)(intptr_t)id,pressed,button);
    if (id)
        callback_call_4((struct callback *)(intptr_t)id,pressed,button,x,y);
}

JNIEXPORT void JNICALL Java_org_navitproject_navit_NavitGraphics_motionCallback( JNIEnv* env, jobject thiz,
        jlong id, jint x, jint y) {
    dbg(lvl_debug,"enter %p %d %d",(struct callback *)(intptr_t)id,x,y);
    if (id)
        callback_call_2((struct callback *)(intptr_t)id,x,y);
}

JNIEXPORT void JNICALL Java_org_navitproject_navit_NavitGraphics_keypressCallback( JNIEnv* env, jobject thiz,
        jlong id, jstring str) {
    const char *s;
    dbg(lvl_debug,"enter %p %p",(struct callback *)(intptr_t)id,str);
    s=(*env)->GetStringUTFChars(env, str, NULL);
    dbg(lvl_debug,"key=%s",s);
    if (id)
        callback_call_1((struct callback *)(intptr_t)id,s);
    (*env)->ReleaseStringUTFChars(env, str, s);
}

JNIEXPORT void JNICALL Java_org_navitproject_navit_NavitTimeout_timeoutCallback( JNIEnv* env, jobject thiz,
        jlong id) {
    dbg(lvl_debug,"enter %p %p %p",thiz,(void *)id, (void *)(intptr_t)id);
    void (*event_handler)(void *) = *((void **)(intptr_t)id);
    event_handler((void*)(intptr_t)id);
}

JNIEXPORT void JNICALL Java_org_navitproject_navit_NavitVehicle_vehicleCallback( JNIEnv * env, jobject thiz,
        jlong id, jobject location) {
    callback_call_1((struct callback *)(intptr_t)id, (void *)location);
}

JNIEXPORT void JNICALL Java_org_navitproject_navit_NavitWatch_poll( JNIEnv* env, jobject thiz, jlong func, jint fd,
        jint cond) {
    void (*pollfunc)(JNIEnv *env, int fd, int cond)=(void *)func;

    pollfunc(env, fd, cond);
}

JNIEXPORT void JNICALL Java_org_navitproject_navit_NavitWatch_watchCallback( JNIEnv* env, jobject thiz, jlong id) {
    dbg(lvl_debug,"enter %p %p",thiz, (void *)(intptr_t)id);
    callback_call_0((struct callback *)(intptr_t)id);
}

JNIEXPORT void JNICALL Java_org_navitproject_navit_NavitSensors_sensorCallback( JNIEnv* env, jobject thiz,
        jlong id, jint sensor, jfloat x, jfloat y, jfloat z) {
    dbg(lvl_debug,"enter %p %p %f %f %f",thiz, (void *)(intptr_t)id,x,y,z);
    callback_call_4((struct callback *)(intptr_t)id, sensor, &x, &y, &z);
}

JNIEXPORT void JNICALL Java_org_navitproject_navit_NavitTraff_onFeedReceived(JNIEnv * env, jobject thiz,
        jlong id, jstring feed) {
    const char *s;
    s = (*env)->GetStringUTFChars(env, feed, NULL);
    if (id)
        callback_call_1((struct callback *)(intptr_t) id, s);
    (*env)->ReleaseStringUTFChars(env, feed, s);
}

// type: 0=town, 1=street, 2=House#
void android_return_search_result(struct jni_object *jni_o, int type, struct pcoord *location, const char *address) {
    struct coord_geo geo_location;
    struct coord c;
    jstring jaddress = NULL;
    JNIEnv* env;
    env=jni_o->env;
    jaddress = (*env)->NewStringUTF(jni_o->env,address);

    c.x=location->x;
    c.y=location->y;
    transform_to_geo(location->pro, &c, &geo_location);

    (*env)->CallVoidMethod(jni_o->env, jni_o->jo, jni_o->jm, type, geo_location.lat, geo_location.lng, jaddress);
    (*env)->DeleteLocalRef(jni_o->env, jaddress);
}

JNIEXPORT jstring JNICALL Java_org_navitproject_navit_NavitAppConfig_callbackLocalizedString( JNIEnv* env, jclass thiz,
        jstring str) {
    const char *s;
    const char *localized_str;

    s=(*env)->GetStringUTFChars(env, str, NULL);
    //dbg(lvl_debug,"*****string=%s",s);

    localized_str=navit_nls_gettext(s);
    //dbg(lvl_debug,"localized string=%s",localized_str);

    // jstring dataStringValue = (jstring) localized_str;
    jstring js = (*env)->NewStringUTF(env,localized_str);
    (*env)->ReleaseStringUTFChars(env, str, s);
    return js;
}


JNIEXPORT jstring JNICALL Java_org_navitproject_navit_NavitGraphics_getDefaultCountry( JNIEnv* env, jobject thiz,
        jint channel, jstring str) {
    struct attr search_attr, country_name, country_iso2, *country_attr;
    struct tracking *tracking;
    struct search_list_result *res;
    jstring return_string = NULL;

    struct attr attr;
    dbg(lvl_debug,"enter %d %p",channel,str);

    config_get_attr(config_get(), attr_navit, &attr, NULL);

    country_attr=country_default();
    tracking=navit_get_tracking(attr.u.navit);
    if (tracking && tracking_get_attr(tracking, attr_country_id, &search_attr, NULL)) {
        country_attr = &search_attr;
    }
    if (country_attr) {
        struct country_search *cs=country_search_new(country_attr, 0);
        struct item *item=country_search_get_item(cs);
        if (item && item_attr_get(item, attr_country_name, &country_name)) {
            struct mapset *ms=navit_get_mapset(attr.u.navit);
            struct search_list *search_list = search_list_new(ms);
            search_attr.type=attr_country_all;
            dbg(lvl_debug,"country %s", country_name.u.str);
            search_attr.u.str=country_name.u.str;
            search_list_search(search_list, &search_attr, 0);
            while((res=search_list_get_result(search_list))) {
                dbg(lvl_debug,"Get result: %s", res->country->iso2);
            }
            if (item_attr_get(item, attr_country_iso2, &country_iso2)) {
                return_string = (*env)->NewStringUTF(env, country_iso2.u.str);
            }
        }
        country_search_destroy(cs);
    }

    return return_string;
}


JNIEXPORT jobjectArray JNICALL Java_org_navitproject_navit_NavitGraphics_getAllCountries( JNIEnv* env,
        jclass thiz) {
    struct attr search_attr;
    struct search_list_result *res;
    GList* countries = NULL;
    int country_count = 0;
    jobjectArray all_countries;

    struct attr attr;
    dbg(lvl_debug,"enter");

    config_get_attr(config_get(), attr_navit, &attr, NULL);

    struct mapset *ms=navit_get_mapset(attr.u.navit);
    struct search_list *search_list = search_list_new(ms);
    jobjectArray current_country = NULL;
    search_attr.type=attr_country_all;
    //dbg(lvl_debug,"country %s", country_name.u.str);
    search_attr.u.str=g_strdup("");//country_name.u.str;
    search_list_search(search_list, &search_attr, 1);
    while((res=search_list_get_result(search_list))) {
        dbg(lvl_debug,"Get result: %s", res->country->iso2);

        if (strlen(res->country->iso2)==2) {
            jstring j_iso2 = (*env)->NewStringUTF(env, res->country->iso2);
            jstring j_name = (*env)->NewStringUTF(env, navit_nls_gettext(res->country->name));

            current_country = (jobjectArray)(*env)->NewObjectArray(env, 2, (*env)->FindClass(env, "java/lang/String"), NULL);

            (*env)->SetObjectArrayElement(env, current_country, 0,  j_iso2);
            (*env)->SetObjectArrayElement(env, current_country, 1,  j_name);

            (*env)->DeleteLocalRef(env, j_iso2);
            (*env)->DeleteLocalRef(env, j_name);

            countries = g_list_prepend(countries, current_country);
            country_count++;
        }
    }

    search_list_destroy(search_list);
    all_countries = (jobjectArray)(*env)->NewObjectArray(env, country_count, (*env)->GetObjectClass(env,current_country),
                    NULL);

    while(countries) {
        (*env)->SetObjectArrayElement(env, all_countries, --country_count, countries->data);
        countries = g_list_delete_link( countries, countries);
    }

    return all_countries;
}


JNIEXPORT jstring JNICALL Java_org_navitproject_navit_NavitGraphics_getCoordForPoint( JNIEnv* env,
        jobject thiz, jint x, jint y, jboolean absoluteCoord) {

    jstring return_string = NULL;

    struct attr attr;
    config_get_attr(config_get(), attr_navit, &attr, NULL);

    struct transformation *transform=navit_get_trans(attr.u.navit);
    struct point p;
    struct coord c;
    struct pcoord pc;

    p.x = x;
    p.y = y;

    transform_reverse(transform, &p, &c);

    pc.x = c.x;
    pc.y = c.y;
    pc.pro = transform_get_projection(transform);

    char coord_str[32];
    if (absoluteCoord) {
        pcoord_format_absolute(&pc, coord_str, sizeof(coord_str), ",");
    } else {
        pcoord_format_degree_short(&pc, coord_str, sizeof(coord_str), " ");
    }

    dbg(lvl_debug,"Display point x=%d y=%d is \"%s\"",x,y,coord_str);
    return_string = (*env)->NewStringUTF(env,coord_str);

    return return_string;
}


JNIEXPORT jobject JNICALL Java_org_navitproject_navit_NavitCallbackHandler_callbackCmdChannel( JNIEnv* env,
        jclass thiz, jint command) {

    struct attr attr;
    const char *s;
    dbg(lvl_debug,"enter %d\n",command);
    config_get_attr(config_get(), attr_navit, &attr, NULL);

    switch(command) {
    case 1:
        // zoom in
        navit_zoom_in_cursor(attr.u.navit, 2);
        navit_draw(attr.u.navit);
        break;
    case 2:
        // zoom out
        navit_zoom_out_cursor(attr.u.navit, 2);
        navit_draw(attr.u.navit);
        break;
    case 3:
        // block
        navit_block(attr.u.navit, 1);
        break;
    case 4:
        // unblock
        navit_block(attr.u.navit, 0);
        break;
    case 5:
        // cancel route
        navit_set_destination(attr.u.navit, NULL, NULL, 0);
        navit_draw(attr.u.navit);
        break;
    default:
        dbg(lvl_error, "Unknown command: %d", command);
        break;
    }
}

JNIEXPORT jint JNICALL Java_org_navitproject_navit_NavitCallbackHandler_callbackMessageChannel( JNIEnv* env,
        jclass thiz, jint channel, jstring str) {
    struct attr attr;
    const char *s;
    jint ret = 0;
    dbg(lvl_debug,"enter %d %p",channel,str);
    config_get_attr(config_get(), attr_navit, &attr, NULL);

    switch(channel) {
    case 6: {// add a map to the current mapset, return 1 on success
        struct mapset *ms = navit_get_mapset(attr.u.navit);
        struct attr type, name, data, *attrs[4];
        const char *map_location = (*env)->GetStringUTFChars(env, str, NULL);
        dbg(lvl_debug, "*****string=%s", map_location);
        type.type = attr_type;
        type.u.str = "binfile";

        data.type = attr_data;
        data.u.str = g_strdup(map_location);

        name.type = attr_name;
        name.u.str = g_strdup(map_location);

        attrs[0] = &type;
        attrs[1] = &data;
        attrs[2] = &name;
        attrs[3] = NULL;

        struct map *new_map = map_new(NULL, attrs);
        if (new_map) {
            struct attr map_a;
            map_a.type = attr_map;
            map_a.u.map = new_map;
            ret = mapset_add_attr(ms, &map_a);
            navit_draw(attr.u.navit);
        }
        (*env)->ReleaseStringUTFChars(env, str, map_location);
        break;
    }
    case 7: { // remove a map from the current mapset, return 1 on success
        struct mapset *ms = navit_get_mapset(attr.u.navit);
        struct attr map_r;
        const char *map_location = (*env)->GetStringUTFChars(env, str, NULL);
        struct map *delete_map = mapset_get_map_by_name(ms, map_location);

        if (delete_map) {
            dbg(lvl_debug, "delete map %s (%p)", map_location, delete_map);
            map_r.type = attr_map;
            map_r.u.map = delete_map;
            ret = mapset_remove_attr(ms, &map_r);
            navit_draw(attr.u.navit);
        }
        (*env)->ReleaseStringUTFChars(env, str, map_location);
        break;
    }
    case 5:
        // call a command (like in gui)
        s = (*env)->GetStringUTFChars(env, str, NULL);
        dbg(lvl_debug, "*****string=%s", s);
        command_evaluate(&attr, s);
        (*env)->ReleaseStringUTFChars(env, str, s);
        break;
    case 4: { // navigate to display position
        char *pstr;
        struct point p;
        struct coord c;
        struct pcoord pc;
        struct transformation *transform = navit_get_trans(attr.u.navit);

        s = (*env)->GetStringUTFChars(env, str, NULL);
        char parse_str[strlen(s) + 1];
        strcpy(parse_str, s);
        (*env)->ReleaseStringUTFChars(env, str, s);
        dbg(lvl_debug, "*****string=%s", parse_str);

        // set destination to (pixel-x#pixel-y)
        // pixel-x
        pstr = strtok(parse_str, "#");
        p.x = atoi(pstr);
        // pixel-y
        pstr = strtok(NULL, "#");
        p.y = atoi(pstr);

        dbg(lvl_debug, "11x=%d", p.x);
        dbg(lvl_debug, "11y=%d", p.y);

        transform_reverse(transform, &p, &c);

        pc.x = c.x;
        pc.y = c.y;
        pc.pro = transform_get_projection(transform);

        char coord_str[32];
        pcoord_format_degree_short(&pc, coord_str, sizeof(coord_str), " ");

        dbg(lvl_debug,"Setting destination to %s",coord_str);
        // start navigation asynchronous
        navit_set_destination(attr.u.navit, &pc, coord_str, 1);
    }
    break;
    case 3: {
        // navigate to geo position
        char *name;
        s = (*env)->GetStringUTFChars(env, str, NULL);
        char parse_str[strlen(s) + 1];
        strcpy(parse_str, s);
        (*env)->ReleaseStringUTFChars(env, str, s);
        dbg(lvl_debug, "*****string=%s", s);

        // set destination to (lat#lon#title)
        struct coord_geo g;
        char *p;
        char *stopstring;

        // latitude
        p = strtok (parse_str,"#");
        g.lat = strtof(p, &stopstring);
        // longitude
        p = strtok (NULL, "#");
        g.lng = strtof(p, &stopstring);
        // description/name of the place identified by lat and long
        name = strtok (NULL, "#");

        dbg(lvl_debug, "lat=%f", g.lat);
        dbg(lvl_debug, "lng=%f", g.lng);
        dbg(lvl_debug, "str1=%s", name);

        struct coord c;
        transform_from_geo(projection_mg, &g, &c);

        struct pcoord pc;
        pc.x = c.x;
        pc.y = c.y;
        pc.pro = projection_mg;
        char coord_str[32];
        if (!name || *name == '\0') {     /* When name is an empty string, use the geo coord instead */
            pcoord_format_degree_short(&pc, coord_str, sizeof(coord_str), " ");
            name = coord_str;
        }
        // start navigation asynchronous
        navit_set_destination(attr.u.navit, &pc, name, 1);
    }
    break;
    default:
        dbg(lvl_error, "Unknown command: %d", channel);
    }

    return ret;
}

static char *postal_str(struct search_list_result *res, int level) {
    char *ret=NULL;
    if (res->town->common.postal)
        ret=res->town->common.postal;
    if (res->town->common.postal_mask)
        ret=res->town->common.postal_mask;
    if (level == 1)
        return ret;
    if (res->street->common.postal)
        ret=res->street->common.postal;
    if (res->street->common.postal_mask)
        ret=res->street->common.postal_mask;
    if (level == 2)
        return ret;
    if (res->house_number->common.postal)
        ret=res->house_number->common.postal;
    if (res->house_number->common.postal_mask)
        ret=res->house_number->common.postal_mask;
    return ret;
}

static char *district_str(struct search_list_result *res, int level) {
    char *ret=NULL;
    if (res->town->common.district_name)
        ret=res->town->common.district_name;
    if (level == 1)
        return ret;
    if (res->street->common.district_name)
        ret=res->street->common.district_name;
    if (level == 2)
        return ret;
    if (res->house_number->common.district_name)
        ret=res->house_number->common.district_name;
    return ret;
}

static char *town_str(struct search_list_result *res, int level) {
    char *town=res->town->common.town_name;
    char *district=district_str(res, level);
    char *postal=postal_str(res, level);
    char *postal_sep=" ";
    char *district_begin=" (";
    char *district_end=")";
    char *county_sep = ", Co. ";
    char *county = res->town->common.county_name;
    if (!postal)
        postal_sep=postal="";
    if (!district)
        district_begin=district_end=district="";
    if (!county)
        county_sep=county="";

    return g_strdup_printf("%s%s%s%s%s%s%s%s", postal, postal_sep, town, district_begin, district,
                           district_end, county_sep, county);
}

static void android_search_end(struct android_search_priv *search_priv) {
    dbg(lvl_debug, "End search");
    JNIEnv* env = search_priv->search_result_obj.env;
    if (search_priv->idle_ev) {
        event_remove_idle(search_priv->idle_ev);
        search_priv->idle_ev=NULL;
    }
    if (search_priv->idle_clb) {
        callback_destroy(search_priv->idle_clb);
        search_priv->idle_clb=NULL;
    }
    jclass cls = (*env)->GetObjectClass(env,search_priv->search_result_obj.jo);
    jmethodID finish_MethodID = (*env)->GetMethodID(env, cls, "finishAddressSearch", "()V");
    if(finish_MethodID != 0) {
        (*env)->CallVoidMethod(env, search_priv->search_result_obj.jo, finish_MethodID);
    } else {
        dbg(lvl_error, "Error method finishAddressSearch not found");
    }

    search_list_destroy(search_priv->search_list);
    g_strfreev(search_priv->phrases);
    g_free(search_priv);
}

static enum attr_type android_search_level[] = {
    attr_town_or_district_name,
    attr_street_name,
    attr_house_number
};

static void android_search_idle_result(struct android_search_priv *search_priv, struct search_list_result *res) {
//    commented out because otherwise cyclomatic complexity needleslly reported as too high
//    dbg(lvl_debug, "Town: %s, Street: %s",res->town ? res->town->common.town_name : "no town",
//        res->street ? res->street->name : "no street");
    search_priv->found = 1;
    switch (search_priv->search_attr.type) {
    case attr_town_or_district_name: {
        gchar *town = town_str(res, 1);
        android_return_search_result(&search_priv->search_result_obj, 0, res->town->common.c, town);
        g_free(town);
    }
    break;
    case attr_street_name: {
        gchar *town = town_str(res, 2);
        gchar *address = g_strdup_printf("%.101s,%.101s, %.101s", res->country->name, town, res->street->name);
        android_return_search_result(&search_priv->search_result_obj, 1, res->street->common.c, address);
        g_free(address);
        g_free(town);
    }
    break;
    case attr_house_number: {
        gchar *town = town_str(res, 3);
        gchar *address = g_strdup_printf("%.101s, %.101s, %.101s %.15s", res->country->name, town,
                                         res->street->name, res->house_number->house_number);
        android_return_search_result(&search_priv->search_result_obj, 2, res->house_number->common.c, address);
        g_free(address);
        g_free(town);
    }
    break;
    default:
        dbg(lvl_error, "Unhandled search type %d", search_priv->search_attr.type);
    }
}

static void android_search_idle(struct android_search_priv *search_priv) {
    dbg(lvl_debug, "enter android_search_idle");

    struct search_list_result *res = search_list_get_result(search_priv->search_list);
    if (res) {
        android_search_idle_result(search_priv, res);
    } else {
        int level = search_list_level(search_priv->search_attr.type) - 1;

        if (search_priv->found) {
            search_priv->found = 0;
            if (search_priv->search_attr.type != attr_house_number) {
                level++;
            }
        }
        dbg(lvl_info, "test phrase: %d,%d, %d, level %d", search_priv->current_phrase_per_level[0],
            search_priv->current_phrase_per_level[1], search_priv->current_phrase_per_level[2], level)
        do {
            while (!search_priv->phrases[++search_priv->current_phrase_per_level[level]]) {
                dbg(lvl_info, "next phrase: %d,%d, %d, level %d", search_priv->current_phrase_per_level[0],
                    search_priv->current_phrase_per_level[1], search_priv->current_phrase_per_level[2], level)
                if (level > 0) {
                    search_priv->current_phrase_per_level[level] = -1;
                    level--;
                } else {
                    android_search_end(search_priv);
                    return;
                }
            }
        } while (level > 0 ? search_priv->current_phrase_per_level[level] == search_priv->current_phrase_per_level[level-1] :
                 0);
        dbg(lvl_info, "used phrase: %d,%d, %d, level %d, '%s'", search_priv->current_phrase_per_level[0],
            search_priv->current_phrase_per_level[1], search_priv->current_phrase_per_level[2], level,
            attr_to_name(android_search_level[level]))
        dbg(lvl_debug, "Search for '%s'", search_priv->phrases[search_priv->current_phrase_per_level[level]]);
        search_priv->search_attr.type = android_search_level[level];
        search_priv->search_attr.u.str = search_priv->phrases[search_priv->current_phrase_per_level[level]];
        struct attr test;
        test.type = android_search_level[level];
        test.u.str = search_priv->phrases[search_priv->current_phrase_per_level[level]];
        search_list_search(search_priv->search_list, &test, search_priv->partial);
    }
    dbg(lvl_info, "leave");
}

static void start_search(struct android_search_priv *search_priv, const char *search_string) {
    dbg(lvl_debug,"enter %s", search_string);
    char *str=search_fix_spaces(search_string);
    search_priv->phrases = g_strsplit(str, " ", 0);
    //ret=search_address_town(ret, sl, phrases, NULL, partial, jni);

    dbg(lvl_debug,"First search phrase %s", search_priv->phrases[0]);
    search_priv->search_attr.u.str= search_priv->phrases[0];
    search_priv->search_attr.type=attr_town_or_district_name;
    search_list_search(search_priv->search_list, &search_priv->search_attr, search_priv->partial);

    search_priv->idle_clb = callback_new_1(callback_cast(android_search_idle), search_priv);
    search_priv->idle_ev = event_add_idle(50,search_priv->idle_clb);
    //callback_call_0(search_priv->idle_clb);

    g_free(str);
    dbg(lvl_debug,"leave");
}

JNIEXPORT jlong JNICALL Java_org_navitproject_navit_NavitAddressSearchActivity_callbackStartAddressSearch( JNIEnv* env,
        jobject thiz, jint partial, jstring country, jstring str) {
    struct attr attr;
    const char *search_string =(*env)->GetStringUTFChars(env, str, NULL);
    dbg(lvl_debug,"search '%s'", search_string);

    config_get_attr(config_get(), attr_navit, &attr, NULL);

    jclass cls = (*env)->GetObjectClass(env,thiz);
    jmethodID aMethodID = (*env)->GetMethodID(env, cls, "receiveAddress", "(IFFLjava/lang/String;)V");
    struct android_search_priv *search_priv = NULL;

    if(aMethodID != 0) {
        struct mapset *ms4=navit_get_mapset(attr.u.navit);
        struct attr country_attr;
        const char *str_country=(*env)->GetStringUTFChars(env, country, NULL);
        struct search_list_result *slr;
        int count = 0;

        search_priv = g_new0( struct android_search_priv, 1);
        search_priv->search_list = search_list_new(ms4);
        search_priv->partial = partial;
        search_priv->current_phrase_per_level[1] = -1;
        search_priv->current_phrase_per_level[2] = -1;

        country_attr.type=attr_country_iso2;
        country_attr.u.str=g_strdup(str_country);
        search_list_search(search_priv->search_list, &country_attr, 0);

        while ((slr=search_list_get_result(search_priv->search_list))) {
            count++;
        }
        if (!count)
            dbg(lvl_error,"Country not found");

        dbg(lvl_debug,"search in country '%s'", str_country);
        (*env)->ReleaseStringUTFChars(env, country, str_country);

        search_priv->search_result_obj.env = env;
        search_priv->search_result_obj.jo = (*env)->NewGlobalRef(env, thiz);
        search_priv->search_result_obj.jm = aMethodID;

        start_search(search_priv, search_string);
    } else
        dbg(lvl_error,"**** Unable to get methodID: fillStringArray");

    (*env)->ReleaseStringUTFChars(env, str, search_string);

    return (jlong)(long)search_priv;
}

JNIEXPORT void JNICALL Java_org_navitproject_navit_NavitAddressSearchActivity_callbackCancelAddressSearch( JNIEnv* env,
        jobject thiz, jlong handle) {
    struct android_search_priv *priv = (void*)(long)handle;

    if (priv)
        android_search_end(priv);
    else
        dbg(lvl_error, "Error: Cancel search failed");
}
