#include "PLSide.h"

char* _get_keybuffer(char *event, UV addr, int *len) {
    // the maximal keybuffer is the key for the function:
    // event###addresstofunc\0
    if (event == NULL) {
         int number_of_digits = snprintf(NULL, 0, "%"UVuf, addr);
        *len = number_of_digits + 1;
    }
    else {
        int number_of_digits = snprintf(NULL, 0, "%"UVuf, addr);
        *len = strlen(event) + 3 + number_of_digits + 1;
    }
    char *keybuffer = malloc(*len);
    if (keybuffer == NULL) {
        croak("Speicherfehler\n");
    }
    return keybuffer;
}

char* _get_keybuffer2(pTHX_ char *event, SV* funcname, int *len) {
    // the maximal keybuffer is the key for the function:
    // event###addresstofunc\0
    if (event == NULL) {
         int number_of_digits = snprintf(NULL, 0, "%s", SvPV_nolen(funcname));
        *len = number_of_digits + 1;
    }
    else {
        int number_of_digits = snprintf(NULL, 0, "%s", SvPV_nolen(funcname));
        *len = strlen(event) + 3 + number_of_digits + 1;
    }
    char *keybuffer = malloc(*len);
    if (keybuffer == NULL) {
        croak("Speicherfehler\n");
    }
    return keybuffer;
}


HV* _get_smart_cb_hash(pTHX_ UV objaddr,char *event,SV *funcname, char* hashName) {
    HV* Obj_Cbs;
    int len, n;
    char *keybuffer = _get_keybuffer2(aTHX_ event,funcname, &len);

    HV *Callbacks = get_hv(hashName, 0);
    if (Callbacks == NULL) {
        croak("pEFL::PLSide::Callbacks hash does not exist\n");
    }

    n = snprintf(keybuffer, len, "%" UVuf, objaddr);
    if (hv_exists(Callbacks,keybuffer,strlen(keybuffer))) {
        SV** Obj_CbsPtr = hv_fetch(Callbacks, keybuffer,strlen(keybuffer),FALSE);
        Obj_Cbs = (HV*) (SvRV(*Obj_CbsPtr));
    }
    else {
        croak("Error in _get_smart_cb_hash: No callbacks found\n");
    }


    n = snprintf(keybuffer, len, "%s###%s", event, SvPV_nolen(funcname));
    SV** Cb_DataPtr = hv_fetch(Obj_Cbs, keybuffer, strlen(keybuffer), FALSE);
    HV *cb_data = (HV*) SvRV(*Cb_DataPtr);

    free(keybuffer);

    return cb_data;
}

_perl_callback *perl_save_callback(pTHX_ SV *func, UV objaddr, char *event, char *hashName) {
    _perl_callback *cb;

    cb = (_perl_callback *)malloc(sizeof(_perl_callback));
    memset(cb, '\0', sizeof(_perl_callback));
    cb->funcname = newSV(0);

    if (func && SvOK(func)) {
        cv_name((CV*)SvRV(func),cb->funcname,0);
    }
    else {
        croak("No function passed\n");
    }

    if (objaddr) {
        cb->objaddr = objaddr;
    }
    else {
        croak("EvasObject missing\n");
    }

    if (event) {
            strcpy(cb->event, event);
    }

    // For Smart Callbacks and Evas Callbacks we have to save
    // a pointer to the c struct in the perl hash, too
    // This is necessary for deletion
    if (strcmp(hashName,"pEFL::PLSide::Format_Cbs") == 0) {
        HV *cb_data = _get_format_cb_hash(aTHX_ cb->objaddr);
        hv_store(cb_data, "cstructaddr",11,newSVuv(PTR2UV(cb)),0);
    }
    else if (strcmp(hashName,"pEFL::PLSide::Callbacks") == 0) {
        HV *smart_cb = _get_smart_cb_hash(aTHX_ cb->objaddr,cb->event,cb->funcname, hashName);
        hv_store(smart_cb, "cstructaddr",11,newSVuv(PTR2UV(cb)),0);
    }

    return cb;
}


void call_perl_sub(void *data, Evas_Object *obj, void *event_info) {
    dTHX;
    dSP;

    int n;
    int count;
    SV *s_obj = newSV(0);
    _perl_callback *perl_saved_cb = data;

    SV *s_ei  = newSV(0);
    if (event_info) {
        if (SvTRUE(get_sv("pEFL::Debug",0)))
			fprintf(stderr, "event has an event info\n");
        IV adress;
        adress = PTR2IV(event_info);
        sv_setiv(s_ei,adress);
    }

    HV *cb_data = _get_smart_cb_hash(aTHX_ perl_saved_cb->objaddr,perl_saved_cb->event,perl_saved_cb->funcname, "pEFL::PLSide::Callbacks");

    SV *pclass = *( hv_fetch(cb_data, "pclass",6,FALSE) );
    SV *func = *( hv_fetch(cb_data, "function",8,FALSE) );
    SV *args = *( hv_fetch(cb_data, "data",4,FALSE) ) ;



    sv_setref_pv(s_obj, SvPV_nolen(pclass), obj);

    ENTER;
    SAVETMPS;

    PUSHMARK(SP);

    XPUSHs(args);
    XPUSHs(sv_2mortal(s_obj));
    XPUSHs(sv_2mortal(s_ei));


    PUTBACK;

    count = call_sv(func, G_DISCARD);
    if (count != 0) {
        croak("Expected 0 value got %d\n", count);
    }

    FREETMPS;
    LEAVE;

    /* TODO free data? */
}

