#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);
}


void call_perl_edje_message_handler(void *data, Evas_Object *obj, int type, int id,void *msg) {
	dTHX;
	dSP;

	int n;
	int count;
	SV *s_data, *s_obj, *s_type, *s_id, *s_msg;
	char messageClass[26];
	
	_perl_callback *perl_saved_cb = data;
	
	// Get data
	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) );
	
	// Data
	s_data = *( hv_fetch(cb_data, "data",4,FALSE) ) ;
		
	// Object
	s_obj = newSV(0);
	sv_setref_pv(s_obj, SvPV_nolen(pclass), obj);
	
	// Type
	s_type = newSViv(type);
	
	//ID
	s_id = newSViv(id);
	
	// msg
	s_msg  = newSV(0);
	if (type == EDJE_MESSAGE_STRING) {
		strcpy(messageClass,"EdjeMessageStringPtr");
	}
	else if (type == EDJE_MESSAGE_INT) {
		strcpy(messageClass,"EdjeMessageStringIntPtr");
	}
	else if (type == EDJE_MESSAGE_FLOAT) {
		strcpy(messageClass,"EdjeMessageFloatPtr");
	}
	else if (type == EDJE_MESSAGE_STRING_SET) {
		strcpy(messageClass,"EdjeMessageStringSetPtr");
	}
	else if (type == EDJE_MESSAGE_INT_SET) {
		strcpy(messageClass,"EdjeMessageIntSetPtr");
	}
	else if (type == EDJE_MESSAGE_FLOAT_SET) {
		strcpy(messageClass,"EdjeMessageFloatSetPtr");
	}
	else if (type == EDJE_MESSAGE_STRING_INT) {
		strcpy(messageClass,"EdjeMessageStringIntPtr");
	}
	else if (type == EDJE_MESSAGE_STRING_FLOAT) {
		strcpy(messageClass,"EdjeMessageStringFloatPtr");
	}
	else {
		croak("Not supported message type\n");
	}
	sv_setref_pv(s_msg, messageClass, msg);
	

	ENTER;
	SAVETMPS;

	PUSHMARK(SP);

	XPUSHs(s_data);
	XPUSHs(sv_2mortal(s_obj));
	XPUSHs(sv_2mortal(s_type));
	XPUSHs(sv_2mortal(s_id));
	XPUSHs(sv_2mortal(s_msg));


	PUTBACK;

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

	FREETMPS;
	LEAVE;

	/* TODO free 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, "ElmEntryPtr", 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;
	}
}