void call_perl_evas_event_cb(void *data, Evas *e, Evas_Object *obj, void *event_info) {
    dTHX;
    dSP;

    int n;
    int count;
    SV *s_obj = newSV(0);
	SV *s_canvas = newSV(0);
    _perl_callback *perl_saved_cb = data;

    SV *s_ei  = newSV(0);
    if (event_info) {
        if (SvTRUE(get_sv("pEFL::Debug",0)))
			fprintf(stderr, "event has an event info\n");
        IV adress;
        adress = PTR2IV(event_info);
        sv_setiv(s_ei,adress);
    }

    HV *cb_data = _get_smart_cb_hash(aTHX_ perl_saved_cb->objaddr,perl_saved_cb->event,perl_saved_cb->funcname, "pEFL::PLSide::Callbacks");

    SV *pclass = *( hv_fetch(cb_data, "pclass",6,FALSE) );
    SV *func = *( hv_fetch(cb_data, "function",8,FALSE) );
    SV *args = *( hv_fetch(cb_data, "data",4,FALSE) ) ;



    sv_setref_pv(s_obj, SvPV_nolen(pclass), obj);
	sv_setref_pv(s_canvas, "pEFL::Evas::Canvas", e);

    ENTER;
    SAVETMPS;

    PUSHMARK(SP);

    XPUSHs(args);
	XPUSHs(sv_2mortal(s_canvas));
    XPUSHs(sv_2mortal(s_obj));
    XPUSHs(sv_2mortal(s_ei));


    PUTBACK;

    count = call_sv(func, G_DISCARD);
    if (count != 0) {
        croak("Expected 0 value got %d\n", count);
    }

    FREETMPS;
    LEAVE;

    /* TODO free data? */
}

Evas_Object* call_perl_tooltip_content_cb(void *data, Evas_Object *obj, Evas_Object *tooltip) {
    dTHX;
    dSP;

    int count;
    SV *s_content;
    IV tmp;
    Evas_Object *ret_obj;

    _perl_callback *perl_saved_cb = data;
    HV *cb_data = _get_smart_cb_hash(aTHX_ perl_saved_cb->objaddr,perl_saved_cb->event,perl_saved_cb->funcname, "pEFL::PLSide::Callbacks");

    // Object
    SV *s_obj = newSV(0);
    SV *pclass = *( hv_fetch(cb_data, "pclass",6,FALSE) );
    sv_setref_pv(s_obj, SvPV_nolen(pclass), obj);

    // Tooltip
    SV *s_tooltip = newSV(0);
    sv_setref_pv(s_tooltip, "EvasObjectPtr", tooltip);


    SV *func = *( hv_fetch(cb_data, "function",8,FALSE) );
    SV *args = *( hv_fetch(cb_data, "data",4,FALSE) ) ;



    sv_setref_pv(s_obj, SvPV_nolen(pclass), obj);

    ENTER;
    SAVETMPS;

    PUSHMARK(SP);

    XPUSHs(args);
    XPUSHs(sv_2mortal(s_obj));
    XPUSHs(sv_2mortal(s_tooltip));


    PUTBACK;

    if (count != 1) {
        croak("Expected 1 value got %d\n", count);
    }

    s_content = POPs;

    if (!SvROK(s_content)) {
            ret_obj = NULL;
        }
    else {
    	IV tmp = SvIV((SV*)SvRV(s_content));
    	ret_obj = INT2PTR(Evas_Object*,tmp);
    	sv_setref_pv(s_obj, "EvasObjectPtr", obj);
    }

    PUTBACK;
    FREETMPS;
    LEAVE;

    return ret_obj;

    /* TODO free data? */
}

void del_tooltip(void *data, Evas_Object *obj, void *event_info) {
    dTHX;
    dSP;

    int n;
    int count;
    SV *s_obj = newSV(0);
    _perl_callback *perl_saved_cb = data;

    SV *s_ei  = newSV(0);
    if (event_info) {
        if (SvTRUE(get_sv("pEFL::Debug",0)))
			fprintf(stderr, "event has an event info\n");
        IV adress;
        adress = PTR2IV(event_info);
        sv_setiv(s_ei,adress);
    }

    HV *cb_data = _get_smart_cb_hash(aTHX_ perl_saved_cb->objaddr,perl_saved_cb->event,perl_saved_cb->funcname, "pEFL::PLSide::Callbacks");

    SV *pclass = *( hv_fetch(cb_data, "pclass",6,FALSE) );
    SV *func = *( hv_fetch(cb_data, "function",8,FALSE) );
    SV *args = *( hv_fetch(cb_data, "data",4,FALSE) ) ;



    sv_setref_pv(s_obj, SvPV_nolen(pclass), obj);

    ENTER;
    SAVETMPS;

    PUSHMARK(SP);

    XPUSHs(args);
    XPUSHs(sv_2mortal(s_obj));
    XPUSHs(sv_2mortal(s_ei));


    PUTBACK;

    count = call_sv(func, G_DISCARD);
    if (count != 0) {
        croak("Expected 0 value got %d\n", count);
    }

    FREETMPS;
    LEAVE;
    
    
    Safefree(data);
}

// -----------------
// FORMAT CB STUFF
// -----------------
HV* _get_format_cb_hash(pTHX_ UV objaddr) {
    HV* cb_data;
    int len, n;
    char *keybuffer = _get_keybuffer(NULL,objaddr, &len);

    HV *Callbacks = get_hv("pEFL::PLSide::Format_Cbs", 0);
    if (Callbacks == NULL) {
        croak("pEFL::PLSide::Format_Cbs hash does not exist\n");
    }

    n = snprintf(keybuffer, len, "%" UVuf, objaddr);
    if (hv_exists(Callbacks,keybuffer,strlen(keybuffer))) {
        SV** Cb_DataPtr = hv_fetch(Callbacks, keybuffer,strlen(keybuffer),FALSE);
        cb_data = (HV*) (SvRV(*Cb_DataPtr));
    }
    else {
        croak("Error in _get_format_cb_hash: No callbacks found\n");
    }

    free(keybuffer);

    return cb_data;
}

char* call_perl_format_cb(double value, void* data) {
    dTHX;
    dSP;

    int count; STRLEN len;
    SV *s_string; char* buf = NULL;
    SV *s_value = newSV(0);

    _perl_callback *perl_saved_cb = data;

    HV *cb_data = _get_format_cb_hash(aTHX_ perl_saved_cb->objaddr);


    SV *func = *( hv_fetch(cb_data, "function",8,FALSE) );

    sv_setnv(s_value, value);

    ENTER;
    SAVETMPS;

    PUSHMARK(SP);

    XPUSHs(sv_2mortal(s_value));

    PUTBACK;

    count = call_sv(func, G_SCALAR);

    SPAGAIN;

    if (count != 1) {
        croak("Expected 1 value got %d\n", count);
    }

    s_string = POPs;
    len = sv_len(s_string);
    buf = savepv(SvPV(s_string,len));

    PUTBACK;
    FREETMPS;
    LEAVE;

    return buf;
}

void free_buf(char *buf) {
    Safefree(buf);
}

// -------------------------------------
//  ELM_ENTRY
// ---------------------------------------

HV* _get_markup_filter_cb(pTHX_ UV objaddr,SV* funcname) {
    HV* markup_filters;
    HV* cb_data;
    int len, n;

    HV *cbs = get_hv("pEFL::PLSide::MarkupFilter_Cbs", 0);
    if (cbs == NULL) {
        croak("pEFL::PLSide::MarkupFilter_Cbs hash does not exist\n");
    }

    char *addr_keybuffer = _get_keybuffer(NULL, objaddr,&len);
    n = snprintf(addr_keybuffer, len, "%" UVuf, objaddr);

    if (hv_exists(cbs,addr_keybuffer,strlen(addr_keybuffer))) {
        SV** markup_filtersPtr = hv_fetch(cbs, addr_keybuffer, strlen(addr_keybuffer), FALSE);
        markup_filters = (HV*) SvRV(*markup_filtersPtr);
    }
    else {
        croak("No markup filters found\n");
    }

    free(addr_keybuffer);

    char *keybuffer = _get_keybuffer2(aTHX_ NULL,funcname, &len);
    n = snprintf(keybuffer, len, "%s", SvPV_nolen(funcname));
    if (hv_exists(markup_filters,keybuffer,strlen(keybuffer))) {
        SV** cb_dataPtr = hv_fetch(markup_filters, keybuffer, strlen(keybuffer), FALSE);
        cb_data = (HV*) SvRV(*cb_dataPtr);
    }
    else {
        croak("Error in _get_markup_filters_cb: No callbacks found\n");
    }

    free(keybuffer);

    return cb_data;
}

_perl_callback *save_markup_filter_struct(pTHX_ SV *func, UV addr) {
    _perl_callback *cb;

    cb = (_perl_callback *)malloc(sizeof(_perl_callback));
    memset(cb, '\0', sizeof(_perl_callback));
    cb->funcname = newSV(0);

    if (SvPOK(func) && (strcmp(SvPV_nolen(func),"limit_size") == 0)) {
        cb->funcname = func;
    }
    else if (SvPOK(func) && (strcmp(SvPV_nolen(func),"accept_set") == 0)) {
        cb->funcname = func;
    }
    else if (func && SvOK(func)) {
        cv_name((CV*)SvRV(func),cb->funcname,0);
    }
    else {
        croak("No function passed\n");
    }

    if (addr) {
        cb->objaddr = addr;
    }
    else {
        croak("No object address passed\n");
    }


    HV *markup_filter_cb = _get_markup_filter_cb(aTHX_ cb->objaddr,cb->funcname);
    hv_store(markup_filter_cb, "cstructaddr",11,newSVuv(PTR2UV(cb)),0);

    return cb;
}

void call_perl_markup_filter_cb(void *data, Elm_Entry *entry, char **text) {
    dTHX;
    dSP;

    int count; STRLEN len;
    SV *s_string;


    _perl_callback *perl_saved_cb = data;

    HV *cb_data = _get_markup_filter_cb(aTHX_ perl_saved_cb->objaddr, perl_saved_cb->funcname);

    // Get functoion
    SV *func = *( hv_fetch(cb_data, "function",8,FALSE) );

    // Get data
    SV* s_data = *( hv_fetch(cb_data, "data",4,FALSE) );

    // Get Object
    SV *s_obj = newSV(0);
    sv_setref_pv(s_obj, "ElmEntry", entry);

    // text
    SV *s_text = newSVpvn(*text,strlen(*text));

    ENTER;
    SAVETMPS;

    PUSHMARK(SP);

    XPUSHs(s_data);
    XPUSHs(sv_2mortal(s_obj));
    XPUSHs(sv_2mortal(s_text));

    PUTBACK;

    count = call_sv(func, G_SCALAR);

    SPAGAIN;

    if (count != 1) {
        croak("Expected 1 value got %d\n", count);
    }

    s_string = POPs;
    if (SvOK(s_string) && SvPOK(s_string)) {
        len = sv_len(s_string);
        *text = savepv(SvPV(s_string,len));
    }
    else {
        *text = NULL;
    }

    PUTBACK;
    FREETMPS;
    LEAVE;
}

// -------------------------------------
// Genlist Item Class
//------------------------------------

AV* _get_gen_items(pTHX_ UV objaddr) {
    AV* cbs;
    int len, n;
    char *keybuffer = _get_keybuffer(NULL,objaddr, &len);

    HV *GenItc_Cbs = get_hv("pEFL::PLSide::GenItems", 0);
    if (GenItc_Cbs == NULL) {
        croak("pEFL::PLSide::GenItems hash does not exist\n");
    }

    n = snprintf(keybuffer, len, "%" UVuf, objaddr);
    if (hv_exists(GenItc_Cbs,keybuffer,strlen(keybuffer))) {
        SV** CbsPtr = hv_fetch(GenItc_Cbs, keybuffer,strlen(keybuffer),FALSE);
        cbs = (AV*) (SvRV(*CbsPtr));
    }
    else {
        croak("No items found in Genlist\n");
    }

    free(keybuffer);

    return cbs;
}

HV* _get_gen_item_hash(pTHX_ UV objaddr, int item_id) {
    AV *GenItems = _get_gen_items(aTHX_ objaddr);

    SV** GenItem_Ptr = av_fetch(GenItems, (I32) item_id,FALSE);
    HV *GenItem = (HV*) (SvRV(*GenItem_Ptr));
    return GenItem;
}

_perl_gendata *perl_save_gen_cb(pTHX_ UV objaddr, UV itcaddr, int id) {
    _perl_gendata *cb;
    
    New(0,cb,1,_perl_gendata);

    if (objaddr) {
        cb->objaddr = objaddr ;
    }
    else {
        croak("No Gen item class passed\n");
    }

    if (itcaddr) {
        cb->itcaddr = itcaddr ;
    }
    else {
    	cb->itcaddr = 0;
    }

    if (id>=0) {
        cb->item_id = id;
    }
    else {
        croak("No Item id passed \n");
    }


    // We have to save a pointer to the c struct in the perl hash, too
    // This is necessary for deletion
    //if (event != NULL) {
    HV *perl_cb = _get_gen_item_hash(aTHX_ cb->objaddr, id);
    hv_store(perl_cb, "cstructaddr",11,newSVuv(PTR2UV(cb)),0);
    // }

    return cb;
}

int *perl_save_gen_item_data(pTHX_ Elm_Genlist *obj) {
}

HV* _get_gen_hash(pTHX_ UV objaddr, char *hashName) {
    HV* cbs;
    int len, n;
    char *keybuffer = _get_keybuffer(NULL,objaddr, &len);
	
    HV *GenItc_Cbs = get_hv(hashName, 0);
    if (GenItc_Cbs == NULL) {
        croak("pEFL::PLSide::GenItc hash does not exist\n");
    }
	
    n = snprintf(keybuffer, len, "%" UVuf, objaddr);
    if (hv_exists(GenItc_Cbs,keybuffer,strlen(keybuffer))) {
        SV** CbsPtr = hv_fetch(GenItc_Cbs, keybuffer,strlen(keybuffer),FALSE);
        cbs = (HV*) (SvRV(*CbsPtr));
    }
    else {
        croak("Error in _get_gen_hash: No callbacks found\n");
    }

    free(keybuffer);

    return cbs;
}

char* call_perl_gen_text_get(void *data, Evas_Object *obj, const char *part) {
    dTHX;
    dSP;

    int count; STRLEN len;
    SV *s_part;
    SV *s_string;
    char* buf;

    _perl_gendata *perl_saved_cb = data;

    HV *cb_data = _get_gen_hash(aTHX_ perl_saved_cb->itcaddr,"pEFL::PLSide::GenItc");
    SV *func = *( hv_fetch(cb_data, "text_get",8,FALSE) );

    HV *GenItem = _get_gen_item_hash(aTHX_ perl_saved_cb->objaddr, perl_saved_cb->item_id);

    // Get data
    SV* s_data = *( hv_fetch(GenItem, "data",4,FALSE) );

    // Object
    SV *s_obj = newSV(0);
    sv_setref_pv(s_obj, "ElmGenlistPtr", obj);

    // part
    s_part = newSVpvn(part,strlen(part));

    ENTER;
    SAVETMPS;

    PUSHMARK(SP);

    XPUSHs(s_data);
    XPUSHs(sv_2mortal(s_obj));
    XPUSHs(sv_2mortal(s_part));

    PUTBACK;

    count = call_sv(func, G_SCALAR);

    SPAGAIN;

    if (count != 1) {
        croak("Expected 1 value got %d\n", count);
    }

    s_string = POPs;
    len = sv_len(s_string);

    buf = strdup(SvPV(s_string,len));

    PUTBACK;
    FREETMPS;
    LEAVE;

    return buf;
}

Eina_Bool call_perl_gen_state_get(void *data, Evas_Object *obj, const char *part) {
    dTHX;
    dSP;

    int count; STRLEN len;
    SV *s_part;
    SV *s_bool; Eina_Bool e_bool;
    char* buf;

    _perl_gendata *perl_saved_cb = data;

    HV *cb_data = _get_gen_hash(aTHX_ perl_saved_cb->itcaddr,"pEFL::PLSide::GenItc");
    SV *func = *( hv_fetch(cb_data, "state_get",8,FALSE) );

    HV *GenItem = _get_gen_item_hash(aTHX_ perl_saved_cb->objaddr, perl_saved_cb->item_id);

    // Get data
    SV* s_data = *( hv_fetch(GenItem, "data",4,FALSE) );

    // Object
    SV *s_obj = newSV(0);
    sv_setref_pv(s_obj, "ElmGenlistPtr", obj);

    // part
    s_part = newSVpvn(part,strlen(part));

    ENTER;
    SAVETMPS;

    PUSHMARK(SP);

    XPUSHs(s_data);
    XPUSHs(sv_2mortal(s_obj));
    XPUSHs(sv_2mortal(s_part));

    PUTBACK;

    count = call_sv(func, G_SCALAR);

    SPAGAIN;

    if (count != 1) {
        croak("Expected 1 value got %d\n", count);
    }

    s_bool = POPs;
    if (SvTRUE(s_bool) ) {
        e_bool = EINA_TRUE;
    }
    else {
        e_bool = EINA_FALSE;
    }

    PUTBACK;
    FREETMPS;
    LEAVE;

    return e_bool;
}

Evas_Object* call_perl_gen_content_get(void *data, Evas_Object *obj, const char *part) {
    dTHX;
    dSP;

    int count;
    SV *s_part;
    SV *s_content; Evas_Object *ret_obj;
    char* buf;
	
    _perl_gendata *perl_saved_cb = data;

    HV *cb_data = _get_gen_hash(aTHX_ perl_saved_cb->itcaddr,"pEFL::PLSide::GenItc");
    SV *func = *( hv_fetch(cb_data, "content_get",11,FALSE) );

    HV *GenItem = _get_gen_item_hash(aTHX_ perl_saved_cb->objaddr, perl_saved_cb->item_id);
	
    // Get data
    SV* s_data = *( hv_fetch(GenItem, "data",4,FALSE) );

    // Object
    SV *s_obj = newSV(0);
    sv_setref_pv(s_obj, "ElmGenlistPtr", obj);

    // part
    s_part = newSVpvn(part,strlen(part));

    ENTER;
    SAVETMPS;

    PUSHMARK(SP);

    XPUSHs(s_data);
    XPUSHs(sv_2mortal(s_obj));
    XPUSHs(sv_2mortal(s_part));

    PUTBACK;

    count = call_sv(func, G_SCALAR);

    SPAGAIN;

    if (count != 1) {
        croak("Expected 1 value got %d\n", count);
    }

    s_content = POPs;

    if (!SvROK(s_content)) {
            ret_obj = NULL;
        }
    else {
    	IV tmp = SvIV((SV*)SvRV(s_content));
    	ret_obj = INT2PTR(Evas_Object*,tmp);
    	// sv_setref_pv(s_obj, "EvasObjectPtr", obj);
    }

    PUTBACK;
    FREETMPS;
    LEAVE;

    return ret_obj;
}

void call_perl_genitc_del(void *data, Evas_Object *obj) {
	call_perl_gen_del(data,obj, NULL);
}

void call_perl_gen_del(void *data, Evas_Object *obj, void *event_info) {
    dTHX;
    dSP;
    
    _perl_gendata *perl_saved_cb = data;
    	
    int id = perl_saved_cb->item_id;
    
    if (SvTRUE(get_sv("pEFL::Debug",0)))
    	fprintf(stderr,"Calling call_perl_gen_del() on item %d of Genlist with address %"UVuf"\n", id, perl_saved_cb->objaddr);
    
    HV *cb_data = NULL;
    UV itcaddr = perl_saved_cb->itcaddr;
	if (perl_saved_cb->itcaddr != 0) {
    	cb_data = _get_gen_hash(aTHX_ perl_saved_cb->itcaddr,"pEFL::PLSide::GenItc");
    }

    HV *GenItem = _get_gen_item_hash(aTHX_ perl_saved_cb->objaddr, perl_saved_cb->item_id);

    if (cb_data && hv_exists(cb_data,"del",3)) {
        int count;

        SV *func = *( hv_fetch(cb_data, "del",3,FALSE) );
        // Get data
        SV* s_data = *( hv_fetch(GenItem, "data",4,FALSE) );

        // Object
        SV *s_obj = newSV(0);
        sv_setref_pv(s_obj, "ElmGenlistPtr", obj);

        ENTER;
        SAVETMPS;

        PUSHMARK(SP);

        XPUSHs(s_data);
        XPUSHs(sv_2mortal(s_obj));

        PUTBACK;

        count = call_sv(func, G_DISCARD);

        if (count != 0) {
            croak("Expected 0 value got %d\n", count);
        }

        FREETMPS;
        LEAVE;
    }
    // TODO: Check whether hash value exists
    if (SvTRUE(get_sv("pEFL::Debug",0)))
    	fprintf(stderr,"Deleting pEFL::PLSide::GenItems hash entry \n");
    	
    hv_undef(GenItem);
    
    if (SvTRUE(get_sv("pEFL::Debug",0)))
    	fprintf(stderr,"Freeing cstruct \n\n");
    	
    Safefree(data);
}

Eina_Bool call_perl_item_pop_cb(void*data,Elm_Naviframe_Item *it) {
    dTHX;
    dSP;
	
    int count; STRLEN len;
    SV *s_bool; Eina_Bool e_bool;
	
    _perl_gendata *perl_saved_cb = data;
	
    HV *GenItem = _get_gen_item_hash(aTHX_ perl_saved_cb->objaddr, perl_saved_cb->item_id);

    // Object
    SV *s_obj = newSV(0);
    sv_setref_pv(s_obj, "ElmNaviframeItemPtr", it);

    // Get func
    SV* func = *( hv_fetch(GenItem, "func",4,FALSE) );
	
        // Get func_data
        SV *func_data = *( hv_fetch(GenItem, "func_data",9,FALSE) );

        ENTER;
        SAVETMPS;

        PUSHMARK(SP);

        XPUSHs(func_data);
        XPUSHs(sv_2mortal(s_obj));

        PUTBACK;

        count = call_sv(func, G_SCALAR);

    	SPAGAIN;

    	if (count != 1) {
        	croak("Expected 1 value got %d\n", count);
    	}

    	s_bool = POPs;
    	if (SvTRUE(s_bool) ) {
        	e_bool = EINA_TRUE;
    	}
    	else {
        	e_bool = EINA_FALSE;
    	}

    	PUTBACK;
    	FREETMPS;
    	LEAVE;    

    return e_bool;

}

void call_perl_gen_item_selected(void *data, Evas_Object *obj, void *event_info) {
    dTHX;
    dSP;
	
    int count; STRLEN len;

    _perl_gendata *perl_saved_cb = data;

    HV *GenItem = _get_gen_item_hash(aTHX_ perl_saved_cb->objaddr, perl_saved_cb->item_id);

    // Object
    SV *s_obj = newSV(0);
    SV *pclass = *( hv_fetch(GenItem, "pclass",6,FALSE) );
    sv_setref_pv(s_obj, SvPV_nolen(pclass), obj);

    // Get func
    SV* func = *( hv_fetch(GenItem, "func",4,FALSE) );

    // Execute callback only if a callback is saved :-)
    if (func && SvOK(func)) {
        // Get func_data
        SV *func_data = *( hv_fetch(GenItem, "func_data",9,FALSE) );

        // event info
        // TODO: Make an own function? This is also needed by call_perl_sub
        SV *s_ei  = newSV(0);
        if (event_info) {
            if (SvTRUE(get_sv("pEFL::Debug",0)))
				fprintf(stderr, "event has an event info\n");
            IV adress;
            adress = PTR2IV(event_info);
            sv_setiv(s_ei,adress);
        }

        ENTER;
        SAVETMPS;

        PUSHMARK(SP);

        XPUSHs(func_data);
        XPUSHs(sv_2mortal(s_obj));
        XPUSHs(sv_2mortal(s_ei));

        PUTBACK;

        count = call_sv(func, G_DISCARD);
        if (count != 0) {
            croak("Expected 0 value got %d\n", count);
        }

        FREETMPS;
        LEAVE;
    }

}

//
// Edje Signals
//

// Same as _get_gen_items()
// perhaps make one function??
AV* _get_signals(pTHX_ UV objaddr, int items) {
    AV* cbs;
    int len, n;
    char *keybuffer;
    if ( items == 0 ) {
    	keybuffer = _get_keybuffer(NULL,objaddr, &len);
    	n = snprintf(keybuffer, len, "%"UVuf, objaddr);
    }
    else if (items == 1) {
    	keybuffer = _get_keybuffer("###items",objaddr, &len);
    	n = snprintf(keybuffer, len, "%"UVuf"###items", objaddr);
    } 

    HV *Signals_Cbs = get_hv("pEFL::PLSide::EdjeSignals", 0);
    if (Signals_Cbs == NULL) {
        croak("pEFL::PLSide::EdjeSignals hash does not exist\n");
    }

    
    if (hv_exists(Signals_Cbs,keybuffer,strlen(keybuffer))) {
        SV** CbsPtr = hv_fetch(Signals_Cbs, keybuffer,strlen(keybuffer),FALSE);
        cbs = (AV*) (SvRV(*CbsPtr));
    }
    else {
        croak("No signals found\n");
    }

    free(keybuffer);

    return cbs;
}

HV* _get_signal_hash(pTHX_ UV objaddr, int item_id) {
    AV *Items = _get_signals(aTHX_ objaddr, 0);

    SV** Item_Ptr = av_fetch(Items, (I32) item_id,FALSE);
    HV *Item = (HV*) (SvRV(*Item_Ptr));
    return Item;
}

HV* _get_item_signal_hash(pTHX_ UV objaddr, int item_id) {
    AV *Items = _get_signals(aTHX_ objaddr, 1);

    SV** Item_Ptr = av_fetch(Items, (I32) item_id,FALSE);
    HV *Item = (HV*) (SvRV(*Item_Ptr));
    return Item;
}

_perl_signal_cb* perl_save_signal(pTHX_ UV objaddr, int id) {
    _perl_signal_cb *cb;

    New(0,cb,1,_perl_signal_cb);

    if (objaddr) {
        cb->objaddr = objaddr ;
    }
    else {
        croak("Saving Signal Callback failed: No object address passed\n");
    }

    if (id>=0) {
        cb->signal_id = id;
    }
    else {
        croak("Saving Signal Callback failed: No Id passed \n");
    }

    return cb;
}

_perl_signal_cb* perl_save_signal_cb(pTHX_ UV objaddr, int id) {
    _perl_signal_cb *cb;

    cb = perl_save_signal(aTHX_ objaddr,id);

    // For Smart Callbacks and Evas Callbacks we have to save
    // a pointer to the c struct in the perl hash, too
    // This is necessary for deletion
    HV *cb_data = _get_signal_hash(aTHX_ cb->objaddr, cb->signal_id);
    hv_store(cb_data, "cstructaddr",11,newSVuv(PTR2UV(cb)),0);

    return cb;
}

_perl_signal_cb* perl_save_item_signal_cb(pTHX_ UV objaddr, int id) {
    _perl_signal_cb *cb;

    cb = perl_save_signal(aTHX_ objaddr,id);

    // For Smart Callbacks and Evas Callbacks we have to save
    // a pointer to the c struct in the perl hash, too
    // This is necessary for deletion
    HV *cb_data = _get_item_signal_hash(aTHX_ cb->objaddr, cb->signal_id);
    hv_store(cb_data, "cstructaddr",11,newSVuv(PTR2UV(cb)),0);

    return cb;
}

void call_perl_signal_cb(void *data, Evas_Object *layout, const char *emission, const char *source) {
    dTHX;
    dSP;

    int n; int count;

    _perl_signal_cb *perl_saved_cb = data;
    HV *cb_data = _get_signal_hash(aTHX_ perl_saved_cb->objaddr, perl_saved_cb->signal_id);

    SV *func = *( hv_fetch(cb_data, "function",8,FALSE) );
    SV *args = *( hv_fetch(cb_data, "data",4,FALSE) ) ;
    SV *s_obj = newSV(0);
    SV *pclass = *( hv_fetch(cb_data, "pclass",6,FALSE) );
    sv_setref_pv(s_obj, SvPV_nolen(pclass), layout);
    SV *s_emission = newSVpvn(emission,strlen(emission));
    SV *s_source = newSVpvn(source,strlen(source));

    ENTER;
    SAVETMPS;

    PUSHMARK(SP);

    XPUSHs( args );
    XPUSHs(sv_2mortal(s_obj) );
    XPUSHs(sv_2mortal(s_emission) );
    XPUSHs(sv_2mortal(s_source));

    PUTBACK;

    count = call_sv(func, G_DISCARD);
    if (count != 0) {
        croak("Expected 0 value got %d\n", count);
    }

    FREETMPS;
    LEAVE;
}

void call_perl_item_signal_cb(void *data, Elm_Object_Item *layout, const char *emission, const char *source) {
    dTHX;
    dSP;

    int n; int count;

    _perl_signal_cb *perl_saved_cb = data;
    HV *cb_data = _get_item_signal_hash(aTHX_ perl_saved_cb->objaddr, perl_saved_cb->signal_id);

    SV *func = *( hv_fetch(cb_data, "function",8,FALSE) );
    SV *args = *( hv_fetch(cb_data, "data",4,FALSE) ) ;
    SV *s_obj = newSV(0);
    sv_setref_pv(s_obj, "ElmObjectItemPtr", layout);
    SV *s_emission = newSVpvn(emission,strlen(emission));
    SV *s_source = newSVpvn(source,strlen(source));

    ENTER;
    SAVETMPS;

    PUSHMARK(SP);

    XPUSHs( args );
    XPUSHs(sv_2mortal(s_obj) );
    XPUSHs(sv_2mortal(s_emission) );
    XPUSHs(sv_2mortal(s_source));

    PUTBACK;

    count = call_sv(func, G_DISCARD);
    if (count != 0) {
        croak("Expected 0 value got %d\n", count);
    }

    FREETMPS;
    LEAVE;
}

// --------------------------------------
// Ecore Evas Event
// -------------------------------------

HV* _get_ecore_evas_event_cb_hash(pTHX_ UV objaddr) {
    HV* cb_data;
    int len, n;
    char *keybuffer = _get_keybuffer(NULL,objaddr, &len);

    HV *Callbacks = get_hv("pEFL::PLSide::EcoreEvasEvent_Cbs", 0);
    if (Callbacks == NULL) {
        croak("pEFL::PLSide::EcoreEvasEvent_Cbs hash does not exist\n");
    }

    n = snprintf(keybuffer, len, "%" UVuf, objaddr);
    if (hv_exists(Callbacks,keybuffer,strlen(keybuffer))) {
        SV** Cb_DataPtr = hv_fetch(Callbacks, keybuffer,strlen(keybuffer),FALSE);
        cb_data = (HV*) (SvRV(*Cb_DataPtr));
    }
    else {
        croak("Error in _get_ecore_evas_event_ch_hash: No callbacks found\n");
    }

    free(keybuffer);

    return cb_data;
}

void call_perl_ecore_evas_event(pTHX_ Ecore_Evas *ee,char *event) {
    dSP;
    int n, count;
    UV eeaddr = PTR2IV(ee);
    HV *functions = _get_ecore_evas_event_cb_hash(aTHX_ eeaddr);

    // Object
    SV *s_obj = newSV(0);
    sv_setref_pv(s_obj,"EcoreEvasPtr",ee);

    //Get func
    SV *func = *( hv_fetch(functions,event,strlen(event), FALSE) );

    if (func && SvOK(func)) {
        ENTER;
        SAVETMPS;

        PUSHMARK(SP);

        XPUSHs(sv_2mortal(s_obj));

        PUTBACK;

        count = call_sv(func, G_DISCARD);
        if (count != 0) {
            croak("Expected 0 value got %d\n", count);
        }

        FREETMPS;
        LEAVE;
    }
}

void call_perl_ecore_evas_resize(Ecore_Evas *ee) {
    dTHX;
    char *event = "resize";
    call_perl_ecore_evas_event(aTHX_ ee,event);
}

void call_perl_ecore_evas_move(Ecore_Evas *ee) {
    dTHX;
    char *event = "move";
    call_perl_ecore_evas_event(aTHX_ ee,event);
}

void call_perl_ecore_evas_show(Ecore_Evas *ee) {
    dTHX;
    char *event = "show";
    call_perl_ecore_evas_event(aTHX_ ee,event);
}

void call_perl_ecore_evas_hide(Ecore_Evas *ee) {
    dTHX;
    char *event = "hide";
    call_perl_ecore_evas_event(aTHX_ ee,event);
}

void call_perl_ecore_evas_delete_request(Ecore_Evas *ee) {
    dTHX;
    char *event = "delete_request";
    call_perl_ecore_evas_event(aTHX_ ee,event);
}

void call_perl_ecore_evas_destroy(Ecore_Evas *ee) {
    dTHX;
    char *event = "destroy";
    call_perl_ecore_evas_event(aTHX_ ee,event);
}

void call_perl_ecore_evas_focus_in(Ecore_Evas *ee) {
    dTHX;
    char *event = "focus_in";
    call_perl_ecore_evas_event(aTHX_ ee,event);
}

void call_perl_ecore_evas_focus_out(Ecore_Evas *ee) {
    dTHX;
    char *event = "focus_out";
    call_perl_ecore_evas_event(aTHX_ ee,event);
}

void call_perl_ecore_evas_sticky(Ecore_Evas *ee) {
    dTHX;
    char *event = "sticky";
    call_perl_ecore_evas_event(aTHX_ ee,event);
}

void call_perl_ecore_evas_unsticky(Ecore_Evas *ee) {
    dTHX;
    char *event = "unsticky";
    call_perl_ecore_evas_event(aTHX_ ee,event);
}

void call_perl_ecore_evas_mouse_in(Ecore_Evas *ee) {
    dTHX;
    char *event = "mouse_in";
    call_perl_ecore_evas_event(aTHX_ ee,event);
}

void call_perl_ecore_evas_mouse_out(Ecore_Evas *ee) {
    dTHX;
    char *event = "mouse_out";
    call_perl_ecore_evas_event(aTHX_ ee,event);
}

void call_perl_ecore_evas_pre_render(Ecore_Evas *ee) {
    dTHX;
    char *event = "pre_render";
    call_perl_ecore_evas_event(aTHX_ ee,event);
}

void call_perl_ecore_evas_post_render(Ecore_Evas *ee) {
    dTHX;
    char *event = "post_render";
    call_perl_ecore_evas_event(aTHX_ ee,event);
}

void call_perl_ecore_evas_pre_free(Ecore_Evas *ee) {
    dTHX;
    char *event = "pre_free";
    call_perl_ecore_evas_event(aTHX_ ee,event);
}

void call_perl_ecore_evas_state_change(Ecore_Evas *ee) {
    dTHX;
    char *event = "state_change";
    call_perl_ecore_evas_event(aTHX_ ee,event);
}

// --------------------------------------
// Ecore Event Handler
// --------------------------------------

HV* _get_event_handler_hash(pTHX_ int item_id) {
    AV *Task_Cbs = get_av("pEFL::PLSide::EcoreEventHandler_Cbs", 0);
    if (Task_Cbs == NULL) {
        croak("pEFL::PLSide::EcoreEventHandler_Cbs array does not exist\n");
    }

    SV** Task_Ptr = av_fetch(Task_Cbs, (I32) item_id,FALSE);
    HV *Task = (HV*) (SvRV(*Task_Ptr));
    return Task;
}

Eina_Bool call_perl_ecore_event_handler_cb(void *data, int type, void *event) {
    dTHX;
    dSP;
    int n, count;
    SV *s_bool; Eina_Bool e_bool;

    int item_id = (intptr_t) data;
    HV *Task = _get_event_handler_hash(aTHX_ item_id);

    // Object
    SV *s_data = *( hv_fetch(Task,"data",4, FALSE) );

	// Type
	// TODO: Not needed to save in the EventHandler Hash
	// instead create a new SvIV?
    SV *s_type = *( hv_fetch(Task,"type",4, FALSE) );

	// eventinfo
	SV *s_event  = newSV(0);
    if (event) {
        if (SvTRUE(get_sv("pEFL::Debug",0)))
			fprintf(stderr, "event has an event info\n");
        IV adress;
        adress = PTR2IV(event);
        sv_setiv(s_event,adress);
    }

    //Get func
    SV *func = *( hv_fetch(Task,"function",8, FALSE) );

    if (func && SvOK(func)) {
        ENTER;
        SAVETMPS;

        PUSHMARK(SP);

        XPUSHs(s_data);
        XPUSHs(s_type);
        XPUSHs(s_event);

        PUTBACK;

        count = call_sv(func, G_SCALAR);

        SPAGAIN;

        if (count != 1) {
            croak("Expected 1 value got %d\n", count);
        }

        s_bool = POPs;

        if (SvTRUE(s_bool) ) {
        e_bool = EINA_TRUE;
    }
    else {
        e_bool = EINA_FALSE;
    }

    PUTBACK;
    FREETMPS;
    LEAVE;

    return e_bool;
    }
}

// --------------------------------------
// Ecore Task Cbs
// -------------------------------------

HV* _get_task_hash(pTHX_ int item_id) {
    AV *Task_Cbs = get_av("pEFL::PLSide::EcoreTask_Cbs", 0);
    if (Task_Cbs == NULL) {
        croak("pEFL::PLSide::EcoreTask_Cbs array does not exist\n");
    }

    SV** Task_Ptr = av_fetch(Task_Cbs, (I32) item_id,FALSE);
    HV *Task = (HV*) (SvRV(*Task_Ptr));
    return Task;
}

Eina_Bool call_perl_task_cb(void *data) {
    dTHX;
    dSP;
    int n, count;
    SV *s_bool; Eina_Bool e_bool;

    int item_id = (intptr_t) data;
    HV *Task = _get_task_hash(aTHX_ item_id);

    // Object
    SV *s_data = *( hv_fetch(Task,"data",4, FALSE) );

    //Get func
    SV *func = *( hv_fetch(Task,"function",8, FALSE) );

    if (func && SvOK(func)) {
        ENTER;
        SAVETMPS;

        PUSHMARK(SP);

        XPUSHs(s_data);

        PUTBACK;

        count = call_sv(func, G_SCALAR);

        SPAGAIN;

        if (count != 1) {
            croak("Expected 1 value got %d\n", count);
        }

        s_bool = POPs;

        if (SvTRUE(s_bool) ) {
        e_bool = EINA_TRUE;
    }
    else {
        e_bool = EINA_FALSE;
    }

    PUTBACK;
    FREETMPS;
    LEAVE;

    return e_bool;
    }
}
